簡體   English   中英

4位ALU VHDL代碼

[英]4bit ALU VHDL code

我正在為4位ALU編寫代碼,當我想編寫左移操作時遇到問題。 我有兩個輸入(操作數A和操作數B)。 我想將操作數B轉換為十進制(例如,將“ 0010”轉換為“ 2”),然后將操作數A左移2次。 我的代碼已編譯,但我不確定它是否正確。 先感謝您。

entity ALU is
  port(
    reset_n     : in  std_logic;
    clk         : in  std_logic;
    OperandA    : in  std_logic_vector(3 downto 0);
    OperandB    : in  std_logic_vector(3 downto 0);
    Operation   : in  std_logic_vector(2 downto 0);
    Start       : in  std_logic;
    Result_Low  : out std_logic_vector(3 downto 0);
    Result_High : out std_logic_vector(3 downto 0);
    Ready       : out std_logic;
    Errorsig    : out std_logic);
end ALU;

architecture behavior of ALU is
  signal loop_nr : integer range 0 to 15;
begin
  process (reset_n, clk, operation)
    variable tempHigh : std_logic_vector(4 downto 0);
  begin
    if (reset_n = '0') then
      Result_Low  <= (others => '0');
      Result_High <= (others => '0');
      Errorsig    <= '0';
    elsif (clk'event and clk = '1') then
      case operation is
        when "001" =>
          for i in 0 to loop_nr loop
            loop_nr     <= to_integer(unsigned(OperandB));
            Result_Low  <= OperandA(2 downto 0)&'0';
            Result_High <= tempHigh(2 downto 0) & OperandA(3);
          end loop;
          Ready    <= '1';
          Errorsig <= '0';
        when "010" =>
          Result_Low  <= OperandB(0)& OperandA(3 downto 1);
          Result_High <= OperandB(3 downto 1);
          Ready       <= '1';
        when others =>
          Result_Low <= (others => '0');
          ready      <= '0';
          Errorsig   <= '0';
      end case;
    end if;
  end process;
end behavior;

要向左移動兩次,語法應如下所示:

A <= A sll 2; -左移邏輯2位

我不太明白為什么需要將操作數B轉換為十進制。 可以將其用作二進制或十進制值,或與此有關的十六進制值,與保存該基准的時間無關。

VHDL-2008之前,操作符sll可能不會一直按預期工作(請在此處閱讀更多信息 ),因此請考慮改用ieee.numeric_std函數進行移位,例如:

y <= std_logic_vector(shift_left(unsigned(OperandA), to_integer(unsigned(OperandB))));

還要注意,在端口Result_High聲明為std_logic_vector(3 downto 0) ,但在第41行中將Result_High <= OperandB(3 downto 1)Result_High <= OperandB(3 downto 1) ,而賦值的大小要比大小小一位。

假設代碼是使用ieee.numeric_std的。

敦促您使用sll之類的原因是因為在一般情況下,綜合工具不支持帶有非靜態邊界(loop_nr)的循環語句。 展開的循環需要靜態值來確定展開的循環迭代次數(生成多少硬件)。

正如Morten所指出的那樣,您的代碼沒有分析,這與您斷言它可以編譯相反。

在代碼開頭插入以下四行之后,我們在第41行看到錯誤:

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
--(blank, a spacer that doesn't show up in the code highlighter)

ghdl -a ALU.vhdl ALU.vhdl:41:26: length of value does not match length of target ghdl: compilation error

看起來像

      Result_High <= '0' & OperandB(3 downto 1);

是在case語句中選擇“ 010”(硬編碼為1的srl等效項,大概是為了匹配sll等效項的正確行為)。 之后,您的設計說明將進行分析。

此外,還有其他算法描述錯誤未反映在VHDL語法或語義錯誤中。

編寫一個簡單的測試平台:

library ieee;
use ieee.std_logic_1164.all;

entity alu_tb is
end entity;

architecture foo of alu_tb is

    signal reset_n:        std_logic := '0';
    signal clk:            std_logic := '0';
    signal OperandA:       std_logic_vector(3 downto 0) :="1100"; -- X"C"
    signal OperandB:       std_logic_vector(3 downto 0) :="0010"; -- 2
    signal Operation:      std_logic_vector(2 downto 0):= "001"; -- shft right
    signal Start:          std_logic;  -- Not currently used
    signal Result_Low:     std_logic_vector(3 downto 0);
    signal Result_High:    std_logic_vector(3 downto 0);
    signal Ready:          std_logic;
    signal Errorsig:       std_logic;

begin

DUT:
entity work.ALU
    port map (
        reset_n => reset_n,
        clk => clk,
        OperandA => OperandA,
        OperandB => OperandB,
        Operation => Operation,
        Start => Start,
        Result_Low => Result_Low,
        Result_High => Result_High,
        Ready => Ready,
        Errorsig => Errorsig
    );

CLOCK:
    process
    begin
        wait for 10 ns;
        clk <= not clk;
        if Now > 100 ns then
            wait;
        end if;
    end process;

STIMULUS:
    process
    begin
        wait for 20 ns;
        reset_n <= '1';
        wait;
    end process;

end architecture;

給我們示范:

png png

首先要注意的是Result_High得到一些“ U”。 這是由於未初始化或分配tempHigh引起的。

接下來要注意的是移位結果是錯誤的(Result_Low和Result_High)。 我希望您希望在Result_High中輸入“ 0011”,在Result_Low中輸入“ 0000”。

您將看到僅左移的結果-Result_High中的('U','U','U','1')和Result_Low中的“ 1000”。

這是由於以增量周期執行循環語句(沒有介入模擬時間流逝)引起的。 在過程語句中,每個信號只有一個驅動程序。 最終結果是,當前模擬時間只有一個將來值,而最后分配的值將是當前模擬時間在投影輸出波形中計划的那個值。 (本質上,循環語句中對信號的分配只發生一次,並且由於連續值取決於發生的分配,因此看起來只有一個分配)。

有兩種方法可以解決此問題。 第一種是使用在循環內部分配的變量,並在循環語句之后將相應的信號分配給變量。 如前所述,循環綁定不是靜態的,您無法合成循環。

第二種方法是通過順序執行換檔分配來消除循環。 每個時鍾基本上1個移位,表示最后一個移位發生后為就緒。

還可以通過使用case語句(或在VHDL 2008中使用順序條件信號分配或順序選擇的信號分配,如果您的綜合工具供應商支持的話)來規避循環的靜態邊界問題。 這具有在一個時鍾中運行的優點。

請注意,所有這些都需要具有一個整數變量,該變量包含to_integer(unsigned(OperandB))。

當您的綜合工具供應商支持sll(對於另一種情況為srl)或軟件包numeric_std中的SHIFT_LEFT和SHIFT_RIGHT,並且您可以使用它們時,所有這些都可以避免。

不使用sll或SHIFT_LEFT的通用(VHDL 2008之前)修補程序可能是:

begin
    process (reset_n, clk, operation)
      variable tempHigh : std_logic_vector(4 downto 0);
      variable loop_int: integer range 0 to 15;
    begin
      if (reset_n = '0') then
        Result_Low  <= (others => '0');
        Result_High <= (others => '0');
        Errorsig    <= '0';
      elsif (clk'event and clk = '1') then
        case operation is
          when "001" =>
              loop_int := to_integer(unsigned(OperandB));
              case loop_int is
                  when 0 =>
                      Result_Low <= OperandA;
                      Result_High <= (others => '0');
                  when 1 =>
                      Result_Low <= OperandA(2 downto 0) & '0';
                      Result_High <= "000" & OperandA(3);                  
                  when 2 =>
                      Result_Low <= OperandA(1 downto 0) & "00";
                      Result_High <= "00" & OperandA(3 downto 2);
                  when 3 =>
                      Result_Low <= OperandA(0) & "000";
                      Result_High <= "0" & OperandA(3 downto 1);
                  when 4 =>
                      Result_Low <= (others => '0');
                      Result_High <= OperandA(3 downto 0);
                  when 5 =>
                      Result_Low <= (others => '0');
                      Result_High <= OperandA(2 downto 0) & '0';
                  when 6 =>
                      Result_Low <= (others => '0');
                      Result_High <= OperandA(1 downto 0) & "00";
                  when 7 =>
                      Result_Low <= (others => '0');
                      Result_High <= OperandA(0) & "000";
                  when others => 
                      Result_Low <= (others => '0');
                      Result_High <= (others => '0');
              end case;

            -- for i in 0 to loop_nr loop
            --   loop_nr     <= to_integer(unsigned(OperandB));
            --   Result_Low  <= OperandA(2 downto 0)&'0';
            --   Result_High <= tempHigh(2 downto 0) & OperandA(3);
            -- end loop;

            Ready    <= '1';
            Errorsig <= '0';

這使:

帶案例陳述的alu測試台

正確答案(全部不使用信號loop_nr)。

注意,case語句中的所有選擇都沒有包含在簡單的測試平台中。

當然,像大多數事情一樣,有兩種以上的方法可以獲得期望的結果。

您可以對Result_High和Result_Low使用連續的2到1多路復用器,每個級均從上一級的輸出(或第一級的OperandA)作為A輸入,選擇是OperandB的適當“位”,並且B輸入到多路復用器,前一級輸出邏輯上移位1(填充為“ 0”)。

多路復用器可以是函數,組件或過程語句。 通過使用三對一多路復用器,可以實現對稱移位操作指定的兩種操作(向左和向右)。 如果要包括帶符號的移位,可以用符號位值代替“ 0”填充的右移位。 ...

對於那些可以分派有效的連續“操作”值的情況,您還應該分配“就緒<='0”。

並且由於您對答案之一的評論要求使用具有整數值的循環:

   process (reset_n, clk, operation)
      variable tempHigh : std_logic_vector(4 downto 0);
      variable tempLow:     std_logic_vector(3 downto 0); --added
      variable loop_int: integer range 0 to 15;   --added
    begin
      if (reset_n = '0') then
        Result_Low  <= (others => '0');
        Result_High <= (others => '0');
        Errorsig    <= '0';
      elsif (clk'event and clk = '1') then
        case operation is
          when "001" =>
              tempLow := OperandA; --added
              tempHigh := (others => '0'); --added 
              loop_int := to_integer(unsigned(OperandB)); --added

            -- for i in 0 to loop_nr loop
            --   loop_nr     <= to_integer(unsigned(OperandB));
            --   Result_Low  <= OperandA(2 downto 0)&'0';
            --   Result_High <= tempHigh(2 downto 0) & OperandA(3);
            -- end loop;

-- More added:
        if loop_int /= 0 then
            for i in 1 to loop_int loop
                tempHigh (3 downto 0) := tempHigh (2 downto 0) & tempLow(3);
                -- 'read' tempLow(3) before it's updated
                tempLow := tempLow(2 downto 0) & '0';
            end loop;
            Result_Low <= tempLow;
            Result_High <= tempHigh(3 downto 0);
        else 
            Result_Low <= OperandA;
            Result_High <= (others => '0');
        end if;
        Ready    <= '1';
        Errorsig <= '0';

這使: 帶循環變量的alu_tb

為了說明兩半的Result都在工作,OperandA的默認值已更改為“ 0110”:

帶有循環變量OperandA的alu_tb為“ 0110”

還要注意,循環從1而不是0開始,以防止發生額外的移位,並且檢查非零loop_int以防止for循環至少執行一次。

在這些情況下是否可以進行可綜合循環?

是。

循環必須處理所有可能的移位(loop_int的范圍),並測試i是否低於移位閾值:

  process (reset_n, clk, operation)
    variable tempHigh : std_logic_vector(4 downto 0);
    variable tempLow:     std_logic_vector(3 downto 0); --added
    subtype loop_range is integer range 0 to 15;
    variable loop_int: integer range 0 to 15;   --added
  begin
    if (reset_n = '0') then
      Result_Low  <= (others => '0');
      Result_High <= (others => '0');
      Errorsig    <= '0';
    elsif (clk'event and clk = '1') then
     case operation is
        when "001" =>
            tempLow := OperandA; --added
            tempHigh := (others => '0'); --added 
            loop_int := to_integer(unsigned(OperandB)); --added
        for i in loop_range loop
            if i < loop_int then
                tempHigh (3 downto 0) := tempHigh (2 downto 0) & tempLow(3);
                -- 'read' tempLow(3) before it's updated
                tempLow := tempLow(2 downto 0) & '0'; 
            end if;
        end loop;
            Result_Low <= tempLow;
            Result_High <= tempHigh(3 downto 0);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM