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.
Related
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.
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
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.
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.
I keep getting a error when i run this code, What wrong with the code?
create or replace function f_vars(line varchar2,delimit varchar2 default ',')
return line_type is type line_type is varray(1000) of varchar2(3000);
sline varchar2 (3000);
line_var line_type;
pos number;
begin
sline := line;
for i in 1 .. lenght(sline)
loop
pos := instr(sline,delimit,1,1);
if pos =0 then
line_var(i):=sline;
exit;
endif;
string:=substr(sline,1,pos-1);
line_var(i):=string;
sline := substr(sline,pos+1,length(sline));
end loop;
return line_var;
end;
LINE/COL ERROR
20/5 PLS-00103: Encountered the symbol "LOOP" when expecting one of
the following:
if
22/4 PLS-00103: Encountered the symbol "end-of-file" when expecting
one of the following:
end not pragma final instantiable order overriding static
member constructor map
Stack Overflow isn't really a de-bugging service.
However, I'm feeling generous.
You have spelt length incorrectly; correcting this should fix your first error. Your second is caused by endif;, no space, which means that the if statement has no terminator.
This will not correct all your errors. For instance, you're assigning something to the undefined (and unnecessary) variable string.
I do have more to say though...
I cannot over-emphasise the importance of code-style and whitespace. Your code is fairly unreadable. While this may not matter to you now it will matter to someone else coming to the code in 6 months time. It will probably matter to you in 6 months time when you're trying to work out what you wrote.
Secondly, I cannot over-emphasise the importance of comments. For exactly the same reasons as whitespace, comments are a very important part of understanding how something works.
Thirdly, always explicitly name your function when ending it. It makes things a lot clearer in packages so it's a good habit to have and in functions it'll help with matching up the end problem that caused your second error.
Lastly, if you want to return the user-defined type line_type you need to declare this _outside your function. Something like the following:
create or replace object t_line_type as object ( a varchar2(3000));
create or replace type line_type as varray(1000) of t_line_type;
Adding whitespace your function might look something like the following. This is my coding style and I'm definitely not suggesting that you should slavishly follow it but it helps to have some standardisation.
create or replace function f_vars ( PLine in varchar2
, PDelimiter in varchar2 default ','
) return line_type is
/* This function takes in a line and a delimiter, splits
it on the delimiter and returns it in a varray.
*/
-- local variables are l_
l_line varchar2 (3000) := PLine;
l_pos number;
-- user defined types are t_
-- This is a varray.
t_line line_type;
begin
for i in 1 .. length(l_line) loop
-- Get the position of the first delimiter.
l_pos := instr(l_line, PDelimiter, 1, 1);
-- Exit when we have run out of delimiters.
if l_pos = 0 then
t_line_var(i) := l_line;
exit;
end if;
-- Fill in the varray and take the part of a string
-- between our previous delimiter and the next.
t_line_var(i) := substr(l_line, 1, l_pos - 1);
l_line := substr(l_line, l_pos + 1, length(l_line));
end loop;
return t_line;
end f_vars;
/