I am trying to build a 16 bit barrel shifter with left and right shift capabilities. I am having some issues with how to structure the code so that it will do what I think I want to do.
I have an opcode input that decides on the direction, an input vector to be shifted, an output vector, and a position vector with 4 bits.
I am using the position vector to set a shift 'level' in a way. I want to check position(0) and if it is set to 1, shift one position. And then check position(1) and shift 2 positions, position(3) 4 positions, and position(3) 8 positions.
I think that if I run through every position in the position vector and sequentially shift the bits it should eventually get all options. I will add the other opcode when this direction works properly.
I am stuck on the assigning the intermediate vectors for the architecture. I am not sure what the proper method should be and I can not find anything in google. Perhaps a Case system would work better.
entity eds_shifter is
port ( a : in bit_vector(15 downto 0) ; --input
pos : in bit_vector(3 downto 0); -- position
opc : in bit_vector(3 downto 0); -- opcode
y : out bit_vector(15 downto 0) --output
);
end entity eds_shifter ;
architecture behavior of eds_shifter is
begin
process(a,pos,opc) -- input vectors
signal s1,s2,s3,s4 : bit_vector(15 downto 0); -- intermediate steps
begin
s1 <= a(14 downto 0) & '0' when pos(0) = '1' and opc = "0000" else -- shifting left by one
s1 <= a when pos(0) = '0' and opc = "0000" else -- carryover vector to next shift position
s2 <= s1(13 downto 0) & '0' when pos(1) = '1' and opc = "0000" else -- shift previous vector by 2
s2 <= s1 when pos(1) = '0' and opc = "0000" else
s3 <= s2(12 downto 0) & '0' when pos(2) = '1' and opc = "0000" else -- shift another 4 places
s3 <= s2 when pos(2) = '0' and opc = "0000" else
s4 <= s3(7 downto 0) & '0' when pos(3) = '1' and opc = "0000" else -- shift another 8 places
s4 <= s3 when pos(3) = '0' and opc = "0000" else
y <= s4 when opc = "0000";
end process;
end architecture behavior;
Error Message
** Error: */02/eds_shifter.vhd(14): Illegal sequential statement.
** Error: */02/eds_shifter.vhd(15): Type error resolving infix expression "<=" as type std.standard.bit_vector.
EDIT
Thank you for the detailed response. I understand that there are generic shift functions built in but I want to try to figure out how to implement it myself.
Thanks for the tip about the bit_vector. From what I understand bit types are only ok to use when you are sure that the input is not multisourced. In this case it should probably be ok, but I will go ahead and keep it in mind for the future.
I think the concept I want to try is sound. If you check each bit in position for a 1 and shift by the appropriate amount at the end the number of bits shifted will equal the value of the position vector.
a = "1111 1111 1111 1111" and pos = "1010" so we need to shift by decimal 10 places. So we do the first iteration and it has no change, the second shifts by 2 bits, third iteration shifts by 0 bits, and the fourth shifts by 8 bits for a total of 10 bits shifted to get a="1111 1100 0000 0000".
My issue is not with the specific shifting operation (perhaps it is wrong and if so will use a different method, right now I am simply curious about how to implement my idea), my issue is writing code that will update the vector and then check the next position in the position vector. I was looking at what you posted about the delta cycle and sensitivity list. VHDL can be confusing since you have to describe the real world in terms of operations rippling through a processor. And it requires a different thinking than with standard programming.
I tried the below code but it only changes and shifts in my test bench once. I set up a test bench that runs through all combinations of positions but with the following code it only shifts at pos = 1000 and then it shifts 8 places. Is there any of forcing the code to check at each if statement, not only the last one?
entity eds_shifter is
port ( a : in bit_vector(15 downto 0) ; --input
pos : in bit_vector(3 downto 0); -- position
opc : in bit_vector(3 downto 0); -- opcode
y : out bit_vector(15 downto 0) --output
);
end entity eds_shifter ;
architecture behavior of eds_shifter is
begin
process(a,pos,opc) -- input vectors
begin
if pos(0) = '1' and opc = "0000" then -- shifting left by one
y <= a(14 downto 0) & "0";
else
y <= a;
end if;
if pos(1) = '1' and opc = "0000" then -- shifting left by two
y <= a(13 downto 0) & "00";
else
y <= a;
end if;
if pos(2) = '1' and opc = "0000" then -- shifting left by four
y <= a(11 downto 0) & "0000";
else
y <= a;
end if;
if pos(3) = '1' and opc = "0000" then -- shifting left by eight
y <= a(7 downto 0) & "00000000";
else
y <= a;
end if;
end process;
end architecture behavior;
Test Bench
entity eds_shifter_tb is
end eds_shifter_tb;
architecture behavior of eds_shifter_tb is
signal opc: bit_vector(3 downto 0);
signal pos: bit_vector(3 downto 0);
signal Y : bit_vector (15 downto 0);
signal a: bit_vector (15 downto 0);
begin
dut: entity work.eds_shifter(behavior)
port map(opc => opc,
pos => pos,
Y => Y,
a => a); -- assigns all ports to entity spec
a <= (others => '1'),
(others => '1') after 30 ns;
opc <= "0000",
"0001" after 30 ns;
pos <= "0000",
"0001" after 2 ns,
"0010" after 4 ns,
"0011" after 6 ns,
"0100" after 8 ns,
"0101" after 10 ns,
"0110" after 12 ns,
"0111" after 14 ns,
"1000" after 16 ns,
"1001" after 18 ns,
"1010" after 20 ns,
"1011" after 22 ns,
"1100" after 24 ns,
"1101" after 26 ns,
"1110" after 28 ns,
"1111" after 30 ns,
"0000" after 32 ns,
"0001" after 34 ns,
"0010" after 36 ns,
"0011" after 38 ns,
"0100" after 40 ns,
"0101" after 42 ns,
"0110" after 44 ns,
"0111" after 46 ns,
"1000" after 48 ns,
"1001" after 50 ns,
"1010" after 52 ns,
"1011" after 54 ns,
"1100" after 56 ns,
"1101" after 58 ns,
"1110" after 60 ns,
"1111" after 62 ns;
end behavior;
I think you're missing a complete understanding of how hardware description differs from writing software and how processes work in VHDL. That said, you can use several concurrent assignments to do what you want (a cascade style barrel shifter). See the model below:
-- note, entirely untested!
library ieee;
use ieee.std_logic_1164.all;
entity eds_shifter is
port ( a : in std_logic_vector(15 downto 0) ; --input
pos : in std_logic_vector(3 downto 0); -- position
opc : in std_logic_vector(3 downto 0); -- opcode
y : out std_logic_vector(15 downto 0) --output
);
end entity eds_shifter ;
architecture behavior of eds_shifter is
signal s1,s2,s3,s4 : std_logic_vector(15 downto 0); -- intermediate steps
begin
--opcode 0000 is shift left, else is shift right
s1 <= a(14 downto 0) & '0' when pos(0) = '1' and opc = "0000" else --maybe shift by 1 place
'0' & a(15 downto 1) when pos(0) = '1' and opc /= "0000" else
a;
s2 <= s1(13 downto 0) & "00" when pos(1) = '1' and opc = "0000" else --maybe shift 2 places
"00" & s1(15 downto 2) when pos(1) = '1' and opc /= "0000" else
s1;
s3 <= s2(11 downto 0) & x"0" when pos(2) = '1' and opc = "0000" else --maybe shift 4 places
x"0" & s2(15 downto 4) when pos(2) = '1' and opc /= "0000" else
s2;
s4 <= s3(7 downto 0) & x"00" when pos(3) = '1' and opc = "0000" else --maybe shift 8 places
x"00" & s3(15 downto 8) when pos(3) = '1' and opc /= "0000" else
s3;
y <= s4;
end architecture behavior;
The recommended method of accomplishing shifting in general remains using the built-in functions, and they will likely turn into the same hardware post-synthesis if the optimizer is doing its job. While exercises like this are good for understanding how hardware works, realize that this is no more efficient, and it isn't useful as a small component of a larger project because it will increase code size and reduce readability for no benefit.
There are several issues in the code:
when can't be used as sequential statement (in process; before VHDL-2008), and
the else part also looks like a statement, which is not correct syntax
signal can't be declared in process
The new value of a signal take a delta cycle before it is available, so
the s* must be in the sensitivity list for the value to ripple through
VHDL provides standard shift functions, which should be used, instead of the
home build (that even looks wrong). Note that VHDL shift in-fix operators
(before VHDL-2008) are known to give unexpected results, so use the functions
instead; read more
here.
So based on this the code with process can be updated to:
library ieee;
use ieee.numeric_bit.all;
architecture behavior of eds_shifter is
begin
process(a, pos, opc) -- input vectors
begin
if opc = "0000" then -- Shift left
y <= bit_vector(shift_left(unsigned(a), to_integer(unsigned(pos))));
else -- Assuming shift right
y <= bit_vector(shift_right(unsigned(a), to_integer(unsigned(pos))));
end if;
end process;
end architecture behavior;
Or with concurrent when, then process can be replaced with:
y <= bit_vector(shift_left(unsigned(a), to_integer(unsigned(pos)))) when opc = "0000" else
bit_vector(shift_right(unsigned(a), to_integer(unsigned(pos))));
Note bit_vector is not widely used, so consider updating to use
std_logic_vector instead, whereby the code can be:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity eds_shifter is
port (a : in std_logic_vector(15 downto 0); --input
pos : in std_logic_vector(3 downto 0); -- position
opc : in std_logic_vector(3 downto 0); -- opcode
y : out std_logic_vector(15 downto 0) --output
);
end entity eds_shifter;
architecture behavior of eds_shifter is
begin
y <= std_logic_vector(shift_left(unsigned(a), to_integer(unsigned(pos)))) when (opc = "0000") else
std_logic_vector(shift_right(unsigned(a), to_integer(unsigned(pos))));
end architecture behavior;
In your modified code:
process(a,pos,opc) -- input vectors
begin
if pos(0) = '1' and opc = "0000" then -- shifting left by one
y <= a(14 downto 0) & "0";
else
y <= a;
end if;
if pos(1) = '1' and opc = "0000" then -- shifting left by two
y <= a(13 downto 0) & "00";
else
y <= a;
end if;
...
You are overwriting the assignment to y with each new if block. Signal assignment in processes is scheduled for the end of the process, not performed immediately. Your original code, with intermediate signals, would have worked had you fixed the syntax and added the intermediate signals to the sensitivity list as Morten suggested. Alternately, you could use a variable, as follows (note that in this version, the else in each block would have been redundant):
process (a, pos, opc)
variable result : std_logic_vector(15 downto 0);
begin
result := a;
if pos(0) = '1' and opc = "0000" then
result := result(14 downto 0) & '0';
end if;
if pos(1) = '1' and opc = "0000" then
result := result(13 downto 0) & "00";
end if;
...
y <= result;
end process;
Unless you eventually want to pipeline your shifter, in which case you should go back to intermediate signals, and you'd also need to pipeline your other inputs to match. But then maybe we're getting ahead of ourselves...
Related
I am new to VHDL and I am trying to do a simple application with a led matrix (8x8). My goal is to turn on the leds of the matrix so I can see a smiley face. For some reason none of the leds turn on.
In order to see what's wrong I tried to turn on all leds on each line at a time by commenting the case statement and giving cols<="00000000" before the statement, the result is that the only line that turns on is the first, it keeps turning on and off each second.
I made the frequency divider for 1 second just to see if the code works correctly.
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.std_logic_unsigned.all;
entity main is
Port ( clk : in STD_LOGIC;
rows : out STD_LOGIC_VECTOR (7 downto 0);
cols : out STD_LOGIC_VECTOR (7 downto 0));
end main;
architecture Behavioral of main is
signal count: std_logic_vector(7 downto 0):= "00000001";
signal clk1Hz: std_logic_vector(26 downto 0);
begin
process(clk)
begin
if rising_edge(clk) then
if clk1Hz = X"5F5E0FF" then
clk1Hz <= "000" & X"000000";
else
clk1Hz <= clk1Hz + 1;
end if;
if clk1Hz(26) = '1' then
if count = "10000000" then
count <= "00000001";
else
count(7 downto 1) <= count(6 downto 0);
count(0) <= '0';
end if;
rows <= count;
case count is
when "00000001" => cols <= "11111111";
when "00000010" => cols <= "11011011";
when "00000100" => cols <= "11011011";
when "00001000" => cols <= "11111111";
when "00010000" => cols <= "00111100";
when "00100000" => cols <= "10000001";
when "01000000" => cols <= "11000011";
when "10000000" => cols <= "11111111";
when others => cols <= "11111111";
end case;
end if;
end if;
end process;
end Behavioral;
Do you realize that if clk1Hz(26) = '1' then stays true from X"4000000" to X"5F5E0FF"?
You most likely want to change count only on the exact X"4000000" value, no? And not continuously for 1/3rd of the time...
I can tell you didn't simulate this. The clk1Hz signal wasn't initialized and wasn't reset. It won't spin in sim without this, since it initializes to X. However, on hardware it will work just fine.
So, when your clk counter hits x400_0000, bit (26) is set and your row/col shifters start going like mad for 1/3 of a second. Then when the clk counter resets, all activity stops.
Is this really what you want? I can see from simulating this that rows and cols are both shifting correctly, albeit for only a third of a second.
During the pause, rows stops at 0x80, and cols at 0xFF.
I am making a generic N-bit ALU in VHDL. I am having trouble assigning the value for the carry for addition, or borrow for subtraction. I have tried the following:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity alu is
generic(n: integer :=1); --Default to 1
port (
a : in std_logic_vector(n-1 downto 0);
b : in std_logic_vector(n-1 downto 0);
op : in std_logic_vector(1 downto 0);
output : out std_logic_vector(n-1 downto 0);
carryborrow: out std_logic
);
end alu;
architecture Behavioral of alu is
signal result: std_logic_vector(n downto 0);
begin
process( a, b, op )
begin
case op is
when "00" =>
result(n) <= '0';
result(n-1 downto 0) <= a and b; --and gate
output <= result(n-1 downto 0);
carryborrow <= '0';
when "01" =>
result(n) <= '0';
result(n-1 downto 0) <= a or b; --or gate
output <= result(n-1 downto 0);
carryborrow <= '0';
when "10" =>
result(n) <= '0';
result(n-1 downto 0) <= std_logic_vector(signed(a) + signed(b)); --addition
output <= result(n-1 downto 0);
carryborrow <= result(n);
when "11" =>
result(n) <= '0';
result(n-1 downto 0) <= std_logic_vector(signed(a) - signed(b)); --subtraction
output <= result(n-1 downto 0);
carryborrow <= result(n);
when others =>
NULL;
end case;
end process;
end Behavioral;
This seems to set the carryborrow bit to always be 0. How can I assign it to what it should be without type errors?
There are bugs in your code:
i) You have not taken into account the fact that signals are not updated immediately. Consequently, the following lines will not do as I think you are expecting:
result(n) <= '0';
result(n-1 downto 0) <= a and b; --and gate
output <= result(n-1 downto 0);
Instead, you need to take the lines driving output and carryborrow outside the combinational process, as you can see below.
ii) Assuming you wish this code to be synthesisable, simply putting NULL in your always branch will result in latches being inferred. You need to drive result in the others branch, too.
So, making an assumption about how your carry output is to behave with the and and or operations, this is how I would have written your code:
architecture Behavioral of alu is
signal result: std_logic_vector(n downto 0);
begin
process( a, b, op )
begin
case op is
when "00" =>
result <= '0' & (a and b); --and gate
when "01" =>
result <= '0' & (a or b); --or gate
when "10" =>
result <= std_logic_vector(resize(signed(a), n+1) + resize(signed(b), n+1)); --addition
when "11" =>
result <= std_logic_vector(resize(signed(a), n+1) - resize(signed(b), n+1)); --subtraction
when others =>
result <= (others => 'X');
end case;
end process;
output <= result(n-1 downto 0);
carryborrow <= result(n);
end Behavioral;
I normally do this:
result <= std_logic_vector(signed(a(n-1) & a) + signed(b(n-1) & b));
result <= std_logic_vector(signed(a(n-1) & a) - signed(b(n-1) & b));
Sign extend and then do the operation to take care of overflow, when the result is one extra bit long.
Hmm, consider this in a 4 bit environment, say a="0101" and b="1001". Adding them shall give the output="1110", with NO carry.
However, sign extending with resize(signed(a), n+1) and resize(signed(b), n+1) will set a="00101" and b="11001" and hence result="11110" and carryborrow='1', which is wrong!
By sign extending vectors a and b, the numeral range has increased to 5 bits, and thus result needs to be 6 bits to be able to hold carry, and we're back to square one.
Vectors a and b should only be zero extended, that is '0' & a and '0' & b before adding them to result, and then carryborrow, as MSB(Most Significant Bit) of result, will get the correct value.
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:
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:
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:
And to demonstrate both halves of Result are working OperandA's default value has been changed to "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);
I am getting the following warning in Xilinx when I synthesize my code of a 4-bit multiplier:
"This port will be preserved and left unconnected if it belongs to a top-level block or it belongs to a sub-block and the hierarchy of this sub-block is preserved." I dont see any problem in my code that can cause this warning.
The code is as follows:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.numeric_bit.ALL;
-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;
-- Uncomment the following library declaration if instantiating
-- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;
entity mult4X4 is
Port ( Clk : in bit;
St : in bit;
Mplier : in unsigned (3 downto 0);
Mcand : in unsigned (3 downto 0);
Result : out unsigned (7 downto 0);
Done : out bit);
end mult4X4;
architecture behave1 of mult4X4 is
signal State : integer range 0 to 9;
signal ACC : unsigned (8 downto 0);
alias M : bit is ACC(0);
begin
process(Clk)
begin
if Clk'event and Clk = '1' then
case State is
when 0 =>
if St = '1' then
ACC(8 downto 4) <= "00000";
ACC(3 downto 0) <= Mplier;
State <= 1;
end if;
when 1 | 3 | 5 | 7 =>
if M = '1' then
ACC(7 downto 4) <= ACC(7 downto 4) + Mcand;
ACC(8 downto 0) <= '0' & ACC(8 downto 1);
State <= State + 1;
else
ACC <= '0' & ACC(8 downto 1);
State <= State + 2;
end if;
when 2 | 4 | 6 | 8 =>
ACC <= '0' & ACC(8 downto 1);
State <= State + 1;
when 9 =>
Done <= '1';
State <= 0;
Result <= ACC(7 downto 0);
end case;
end if;
end process;
--Done <= '1' when State = 9 else '0';
--Result <= ACC(7 downto 0);
end behave1;
Notice that the warning is speaking of a port. I expect the line just above would tell you precisely which port, but it's easy enough to derive.
The port will be left unconnected. That means it must have no effect. The easiest way to have no effect is to be unused. All of the ports are accounted for in the process, so we look for other ways they aren't meaningful. Done looks like a prime candidate; it is set to 1 when the state machine finishes, but there's no reset; this means it goes from undefined to high, and stays. The optimizer could replace that with just a high output. A reasonable place to reset it would be at the start condition.
In general the code has no reset, which may be fine if the state machine can recover from any state, but won't simulate properly. Don't be sure it will reset just because of the range of the State signal; having ten values, it would require 4 bits to store causing 6 invisible states with undefined behaviour; or it could well be encoded to one hot, which if not reset could cause multiple states to appear simultaneously.
Try simulating it.
Without simulating it myself I'd guess you're getting all '0's on Result.
These:
ACC(7 downto 4) <= ACC(7 downto 4) + Mcand;
ACC(8 downto 0) <= '0' & ACC(8 downto 1);
Will result in the Multiplicand's influence being eradicated. There is no time between assignments to ACC. See IEEE Std 1076-1993 8.4.1 Updating a projected output waveform (-2008 10.5.2.2 Executing a simple assignment statement), the paragraph starting "The sequence of transactions is ..." and the following narrative. The first assignment's scheduled transaction for the affected bits of ACC (7 downto 4) are deleted as old transactions. For composite types (and ACC is) it happens on an element by element basis.
And it looks like it will get all '0's because of the second assignment statement.
Also from simply reading your state machine the second of those two ACC assignments should simply be deleted in States 1 | 3 | 5 | 7?
Addendum
After Yann Verneir pointed out another error with Done never being assigned '0' I figured I'd demonstrate the effects of the two assignment statements to ACC in States 1 | 3 | 5 | 7.
A simple testbench:
library ieee;
use ieee.numeric_bit.all;
entity mult4x4_tb is
end entity;
architecture foo of mult4x4_tb is
signal clk: bit;
signal start: bit;
signal multiplier: unsigned (3 downto 0) := X"2";
signal multiplicand: unsigned (3 downto 0) := X"4";
signal result: unsigned (7 downto 0);
signal done: bit;
begin
DUT:
entity work. mult4x4
port map (
Clk => clk,
St => start,
Mplier => multiplier,
Mcand => multiplicand,
Result => result,
Done => done
);
CLOCK:
process
begin
wait for 10 ns;
clk <= not clk;
if Now > 300 ns then
wait;
end if;
end process;
STIM:
process
begin
wait for 29 ns;
start <= '1';
wait for 20 ns;
start <= '0';
wait;
end process;
end architecture;
gives:
You can see that when using package numeric_bit instead of packages std_logic_1164 and numeric_std we never see anything but all '0's on Result.
Yann's assignment to '1' only on Done can be cured by:
case State is
when 0 =>
if St = '1' then
ACC(8 downto 4) <= "00000";
ACC(3 downto 0) <= Mplier;
State <= 1;
Done <= '0';
end if;
Removing the second signal assignment to ACC in the sequential statements for cases 1 | 3 | 5 | 7 gives an answer that look's right for 2 x 4:
the task is to make a 4-Bit Multiplier that uses FSM. the steps would be 1) multiply 2) shift 3) add.
1011 (this is 11 in binary)
x 1110 (this is 14 in binary)
======
0000 (this is 1011 x 0)
1011 (this is 1011 x 1, shifted 1 position to the left)
1011 (this is 1011 x 1, shifted 2 positions to the left)
1011 (this is 1011 x 1, shifted three positions to the left)
======
10011010 (this is 154 in binary)
http://en.wikipedia.org/wiki/Binary_multiplier
here are my codes:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
entity test is
Port ( CLK : in STD_LOGIC;
RESET : in STD_LOGIC;
Input : in STD_LOGIC_VECTOR (3 downto 0);
Confirm : in STD_LOGIC;
Output : out STD_LOGIC_VECTOR (7 downto 0));
end test;
architecture Behavioral of test is
type state is (R,S0,S1,S2,S3,S4);
signal pstate, nstate: state;
signal A_sig, B_sig: STD_LOGIC_VECTOR(3 downto 0);
begin
process(pstate,Confirm,Input)
variable temp_var: STD_LOGIC_VECTOR(3 downto 0);
variable tempMult_var,tempProd_var: STD_LOGIC_VECTOR(7 downto 0);
begin
case pstate is
when R =>
nstate <= S0;
tempMult_var := (others => '0');
tempProd_var := (others => '0');
A_sig <= (others => '0');
B_sig <= (others => '0');
Output <= (others => '0');
when S0 =>
nstate <= S0;
if (Confirm = '1') then
A_sig <= Input;
nstate <= S1;
end if;
when S1 =>
nstate <= S1;
if (Confirm = '0') then
nstate <= S2;
end if;
when S2 =>
nstate <= S2;
if (Confirm = '1') then
B_sig <= Input;
nstate <= S3;
end if;
when S3 =>
nstate <= S3;
if (Confirm = '0') then
nstate <= S4;
end if;
when S4 =>
nstate <= S0;
for x in 0 to 3 loop
temp_var := (A_sig AND (B_sig(x)&B_sig(x)&B_sig(x)&B_sig(x) ) );
tempMult_var := "0000" & temp_var;
if (x=0) then tempMult_var := tempMult_var;
elsif (x=1) then tempMult_var := tempMult_var(6 downto 0)&"0";
elsif (x=2) then tempMult_var := tempMult_var(5 downto 0)&"00";
elsif (x=3) then tempMult_var := tempMult_var(4 downto 0)&"000";
end if;
tempProd_var := tempProd_var + tempMult_var;
end loop;
Output <= tempProd_var;
tempProd_var := (others => '0');
end case;
end process;
process(CLK,RESET)
begin
if RESET = '1' then
pstate <= R;
elsif rising_edge(CLK) then
pstate <= nstate;
end if;
end process;
end Behavioral;
here are the warnings
after "Synthesize - XST"
WARNING:Xst - Property "use_dsp48" is not applicable for this technology.
WARNING:Xst:737 - Found 4-bit latch for signal <B_sig>.
WARNING:Xst:737 - Found 8-bit latch for signal <Output>.
WARNING:Xst:737 - Found 4-bit latch for signal <A_sig>.
after "Implement Design"
WARNING:Route:447 - CLK Net:A_sig_not0001 may have excessive skew because
WARNING:Route:447 - CLK Net:B_sig_not0001 may have excessive skew because
after "Generate Programming File"
WARNING:PhysDesignRules:372 - Gated clock. Clock net A_sig_not0001 is sourced by
WARNING:PhysDesignRules:372 - Gated clock. Clock net B_sig_not0001 is sourced by
WARNING:PhysDesignRules:372 - Gated clock. Clock net Output_or0000 is sourced by
the simulations are correct but the actual board doesn't have the correct output. what might be the problem?
My humble advice:
Combine your two processes into a single clocked process.
That way you avoid a whole category of asynchronous logic mistakes that are easy for a beginner to make to painful to track down.
Also, whenever you see warnings about latches or gated clocks, revisit your code - both are clear indicators that something is most probably wrong.
Latches typically come from combinatorial processes where signals are only assigned in some cases. For instance A_sig is not assigned in S0, if confirm = 0, and will thus cause a latch to be inferred. In this case, just make sure that A_sig is always set to something, no matter the combination of the control signal values.
In this case the gated clocks probably come from your rather complex combinatorial process, but mostly it's from signals generated by logic for clocking synchronous processes. This can lead to all kind of problems (high FPGA clock line usage and timing/routing issues), especially if your not aware that you're creating additional clocking domains. This can mostly be avoided by running the process in question on the main (be it global or local) system clock and using a clock enable to scale it down if necessary.