简体   繁体   中英

4bit ALU VHDL code

I am writing a code for a 4 bit ALU and I have a problem when I want to write for shift left operation. I have two inputs (operandA and operandB ). I want to convert the operandB into decimal (for example "0010" into '2') and then shift operandA 2 times to the left. my code is compiled but I am not sure that it is true. Thank you in advance.

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;

For shifting left twice the syntax should be the following:

A <= A sll 2; -- left shift logical 2 bits

I don't quite understand why is it required to convert operand B in decimal. It can be used as a binary or decimal value or for that matter hexadecimal value at any point of time irrelevant of the base it was saved in.

The operator sll may not always work as expected before VHDL-2008 (read more here ), so consider instead using functions from ieee.numeric_std for shifting, like:

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

Note also that Result_High is declared in port as std_logic_vector(3 downto 0) , but is assigned in line 41 as Result_High <= OperandB(3 downto 1) , with assign having one bit less than size.

Assumption for code is that ieee.numeric_std is used.

The reason you've been urged to use the likes of sll is because in general synthesis tools don't support loop statements with non-static bounds (loop_nr). Loops are unfolded which requires a static value to determine how many loop iterations are unfolded (how much hardware to generate).

As Morten points out your code doesn't analyze, contrary to you assertion that it compiles.

After inserting the following four lines at the beginning of your code we see an error at line 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

Which looks like

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

was intended in the case statement, choice "010" (an srl equivalent hard coded to a distance of 1, presumably to match the correct behavior of the sll equivalent). After which your design description analyzes.

Further there are other algorithm description errors not reflected in VHDL syntax or semantic errors.

Writing a simple test bench:

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;

Gives us a demonstration:

png png

The first thing that sticks out is that Result_High gets some 'U's. This is caused by tempHigh not being initialized or assigned.

The next thing to notice is that the shift result is wrong (both Result_Low and Result_High). I'd expect you'd want a "0011" in Result_High and "0000" in Result_Low.

You see the result of exactly one left shift - ('U','U','U','1') in Result_High and "1000" in Result_Low.

This is caused by executing a loop statement in delta cycles (no intervening simulation time passage). In a process statement there is only one driver for each signal. The net effect of that is that there is only one future value for the current simulation time and the last value assigned is going to be the one that is scheduled in the projected output waveform for the current simulation time. (Essentially, the assignment in the loop statement to a signal occurs once, and because successive values depend on assignment occurring it looks like there was only one assignment).

There are two ways to fix this behavior. The first is to use variables assigned inside the loop and assign the corresponding signals to the variables following the loop statement. As noted before the loop bound isn't static and you can't synthesis the loop.

The second way is to eliminate the loop by executing the shift assignments sequentially. Essentially 1 shift per clock, signaling Ready after the last shift occurs.

There's also away to side step the static bounds issue for loops by using a case statement (or in VHDL 2008 using a sequential conditional signal assignment of sequential selected signal assignment should your synthesis tool vendor support them). This has the advantage of operating in one clock.

Note all of these require having an integer variable holding to_integer(unsigned(OperandB)).

And all of this can be side stepped when your synthesis tool vendor supports sll (and srl for the other case) or SHIFT_LEFT and SHIFT_RIGHT from package numeric_std, and you are allowed to use them.

A universal (pre VHDL 2008) fix without using sll or SHIFT_LEFT might be:

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

Which gives:

带案例陈述的alu测试台

The right answer (all without using signal loop_nr).

Note that all the choices in the case statement aren't covered by the simple test bench.

And of course like most things there's more than two ways to get the desired result.

You could use successive 2 to 1 multiplexers for both Result_High and Result_Low, with each stage fed from the output of the previous stage (or OperandA for the first stage) as the A input the select being the appropriate 'bit' from OperandB, and the B input to the multiplexers the previous stage output shifted by 1 logically ('0' filled).

The multiplexers can be functions, components or procedure statements. By using a three to one multiplexer you can implement both symmetrical shift Operation specified operations (left and right). Should you want to include signed shifts, instead of '0' filled right shifts you can fill with the sign bit value. ...

You should also be assigning Ready <= '0' for those cases where valid successive Operation values can be dispatched.

And because your comment on one of the answers requires the use of a loop with an integer value:

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

Which gives: 带循环变量的alu_tb

And to demonstrate both halves of Result are working OperandA's default value has been changed to "0110":

带有循环变量OperandA的alu_tb为“ 0110”

Also notice the loop starts at 1 instead of 0 to prevent you from having an extra shift and there's a check for non-zero loop_int to prevent the for loop from executing at least once.

And is it possible to make a synthesizable loop in these circumstances?

Yes.

The loop has to address all possible shifts (the range of loop_int) and test whether or not i falls under the shift threshold:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM