如何在VHDL中表示顺序算法

时间:2013-02-02 21:20:54

标签: vhdl

我来自软件领域,并试图找出如何在VHDL中编码顺序算法。从教科书中可以看出,进程内的语句是按顺序执行的。但我意识到只有变量而不是信号才是真的。在进程内重新发出信号,它们在进程结束时得到更新,并且评估使用右操作数的先前值。所以对于我的理解,它仍然是并发的。出于性能目的,我不能总是使用变量进行复杂计算。

  1. 但是如何使用信号来呈现顺序算法呢?我的第一个 想法正在使用FSM。真的吗? FSM是唯一的途径吗? 在VHDL中正确编码顺序算法?
  2. 如果我认为某个过程中的信号语句是正确的 并发,那么这和信号之间有什么区别 架构级别的并发分配?这个过程是不是 顺序性只适用于变量赋值?

2 个答案:

答案 0 :(得分:3)

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

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

在VHDL中有三种主要的顺序执行方式,目的不同。

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

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

(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;

(2)是Martin Thompson联系的“隐式状态机”(优秀文章!) 与显式状态机相同的备注适用于它。

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;

(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;

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

答案 1 :(得分:1)

  1. 大多数多周期算法可以按照您的建议使用FSM,也可以使用流水线逻辑实现。如果算法由严格的顺序步骤(即无循环)组成,则流水线逻辑可能是更好的选择,FSM通常仅用于需要根据输入的不同控制流的更复杂的算法。

    流水线逻辑实际上是一个很长的组合逻辑链,它使用寄存器分成多个“阶段”,数据从一个阶段流向另一个阶段。添加寄存器是为了减少每个级(两个寄存器之间)的延迟,允许更高的时钟频率,但代价是延迟增加。但请注意,较高的延迟并不意味着较低的吞吐量,因为新数据可以在前一个数据项完成之前开始处理!对于FSM,这通常是不可能的。

  2. 过程中信号分配与架构相反的最大区别在于,您可以在过程中的多个位置为信号分配值,最后一个分配“获胜”。在架构级别,只有一个信号的赋值语句是可能的。许多控制流语句(if,case / when等)也只能在流程中使用,而不能在架构级别使用。