Use Others with Aggregate in VHDL - vhdl

I'm trying to return a word which is defined as:
subtype word is std_logic_vector(word_len - 1 downto 0);
By concatenating a whole bunch of other inputs from a record like so (this is inside a switch block):
return get_opc(ins.op) & std_logic_vector(ins.val) & "0000"; -- TODO Replace these with some sort of others construct
However, different opcodes have different sets of other inputs and so the amount that needs to be right-filled with zeroes changes. I'm not a huge fan of manually hardcoding trailing zeroes, it's a mess ad it won't play well if I resize my word.
My instinct was to try:
return get_opc(ins.op) & std_logic_vector(ins.val) & (others => '0');
But apparently you can't use others as an aggregate in this context.
I know I could create a buffer the size of a word, fill it with zeroes and then slot my different inputs into different slices but AFAIK, that would be extremely verbose, requiring me to specify the start and end point of each slice rather than just concatenating them on the left. This would soon get very messy.
Is there any easy way around this that keeps everything concise?

If you use VHDL-2008 (or VHDL-2019!), you can return an aggregate like this:
return (get_opc(ins.op), std_logic_vector(ins.val), others => '0');
This assumes that your return type is constrained. In other words, this is OK:
function F return word is
begin
return (get_opc(ins.op), std_logic_vector(ins.val), others => '0');
end function;
whereas if your return type is unconstrained like this, then it is not OK:
function F return std_logic_vector is

Related

Is this VHDL code making an assignment conflict?

In VHDL and other hardware languages, it is my understanding that all conditions from true logic in a process happen at the same time. I have a std_logic FLAG variable that appears to me to have a conflict. I saw this code in a publication and I do not understand it. It looks like the FLAG variable can be assigned two values. Is this bad code or am I missing something? (The Process has some input triggers but no CLK.)
The variable names were changed to protect the innocent.
...
process(op_state, ...)
begin
FLAG <= '0';
case op_state is
when STATE_SLEEP =>
FLAG <= '1';
when some other cases not containing FLAG
end case;
end process;
I am going to assume both assignments are in the same process.
That coding style is allowed. If there are multiple assignment in a process the LAST one 'wins' from the previous one. This is even on part of a vector.
I often use this to set a default:
-- Default set all read bits to zero
axil_rdata<= (others => '0');
case (radrs) is
when X"00" => axil_rdata( 7 downto 0) <= control;
when X"04" => axil_rdata(15 downto 0) <= status;
when X"08" => axil_rdata <= counter;
...
Thus all bits of axil_rdata are first set to zero. Then some bits get assigned a new value.

Convert enum type to std_logic_vector VHDL

I want to know if it is possible to convert a enum type, like FSM states to std_logic_vector or integer. I'm doing a testbench with OSVVM for a FSM and I want to use the scoreboard package to automatically compare the expected state with the actual one.
Thanks!
To convert to integer, use:
IntVal := StateType'POS(State) ;
From there, it is easy to convert to std_logic_vector, but I prefer to work with integers when possible as they are smaller in storage than std_logic_vector. For verification, it will be easier if you start to think more about integers when the value is less than 32 bits.
If you need it as std_logic_vector, using only numeric_std you can:
Slv8Val := std_logic_vector(to_unsigned(IntVal, Slv8Val'length)) ;
For verification, I liberally use numeric_std_unsigned, so the conversion is a easier:
Slv8Val := to_slv(IntVal, Slv8Val'length) ;
In the event you have an integer and want to convert it back to a enumerated value, you can use 'VAL.
State := StateType'VAL(IntVal) ;
In OSVVM, we use records with resolved values to create a transaction interface. We have a resoled types for integers (osvvm.ResolutionPkg.integer_max). We transfer enumerated values through the record using 'POS (as we put it in) and 'VAL (as we get it out).
Note don't confuse 'VAL with 'VALUE. 'VALUE converts a string to a value - opposite to 'IMAGE.
You of course learn all of this in SynthWorks' OSVVM class :).
Maybe like this...
function my_func(inp : t_my_enum) return integer is
begin
case inp is
when stateA =>
return 1;
when stateB =>
return 2;
when others =>
return 0;
end case;
end function my_func;
... <= my_func(stateB);`

Efficient synthesizable way to read inputs of variable length in one simulation

I am new to vhdl and I have a question that I could not find an answer to.
I am trying to implement some algorithm that operates on a vector 1024 bits long. So I have to read data from a file and insert them in this temp_vector(1023 downto 0).
The file contains many inputs and for each one the algorithm operates on the output of the previous one. The problem is that the length of this input data is not constant, but each input length varies from 0 to 16000.
In each cycle I can only read 64 bits from my testbench and insert them. At specific moments I also have to append sequences of bits of variable length to the already inserted data. I use the integer bit_position to keep track of the last position of temp_vector I inserted data in, and the integer data_in_length to tell me the actual length of the data that I read in each cycle.
After temp_vector is full or I finished inserting all the input, I stop reading and the algorithm operates on temp_vector and then the reading from the file resumes.
I get correct results in simulations. The problem I have is with FPGA and ASIC synthesis. It has to do with the varying length of the input data.
I came up with two approaches in order to read the file and insert the data to temp_vector.
First
temp_vector_proc : process(clk, rst_n)
begin
if rst_n = '0' then
state <= RESET;
elsif clk'event and clk = '1' then
case state is
when RESET =>
state <= TAKING_DATA;
enable_calculations <= '0';
bit_position <= 0;
temp_vector <= (others => '0');
when TAKING_DATA =>
if take_data = '1' then
for i in 0 to 63 loop
temp_vector(i+bit_position) <= data_in_keyak(i);
end loop;
bit_position <= bit_position +data_in_length;
end if;
when ...
.....
end case;
end if; -- end rst_n = '0'
end process temp_vector_proc;
And second
when TAKING_DATA =>
if take_data = '1' then
temp_vector(63+ bit_position downto bit_position) <= data_in(63 downto 0);
bit_position <= bit_position +data_in_length;
end if;
when ...
.....
end case;
The second approach is working in FPGA but it is taking a lot of time and consuming too many Logic Elements and for design compiler it is returning an error that the range has to be constant.
The first one is also working in FPGA and returning no error in ASIC ( Design compiler), but for the ASIC it runs forever (I let it run overnight). It was stuck at beginning Pass 1 Mapping of the entity that contains the first code.
I hope I explained my problem enough and I would really appreciate some thoughts on how to implement it in a more efficient way.
I do not think I can go with generics because the file is read and operated on in one go, so the lengths are changing during the simulation. I also though about shifting but since the shifting value would be changing each time, i guess that it would still consume a lot of time/area.
Finally could my whole approach be wrong? I mean is it possible that for FPGA and especially for ASIC I need to be working on specific input sizes? That would mean that I should try to write and synthesize code that does not work for all of my file, but for some of its inputs with some specified size only.
Thanks a lot in advance for your time.

How can I extract elements from a record using an integer reference in VHDL?

Firstly here is what I'm aiming to do, using made-up VHDL syntax...
type type_johns_record is
first_element : std_logic;
second_element: std_logic_vector(3 downto 0);
third_element : boolean;
end record;
....
....
for ii in johns_record'range loop
if johns_record.type_johns_record'val(ii) = .... then
exit;
end if;
end loop;
Hopefully you can see that I'm trying to reference the elements of a record using similar syntax to that which can be used to reference an enumerated type. This however (of course) does not work. Is there a similar syntax that will work? My solution at the moment is to use a record_info field and work using std_logic_vectors as shown below....
type type_johns_record is record
first_element : std_logic;
second_element : std_logic_vector(3 downto 0);
third_element : boolean;
record_info : type_integer_array(2 downto 0);
end record;
function type_johns_record2slv(d : type_johns_record) return std_logic_vector is
begin
return (d.first_element & d.second_element & bool2sl(d.third_element));
end function;
constant johns_record_zero : type_johns_record := (first_element => '0',
second_element => "0000",
third_element => false,
record_info => (1, 4, 1));
-- can be used with any type for which a record_info is known
function get_record_element(input : std_logic_vector; element_number : integer; record_info : type_integer_array) return std_logic_vector is
variable r : type_slv32_array(record_info'length-1 downto 0);
variable pos : integer := 0;
begin
for ii in record_info'range loop
r(ii)(record_info(ii)-1 downto 0) := input(pos+record_info(ii)-1 downto pos);
end loop;
return r(element_number)(record_info(element_number)-1 downto 0);
end function;
I can then use these functions (which are in a package) as follows...
for ii in johns_record.record_info'range loop
if get_record_element(type_johns_record2slv(johns_record), ii, johns_record.record_info) = conv_std_logic_vector(4, johns_record.record_info(ii)) then
exit;
end if;
end loop;
This really sucks and specifying the record_info is error prone and only marginally less time consuming that writing out individual element comparisons line by line. Please offer a better solution!!!
In the IEEE VHDL Standards group, there are actually two proposals relating to this:
http://www.eda.org/twiki/bin/view.cgi/P1076/RecordMemberAttribute
and
http://www.eda.org/twiki/bin/view.cgi/P1076/RecordIntrospection
This does not mean relax, someone else will address the issue. Instead, we need you to comment on it and/or propose additional use models (to help with justification). All of our work is done by volunteers - just like you - no memberships required for basic participation. Much of our work is done on the TWIKI and email reflector and all with VHDL background are welcome to participate. Drop me an email, I will get you setup - see my Stack Exchange profile for details.
To participate, start here: http://www.eda.org/twiki/bin/view.cgi/P1076/
Current proposals:
http://www.eda.org/twiki/bin/view.cgi/P1076/CollectedRequirements
Meeting information:
http://www.eda.org/twiki/bin/view.cgi/P1076/MeetingMinutes
If you'd like to shorten your implementation code slightly, you could write a custom function for each record type (which you already have to do to convert to slv; this would just be a bit longer) that returns the nth element, like:
function get_record_element(input : johns_record_type; element_number : natural) return std_logic_vector is
begin
case element_number is
when 0 =>
return to_slv(input.first_element);
when 1 =>
return to_slv(input.second_element);
...
end function;
...
if get_record_element(johns_record, 2) = ... then
where to_slv is just a set of helper functions to convert other types. Is that more or less tedious than writing a more generic function and using an extra record element?
Instead of a record, then an array may be used, where the index value are based on an enumerated type, and the array elements are a super-set of the required type. Declaration like:
type johns_elements_t is (FIRST, SECOND, THIRD);
subtype johns_type_t is std_logic_vector(3 downto 0); -- Super-set of required types
type johns_array_t is array (johns_elements_t) of johns_type_t;
signal johns_array : johns_array_t;
Use is then like:
for johns_index in johns_array'range loop
if johns_array(johns_index) = "0000" then
exit;
end if;
VHDL attributes can be used to convert the values in the enumerated type between natural, like for example:
for natural_index in 0 to johns_elements_t'pos(johns_elements_t'high) loop
if johns_array(johns_elements_t'val(natural_index)) = "0000" then
Access to elements must then take into account how the contents is used for each elements accessed by the values in the enumerated type, but that is the case anyway when using a record, but now where is no type checking in the compiler. Another use case is:
if johns_array(FIRST)(0) = '1' then -- Used as std_logic
johns_array(FIRST)(0) <= '0';
...
In synthesis, the tool is likely to remove the unused bits in elements where only a subset of the bits are used, so the final design is not larger.
Also, some synthesis tools (for example Altera Quartus II) does not accept functions that return variable length results like std_logic_vector, so a solution for synthesis should taken that into account, since a functions like get_record_element may result in an error.

VDHL loop with variable outside of the process (how to be)

How can I avoid variable in this loop (outside the process)?
variable var1 : std_logic_vector (ADRESS_WIDTH-1 downto 0) := (others => '0');
for i in 0 to ADRESS_WIDTH-2 loop
var1 := var1 + '1';
with r_addr select
fifo_data_out <= array_reg(i) when var1,
end loop;
array_reg(ADRESS_WIDTH-1) when others;
This version (in process) isn't correct too - syntax errors
process (r_addr, r_addr1, fifo_data_out, array_reg, r_data1)
variable var1 : std_logic_vector (ADRESS_WIDTH-1 downto 0) := (others => '0');
begin
case r_addr is
when "0000000000" => fifo_data_out <= array_reg(0);
for i in 1 to ADRESS_WIDTH-2 loop
when var1 => fifo_data_out <= array_reg(i);
var1 := var1 + '1';
end loop;
when others => fifo_data_out <= array_reg(ADRESS_WIDTH-1);
end case;
There's a bunch of things that aren't quite right on your implementation. Completely agnostic of what you're trying to accomplish there are some things with VHDL that should be remembered:
Every opening statement must have a closing one.
Every nesting level must have a closing statement to "un-nest" it
You can't put portions of one statement set (the case internals) inside another statement (the for loop), this seems odd, but think about it, what case is it going to attach itself to.
VHDL and hardware programming in general is ridiculously parallel, from one iteration to the next the process is completely independent of all other processes.
Now, looking at your code I see what looks like what you want to accomplish, and this is a perfect example of why you should know a little bit of scripting in another language to help out hardware level programming. You should be as specific as possible when creating a process, know what you want to accomplish and in what bounds, I know this is like every other languages but hardware programming gives you all the tools to hang yourself very very thoroughly. Here's the best that I can make out from your code to clean things up.
async_process : process (r_addr, fifo_data_out, array_reg)
begin
case r_addr is
when "0000000000" => fifo_data_out <= array_reg(0);
when "0000000001" => fifo_data_out <= array_reg(1);
when "0000000002" => fifo_data_out <= array_reg(2);
when others => fifo_data_out <= array_reg(ADRESS_WIDTH-1);
end case;
end process;
r_addr_inc_process : process (clock <or other trigger>, reset)
<This is where you would deal with the bounds of the register address.
If you still want to deal with that variable, do it here.>
end process;
So, as you can see from this, you want to update as few things as possible when you're dealing with a process, this way your sensitivity list is extremely specific, and you can force most updating to occur synchronously instead of asynchronously. The reason your async process can be as such is that it will update every time r_addr is updated, and that happens every clock read, or on some flag, and it gives you a consistent reset state.
With how iterative the process is, you can see how using a scripting language to fill in the 100's of register values would help it from being very time consuming.

Resources