Initializing ROM from array using functions, Synthesis ERROR (VHDL) - function

Ok, so I have a problem with a ROM initialization function.
Before I get into the problem let me explain a bit the nature of my problem and my code.
What I want to do is generate N number of ROMs which I have to use as an input for a module that runs a matching algorithm.
My problem is that I have a file with my signatures (let's say 64 in total) which I want to load in my different ROMs depending on how many I've generated (powers of 2 in my case, e.g. 8 roms of 8 signatures each).
I figured the best way to do it is load the whole text file into an array (using a function outside of the architecture body) which I will then use (again in a function) to load the data into a smaller array which will then be my ROM.
It seemed to me that the synthesizer would then just ignore the big array since I don't actually use it in my architecture.
Problem now is that since the second array input arguments are signal dependent, the synthesizer just ignores them and ties my array to zero (line 57).
Does anyone know how else if there's a way to make this architecture synthesize-able?
library ieee;
use ieee.std_logic_1164.all;
use IEEE.std_logic_signed.all;
use std.textio.all;
entity signatures_rom_partial is
generic(
data_width : integer := 160;
cycle_int : integer :=32;
rom_size : integer := 4
);
port ( clk : in std_logic;
reset : in std_logic;
readlne: in integer range 0 to cycle_int-1; -- user input for array data initialization
address: in integer range 0 to rom_size-1; -- address for data read
data: out std_logic_vector(data_width-1 downto 0) -- data output
);
end signatures_rom_partial;
architecture rom_arch of signatures_rom_partial is
type rom_type is array (0 to cycle_int-1) of bit_vector (data_width-1 downto 0); -- big array for all signatures, not used in arch
type test_type is array (0 to rom_size-1) of std_logic_vector (data_width-1 downto 0); -- smaller ROMs used in arch
--Read from file function--
----------------------------------------------------------------------------------------------------------
impure function InitRomFromFile (RomFileName : in string) return rom_type is --
file RomFile : text is in RomFileName; --
variable RomFileLine : line; --
variable rom : rom_type; --
--
begin --
for i in rom_type'range loop --
readline (RomFile, RomFileLine); --
read (RomFileLine, rom(i)); --
end loop; --
return rom; --
end function; --
----------------------------------------------------------------------------------------------------------
--Function for smaller ROM initialization--
----------------------------------------------------------------------------------------------------------
impure function initPartRom (rom : rom_type; readlne : integer) return test_type is --
variable test_array : test_type; --
--
begin --
for j in test_type'range loop --
test_array(j) := to_stdlogicvector(rom(j+readlne)); --
end loop; --
return test_array; --
end function; --
----------------------------------------------------------------------------------------------------------
constant rom : rom_type := InitRomFromFile("signatures_input.txt");
signal test_array : test_type := initPartRom(rom , readlne); --(LINE 57) SYNTHESIZER IGNORES THESE INPUT ARGUMENTS
begin
process(clk,reset)
begin
if reset='1' then
data<=(others=>'0');
elsif (clk'event and clk='1') then
data <= (test_array(address));
end if;
end process;
end rom_arch;
Synthesis is done using Xilinx ISE, simulation is done with Modelsim in which, my creation works fine :)
Thanks for any help!

With Xilinx ISE (14.7) it is possible to synthesize ROMs and RAMs even if the initial data is read from an external file by function.
The only requirement is, that the reading function must be computable at synthesis time. This is not true for your code because readlne is not static at the point the function is called. You should change it to a generic instead of an input and assign a different value to this generic for every other ROM instance. Then it should work as intended.
A sample implementation of how to read initialization data from a text file in (Xilinx) .mem format can be found in VHDL Library PoC in the namespace PoC.mem.ocrom or PoC.mem.ocram

Related

Verilog, How to pass different parameters when I use generate to instantiation module?

I have a question about parameters passing. I used generate for to do module instantiation. But how to pass different parameters to each module?
For example:
generate
for (i=0;i<N;i=i+1) begin:ModIns
Mod #(.p1(?),.p2(?)) M (
// Signal connection
);
end
endgenerate
For N modules, each with different p1 and p2. How to do that? By the way, the number of parameters is very large, can I pass parameters as a file?
Thanks!
Here are my 2 cents.
Declare a 2-D register and store all the parameters there.
reg [31:0] param_mem1 [N-1:0]; //Assuming parameter size to be 32 bit wide
reg [31:0] param_mem2 [N-1:0];
always#(posedge clk)
begin
for(i=0;i<N;i=i+1)
begin
param_mem1[i] <= i; //Here replace 'i' with your actual parameter value
param_mem2[i] <= i+1; //Dummy assignment, please use intended values
end
end
generate
for (i=0;i<N;i=i+1) begin:ModIns
Mod #(.p1(param_mem1[i]),.p2(param_mem2[i])) M (
// Signal connection
);
end
endgenerate
First: No you can't read it from a file. Parameters have to be known at compile time and a file is only readable in run time.
You can derive them from a genvar or pass them through a hierarchy, but then it more or less stops. Maybe what you are trying to do can be solved in a different way but the scope of the problem you have outlined for us here is rater limited.
As always: Tell us your problem, not your solution.
It would be easier to do this in SystemVerilog because a parameter can be an array, and your generate loop could select an element for each iteration of the loop.
In Verilog, you can pack your elements into a bit-vector, and your generate loop could select a slice for each iteration of the loop.
parameter A1={8'd1, 8'd2, 8'd3, 8'd4, ...};
parameter A2={8'd9, 8'd8, 8'd7, 8'd6, ...};
generate
for (i=0;i<N;i=i+1) begin:ModIns
Mod #(.p1(A1[i*8+:8),.p2(A2[i*8+:8])) M (
// Signal connection
);
end
endgenerate
If you want to define the parameters from a file, you can put the parameter declarations in a separate file and `include the file.
This compiles with VCS:
genvar port_idx;
generate
for (port_idx = 0; port_idx < 5; port_idx++) begin : intf
xactor_t xactor (core_clk, core_rst);
defparam xactor.intf_id = 18 + port_idx;
end

Using a variable in more than one funciton?

it is probably because of me being newbe. Anyways, I want to define some variables to use in more than one function (like global variables in C). I decided to go with shared variables but it gives me the error Cannot reference shared variable "x" inside pure function "y". If I define the variable in a process, then it initializes(erases the value) every process activation.
Architecture SV_example of example is
shared variable temp:std_logic_vector(18 downto 0):= "0000000000000000000";
SIGNAL gct: STD_LOGIC_VECTOR(18 DOWNTO 0);
function register_adder( load_value:std_logic ) return std_logic_vector is
begin
for i in size downto 0 loop
temp(i) := load_value;
end loop;
return temp;
end register_adder;
p1 : process (CLK)
begin
if rising_edge(CLK) then
gct <= register_loader('1');
end if;
end process;
end SV_example;
In VHDL (at least with VHDL '93 and later), functions are pure by default. A pure function is one that has no side effects. It depends only on its inputs, with no dependency on (non-static) outside information.
So, your fix is to declare the function impure. I had to do some editing to your example to make it an MCVE. The fix:
library ieee;
use ieee.std_logic_1164.all;
entity example is
port
(
CLK : in std_logic
);
end entity example;
Architecture SV_example of example is
shared variable temp:std_logic_vector(18 downto 0):= "0000000000000000000";
SIGNAL gct: STD_LOGIC_VECTOR(18 DOWNTO 0);
impure function register_adder( load_value:std_logic ) return std_logic_vector is
begin
for i in temp'length-1 downto 0 loop
temp(i) := load_value;
end loop;
return temp;
end register_adder;
begin
p1 : process (CLK)
begin
if rising_edge(CLK) then
gct <= register_adder('1');
end if;
end process;
end SV_example;
Note this works only in VHDL '93. The use of a shared variable changes significantly in VHDL '02 and later.
One final note. Shared variables generally are not synthesizable (the only example I can think of is the inference model for a RAM). Shared variables are usually only for general purpose use in hardware or testbench modeling.
Shared variable often goes with protected types, which can handle encapsulation of data. An example using protected type is shown below:
architecture SV_example of example is
signal gct : std_logic_vector(18 downto 0);
type temp_t is protected
impure function register_adder(load_value : std_logic) return std_logic_vector;
end protected;
type temp_t is protected body
variable temp : std_logic_vector(18 downto 0) := (others => '0');
impure function register_adder(load_value : std_logic) return std_logic_vector is
begin
for i in temp'range loop
temp(i) := load_value;
end loop;
return temp;
end function;
end protected body;
shared variable temp_sv : temp_t;
begin
p1 : process (CLK)
begin
if rising_edge(CLK) then
gct <= temp_sv.register_adder('1');
end if;
end process;
end SV_example;
For access to the internal temp data, a function get_temp can be written, if needed.

Why am I getting a "No matching subprogram was found." error?

I wrote a function inside a package file, and I'm calling it inside the main vhd file. It seems to me everything is correctly in place. But the Sigasi editor says "No matching subprogram was found." at the line where I called the function.
This is the package file content:
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.numeric_std.ALL;
PACKAGE pkg IS
TYPE t IS ARRAY (positive RANGE <>) OF std_logic_vector(7 DOWNTO 0);
FUNCTION char2byte (SIGNAL yazi_char: character) RETURN std_logic_vector;
END pkg;
PACKAGE BODY pkg IS
FUNCTION char2byte (SIGNAL yazi_char: character) RETURN std_logic_vector IS
VARIABLE yazi_byte: std_logic_vector;
BEGIN
case yazi_char is
when '0' => yazi_byte:=x"30";
when '1' => yazi_byte:=x"31";
when '2' => yazi_byte:=x"32";
....
when others =>
end case;
RETURN yazi_byte;
END char2byte;
END pkg;
And this is the main file content:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use WORK.pkg.ALL;
entity rs232 is
port(
clk:in std_logic;
tx_port:out std_logic
);
end rs232;
architecture Behavioral of rs232 is
signal yazi_byte: t;
begin
yazi_byte<=char2byte("y");
process(clk)
begin
....
end process;
end Behavioral;
Where is the fault? The funny thing is, ISE is giving another error, not a "No matching subprogram was found.".
It is:
ERROR:HDLParsers:522 - "D:/UME/FPGA/ise_projeleri/RS232_TEST/pkg.vhd" Line 16. Variable yazi_byte is not constrained.
ERROR:HDLParsers:3304 - "D:/UME/FPGA/ise_projeleri/RS232_TEST/pkg.vhd" Line 16. Can not determine the "others" values in aggregate. (LRM 7.3.2)
Thanks for helping.
You have lots of subtle errors. Some of them noted already, others not. Lets start with types. "y" is a single element string (string(1 to 1)). char2byte requires a character, such as 'y'. As #Brian pointed out, char2byte returns std_logic_vector and not type t (an array of std_logic_vector).
On char2byte you have declared the character input to be a signal. This would mean that you need to map a signal to it and not a literal, such as 'y'. You probably want to declare it as a constant instead (or just leave the class off). In the code below, note the fix to yazi_byte (also noted by #Andy).
FUNCTION char2byte (CONSTANT yazi_char: character) RETURN std_logic_vector IS
VARIABLE yazi_byte: std_logic_vector(7 downto 0) ;
BEGIN
case yazi_char is
when '0' => yazi_byte:=x"30";
when '1' => yazi_byte:=x"31";
when '2' => yazi_byte:=x"32";
....
when others =>
end case;
RETURN yazi_byte;
END char2byte;
Note a clever solution to Char2Byte may use an array of std_logic_vector(7 downto 0) that is indexed by type character (perhaps a project for another day). See #David's post here for an idea: Missing EOF at function. Indexing the array would work similar to a subprogram call.
If you only have char2byte, then your testbench needs to receive a std_logic_vector value (also see #Brian's fix):
architecture Behavioral of rs232 is
signal slv_byte: std_logic_vector(7 downoto 0) ;
begin
slv_byte<=char2byte('y');
If your testbench really needs to work with strings and type t, then a function that takes type string and returns type t, such as the following partially built function would be helpful:
FUNCTION string2slv (yazi_str : string) RETURN t is
variable result : t (yazi_str'range) ;
begin
. . .
end FUNCTION string2slv ;
The problem is that you are confusing a std_logic_vector with t, an array of std_logic_vector.
signal yazi_byte: t;
begin
yazi_byte<=char2byte("y");
...
Now t is an unconstrained array, which allows you to declare ts of different sizes when you use the package. There are two ways to do this :
declare a t and initialise it in the declaration : the initialiser (function call or array aggregate) defines its size. Works for signals but especially useful for constants
constrain it to the size you need in the declaration.
Thus the first example is constrained to be 4 bytes long:
constant deadbeef : t := (X"DE", X"AD", X"BE", X"EF");
And your second example is currently 1 byte long: however you still need to address that byte within it... This is actually why you get the obscure-looking error message.
signal yazi_byte: t(0 downto 0);
begin
yazi_byte(0) <= char2byte("y");
...
VHDL has operator and function overloading, so the compiler is looking for a "char2byte" function that returns an array of std_logic_vector because that's the type of the variable you are assigning to. It can't find one (hence "no matching subprogram") because the only "char2byte" you wrote returns a single std_logic_vector, not an array of them.
So addressing a single element of that array will let it find your char2byte - and now the error message makes perfect sense...
The variable yazi_byte needs to be constrained explicitly, as the error message says. It looks like it has a range of eight bit, so:
VARIABLE yazi_byte: std_logic_vector(7 downto 0);
would work.

VHDL, using functions in for generate statement

VHDL, using functions in for generate statement
I have a component that should be instantiated about 8000 times, I used for-generate statement with the help of some constant values for reducing amount of code, but I had to declare a function for parametrization of component connections.
My function looks like this:
function dim1_calc (
cmp_index : integer;
prt_index : integer
) return integer is
variable updw : integer := 0;
variable shft_v : integer := 0;
variable result : integer := 0;
begin
if (cmp_index < max_up) then
updw := 1;
else
updw := 2;
end if;
case prt_index is
when 1 =>
shft_v := cnst_rom(updw)(1) + (i-1);
when 2 =>
shft_v := cnst_rom(updw)(2) + (i);
--
--
--
when 32 =>
shft_v := cnst_rom(updw)(32) + (i);
when others =>
shft_v := 0;
end case;
if (updw = 1) then
if (shft_v = min_up & ((prt_index mod 2) = 0)) then
result <= max_up;
elsif (shft_v = max_up & ((prt_index mod 2) = 1)) then
result <= min_up;
elsif (shft_v < max_up) then
result <= shft_v;
else
result <= shft_v - max_up;
end if;
else
--something like first condition statements...
--
--
end if;
return result;
end function;
and part of my code that uses this function plus some related part looks like this:
--these type definitions are in my package
type nx_bits_at is array (natural range <>) of std_logic_vector (bits-1 downto 0);
type mxn_bits_at is array (natural range <>) of nx_bits_at;
--
--
--
component pn_cmpn is
port(
clk : in std_logic;
bn_to_pn : in nx_bits_at(1 to row_wght);
pn_to_bn : out nx_bits_at(1 to row_wght)
);
end component;
--
--
--
signal v2c : mxn_bits_at(1 to bn_num)(1 to col_wght);
signal c2v : mxn_bits_at(1 to pn_num)(1 to row_wght);
--
--
--
gen_pn : for i in (1 to pn_num) generate
ins_pn : pn_cmpn port map (
clk => clk,
bn_to_pn(1) => b2p (dim1_calc(i, 1)) (dim2_calc(i, 1)),
bn_to_pn(2) => b2p (dim1_calc(i, 2)) (dim2_calc(i, 2)),
.
.
.
bn_to_pn(32) => b2p (dim1_calc(i, 32)) (dim2_calc(i, 32)),
pn_to_bn => p2b (i)
);
end generate;
I know that using too many sequential statements together is not appropriate in general, and I'm avoiding them as much as possible, but in this case I assumed that this function won't synthesize into some real hardware, and synthesizer just calculates the output value and will put it in corresponding instantiations of that component. Am I right? or this way of coding leads to extra hardware compared to just 8000 instantiations.
PS1: Initially I used "0 to..." for defining ranges of the 2nd and 3rd dimension of my arrays, but because of confusion that were made in dimension calculation function based on for-generate statement parameter, I replaced them with "1 to...". Is that an OK! coding style or should I avoid it?
PS2: Is there a way that port mapping part in above code combines into something like this:
(I know this is strongly wrong, it's just a clarification of what I want)
gen_pn : for i in (1 to pn_num) generate
ins_pn : pn_cmpn port map (
clk => clk,
gen_bn_to_pn : for j in (1 to 32) generate
bn_to_pn(j) => b2p (dim1_calc(i, j)) (dim2_calc(i, j)),
end generate;
pn_to_bn => p2b (i)
);
end generate;
Let me give another example
Assume that I have a component instantiation like this:
ins_test : test_comp port map (
clk => clk,
test_port(1) => test_sig(2)
test_port(2) => test_sig(3)
test_port(3) => test_sig(4)
);
Is there a way that I can use for generate here? something like:
ins_test : test_comp port map (
clk => clk,
gen_pn : for i in (1 to 3) generate
test_port(i) => test_sig(i+1)
end generate;
);
PS3: Is it possible to call a function inside another function in VHDL?
Functions are usable this way. If you encounter problems, I am sure they will regard details in the design or design tools, rather than the basic approach.
One potential issue is that the function refers to some external "things" such as max_up, i, cnst_rom whose declarations are not part of the function nor parameters to it. This makes it an "impure function" which - because it refers to external state or even modifies it - has restrictions on calling it (because the external state may change, results may depend on order of evaluation etc).
If you can make it pure, do so. I have a feeling that max_up, cnst_rom are constants : if they aren't used elsewhere, declare them local to the function. And i probably ought to be a parameter.
If this is not possible, make the external declarations constants, and preferably wrap them and the function together in a package.
This will just generate the values you need in a small, comprehensible, maintainable form, and not an infinite volume of hardware. I have used a complex nest of functions performing floating point arithmetic then fiddly range reduction and integer rounding to initialise a lookup table, so fundamentally the approach does work.
Potential pitfall:
Some design tools have trouble with perfectly valid VHDL, if its use is slightly unorthodox. Synplicity cannot synthesise some forms of function (which DO generate hardware) though has no trouble with the equivalent procedure returning the result through an OUT parameter!. XST is considerably better.
XST parsing my lookup table init has an absurd slowdown, quadratic in the number of function calls. But only if you are using the old VHDL parser (the default for Spartan-3). Spartan-6 uses the new parser and works fine ( under a second instead of half an hour!) as do Modelsim and Isim. (haven't tried Synplicity on that project)
Some tools object to unorthodox things in port maps : you may get away with function calls there; or you may have to workaround tool bugs by initialising constants with the calls, and using those constants in the port maps.
And your supplementary questions:
PS1) The correct coding style for an array range is ... whatever makes your intent clear.
If you find yourself mentally offsetting by 1 and getting confused or even making errors, STOP! and improve the design.
Some good array indexing styles:
type colour is (red, green, blue);
subtype brightness is natural range 0 to 255;
hue : array (colour) of brightness;
gamma : array (brightness) of brightness;
-- here 0 is a legitimate value
channel : array (1 to 99) of frequency;
PS2) I think you're asking if you can nest generate statements. Yes.
Details may be awkward and difficult, but yes.
PS3) Yes of course! You can even declare functions local to others; eliminating the possibility they will be accidentally called somewhere they make no sense. They (impure functions) can access the execution scope of the outer function (or process), simplifying parameter lists.
Q1 - in this case I assumed that this function won't synthesize into some ...
It depends on which synthesizer you're using. See this relevant question and comments below.
Q2 - PS1: Initially I used "0 to..." for defining ranges of the ...
Surely it's OK. And please allow we to post a suggestion on coding style here. (from this book)
When defining the loop parameter specification, either use a type (or subtype) definition, or use predefined object attributes (e.g., PredefinedObject'range, PredefinedObject'length - 1 downto 0). Avoid using discrete range (e.g., 1 to 4).
This rule makes the code more reusable and flexible for maintenance.
Q3 - PS2: Is there a way that port mapping part in above code combines into ...
I think this is why you asked the 4th question. So refer to the next answer:).
Q4 - Is it possible to call a function inside another function in VHDL?
Though I can't find some official reference to this, the answer is yes.
PS: Coding rules are defined by the synthesizer tools. So the best way to find an answer is to try it yourself.

VHDL bit rotation function syntax error?

I am doing this for my school work where I'm making my own rolling/shifting function.
Below is the code I wrote, but when i try to compile it i get syntax error on rownum<=rol(rowcount,1);
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
architecture main of proj is
function "rol" (a: std_logic_vector; n : natural)
return std_logic_vector is
begin
return std_logic_vector(unsigned(a) rol n);
end function;
signal rownum : std_logic_vector(2 downto 0);
signal rowcount : std_logic_vector(2 downto 0);
begin
process begin
wait until rising_edge(i_clock);
**rownum<=rol(rowcount,1);**
end process;
end architecture main;
There are a couple of things that need to be addressed here.
Firstly, you need an entity statement:
entity proj is
port(
i_clock : in std_logic
);
end proj;
This declares what signals are inputs and outputs for your entity. In this case, it's just a clock. You can add rownum and rowcount inputs and outputs too as needed.
Your function name shouldn't be in inverted commas, and overloading an existing operator isn't a good idea either.
function rol_custom (a: std_logic_vector; n : natural)
return std_logic_vector is
begin
return std_logic_vector(unsigned(a) rol n);
end function;
Here's the synthesizable code:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity proj is
port(
i_clock : in std_logic
);
end proj;
architecture main of proj is
function rol_custom (a: std_logic_vector; n : natural)
return std_logic_vector is
begin
return std_logic_vector(unsigned(a) rol n);
end function;
signal rownum : std_logic_vector(2 downto 0);
signal rowcount : std_logic_vector(2 downto 0);
begin
process begin
wait until rising_edge(i_clock);
rownum<=rol_custom(rowcount,1);
end process;
end architecture main;
However, even though this now should synthesize, the results won't make any sense, because rowcount has not been given a value. In order to define it, you might want to add a process which drives the signal based on certain criteria (a counter?) or add it as an input in the entity definition.
If your vectors represent numbers, you should use a numerical type for them. Use the ieee.numeric_std library and a unsigned or signed type as appropriate. rol will then just work. You won't have to create your own.