everyone!
Please, can you explain me a strange thing.
The numeric_std library contains a function "+", addition. I inserted a description of the function.
-- Id: A.3
function "+" (L, R: UNSIGNED) return UNSIGNED;
-- Result subtype: UNSIGNED(MAX(L'LENGTH, R'LENGTH)-1 downto 0).
-- Result: Adds two UNSIGNED vectors that may be of different lengths.
For instance, we have:
a : in unsigned(2 downto 0);
b : in unsigned(1 downto 0);
result : out unsigned(2 downto 0);
So, If a is "111" and b is "01" the result will be "000". The reason is an overflow.
My question is why the library allows it, why it does not expand the results length? Why a compiler does not warn me about the overflow? Is it possible to add the values without the overflow? And how should I prevent or find to occur the overflow.
Thank you!
In hardware, we have incrementers and decrementers - which "roll over" as part of their normal operation, we have accumulators - which should never roll over, and we have math units which need to maintain full precision of the operation.
In VHDL, when we do an operation, it must produce an exact sized result. Incrementers, decrementers, and accumulators all need to keep the same size result, where as math units that need to maintain full precision of the operation need to increase in size.
Hence, the "+" can only meet one of these needs. The other part needs to be handled by code.
For numeric_std, the rule for "+" is as you state, the size of the result is the size of the largest array operand. What is interesting is that in ieee.fixed_pkg, the size of the result is the 1 + size of the largest array operand.
Lets take a look at how this impacts our math.
signal Co : std_logic ; -- to throw away carry out
signal Incrementer_unsigned : unsigned(3 downto 0) ;
signal Incrementer_ufixed : ufixed(3 downto 0) ;
signal Accumulator_unsigned : unsigned(3 downto 0) ;
signal Next_unsigned : unsigned(3 downto 0) ;
signal Accumulator_ufixed : ufixed(3 downto 0) ;
signal Next_ufixed : ufixed(3 downto 0) ;
signal Sum_unsigned : unsigned(8 downto 0) ;
signal A_unsigned : unsigned(7 downto 0) ;
signal B_unsigned : unsigned(7 downto 0) ;
signal Sum_ufixed : ufixed(8 downto 0) ;
signal A_ufixed : ufixed(7 downto 0) ;
signal B_ufixed : ufixed(7 downto 0) ;
. . .
process (Clk)
begin
if rising_edge(Clk) then
-- Incrementer / Decrementer with unsigned or ufixed. Result same size
Incrementer_unsigned <= Incrementer_unsigned + 1 ;
(Co, Incrementer_ufixed) <= Incrementer_ufixed + 1 ;
-- Accumulator with unsigned or ufixed. Result same size
Accumulate_unsigned <= Accumulate_unsigned + Next_unsigned ;
(Co, Accumulate_ufixed) <= Accumulate_unfixed + Next_ufixed ;
-- math unit with full precision. Result size increases
Sum_unsigned <= ('0' & A_unsigned) + ('0' & B_unsigned) ;
Sum_ufixed <= A_ufixed + B_ufixed ;
end if ;
end process ;
So for numeric_std, if we threw a warning for the incrementer, we would get numerous FALSE positive warnings and eventually ignore all warnings - that would be a mistake.
For numeric_std, the sizing rules work very well for the incrementer and accumulator. The size of results is what is expected. For the Sum_unsigned, we have to upsize one argument (if they are the same size), I chose to upsize both. There is a resize function that I chose not to use.
For fixed_pkg, for the incrementer and accumulator, we had to downsize the result by using an aggregate on the left hand side to remove the carry out (this code is VHDL-2008). There is also a resize that can handle this. For the Sum_ufixed, the size of the result matched our application.
One thing this demonstrates is that no matter which rule you pick, there is something the person writing the code must do.
For Sum_ufixed, the results get much more exciting if we have:
signal Sum_ufixed10 : ufixed(9 downto 0) ;
. . .
Sum_ufixed10 <= (A_ufixed + B_ufixed) + (C_ufixed + D_ufixed) ;
Because when you follow the rules, the size of the associativity identical expression ends up one bit bigger. While I have shown parentheses below, it is the same with them.
signal Sum_ufixed11 : ufixed(10 downto 0) ;
. . .
Sum_ufixed11 <= ((A_ufixed + B_ufixed) + C_ufixed) + D_ufixed ;
Also note that both packages support "+"[unsigned/ufixed, natural] return unsigned/ufixed, however, the natural argument does not influence the sizing. My take on this is 0 and 1 are simple to use, but other values must be scrutinized. If the integer is truncated, there is a warning, but in my mind maybe it should have been a failure instead (which would be consistent with what happens with array values). This certainly is open for discussion in the IEEE working group (see http://www.eda-twiki.org/cgi-bin/view.cgi/P1076/WebHome).
You're right, a warning about the use of this operator would be helpful. However, it is up to the developer to know if they need an addition operation that rolls over, or one in which and overflow will cause bad behavior.
When performing addition, and an overflow is undesired, the result needs to be 1 bit longer than the longest operand. Then you can check for overflow by seeing if the most significant bit is set to '1'.
Related
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.
Please consider this very simple minimal reproducible code:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity test is
generic ( LENGTH : integer range 1 to 16 := 5 );
Port ( x : in STD_LOGIC;
y : out STD_LOGIC_VECTOR(15 downto 0)
);
end test;
architecture Behavioral of test is
signal a : std_logic_vector (15 downto 0);
signal b : std_logic_vector (LENGTH - 1 downto 0);
signal i : integer range 0 to LENGTH-1 := 1;
begin
y <= a;
process
begin
if i = LENGTH then
i <= 1;
else
a <= a(15 downto i + 1) & b(i downto 0);
end if;
i <= i + 1;
end process;
end Behavioral;
My need is to join some elements of b into a, depending on i. By running the RTL on Vivado, it says:
[Synth 8-690] width mismatch in assignment; target has 16 bits, source has 20 bits
I don't really get why. Anyhow, the overall range will be 15 - (i + 1) + (i - 0) = 15 ... 0 and fits in the 16 bits of output -- what's the deal for 20 bits?
I should say the problem vanishes (obviously) if I use plain constants instead of i, but I still don't get what's going on.
For runtime variable I (as per the question)...
instead of a big CASE, you can use the value of I to generate masks, and evaluate (A and MASKA) or (B and MASKB). Which is equivalent to the multiplexer the synthesis tool would generate if it wasn't broken.
For generic I (it's not fair to move the goalposts in the comments!)
this approach generates unnecessary hardware, which will be optimised out by any competent synthesis tool.
(There are of course other problems with this code; I assume you deleted the clock, taking the MCVE notion a bit too far. You should leave it valid synthesisable code)
I want to write a function in VHDL which is given the top few bits of a std_logic_vector and does stuff to them but it seems to be that the indexing of my function still starts counting at the bottom of the whole vector.
I can get around this by first assigning my vector to a temporary signal and using that but I'm worried that I don't understand what's going on here.
Could someone explain why a and b don't get the same output in the below?
architecture rtl of inds is
function top_bit (c : std_logic_vector) return std_logic is
begin
return c(c'length-1);
end top_bit;
signal temp : std_logic_vector(2 downto 0);
begin
temp <= input(3 downto 1);
a <= top_bit(temp);
b <= top_bit(input(3 downto 1));
end rtl;
If you give them the input "0100", you get a='0', b='1'.
If you give them the input "1000", you get a='1', b='0'.
So a=temp(2)=input(3) and b=input(2) which is input("length of c" -1).
I don't think this makes sense, can someone justify it for me.
Edit: if you replace the declaration line with:
function top_bit (c : std_logic_vector(2 downto 0)) return std_logic is
then it works as I'd expect.
I suppose the vector c takes it's indexing from the vector it's given.
I'd like to see a function which takes an arbitrary slice of a vector and returns the top bit of that slice.
You are using the 'length attribute, where you could be using 'high. I think this would do what you're asking for.
I've got a print out on my wall of the table here http://www.csee.umbc.edu/portal/help/VHDL/attribute.html as a reference for what attributes are available.
The issue, is that c'length returns the length of the vector which is not necessarily a valid index. For example, say I declared the following signal:
signal temp : std_logic_vector(7 downto 4);
This would cause a range error calling top_bit. As you note in your comment on scary_jeff's answer, not all vectors are x downto 0. They could be x downto y. Or they could even by 0 to x or x to y. Assuming that c'length-1 is the top bit is only true if c is declared as std_logic_vector(N-1 downto 0) (which you discovered in your answer).
Just as a clarification. scary_jeff's answer is the correct way. However, you need to resolve what is meant by "top_bit". What if you are given a to vector, such as:
signal temp : std_logic_vector(4 to 7)
What is top bit? Bit 4 or bit 7? If you use 'high, you'll get bit 7. Is this the top bit? If you want bit 4 to be the top bit, you'll need to use 'low.
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.
I have a vector signal tmp : std_logic_vector(15 downto 0)
I have to shift it to left or right of n bit. how can I realize this operation. I thought to concatenation operation but I didn't know how use it.
Use the ieee.numeric_std library, and the appropriate vector type for the numbers you are working on (unsigned or signed).
Then the operators are `sla`/`sra` for arithmetic shifts (ie fill with sign bit on right shifts and lsb on left shifts) and `sll`/`srl` for logical shifts (ie fill with '0's).
You pass a parameter to the operator to define the number of bits to shift:
A <= B srl 2; -- logical shift right 2 bits
---
Update:
---
I have no idea what I was writing above (thanks to Val for pointing that out!)
Of course the correct way to shift signed and unsigned types is with the shift_left and shift_right functions defined in ieee.numeric_std.
The shift and rotate operators sll, ror etc are for vectors of boolean, bit or std_ulogic, and can have interestingly unexpected behaviour in that the arithmetic shifts duplicate the end-bit even when shifting left.
And much more history can be found here:
http://jdebp.eu./FGA/bit-shifts-in-vhdl.html
However, the answer to the original question is still
sig <= tmp sll number_of_bits;
There are two ways that you can achieve this. Concatenation, and shift/rotate functions.
Concatenation is the "manual" way of doing things. You specify what part of the original signal that you want to "keep" and then concatenate on data to one end or the other. For example: tmp <= tmp(14 downto 0) & '0';
Shift functions (logical, arithmetic): These are generic functions that allow you to shift or rotate a vector in many ways. The functions are: sll (shift left logical), srl (shift right logical). A logical shift inserts zeros. Arithmetric shifts (sra/sla) insert the left most or right most bit, but work in the same way as logical shift. Note that for all of these operations you specify what you want to shift (tmp), and how many times you want to perform the shift (n bits)
Rotate functions: rol (rotate left), ror (rotate right). Rotating does just that, the MSB ends up in the LSB and everything shifts left (rol) or the other way around for ror.
Here is a handy reference I found (see the first page).
I would not suggest to use sll or srl with std_logic_vector.
During simulation sll gave me 'U' value for those bits, where I expected 0's.
Use shift_left(), shift_right() functions.
For example:
OP1 : in std_logic_vector(7 downto 0);
signal accum: std_logic_vector(7 downto 0);
-- ...
accum <= std_logic_vector(shift_left(unsigned(accum), to_integer(unsigned(OP1))));
accum <= std_logic_vector(shift_right(unsigned(accum), to_integer(unsigned(OP1))));
Personally, I think the concatenation is the better solution. The generic implementation would be
entity shifter is
generic (
REGSIZE : integer := 8);
port(
clk : in str_logic;
Data_in : in std_logic;
Data_out : out std_logic(REGSIZE-1 downto 0);
end shifter ;
architecture bhv of shifter is
signal shift_reg : std_logic_vector(REGSIZE-1 downto 0) := (others<='0');
begin
process (clk) begin
if rising_edge(clk) then
shift_reg <= shift_reg(REGSIZE-2 downto 0) & Data_in;
end if;
end process;
end bhv;
Data_out <= shift_reg;
Both will implement as shift registers. If you find yourself in need of more shift registers than you are willing to spend resources on (EG dividing 1000 numbers by 4) you might consider using a BRAM to store the values and a single shift register to contain "indices" that result in the correct shift of all the numbers.
This is typically done manually by choosing the appropriate bits from the vector and then appending 0s.
For example, to shift a vector 8 bits
variable tmp : std_logic_vector(15 downto 0)
...
tmp := x"00" & tmp(15 downto 8);
Hopefully this simple answer is useful to someone
add_Pbl <= to_stdlogicvector(to_bitvector(dato_cu(25 downto 2)) sll 1);
add_Pbl is a std_logic_vector of 24 bit
dato_cu is a std_logic_vector of 32 bit
First, you need to convert the std_logic_vector with to_bitvector() function
because sll statement works with logic 1 and 0 bits.