2014-02-09 45 views
2

在关于可配置嵌入式系统(在ZYNQ-7010上)的大学课程中,我们最近实现了一个(初始)低通图像滤波器,该滤波器将应用一维高斯核(0.25 * [1 2 1])到来自Block RAM的数据。VHDL - 队列中的变量与信号行为

我们决定缓存(即队列)三个像素,然后在数据输出过程中在线操作它们。我们的第一种方法是具有三个过程变量,并使它们在时间方面翻转过来;其中,以下是全部过程:

process (clk25) 
    -- queue 
    variable pixelMinus2 : std_logic_vector(11 downto 0) := (others => '0'); 
    variable pixelMinus1 : std_logic_vector(11 downto 0) := (others => '0'); 
    variable pixelCurrent : std_logic_vector(11 downto 0) := (others => '0'); 

    -- temporaries 
    variable r : unsigned(3 downto 0); 
    variable g : unsigned(3 downto 0); 
    variable b : unsigned(3 downto 0); 
begin 
    if clk25'event and clk25 = '1' then 
     pixelMinus2 := pixelMinus1; 
     pixelMinus1 := pixelCurrent; 
     pixelCurrent := RAM(to_integer(UNSIGNED(addrb))); 

     IF slv_reg0(3) = '0' THEN 
      -- bypass filter for debugging 
      dob <= pixelCurrent; 
     ELSE 
      -- colors are 4 bit each in a 12 bit vector 
      -- division by 4 is done by right shifting by 2 
      r := (
          ("00" & unsigned(pixelMinus2(11 downto 10))) 
         + ("00" & unsigned(pixelMinus1(11 downto 10))) 
         + ("00" & unsigned(pixelMinus1(11 downto 10))) 
         + ("00" & unsigned(pixelCurrent(11 downto 10))) 
        ); 

      g := (
          ("00" & unsigned(pixelMinus2(7 downto 6))) 
         + ("00" & unsigned(pixelMinus1(7 downto 6))) 
         + ("00" & unsigned(pixelMinus1(7 downto 6))) 
         + ("00" & unsigned(pixelCurrent(7 downto 6))) 
        ); 

      b := (
          ("00" & unsigned(pixelMinus2(3 downto 2))) 
         + ("00" & unsigned(pixelMinus1(3 downto 2))) 
         + ("00" & unsigned(pixelMinus1(3 downto 2))) 
         + ("00" & unsigned(pixelCurrent(3 downto 2))) 
        ); 

      dob <= std_logic_vector(r) & std_logic_vector(g) & std_logic_vector(b); 
     END IF; 
    end if; 
end process; 

然而,事实证明这是一个可怕的错误;合成会花费很长时间,估计LUT的使用量约为设备能力的130%。

我们后来改的实施,使用信号,而不是变量解决了所有的问题;硬件按预期运行,LUT使用率下降到百分之几。

我的问题是什么导致这里的问题,当使用变量,从我们的理解,它应该像那样工作。

回答

2

当一个变量在所述方法中使用的pixelCurrent,则该值是 立即更新和可用的,其中信号的值是没有准备好 直到下一个周期。

所以当一个变量是使用时,这条线实现具有异步 读取基于addrb一个RAM:

pixelCurrent := RAM(to_integer(UNSIGNED(addrb))); 

凡分配给一个信号将实现具有同步读,其中 值读出的RAM从RAM直到下一个周期才可用。

典型的FPGA技术有专用硬件用于与同步 读取的RAM,但具有异步的RAM与组合逻辑(查找表 /LUT)制成。

因此,使用一个变量 pixelCurrent时出现的LUT量庞大是因为合成工具试图映射与 异步RAM中读入的LUT,这通常需要一个庞大的LUT 的量,并且使结果RAM非常慢。

在流水线设计,它听起来就像异步RAM读出的不 必需的,所以如果pixelCurrent是一个信号,一个同步RAM来代替 和综合工具将映射RAM中的内部RAM的硬件块,与 代码等:

pixelMinus2 := pixelMinus1; 
pixelMinus1 := pixelCurrent; 
pixelCurrent <= RAM(to_integer(UNSIGNED(addrb))); 
3

信号,作为进程间通信的装置,有分配语义精心设计,以避免竞态条件和危害。血腥的细节见this Q&Athis link to "VHDL's crown jewel"

因此,当分配pixelCurrent(信号)

pixelCurrent <= RAM(to_integer(UNSIGNED(addrb))); 

分配不会发生直到过程暂停(其为RTL代码一般是当处理退出,并在灵敏度列表),并且将结果在此过程中不可用,直到它在if rising_edge(clk25)醒来。这就创建了一个流水线寄存器。

VHDL过程中的变量像任何其他命令式语言(C等)中的过程中的变量一样行为 - 一旦更新,它们的新值立即可用。

因此,以下:

pixelCurrent := RAM(to_integer(UNSIGNED(addrb))); 

IF slv_reg0(3) = '0' THEN 
    -- bypass filter for debugging 
    dob <= pixelCurrent; 

传播pixelCurrent的新值写入过程的其余部分,产生它试图完成单个时钟周期内的一切一个巨大的设计。

有两种解决方案:我的首选是使用流水线寄存器的信号,因为您可以以最自然的方式(第一阶段第一阶段)使用describe the pipeline

第二个解决方案,使用变量作为流水线寄存器 - 具有讽刺意味你已经部分地采用这种解决方案 -

pixelMinus2 := pixelMinus1; 
pixelMinus1 := pixelCurrent; 
pixelCurrent := RAM(to_integer(UNSIGNED(addrb))); 

是向后描述管道,使分配给一个变量来最后一次使用它的值之后, 。

只需在大IF slv_reg0(3)之后移动这三个作业,您的变量版本就可以工作。

验证了这两种方法生成相同的硬件后,选择您认为导致设计最清晰(最容易理解)的方法。