How can I extract elements from a record using an integer reference in VHDL? - 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.

Related

VHDL: How to handle unconstrained arrays returned by functions as inputs to entity ports?

I'm trying to write some fairly generic VHDL code but I'm running into
a situation where I don't understand the standard well enough. (I'm
using VHDL-2008.)
I have written a function that operates on unconstrained
std_logic_vector(s) and returns an unconstrained
std_logic_vector. However, it seems as if I am unable to use this
function as an input to a port in my entity if I pass two
(constrained) std_logic_vectors to it (see instantiation of test_2 in
my example program). However, for some reason it seems to work ok if I
pass bit string literals to it (see instantiation of test_1).
Can someone explain why the I am not allowed to use the concatenate()
function as an input in the instantiation of test_2 while I am allowed
to use a very similar construct in the instantiation of test_1?
To try the code with ModelSim I compiled it with vcom -2008 unconstrained_example.vhd
-- test entity/architecture
library ieee;
use ieee.std_logic_1164.all;
entity test is
port (value : in std_logic_vector);
end entity;
architecture a of test is
begin
-- Intentionally empty
end architecture;
library ieee;
use ieee.std_logic_1164.all;
-- Test instantiation
entity testit is
end entity;
architecture a of testit is
signal my_constrained_slv1 : std_logic_vector(5 downto 0);
signal my_constrained_slv2 : std_logic_vector(9 downto 0);
function concatenate(value1 : std_logic_vector; value2 : std_logic_vector) return std_logic_vector is
begin
return value1 & value2;
end function;
begin
process begin
-- Using the function in this context seems to work ok
report "Value is " & to_string(concatenate(my_constrained_slv1, my_constrained_slv2));
wait;
end process;
-- This instantiation seems to work
test_1: entity work.test
port map (
value => concatenate("000000", "1111111111"));
-- For this entity instantiation I'm getting an error from ModelSim:
-- ** Error: unconstrained_example.vhd(43): (vcom-1383) Implicit signal in port map for port "value" is not fully constrained.
test_2: entity work.test
port map (
value => concatenate(my_constrained_slv1, my_constrained_slv2));
end architecture;
Your function call is not a conversion function and also does not fulfill the requirements to spare an implicit signal declaration.
VHDL-2008 allows such complex expressions in a port association. The language says, that in such cases, an implicit signal will be created:
If the actual part of a given association element for a formal signal port of a block is the reserved word inertial followed by an expression, or is an expression that is not globally static, then the given association element
is equivalent to association of the port with an anonymous signal implicitly declared in the declarative region that immediately encloses the block. The signal has the same subtype as the formal signal port and is the target of an implicit concurrent signal assignment statement of the form:
anonymous <= E;
where E is the expression in the actual part of the given association element. The concurrent signal assignment statement occurs in the same statement part as the block.
Source: IEEE-1076-2017 Draft 5a
signal temp : std_logic_vector; -- derived from the formal in the port association list
temp <= concatenate(my_constrained_slv1, my_constrained_slv2);
test_2: entity work.test
port map (
value => temp
);
end block;
The problem is, that VHDL needs to infer the type for the implicit signal temp from the formal in the port association list (value : std_logic_vector). It knows, that it is a std_logic_vector, but no constraints are known, due to the unconstrained port.
So if port value in entity test is constrained, it should work:
entity test is
port (
value : in std_logic_vector(15 downto 0)
);
end entity;
I came up with the following workaround which is quite ugly but
fulfills my main criteria that I should not have to manually specify
any widths or repeat any information. By hiding away the call to concatenate in a function I can
reuse the function to get the range further down. A short experiment indicates that Vivado 2015.4 accepts this construct as well.
test_2_helper : block
impure function test_2_help_func return std_logic_vector is
begin
-- This is the only place I have to change in case the assignment has to
-- change in some way. (E.g. use other variables or become more complicated, etc.)
return concatenate(my_constrained_slv1, my_constrained_slv2);
end function;
signal test_2_helper_sig : std_logic_vector(test_2_help_func'range);
begin
test_2: entity work.test
port map (
-- It seems to be syntactically legal to use test_2_help_func(test_2_help_func'range)
-- here. Unfortunately this does not work in simulation. Probably because the
-- test_2_help_func does not have any explicit arguments and this may cause issues
-- with the event driven simulation. As a work around the test_2_helper_sig signal
-- is assigned every clock cycle below instead.
value => test_2_helper_sig);
process
begin
-- Note: If you remove the wait for the clock edge and instead use process(all)
-- test_2_helper_sig does not seem to change during simulation, at least in
-- Modelsim 10.6 where I tested this.
wait until rising_edge(clk);
test_2_helper_sig <= test_2_help_func;
end process;
end block;
Note: This is inspired by the following answer: VHDL - Why does using the length attribute directly on a function produce a warning?

write on invalid address to RAM in VHDL, Verilog, sim behaviour

Lets have a BRAM or any other memory under Verilog or VHDL.
For example this:
module raminfr (clk, we, a, di, do);
input clk;
input we;
input [4:0] a;
input [3:0] di;
output [3:0] do;
reg [3:0] ram [31:0];
always #(posedge clk) begin
if (we)
ram[a] <= di;
end
assign do = ram[a];
endmodule
Now lets assume that we have already write valid data to "ram".
Does simulators invavalidate all items in "ram" if address "a" will have invalid value (4'bxxxx) ("we"=1 and clk will have posedge)?
Or it lets values in ram as they were?
For Verilog
The IEEE Std 1800-2009 SystemVerilog standard subsumed both the IEEE Std 1364-2005 Verilog standard and the IEEE Std 1800-2005 SystemVerilog standard. The IEEE Std 1800-2012 SystemVerilog standard supplanted the -2009 version.
See IEEE Std 1800-2012 7.4.6 Indexing and slicing of array:
If an index expression is out of bounds or if any bit in the index expression is x or z, then the index shall be invalid. Reading from an unpacked array of any kind with an invalid index shall return the value specified in Table 7-1. Writing to an array with an invalid index shall perform no operation, with the exceptions of writing to element [$+1] of a queue (described in 7.10.1) and creating a new element of an associative array (described in 7.8.6). Implementations may issue a warning if an invalid index occurs for a read or write operation on an array.
For VHDL
An equivalent VHDL design description might be:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity raminfr is
port (
clk: in std_logic;
we: in std_logic;
a: in std_logic_vector(4 downto 0);
di: in std_logic_vector(3 downto 0);
do: out std_logic_vector(3 downto 0)
);
end entity;
architecture behave of raminfr is
type ram_type is array (0 to 15) of std_logic_vector (3 downto 0);
signal ram: ram_type;
begin
process (clk)
begin
if rising_edge(clk) then
if we = '1' then
ram(to_integer(unsigned(a))) <= di;
end if;
end if;
end process;
do <= ram(to_integer(unsigned(a)));
end architecture;
Where the numeric_std package to_integer function is used to convert the array value of a to an integer index. (VHDL is a bit more flexible here, array indexes can either be integer types or enumerated types, together referred to as discrete types).
Reading the source for to_integer we see it will convert an input unsigned array value containing an 'X' to all 'X's and the 'LEFT value of that being 'X' will return a natural integer subtype value of 0 and optionally report that:
if (XARG(XARG'left) = 'X') then
assert NO_WARNING
report "NUMERIC_STD.TO_INTEGER: metavalue detected, returning 0"
severity warning;
return 0;
end if;
These warnings can be disabled implementation wide by changing the value of NO_WARNING which is locally static. Using to_integer (or with Synopsys package std_logic_arith and function conv_integer) the index will be 0.
Additionally VHDL implementations have the ability to stop simulation on severity warning reports.
While you could handle the result of an assignment with an index provided as an array value differently in (as in 2, 4, 8, 16 or 32 invalidated memory locations depending on the number of 'X' element values of a here), you've already compromised the integrity of your design model state and some corrective should be taken before counting on the simulation results for either a SystemVerilog or VHDL design model.
The overhead of complex fiddling doesn't seem worthwhile in general. These warnings are of a class of warnings that should be reviewed before synthesis.
These warning can occur before array values are reset when their default initial values are metavalues. You can prevent that in most FPGA designs by initializing a and any predecessors so a contains a binary representing value or simply ignoring reports at time 0 before a reset takes affect.
You can also prevent the write to address 0 by initializing we and any predecessor(s) to '0'.
Simulator will not invalidate anything. Instead the write will be ignored.
For reads in such a situation, it will return 'x' though.

VHDL signal dimension issue when reducing a generic value down to 0

How can one handle this case:
entity foo is
generic (
num_instances : natural := 8
);
port (
data_in_per_instance : in std_logic_vector(num_instances-1 downto 0);
data_out_per_instance : out std_logic_vector(num_instances-1 downto 0)
);
end foo;
architecture bar of foo is
component do_stuff is
port(
din : in std_logic;
dout : out std_logic
);
end component do_stuff;
signal sig_per_instance : std_logic_vector(num_instances-1 downto 0);
begin
L1: for i in 0 to num_instances-1 generate
L2: do_stuff
port map(
din => data_in_per_instance(i),
dout => data_out_per_instance(i)
);
end generate;
end bar;
The problem here is that if num_instances is reduced to 0 there will be an error when the signal ranges (num_instances-1 downto 0) are evaluated....
Is there an elegant way around this? All I can come up with is using a function like max(num_instances-1,0) to prevent this problem (but then synthesis may not give me exactly what I want, i.e. nothing).
Is there some way to handle this case a little more seamless?
A simple answer is to wrap the for-generate statement in an if-generate, if num_instances /= 0 generate ... Unfortunately if-generates have no else part, however there is nothing to stop you adding a second if num_instances = 0 generate ... statement to handle that special case.
Alternatively you can wrap the problem part of the for-generate using if-generate.
EDIT: re the need for a correct signal declaration guarded by the if ... generate.
AHA! You may have discovered a legitimate use for VHDL's (in my experience) rarely used "block" statement!
A block statement may contain signal declarations, and can be wrapped in a generate.
So this is valid VHDL:
Normality : block is
-- signal declarations here
begin
-- concurrent code here
end block Normality;
and it can be legally wrapped in a generate statement...
A std_logic_vector with an empty range (for example 3 downto 4) is perfectly legitimate - it's called a "null range" by the LRM. If you create one, then (at least with Modelsim) you will get warnings, which is usually considered poor form. To eliminate those, then Brian's block suggestion is what you'll have do.
Similarly the for..generate will result in an empty loop and no instances. No warnings for this.
I'm intrigued why you'd want a block with no instances in? I'd make num_instances a positive to force at least one of them, but that may not actually what you want!

I don't understand what's wrong with this VHDL code?

I have the following code:
entity wave_select is
port( address:in std_logic_vector(6 downto 0);
ws1: in std_logic;
ws0: in std_logic;
wave_out: out std_logic_vector(6 downto 0));
end wave_select;
architecture choose_arch of wave_select is
signal internal_sine:std_logic_vector(6 downto 0);
signal internal_tri:std_logic_vector(6 downto 0);
signal internal_sqr:std_logic_vector(6 downto 0);
begin
U0: entity sine_tbl port map(addr=>address, sine_val=>internal_sine);
U1: entity triangle_tbl port map(addr=>address, tri_val=>internal_tri);
U2: entity square_tbl port map(addr=>address, square_val=>internal_sqr);
process (std_logic_vector'(ws1, ws0))
begin
case ws_combo is
when "01" => wave_out<=internal_sine;
when "10" => wave_out<=internal_tri;
when "11" => wave_out<=internal_sqr;
when others =>wave_out<=(others => '-');
end case;
end process;
end choose_arch;`
Whenever I try to compile this, I get the following errors:
Identifier/keyword expected (for the process line)
Keyword end expected (for the when "10" line)
Design unit declaration expected (for the same line as keyword error)
FIXED THE QUESTION
As it stands right now, this has a number of problems, mostly with fairly basic syntax.
Although it seems likely you mean the combination of ws0 and ws1 to be treated as ws_combo, you haven't done anything to tell the synthesizer that, so it treats ws_combo as simply undefined.
At least as far as I know, you can't combine signals in the process sensitivity list like you've done. The sensitivity list is to tell what external signals this process responds to, not much else.
You don't have a definition of wave_out (unless it's also in your entity declaration).
You don't have definitions of internal_sine, internal_tri, or internal_sqr. Hard to guess what type they should be without knowing the type of wave_out.
As an interim idea of how this might turn out, I've fixed some of the syntax errors, added an entity declaration that declares ws0, ws1 and wave_out, then sets wave_out to values suitable to the type I've given it (in this case, just took binary input and produced Grey code output).
entity controller1 is
port (
ws1 : in std_logic;
ws0 : in std_logic;
wave_out : out std_logic_vector(1 downto 0)
);
end;
architecture whatever of controller1 is
begin
impl: process(ws0, ws1)
begin
case std_logic_vector'(ws1,ws0) is
when "01" => wave_out<="01";
when "10" => wave_out<="11";
when "11" => wave_out<="10";
when others =>wave_out<=(others => '-');
end case;
end process;
end whatever;
Of course, this also needs the typical library and using to get declarations for std_logic and std_logic_vector, but with those added the synthesizer seems to accept it. Of course, some other synthesizer (I'm checked it with Synplify) might find a problem I missed, but I think that probably covers at least most of the obvious problems.
What are you attempting to achieve with the std_logic_vector' in this line?
process (std_logic_vector'(ws1, ws0))
If you just change that for the more conventional
process (ws1, ws0)
I imagine it will help.
But I assume ws_combo is a signal like
ws_combo = ws1&ws0;
so
process (ws_combo)
would be better still.

What VHDL datatype should I use for a memory address?

I'm developing a description of a BIST engine, and I've been asked by my manager to transition from Verilog to VHDL. I'm very rusty with VHDL, and I can't figure out the right datatype to give to the address register in my code. Most of the time, the address is used to index into arrays.
data : std_logic_vector (2**W-1 downto 0);
...
output = data(addr);
Sometimes though, I need to perform bitwise operations (for example, this code that finds the least-significant 1 in the address):
least_one(0) <= addr(0);
PRIORITY_ENCODER : for i in 1 to (W-1) generate
least_one(i) <= addr(i) and not or_reduce(addr(i-1 downto 0));
end generate PRIORITY_ENCODER;
least_one(W) <= not or_reduce(addr);
Finally, I also rely on the address wrapping around without problem when it overflows (i.e. 1111+1 = 0, and 0-1 = 1111).
So, given all these different uses, what datatype or subtype do I give to the address? When I use integer and the related types, I get errors when I perform the bitwise operations:
ncvhdl_p: *E,APNPFX (filename,17|20): can not make sense of P(...)
When I use std_logic_vector or similiar, I get errors trying to use the address as an array index:
ncvhdl_p: *E,INTYMM (filename,52|17): array index type mismatch [6.4]
I seem to be in a no-win situation here. What data type do I use? Please note, the solution must be synthesizable. Thanks
You want bitwise access and wrapping behaviour:
make addr fundamentally an unsigned vector.
Then you need access to it as an integer:
If you need it as an integer on just one line, use the to_integer call on just that line.
If you need it as an integer in more than one place, create another signal to "shadow" it and put a continuous assignment in the architecture
Like this:
signal addr_int:natural;
....
addr_int <= to_integer(addr);
In this case I would use unsigned type.
This will work very similar to how you are used to std_logic_vector operating in terms of generic bit access, but you can also do arithmetic operations on the address and easily convert to/from integer type, if necessary. Plus it doesn't dirty the sense of std_logic_vector with the "dreaded" std_logic_unsigned package.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
...
architecture myarch of myent is
signal address : unsigned(numbits-1 downto 0);
...
begin
-- as an example
addr_counter : process(sysclk, reset)
begin
if reset = '1' then
address <= (others => '0');
elsif rising_edge(sysclk) then
address <= address + 1;
end if;
end process addr_counter;
...

Resources