【问题标题】:4bit ALU VHDL code4bit ALU VHDL 代码
【发布时间】:2015-05-02 03:47:08
【问题描述】:

我正在为 4 位 ALU 编写代码,但在编写左移操作时遇到了问题。我有两个输入(操作数A 和操作数B)。我想将operandB 转换为十进制(例如“0010”转换为“2”),然后将operandA 向左移动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;

【问题讨论】:

    标签: vhdl


    【解决方案1】:

    左移两次的语法应该如下:

    A

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

    【讨论】:

    • 谢谢,但我不允许使用 SHL 或 sll2 之类的函数,我必须使用循环并将第一个输入移动第二个输入步长(十进制)。我的输出有 8 位,输入每个都是 4 位。例如,如果输出 B 为 (0101),则第一个输出移位第 5 个,剩下的是 A 的移位版本和第二个输出中超出的位(第 5 位)。
    【解决方案2】:

    运算符sll 在 VHDL-2008 之前可能并不总是按预期工作(阅读更多 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 &lt;= OperandB(3 downto 1), assign 比 size 小一位。

    假设代码是使用ieee.numeric_std

    【讨论】:

      【解决方案3】:

      您被敦促使用 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”(一个srl等价的hard 编码到 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;
      

      给我们做个示范:

      首先突出的是 Result_High 有一些“U”。这是 由于 tempHigh 未初始化或未分配。

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

      您会看到恰好一次左移的结果 - ('U','U','U','1') Result_High 和 Result_Low 中的“1000”。

      这是由于在增量循环中执行循环语句(没有干预 模拟时间流逝)。在流程声明中只有一个驱动程序 每个信号。其最终结果是只有一个未来价值 对于当前模拟时间,最后分配的值将是 在当前的投影输出波形中安排的一个 模拟时间。 (本质上,循环语句中的赋值 信号出现一次,因为连续的值取决于分配 发生它看起来只有一个任务)。

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

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

      还可以通过使用 case 语句(或在 VHDL 2008 中使用顺序条件信号 如果您的合成应分配顺序选择的信号分配 工具供应商支持它们)。这具有在一个时钟内运行的优势。

      请注意,所有这些都需要持有一个整数变量 to_integer(unsigned(OperandB)).

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

      不使用 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';
      

      这给出了:

      正确答案(全部不使用信号 loop_nr)。

      请注意,case 语句中的所有选项都没有包含在简单的 测试台。

      当然,与大多数事情一样,获得所需的方法不止两种 结果。

      您可以为 Result_High 和 Result_Low,每个阶段从前一阶段的输出馈送(或 第一阶段的操作数A)作为A输入,选择是适当的 来自 OperandB 的“位”,以及前一级多路复用器的 B 输入 输出逻辑移位 1(填充“0”)。

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

      您还应该为那些有效的情况分配 Ready

      而且因为您对其中一个答案的评论需要使用具有整数值的循环:

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

      这给出了:

      为了证明 Result 的两半都在工作,OperandA 的默认值已更改为“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);
      

      【讨论】:

      • 非常感谢。我以这种方式更正了我的代码:
      • 你很幸运,我也没有让你涉足顺序实现(每时钟移位)。如果您实施了 Start 就会有。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-09-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多