如何用VHDL表示顺序算法

1 vhdl

我来自软件领域,试图找出如何用 VHDL 编写顺序算法。从课本上看,进程内的语句是顺序执行的。但我意识到,只有当涉及到变量而不是信号时,这才是正确的。进程内的重新信号,它们在进程结束时更新,并且评估使用右操作数的先前值。所以根据我的理解,它仍然是并发的。出于性能目的,我不能总是使用变量进行复杂计算。

  1. 但如何使用信号来呈现时序算法呢?我最初的想法是使用 FSM。真的吗?FSM 是在 VHDL 中正确编码顺序算法的唯一方法吗?
  2. 如果我是对的,进程中的信号语句是并发的,那么这与架构级别的信号并发分配有什么区别?进程的顺序性质是否仅适用于变量赋值?

小智 5

当您尝试在不同的周期中执行算法的步骤时,您已经意识到流程中的“顺序”构造本身并不能做到这一点 - 事实上,变量没有帮助。顺序程序 - 除非它使用显式的“等待 some_event”,例如等待上升沿(clk) - 将在单个时钟周期内展开并执行。

正如您可能已经发现的使用变量一样,这可能是一个相当长的时钟周期。

VHDL 中有三种顺序执行的主要方法,其目的各不相同。

让我们尝试在 a 和 b 之间实现线性插值,

a, b, c, x : unsigned(15 downto 0);
x <= ((a * (65536 - c)) + (b * c)) / 65536;
Run Code Online (Sandbox Code Playgroud)

(1) 是经典状态机;最好的形式是单进程SM。这里,计算被分解为几个周期,确保一次最多进行一个乘法(乘法器很昂贵!),但 C1 是并行计算的(加法/减法很便宜!)。它可以安全地用变量而不是信号来重写中间结果。

type state_type is (idle, step_1, step_2, done);
signal state     : state_type := idle;
signal start     : boolean := false;
signal c1        : unsigned(16 downto 0); -- range includes 65536!
signal p0, p1, s : unsigned(31 downto 0);

process(clk) is
begin
   if rising_edge(clk) then
      case state is
      when idle   => if start then
                        p1    <= b * c;
                        c1    <= 65536 - c;
                        state <= step_1;
                     end if;
      when step_1 => P0 <= a * c1;
                     state <= step_2;
      when step_2 => s <= p0 + p1;
                     state <= done;
      when done   => x <= s(31 downto 16);
                     if not start then  -- avoid retriggering
                        state <= idle;  
                     end if;
      end case;
   end if;
end process;
Run Code Online (Sandbox Code Playgroud)

(2) 是Martin Thompson 链接的“隐式状态机”(优秀的文章!)...编辑以添加链接,因为 Martin 的答案消失了。与显式状态机相同的注释也适用于它。

process(clk) is
begin
   if start then
      p1 <= b * c;
      c1 <= 65536 - c;
      wait for rising_edge(clk);
      p0 <= a * c1;
      wait for rising_edge(clk);
      s  <= p0 + p1;
      wait for rising_edge(clk);
      x  <= s(31 downto 16);
      while start loop
         wait for rising_edge(clk);
      end loop;
   end if;
end process;
Run Code Online (Sandbox Code Playgroud)

(3)是流水线处理器。在这里,执行需要几个周期,但一切都是并行发生的!管道的深度(以周期为单位)允许每个逻辑顺序步骤以顺序方式发生。当长计算链被分解为周期大小的步骤时,这可以实现高性能......

    signal start     : boolean := false;
    signal c1        : unsigned(16 downto 0); -- range includes 65536!
    signal pa, pb, pb2, s : unsigned(31 downto 0);
    signal a1        : unsigned(15 downto 0);

process(clk) is
begin
   if rising_edge(clk) then
      -- first cycle
      pb <= b * c;
      c1 <= 65536 - c;
      a1 <= a;     -- save copy of a for next cycle
      -- second cycle
      pa <= a1 * c1;  -- NB this is the LAST cycle copy of c1 not the new one!
      pb2 <= pb;   -- save copy of product b
      -- third cycle
      s  <= pa + pb2;
      -- fourth cycle
      x  <= s(31 downto 16);
   end if;
end process;
Run Code Online (Sandbox Code Playgroud)

在这里,资源不是共享的;它将使用 2 个乘法器,因为每个时钟周期有 2 个乘法器。它还将使用更多的寄存器来存储中间结果和副本。然而,在每个周期中给定 a、b、c 的新值,它将在每个周期输出一个新结果 - 从输入延迟四个周期。