4位ALU VHDL代码

时间:2014-05-30 09:05:25

标签: vhdl

我正在为4位ALU编写代码,当我想为左移操作编写时我遇到了问题。我有两个输入(operandA和operandB)。我想将操作数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;

3 个答案:

答案 0 :(得分:2)

对于左移两次,语法应如下:

A&lt; = A sll 2; - 左移逻辑2位

我不太明白为什么需要以十进制转换操作数B.它可以用作二进制或十进制值,也可以用作与保存基数无关的任何时间点的十六进制值。

答案 1 :(得分:0)

在VHDL-2008之前,运营商sll可能无法始终按预期工作(详情请参阅 here), 所以请考虑使用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), 分配一个小于大小的位。

代码的假设是使用ieee.numeric_std

答案 2 :(得分:0)

你被催促使用sll之类的原因是因为一般情况 综合工具不支持具有非静态边界的循环语句 (loop_nr)。循环展开,需要静态值来确定方式 展开了许多循环迭代(要生成多少硬件)。

正如Morten指出你的代码没有分析,与你断言相反 它编译。<​​/ p>

在代码的开头插入以下四行后,我们看到了 第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);

意图在案例陈述中,选择“010”(srl相当难 编码到1的距离,大概是为了匹配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;

给我们一个示范:

alu_tb png

首先要说的是Result_High获得了一些'U'。这是 由tempHigh未初始化或分配引起的。

接下来要注意的是移位结果是错误的(Result_Low 和Result_High)。我希望你在Result_High和“0000”中想要一个“0011” Result_Low。

你看到正好一个左移的结果 - ('U','U','U','1') Result_Low中的Result_High和“1000”。

这是由在delta周期中执行循环语句引起的(没有干预 模拟时间段。在进程语句中只有一个驱动程序 每个信号。这样的净效果是只有一个未来值 对于当前模拟时间和最后分配的值将是 在电流的预计输出波形中安排的那个 模拟时间。 (基本上,循环语句中的赋值为 信号发生一次,因为连续值取决于赋值 发生它似乎只有一个任务。)

有两种方法可以解决此问题。首先是使用变量 在循环内部分配并将相应的信号分配给 循环语句后面的变量。如前所述,循环绑定不是 静态,你不能合成循环。

第二种方法是通过执行移位分配来消除循环 顺序。基本上每个时钟1个移位,在最后一个信号之后发出信号 发生转变。

还可以通过使用a来支持循环的静态边界问题 case语句(或在VHDL 2008中使用顺序条件信号 你的综合应该分配顺序选择的信号分配 工具供应商支持他们)。这具有在一个时钟内操作的优点。

注意所有这些都需要保持整数变量 to_integer(无符号(运算数))。

当您的综合工具供应商支持时,所有这一切都可以进行 sll(和另一种情况下的srl)或来自包的SHIFT_LEFT和SHIFT_RIGHT numeric_std,您可以使用它们。

不使用sll或SHIFT_LEFT的通用(pre 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 test bench with case statement

正确答案(所有不使用信号loop_nr)。

请注意,case语句中的所有选项都不包含在simple中 试验台。

当然,与大多数事情一样,有两种以上的方法可以达到预期效果 结果

对于Result_High和,您可以使用连续的2到1个多路复用器 Result_Low,每个阶段从前一阶段的输出(或 OperandA作为第一阶段)作为A输入,选择是合适的 来自OperandB的'bit',以及前一阶段的多路复用器的B输入 输出逻辑上移1('0'填充)。

多路复用器可以是函数,组件或过程语句。通过 使用三对一多路复用器,您可以实现对称移位 操作指定的操作(左和右)。如果你想包括签名班次, 而不是'0'填充右移,您可以填充符号位值。 ...

对于有效的情况,您还应该为Ready&lt; ='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 with loop variables

为了证明结果的两半都工作,OperandA的默认值已经改为“0110”:

alu_tb with loop variables OperandA is "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);