How to write std_logic_vector assignment with input-dependent range in VHDL? - vhdl

I am trying to copy some part of a std_logic_vector into another, at a position (index) depending on an input. This can be synthesized in Vivado, but I want to use another tool (SymbiYosys, https://github.com/YosysHQ/SymbiYosys) for formal verification. SymbiYosys can use Verific as frontend to process VHDL, but Verific does not accept this. Here is a small piece of code which reproduces the problem. Verific complains that the "left range bound is not constant". So, is there a workaround to make Verific accept such variable range assignments ?
I already found this post VHDL: slice a various part of an array which proposes to use a loop and to assign values bit per bit, but I would rather not change my code now that it works with Vivado. Also I think such a loop would impair code readability, and perhaps implementation efficiency. Therefore, I am looking for a different method (maybe a way to turn this error into a warning, or a less drastic code modification).
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.NUMERIC_STD.all;
entity test is
port(
clk : in std_logic;
prefix : in std_logic_vector( 8*8 -1 downto 0);
msgIn : in std_logic_vector(128*8 -1 downto 0);
msgLength : in integer range 1 to 128;
test_out : out std_logic_vector((128+8)*8 -1 downto 0)
);
end test;
architecture behav of test is
begin
process (clk)
begin
if rising_edge(clk) then
test_out <= (others => '0');
test_out((msgLength+8)*8 -1 downto msgLength*8) <= prefix;
test_out( msgLength *8 -1 downto 0) <= msgIn(msgLength*8 -1 downto 0);
end if;
end process;
end behav;

A bit of shifting should make it (if your tools support the srl and sll operators). First left-align your message (left shift), left-pad it with your prefix and, finally, right-shift it:
process (clk)
variable tmp1: std_logic_vector(128*8 -1 downto 0);
variable tmp2: std_logic_vector((128+8)*8 -1 downto 0);
begin
if rising_edge(clk) then
tmp1 := msgIn sll (8 * (128 - msgLength)); -- left-align
tmp2 := prefix & tmp1; -- left-pad
test_out <= tmp2 srl (8 * (128 - msgLength)); -- right-shift
end if;
end process;
Remarks:
In case your tools do not support the srl and sll operators on std_logic_vector, try to work with bit_vector, instead. srl and sll have been introduced in the standard in 1993. Example:
process (clk)
variable tmp1: bit_vector(128*8 -1 downto 0);
variable tmp2: bit_vector((128+8)*8 -1 downto 0);
begin
if rising_edge(clk) then
tmp1 := to_bitvector(msgIn) sll (8 * (128 - msgLength));
tmp2 := to_bitvector(prefix) & tmp1;
test_out <= to_stdlogicvector(tmp2 srl (8 * (128 - msgLength)));
end if;
end process;
The synthesis result may be huge and slow because this 1088 bits barrel shifter with 128 possible different shifts is a kind of monster.
If you have time (I mean several clock cycles) to do it, there are probably much smaller and more efficient solutions.

Related

8 bit serial to parallel shifter in vhdl

I programmed an 8-bit shifter in vhdl:
entity 8b is
port(s, clk : in std_logic; p : out std_logic_vector (7 downto 0));
end entity;
architecture arch of 8b is
Signal iq : std_logic_vector (7 downto 0);
begin
process(clk)
begin
if rising_edge(clk) then
iq(7) <= s;
iq(6 downto 0) <= iq(7 downto 1);
end if;
end process;
p <= iq;
end architecture;
The idea is that I'm taking input and giving it to my first D-FF.
Then over the next 7 cycles, the other Flip Flops get the other serial inputs which will be given to the parallel output p.
However, I'm not sure if this logic is flawed because this is the solution we got for this exercise:
architecture behavior of 8b is
signal p_intern : std_logic_vector(7 downto 0);
begin
P <= p_intern;
process(CLK)
begin
if rising_edge(CLK) then
p_intern <= p_intern(6 downto 0) & S;
end if;
end process;
end architecture;
But I don't get the p_intern <= p_inter(6 downto 0) & S; part.
Can someone please explain the logic behind this and if my version is also valid?
The only difference between the two implementations seem to be the lines
iq(7) <= s;
iq(6 downto 0) <= iq(7 downto 1);
vs.
p_intern <= p_intern(6 downto 0) & S;
and that iq is named p_intern. Let's assume they are both named iq for the sake of comparison.
Let's see what they are doing:
The first implementation (yours) assigns to the positions of iq:
7 6 5 ... 1 0
s iq(7) iq(6) ... iq(2) iq(1)
The second implementation (the solution) assigns
7 6 5 ... 1 0
iq(6) iq(5) iq(4) ... iq(0) s
Where iq(6 downto 0) & s means "concatenate s to the right of iq(6 downto 0)".
So they are not equivalent. Your implementation shifts in the values from the left, and the solution shifts in the values from the right. Which one is correct depends on the specification (presumably the solution is correct).

Use of conv_integer in VHDL

I am in the process of trying to write some code that will simply just shift a 32 bit vector left or right, with a 5 bit input that will be used for the shift amount (shamt). The issue I am having is trying to convert an std_logic_vector to an integer. My code is this:
library ieee;
use ieee.STD_LOGIC_1164.all;
use ieee.STD_LOGIC_ARITH.all;
entity shiftlogical is
port(x : in std_logic_vector(31 downto 0);
shamt : in std_logic_vector( 4 downto 0);
y : out std_logic_vector(31 downto 0));
end shiftlogical;
architecture beh of shiftlogical is
signal shift : integer;
signal temp : std_logic_vector(31 downto 0);
begin
shift <= conv_integer(unsigned(shamt));
temp <= x(shift downto 0);
y <= temp;
end beh;
The code is not complete I know, but to test some ideas I am trying to pass "00010" (2) into shamt, but shift comes out to be -2147483648. But I cannot figure out why it is doing this, nor can I find any resources online that shows anything different than what I am doing. I greatly appreciate any help.
-2147483648 (-2**31) is the default initial value for integers, being the leftmost, most negative value in its range. It suggests that the signal assignment to shift has not executed. Most likely because it is a continuous assignment and there hasn't been an event on shamt to cause it to update.
std_logic_arith is not an IEEE standard library. You should use to_integer() from ieee.numeric_std instead. It is also beneficial to keep numeric ports as unsigned or signed so that your intent is clear and to minimize type conversions. Also, you cannot directly assign the variable length slice of x to temp since their lengths do not match. You should use resize() (from numeric_std) to extend the length back to 32-bits or rethink your approach.
I fixed the obvious typo in the entity name, started the simulation (ModelSim) and forced the signal shamt to "00010". Then just after trying to run for 1 ps, ModelSim complains about:
Fatal: (vsim-3420) Array lengths do not match. Left is 32 (31 downto 0). Right is 0 (-2147483648 downto 0 (null array)).
Time: 0 ps Iteration: 0 Process: /shiftlogical/line__16 File: shiftlogical.vhdl
Fatal error in Architecture beh at shiftlogical.vhdl line 16
That is because all your concurrent statements are executed in parallel. The new signal values are scheduled for the next delta cycle within the simulation. Thus, the line
temp <= x(shift downto 0);
is executed with the old value of shift which is the initial value of this signal. The initial value of an integer is -2**31 as also Kevin pointed out.
Of course you can initialize the signal shift, but the only value which will not result in an error will be 31 because in this asignment the signal on the left and the expression on the right must match in array (std_logic_vector) size. The signal shamt must be forced to "11111" as well, so that shift keeps 31.
You cannot easily fix this, because for a left shift you must add zeros at the right (LSB) and for a right shift zeros or the sign at the left (MSB).
#Martin Zabel what I had really tested there was to see if shift would hold an integer value which it did until I tried to pass it in for temp <= x(shift downto 0); What I realized was that the signal needed to really be a variable to work as intended and as follows my code consists of:
library ieee;
use ieee.STD_LOGIC_1164.all;
use ieee.STD_LOGIC_ARITH.all;
entity shiftlogical is
port(x: in std_logic_vector(31 downto 0);
shamt: in std_logic_vector(4 downto 0);
dir: in std_logic;
y: out std_logic_vector(31 downto 0));
end shiftlogical;
architecture beh of shiftlogical is
begin
process(dir)
variable shift : integer;
begin
shift := conv_integer(unsigned(shamt));
if(dir = '0') then --Left shift
y(31 downto shift) <= x(31-shift downto 0);
y(shift downto 0) <= (others => '0');
elsif(dir = '1') then --Right shift
y(31-shift downto 0) <= x(31 downto shift);
y(31 downto 31-shift) <= (others => '0');
else --Always left shift
y(31 downto shift) <= x(31-shift downto 0);
y(shift downto 0) <= (others => '0');
end if;
end process;
end beh;

Accessing 2 elements of the same array in VHDL

I am trying to assign 2 values from 2 different addresses in my array in VHDL, but somehow they always return to me a wrong value (most of the time, zero). I tested it with only 1 address and 1 data output it returned the correct value.
architecture Behavioral of registerFile is
type reg_type is array (31 downto 0) of std_logic_vector (31 downto 0);
signal REG : reg_type := (x"00000031", x"00000030", x"00000029", x"00000028", x"00000027", x"00000026", x"00000025", x"00000024", x"00000023", x"00000022", x"00000021", x"00000020",x"00000019",x"00000018", x"00000017", x"00000016", x"00000015", x"00000014", x"00000013", x"00000012", x"00000011", x"00000010", x"00000009", x"00000008", x"00000007",x"00000006", x"00000005", x"00000004", x"00000003", x"00000004", x"00000001", x"00000000");
begin
process(clk)
begin
if clk'event and clk='1' then
if ENABLE = '1' then
if readReg = '1' then -- read from register
DATAone <= REG(conv_integer(ADDRone));
DATAtwo <= REG(conv_integer(ADDRtwo));
else
REG(conv_integer(ADDRone)) <= DATAone;
REG(conv_integer(ADDRtwo)) <= DATAtwo;
end if;
end if;
end if;
end process;
end Behavioral;
Would appreciate some help, I tried googling but it's all either multidimensional arrays or only accessing 1 element at a time.
Thanks.
I'm not sure that this is synthesizable in most fabric. You could create two copies of the reg array and index into each of them.
It seems like you are trying to implement a quad-port memory. Anyway, even if your register file is not exactly a 4-port memory, it probably can be implemented around one.
Altera has an example of such a memory in their Advanced Synthesis Cookbook. The picture below shows the relevant part:
If use the Altera example files, it will instantiate Altera primitives, and use FPGA block RAM for storage. If you are concerned about portability, or you just want to look at some VHDL code that does what you want, check the example below. It implements roughly the same circuit shown in the figure, and it will most likely be synthesized as distributed memory in the FPGA.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
-- Quad-port RAM with 2 read ports 2 write ports. The design uses 2 memory blocks
-- (MAIN_MEMORY and SHADOW_MEMORY) to allow for simultaneous writes. Port A writes to
-- main memory, Port B writes to shadow memory. On a read from either port, data is
-- read from the memory block that was most recently written at the given position.
entity quad_port_ram is
generic (
ADDRESS_WIDTH: natural := 5;
DATA_WIDTH: natural := 32
);
port (
clock: in std_logic;
read_addr_a: in natural range 0 to 2**ADDRESS_WIDTH-1;
read_data_a: out std_logic_vector(DATA_WIDTH-1 downto 0);
write_addr_a: in natural range 0 to 2**ADDRESS_WIDTH-1;
write_data_a: in std_logic_vector(DATA_WIDTH-1 downto 0);
write_enable_a: in std_logic;
read_addr_b: in natural range 0 to 2**ADDRESS_WIDTH-1;
read_data_b: out std_logic_vector(DATA_WIDTH-1 downto 0);
write_addr_b: in natural range 0 to 2**ADDRESS_WIDTH-1;
write_data_b: in std_logic_vector(DATA_WIDTH-1 downto 0);
write_enable_b: in std_logic
);
end;
architecture rtl of quad_port_ram is
type memory_type is (MAIN_MEMORY, SHADOW_MEMORY);
type memory_type_array is array (natural range <>) of memory_type;
-- Keep track of which memory has the most recently written data for each address
signal most_recent_port_for_address: memory_type_array(0 to 2**ADDRESS_WIDTH-1);
type memory_array is array (0 to 2**ADDRESS_WIDTH-1) of std_logic_vector(DATA_WIDTH-1 downto 0);
type dual_memory_array is array (memory_type) of memory_array;
-- Store the actual memory bits. Access like this:
-- memory_data(memory_type)(address)(bit_position)
signal memory_data: dual_memory_array;
-- Auxiliary signals to decide where to read the data from (main or shadow)
signal most_recent_port_for_addr_a, most_recent_port_for_addr_b: memory_type;
begin
process (clock) begin
if rising_edge(clock) then
if write_enable_a then
memory_data(MAIN_MEMORY)(write_addr_a) <= write_data_a;
most_recent_port_for_address(write_addr_a) <= MAIN_MEMORY;
end if;
if write_enable_b then
if (write_enable_a = '0') or (write_addr_a /= write_addr_b) then
memory_data(SHADOW_MEMORY)(write_addr_b) <= write_data_b;
most_recent_port_for_address(write_addr_b) <= SHADOW_MEMORY;
end if;
end if;
end if;
end process;
most_recent_port_for_addr_a <= most_recent_port_for_address(read_addr_a);
most_recent_port_for_addr_b <= most_recent_port_for_address(read_addr_b);
read_data_a <= memory_data(most_recent_port_for_addr_a)(read_addr_a);
read_data_b <= memory_data(most_recent_port_for_addr_b)(read_addr_b);
end;

vhdl "parse error, unexpected FOR"

I try to write programm on vhdl in ise 14.4 for crc16 calculation but dont understand why get "parse error, unexpected FOR" in it. Tried to put it into process but it dont works too.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
entity crc16 is port(
clk : in STD_LOGIC:='0');
end crc16;
architecture Behavioral of crc16 is
signal data:std_logic_vector(15 downto 0):="1010101010101010";
signal ext_data:std_logic_vector(31 downto 0);
signal crc16_original:std_logic_vector(15 downto 0):="1100000000000010";
signal crc16:std_logic_vector(15 downto 0);
signal position:std_logic_vector(5 downto 0);
signal crc_out:std_logic_vector(14 downto 0);
signal i:std_logic_vector(5 downto 0);
begin
for i in 1 to 15 loop
ext_data(i+16)<=data(i);
end loop;
for i in 1 to 15 loop
ext_data(i)<='0';
end loop;
while ext_data > "111111111111111" loop
for i in 0 to 31 loop
if ext_data(i)="1" position=i;
end loop;
crc16<= crc16_original srl 31-position;
ext_data<=ext_data xor crc16;
end loop;
for i in 0 to 14 loop
crc_out(i)<=ext_data(i);
end loop;
end Behavioral;
There are several issues to point out:
The for-loop must be in a process, so that is likely to cause the “parse error, unexpected FOR” that you see.
The relation compare with > may give unexpected result for std_logic_vector, so you may take a look at the numeric_std package for casting as for example unsigned(std_logic_vector) before comparison is made.
Compare ext_data(i) = "1" is illegal, since "1" is taken as std_logic_vector, where as ext_data(i) is std_logic; instead ext_data(i) = '1' will compile.
Illegal construction around if ext_data(i) = "1" position=i;, since no then etc.
There is an signal with identifier i, which i is also used as loop variable, with the result that position <= i is taken as an integer assign to std_logic_vector; use different names for signals and loop variables.
Assign to signal is not position = i but position <= i, like elsewhere.
Expression 31-position mixes integer and std_logic_vector, which can't be done with the selected packages. Use casting with unsigned.
The ext_data<=ext_data xor crc16 uses different size arguments, since ext_data is 32 bits and crc16 is 16 bits; this does probably not yield the expected result.
srl is not defined for std_logic_vector (VHDL-2002), so consider casting with unsigned for well-defined behavior.
Assuming that that your code is "sandbox" code, since it has no outputs.
Based on the above, you may consider doing some initial experiments with smaller designs, in order to get familiar with the different VHDL constructions, and learn how this simulates and maps to hardware; remember VHDL is a "Hardware Description Language" and not a programming language.
Below is some code that compiles in ModelSim, but is unlikely to give the expected result:
library ieee;
use ieee.std_logic_1164.all;
entity crc16 is port(
clk : in std_logic := '0');
end crc16;
library ieee;
use ieee.numeric_std.all;
architecture Behavioral of crc16 is
signal data : std_logic_vector(15 downto 0) := "1010101010101010";
signal ext_data : std_logic_vector(31 downto 0);
signal crc16_original : std_logic_vector(15 downto 0) := "1100000000000010";
signal crc16 : std_logic_vector(15 downto 0);
signal position : std_logic_vector(5 downto 0);
signal crc_out : std_logic_vector(14 downto 0);
signal i_sig : std_logic_vector(5 downto 0);
begin
process (clk) is
begin
if rising_edge(clk) then
for i in 1 to 15 loop
ext_data(i+16) <= data(i);
end loop;
for i in 1 to 15 loop
ext_data(i) <= '0';
end loop;
while ext_data > "111111111111111" loop
for i in 0 to 31 loop
if ext_data(i) = '1' then
position <= i_sig; -- TBD[Probably not right code, but compiles]
end if;
end loop;
crc16 <= std_logic_vector(unsigned(crc16_original) srl (31 - to_integer(unsigned(position))));
ext_data <= ext_data xor crc16;
end loop;
for i in 0 to 14 loop
crc_out(i) <= ext_data(i);
end loop;
end if;
end process;
end Behavioral;

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