Multiply and shift the result in VHDL - vhdl

I have two 8-bit unsigned vectors named a and b, and 16-bit unsigned vector called result. I want to compute a * b * 4 and assign it to result. I do not care about overflow because I know that it will not happen for some reason, it is not important. Is the following way correct?
result <= a * b;
result <= result(13 downto 0) & "00";
(Assume these two lines are in a clocked process.)

No, this will not work. It will just assign to result its previous value left-shifted by 2 positions. This is due to the simulation semantics. To make it short, if you assign several times the same signal in a process, during the same simulation step, the last assignment overrides the others, just like if they did not exist. In your case you can delete the result <= a * b; line, it will behave the same. And of course, synthesizers implement something that has the same behaviour.
Use an intermediate variable, maybe:
process(...)
variable tmp: unsigned(15 downto 0);
begin
...
tmp := a * b;
result <= tmp(13 downto 0) & "00";
...
end process;
Variant:
process(...)
variable tmp: unsigned(17 downto 0);
begin
...
tmp := (a & '0') * (b & '0');
result <= tmp(15 downto 0);
...
end process;
And I recommend finding and reading a good book about VHDL that explains the semantics of the language, especially the difference between variables and signals, the delayed assignment of signals... Note that understanding this will also be very helpful to program with other HDLs like SystemVerilog or SystemC.

Related

Combining `others` expression with `signed` cast

Let var stand for a signed vector (library IEEE.NUMERIC_STD.ALL) of size m.
Let foo be another variable of type std_logic_vector(n-1 downto 0), where n is smaller than m.
I want to concatenate a '0' left of foo, then pad it with zeroes in its right until it size is m and then store the result in var.
I tried
rdsor <= signed('0' & divisor & others=>'0');
But Xilinx complains with the following message on synthesis:
Syntax error near "others".
How do I do what I want?
Assuming rdsor is equivalent to your theoretical var and divisor equivalent to foo you could use two assignments in a process statement:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity jsevillamol is
end entity;
architecture fum of jsevillamol is
constant M: natural := 42;
constant N: natural := 23;
signal rdsor: signed (M - 1 downto 0);
signal divisor: std_logic_vector (N - 1 downto 0);
begin
-- rdsor <= signed('0' & divisor & others=>'0');
process (divisor)
begin
rdsor <= (others => '0');
rdsor (rdsor'LEFT downto rdsor'LEFT - divisor'LENGTH)
<= signed('0' & divisor);
end process;
end architecture;
This works because each element of rdsor is a separate signal and there is only one value for any particular time in a projected output waveform. By not providing an after time_expression in the waveform element of the second assignment the elements of rdsor slice will be assigned the second assignments expression values. (The elements of the first assignment are supplanted by the second). This method of overwriting the projected output waveform is commonly used in providing default values prior to incomplete condition coverage with if statements.
This example analyzes, elaborates and simulates, while doing nothing interesting it demonstrates index ranges are constructed properly.
Notice it avoids the issue of concatenation versus aggregation brought up by Matthew Taylor's answer.
For a single signal assignment in a method not sensitive to tool VHDL revision:
architecture fie of jsevillamol is
constant M: natural := 42;
constant N: natural := 23;
signal rdsor: signed (M - 1 downto 0);
signal divisor: std_logic_vector (N - 1 downto 0);
subtype other is signed (rdsor'LEFT - divisor'LENGTH - 1 downto 0);
begin
-- rdsor <= signed('0' & divisor & others=>'0');
rdsor <= '0' & signed(divisor) & other'(others => '0');
end architecture;
This uses concatenation and subsumes the others into an aggregate. There's a subtype declaration for the trailing '0's portion to allow the aggregate expression to be the target of a qualified expression.
This architecture also analyzes, elaborates and simulates proving index arithmetic is correct.
You would need to use others as part of an aggregate not part of a concatenation. Here's a solution using an aggregate and attributes (which relies on you using VHDL 2008):
rdsor <= (rdsor'LEFT => '0', (rdsor'LEFT-1) downto (rdsor'LEFT-divisor'LENGTH) => signed(divisor), others => '0');
https://www.edaplayground.com/x/5Yuw

Using Generate in Vhdl

I have following piece of code in Vhdl now I want to redirect this signal S1 conditionally to the output port, I will be gratful if someone can guide me through this.
Gen: for index in 0 to 4 generate
signal s1 : ARRAY_TYPE; --- array of (0 to 7) std_logic_vector (7 downto 0);
begin
process(CLK)
begin
if (rising_edge (CLK)) then
S1(counter_index) <= S_in((index*8+7) downto (index*8));
end if;
end if;
end process;
end generate Gen;
I know we can use a process inside generate loop but is otherway around also possible! If I declare S1 as global signal it complains of connected to multi driven net? How this is different?
I will be really grateful if someone can guide me through this
Your for-generate loop (from 0 to 4) will be unrolled at elaboration so that what you end up with, effectively, is (leaving out the process code for brevity):
for index = 0:
signal s1_0 : ARRAY_TYPE; --- array of (0 to 7) std_logic_vector (7 downto 0);
s1_0(counter_index) <= S_in(7 downto 0);
for index = 1:
signal s1_1 : ARRAY_TYPE; --- array of (0 to 7) std_logic_vector (7 downto 0);
s1_1(counter_index) <= S_in(15 downto 8);
etc.
You get "copies" because you declared the signal inside the generate loop, each of which is local to just that generate block. When you try to make s1 "global" (not really global, which has a different connotation; just declared for the whole architecture), you get:
for index = 0:
s1(counter_index) <= S_in(7 downto 0);
for index = 1:
s1(counter_index) <= S_in(15 downto 8);
See what happened there? Those statements are concurrent, and assigning to the same bits. That's why you have problems with multiple drivers.
The problem appears to be counter_index. Either you need to index s1 with some combination of your loop index with your other index, as QuantumRipple suggested, or you need to create some intermediate signal, or something.
Note that if you're handling 32-bit data a byte at a time, you probably meant 0 to 3, not 0 to 4.
Although fru1tbat covers the solution to your problem, I wanted to cover your other question:
I know we can use a process inside generate loop but is otherway
around also possible!
You can use a for ... loop inside of a process statement as well. It functions and gets unrolled similarly to a for... generate statement, but inside of a process. It can also be used to implement more complex logic by using variables or overwriting assignments. Here is a syntax reference for it: http://www.ics.uci.edu/~jmoorkan/vhdlref/for_loop.html

AND all elements of an n-bit array in VHDL

lets say I have an n-bit array. I want to AND all elements in the array. Similar to wiring each element to an n-bit AND gate.
How do I achieve this in VHDL?
Note: I am trying to use re-usable VHDL code so I want to avoid hard coding something like
result <= array(0) and array(1) and array(2)....and array(n);
Thanks
Oshara
Solution 1: With unary operator
VHDL-2008 defines unary operators, like these:
outp <= and "11011";
outp <= xor "11011";
outp <= and inp; --this would be your case
However, they might not be supported yet by your compiler.
Solution 2: With pure combinational (and traditional) code
Because in concurrent code you cannot assign a value to a signal more than once, your can create a temp signal with an "extra" dimension. In your case, the output is one-bit, so the temp signal should be a 1D array, as shown below.
-------------------------------------------
entity unary_AND IS
generic (N: positive := 8); --array size
port (
inp: in bit_vector(N-1 downto 0);
outp: out bit);
end entity;
-------------------------------------------
architecture unary_AND of unary_AND is
signal temp: bit_vector(N-1 downto 0);
begin
temp(0) <= inp(0);
gen: for i in 1 to N-1 generate
temp(i) <= temp(i-1) and inp(i);
end generate;
outp <= temp(N-1);
end architecture;
-------------------------------------------
The inferred circuit is shown in the figure below.
Solution 3: With sequential code
This is simpler than solution 2, though you are now using sequential code to solve a purely combinational problem (but the hardware will be the same). You can either write a code similar to that in solution 2, but with a process and loop (the latter, in place of generate) or using a function. Because in sequential code you are allowed to assign a value to a signal more than once, the temp signal of solution 2 is not needed here.
If you have VHDL-2008 available, then reduction and is build into the
language as David Koontz and Pedroni have explained.
If you only have VHDL-2003 and prior available, then you can use a function
like:
function and_reduct(slv : in std_logic_vector) return std_logic is
variable res_v : std_logic := '1'; -- Null slv vector will also return '1'
begin
for i in slv'range loop
res_v := res_v and slv(i);
end loop;
return res_v;
end function;
You can then use the function both inside and outside functions with:
signal arg : std_logic_vector(7 downto 0);
signal res : std_logic;
...
res <= and_reduct(arg);
My favorite, non-VHDL-2008 solution is:
use ieee.std_logic_unsigned.all ; -- assuming not VHDL-2008
. . .
result <= '1' when not MyArray = 0 else '0' ;
With VHDL-2008, I recommend that you use the "and" reduction built-in (see Pedroni's post) and use the IEEE standard package "ieee.numeric_std_unsigned.all" instead of the shareware package "std_logic_unsigned".

I want to check if std_logic_vector contains negative integer

How can I check with if (...) then ... end if; construction if std_logic_vector variable holds the bits of a negative number? If it is negative, I have to assign it a zero value.
I have :
signal sum : std_logic_vector (15 downto 0);
sum<= (...);
if (...) then
sum<=x"00";
end if;
Thank you!
You cannot add two STD_LOGIC_VECTORs, because the language does not know anything about the arithmetic that it should perform. This is because, to the synthesis tool, every signal/port/variable that's declared as STD_LOGIC_VECTOR is nothing more than an array of STD_LOGIC, the multi-valued logic type. Arithmetic on such a type does not make sense.
If you want to use arithmetic on types whose interface is similar to the one exposed by STD_LOGIC_VECTOR, you should use SIGNED (for signed arithmetic) and UNSIGNED (for unsigned arithmetic) types defined in IEEE.NUMERIC_STD. In order to convert between these types, just cast them using the type names explicitly, like this :
std_logic_vector_variable := STD_LOGIC_VECTOR(unsigned_variable);
unsigned_variable := UNSIGNED(std_logic_vector_variable);
So, summing it all up - the signal sum should be declared as SIGNED, since you're obviously going to perform arithmetic on it. Then, you can freely use the comparison and arithmetic operations that you need. The resulting code should look more or less like this :
use IEEE.NUMERIC_STD.ALL;
-- entity and architecture declarations...
signal sum : SIGNED (15 downto 0);
-- inside some process...
if (sum <= 0) then sum <= 0; end if;
The quick and simple hack is to check if the most-significant-bit is 1, indicating a negative number:
result <= (others=>'0') when sum(sum'left)='1' else sum;
Or you can coerce the std_logic_vector into an appropriate type and see if it is negative:
result <= (others=>'0') when signed(sum) < 0 else sum;
Or inside of a process use an if statement instead of a selected signal assignment:
if signed(sum) < 0 then
result <= (others=>'0');
else
result <= sum;
end if;
signal sum : std_logic_vector (15 downto 0);
sum<= x"E8";
if (sum(15)='1') then
sum<=x"00";
end if;
Just check the MSB..
If MSB is 1, that means the number is negative else positive.

Shift Right And Shift Left (SLL/SRL)

so, I'm developing an ALU for MIPS architecture and I'm trying to make a shift left and a shift right so that the ALU can shift any amount of bits.
the Idea I had is to convert the shift value to an integer and select the piece of the entry that'll be on the result(the integer is stored in X) but Quartus doesn't accept a variable value, only constants.
What could I do to make this?
(Cases are on lines "WHEN "1000" =>..." and "WHEN "1001" =>...")
Thanks.
PROCESS ( ALU_ctl, Ainput, Binput, X )
BEGIN
-- Select ALU operation
--ALU_output_mux <= X"00000000"; --padrao
CASE ALU_ctl IS
WHEN "1000" => ALU_output_mux(31 DOWNTO X) <= (Ainput( 31-X DOWNTO 0 ));
WHEN "1001" => ALU_output_mux(31-X DOWNTO 0) <= (Ainput( 31 DOWNTO X ));
WHEN OTHERS => ALU_output_mux <= X"00000000";
END CASE;
END PROCESS;
If Quartus doesn't like it you have two choices:
Write it some way that Quartus does like - you're trying to infer a barrel shifter, so you could write one out longhand and then instantiate that. Potentially expensive in time
Get a different synthesizer that will accept it. Potentially expensive in money.
I have had issues with this in Quartus as well, although your code also has some implicit latches (you are not assigning all bits of the output in your two shift cases).
The work-around I use is to define an intermediate array with all the possible results, then select one of those results using your selector. In your case, something like the following:
subtype DWORD_T is std_logic_vector( 31 downto 0);
type DWORD_A is array (natural range <>) of DWORD_T;
signal shift_L : DWORD_A(31 downto 0);
signal shift_R : DWORD_A(31 downto 0);
signal zero : DWORD_T;
...
zero <= (others=>'0');
process (Ainput)
begin
for index in Ainput'range loop
shift_L(index) <= Ainput(31 - index downto 0) & zero(index - 1 downto 0);
shift_R(index) <= zero(index - 1 downto 0) & Ainput(31 downto index);
end loop;
end process;
ALR_output_mux <= shift_L(to_integer(X)) when ALU_ctl="1000",
shift_R(to_integer(X)) when ALU_ctl="1001",
(others=>'0') when others;
You could work around this by using generate or for to create each shift/rotate level, or you can use the standard functions ({shift,rotate}_{left,right}) for shifting and rotating.

Resources