VHDL - custom shifter - concatenation input (in defined range) and remaining zeros - logic

I am trying to write my own shifter. It all doesn't matter about the shifter, so please don't recommend me to do shifter by different way. The shifter is here only for demonstration my problem that I've reached.
Here is a piece of shifter description:
1: with operand_b select result <=
2: operand_a when "0000",
3: operand_a(14 downto 0) & '0' when "0001",
4: operand_a(13 downto 0) & "00" when "0010",
5: operand_a(12 downto 0) & "000" when "0011",
6: operand_a(11 downto 0) & "0000" when "0100",
7: operand_a(10 downto 0) & "00000" when "0101",
8: operand_a(9 downto 0) & "000000" when "0110",
9: operand_a(8 downto 0) & "0000000" when "0111",
10: operand_a(7 downto 0) & "00000000" when "1000",
11: operand_a(6 downto 0) & "000000000" when "1001",
12: operand_a(5 downto 0) & "0000000000" when "1010",
13: operand_a(4 downto 0) & "00000000000" when "1011",
14: operand_a(3 downto 0) & "000000000000" when "1100",
15: operand_a(2 downto 0) & "0000000000000" when "1101",
16: operand_a(1 downto 0) & "00000000000000" when "1110",
17: operand_a(0) & "000000000000000" when others;
Where operand_b is value used to be shifted by, result is output of the shifter.
As you can see, it is quite bottlenecking. What will happen when we have for instance 64 bit shifter - a lot of useless zeros. I was finding a lot on web how to write that more rationally, but every solution that I tried, like operand_a(13 downto 0) & (others => '0') when "0010" or (15 downto 2 => operand_a(13 downto 0), others => '0') when "0010" or (operand_a(13 downto 0), others => '0') when "0010" doesn't work. Everytime I got a report with some error.
Any solutions, please?
Sorry for my English.
EDIT:
When I replace operand_a(13 downto 0) & "00" when "0010", on the 4th line with:
operand_a(13 downto 0) & (others => '0') when "0010", it returns
Can not determine the "others" values in aggregate. (<= also for line 17)
Size of concat operation is different than size of the target.
(15 downto 2 => operand_a(13 downto 0), others => '0') when "0010", it returns
The type of the element in aggregate does not correspond to any array type.
(operand_a(13 downto 0), others => '0') when "0010", it returns
No array or record type can be found that has elements of types matching the aggregate.
operand_a(13 downto 0) & (1 downto 0 => '0') when "0010",
This one is working, but isn't here any way to use others?

Value expressions such as (operand_a(13 downto 0), others => '0') should work in IEEE Std 1076-2008.
Compare 9.3.3.3 Array aggregates, para 1 and 2:
For an aggregate of a one-dimensional array type, each choice shall specify values of the index type, and the expression of each element association shall be of either the element type or the type of the aggregate. If the type of the expression of an element association is the type of the aggregate, then either the element association shall be positional or the choice shall be a discrete range.
For an element association with a choice that is a discrete range and an expression of the element type of the aggregate, the value of the expression is the element at each index value in the range.
to IEEE Std 1076-1993 Array aggregates para 1:
For an aggregate of a one-dimensional array type, each choice must specify values of the index type, and the expression of each element association must be of the element type. An aggregate of an n-dimensional array type, where n is greater than 1, is written as a one-dimensional aggregate in which the index subtype of the aggregate is given by the first index position of the array type, and the expression specified for each element association is an (n-1)-dimensional array or array aggregate, which is called a subaggregate. A string or bit string literal is allowed as a subaggregate in the place of any aggregate of a one-dimensional array of a character type.
In -2008 we are allowed to have association elements that are the type of the aggregate (a one-dimensional array type). This allows association of slice names of the same type.
At the same time -2008 9.3.3.3 para 7 allows the use of the others choice:
e) As a value expression in an assignment statement, where the target is a declared object (or member thereof), and either the subtype of the target is a fully constrained array subtype or the target is a slice name
(And the target being a slice name has been added from previous revisions.) The rules for where you can use the others choice require you have context that supplies a subtype constraint.
So it tells us the VHDL implementation you are using is not -2008 compatible (or is not being used as -2008 compatible).
This feature is one of generally low priority for implementation in upgrading to -2008 compatibility.
Should you find your VHDL tool lacking support there are other ways to express your shifter such as:
SHIFTER:
process (operand_b, operand_a)
variable b: integer range 0 to operand_a'HIGH;
begin
result <= (others => '0');
if not is_x(operand_b) then -- culling meta-values
b := to_integer(unsigned(operand_b));
for i in result'range loop
if i = b then
result(result'high downto i) <=
operand_a (operand_a'high - i downto 0);
end if;
end loop;
end if;
end process;
This process is synthesis eligible, because the slice ranges depend on static values including the loop constant, loops are unrolled or parallelized for synthesis. There is an assignment statement for each binary value of operand_b and you rely on optimization in synthesis (which does amazingly well for things like multiplexers and shifters).
This method works by writing all of result as '0's then writing the portions of operand_a desired to result. It depends on sequential assignment inside the same process (and can be used with a sequential conditional signal assignment in a -2008 compatible implementation) and with if statement or a case statement. (But not a selected signal assignment which depends on same left hand side target).
The two assignment statements being executed in one process depends on there being a single driver and is supported by synthesis. The last written array element values overwrite earlier array element value writes, there is only one time slot for any particular simulation time in a projected output waveform.

Related

Assigning initial value to VHDL vector

I am just learning the syntax of VHDL
I'd like to assign an initial value of '1' to Qout(0) and the rest '0'.
I cannot find the reference that shows me the correct syntax.
This gave me an error:
signal Qout: Std_Logic_Vector (4 downto 0) :='1';
As user1155120 says, in VHDL the width of the right hand side has to match the width of the left hand side of an assignment operator (<= or :=).
So, you could use the literal that corresponds to a std_logic_vector, which is a string:
signal Qout: Std_Logic_Vector (4 downto 0) := "00001";
(a string literal in VHDL is enclosed within double quotes). Or (and this is what a more experience VHDL user would do) use an aggregate:
signal Qout: Std_Logic_Vector (4 downto 0) := (0 => '1', others => '0');
The construct on the right hand side is an aggregate. An aggregate is a construct for representing composite data types such as arrays (which is what a std_logic_vector is) and record types (like a struct in C). The above example is saying "make element 0 equal to '1' and make all the other elements equal to '0'. Element 0 is the right hand side, because the array was declared (4 downto 0) (not (0 to 4)).
Using an aggregate might be considered a better way of doing it because, whilst not to clear to a beginner, the code is more maintainable: if the width of the signal were to change, you would not have to modify the aggregate.
You might want to seriously consider why you want to initialise this signal at all. If you are using an FPGA, it may be the case that that the corresponding flip-flops will be initialised as you wish. (I assume this signal Qout will become 5 flip-flops because of the name you have chosen.) On a chip this would never ever be the case - your initialisation would be ignored. You might want to consider whether providing a reset to your flip-flops would be a better solution than initialising a signal, eg an active-high synchronous reset:
process (Clock)
begin
if Reset = '1' then
Qout <= (0 => '1', others => '0');
elsif rising_edge(Clock) then
...

how to assign a slice of signal to single std logic without looping?

I have this code
for i in 63 downto 48 loop
s1(i)<= b(31);
end loop;
I want to get rid of the loop , using one assign statment
something like that
s1(63 downto 48)<= b(31)
but it doesnt work because i am assigning a std vector to a single std logic
I want to assign the std vector to another std vector with same value (here b(31) )
Is it possible in vhdl ?
How about this:
s1(63 downto 48) <= (others => b(31));
Jeff's answer should do the trick in this case because the required type (including index range) is known from the assignment target.
But there are cases where it isn't, in more complex expressions, because (others => x) doesn't specify the size of the object created.
So it's also worth knowing you can explicitly specify a range in an array aggregate, and say
s1(63 downto 48) <= (15 downto 0 => b(31));
s1(63 downto 48) <= (63 downto 48 => b(31)); -- a bit clearer
s1(63 downto 48) <= (63 => not b(31), 62 downto 48 => b(31));
etc.

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

VHDL Is there a cleaner way to set specific bit, given the bit number?

Given a bit number, I am trying to set that bit in a std_logic_vector. This is for toggling various clock outputs one at a time.
First of all, I've completely given up on sll, or SHIFT_LEFT which seems to be the obvious way to do it, but which totally doesn't work at all.
variable v_cmd_clk_1: std_logic_vector(11 downto 0);
...
--- set bit number "s_proc_chan", plus 4, in v_cmd_clk_1
v_cmd_clk_1 := "0000" & "0000" & "0000";
v_cmd_clk_1( to_integer ( unsigned(s_proc_chan(2 downto 0))) + 4 ) := '1';
...
-- And then later on in the process assign it to an actual signal
cmd_clk <= v_cmd_clk_0;
Is there a better or cleaner syntax for doing this?
Thanks.
Three suggestions for you. First one uses aggregates:
v_cmd_clk_1 <= (to_integer(unsigned(s_proc_chan(2 downto 0)))+4) => '1', others => '0');
Second one uses integer to unsigned conversion:
v_cmd_clk_1 <= std_logic_vector(to_unsigned(2**(to_integer(unsigned(s_proc_chan(2 downto 0)))+4)); -- No guarantee on parentheses matching
Third one, using shift_left:
v_cmd_clk_1 <= std_logic_vector(shift_left(resize(unsigned'("1"), v_cmd_clk_1'length), to_integer(unsigned(s_proc_chan(2 downto 0)))+4));
The principle of setting a single bit given by index, as you already done, is fine, and it shows the intention of the code, which is setting a bit given by an index.
It would be possible to eliminate the + 4 offset through use of other std_logic_vector ranges, but a decent synthesis tool eliminates the offset, so an add operation is not implemented.
Anyhow, as answer to the comment, the + 4 can be eliminated if a std_logic_vector is addressed directly into 0 to 7, instead of addressing 4 to 11, and the last 4 '0's can just be postpended, like:
variable v_cmd_clk_1 : std_logic_vector(11 downto 0);
variable v_cmd_clk_upper: std_logic_vector( 7 downto 0);
...
v_cmd_clk_upper := (others => '0');
v_cmd_clk_upper(to_integer(unsigned(s_proc_chan(2 downto 0)))) := '1';
v_cmd_clk_1 := v_cmd_clk_upper & "0000";
The aggregate suggested by Jonathan Drolet looks nice, but for example Altera Quartus II won't allow this for synthesis, since it requires constant choice values in aggregates. Using shift or 2** will synthesize.
Note that initial clearing is more general with:
v_cmd_clk_1 := (others => '0');

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