2014-02-09 44 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)之後移動這三個作業,您的變量版本就可以工作。

驗證了這兩種方法生成相同的硬件後,選擇您認爲導致設計最清晰(最容易理解)的方法。