Intel® Quartus® Prime Software
Intel® Quartus® Prime Design Software, Design Entry, Synthesis, Simulation, Verification, Timing Analysis, System Design (Platform Designer, formerly Qsys)

procedure call problem

Altera_Forum
Honored Contributor II
2,698 Views

Hi, 

 

I am having problems using procedures in my simulation. I create the procedure between architecture and begin. I then call the procedure concurrently in the body of the architecture. As I understand it when I call the procedure like this then then it will be evaluated when any of the input signals change, a bit like a process with a sensitivity list. When I simulate however it seems to only evaluate once.  

 

Here is a cut down of the code I am using: 

 

ARCHITECTURE logic OF test_procedure IS PROCEDURE read_stuff ( SIGNAL clk_i : IN std_logic; SIGNAL reset_i : IN std_Logic; SIGNAL empty : IN std_logic; SIGNAL rdreq : OUT std_logic) IS TYPE state_machine IS ( check_empty, clear_rdreq ); VARIABLE state : state_machine; BEGIN IF(reset_i = '1') THEN state := check_empty; rdreq <= '0'; ELSIF(clk_i = '1' AND clk_i'EVENT) THEN CASE state IS WHEN check_empty => IF(empty = '0') THEN rdreq <= '1'; state := clear_rdreq; END IF; WHEN clear_rdreq => rdreq <= '0'; state := check_empty; END CASE; END IF; END PROCEDURE; SIGNAL clk : std_logic; SIGNAL reset : std_logic; SIGNAL empty_signal : std_logic; SIGNAL rdreq_signal : std_logic; BEGIN call_read_stuff : read_stuff ( clk_i => clk, reset_i => reset, empty => empty_signal, rdreq => rdreq_signal ); END ARCHITECTURE;  

 

The aim of the procedure is to read data from a fifo when it is not empty. I know I can do this with a process but I am trying to create a smaller reusable chunk that I can call in a generate as I have a few fifos that I want to pull data out of. This is for simulation only im not trying to create rtl from this. 

 

When I run my simulation the rdreq signal gets set when the empty flag goes low, however rdreq stays high, it doesnt seem to change to the second state and clear the rdreq signal and transition back to evaluate empty again. I also tried to do the same without using the state machine but had no more luck. 

 

Any suggestions would be appreciated.  

 

Thanks 

 

James
0 Kudos
12 Replies
Altera_Forum
Honored Contributor II
962 Views

do you have drive on clk /reset that you input to procedure? can't see any

0 Kudos
Altera_Forum
Honored Contributor II
962 Views

Hi, 

 

Yes I do, I cut down the code a bit too much. I drive the clock and the reset and the empty and rdreq signals are connected to the read side of a scfifo 

 

test_clk : PROCESS BEGIN clk <= '0','1' ADTER 10ns; WAIT FOR 20 ns; END PROCESS; test_reset : PROCESS BEGIN reset <= '1'; WAIT FOR 100 ns; reset <= '0'; WAIT FOR 100 us; END PROCESS; example_fifo : SCFIFO GENERIC MAP ( width => 16, numwords => 8, widthu => 3 ) PORT MAP ( clk => clk, reset => reset, data => data, wrreq => wrreq, full => full, q => q, rdreq => rdreq_signal, empty => empty_signal );  

 

The fifo is being filled with data from another component which I have simulated and is working correctly. Data is coming out of the fifo in the simulation as the rdreq is held high but this is not the desired functionality. 

 

Thanks 

 

James
0 Kudos
Altera_Forum
Honored Contributor II
962 Views

Ok instead of cutting down here is a full simulation file that I am using in modelsim with comments etc. Attached is a image of the wave I am getting. At the cursor you can see that the rdreq signal gets set when the empty signal goes low. It stays set.  

 

--*************************************************************************************** -- File : test_procedure.vhd --*************************************************************************************** -- This component is to test the procedure vhdl functionality --*************************************************************************************** LIBRARY ieee; USE ieee.std_logic_1164.all; USE ieee.numeric_std.all; LIBRARY altera_mf; USE altera_mf.altera_mf_components.all; ENTITY test_procedure IS END ENTITY; ARCHITECTURE test OF test_procedure IS --********************************************************************* -- scfifo component --********************************************************************* COMPONENT scfifo IS GENERIC ( lpm_width : natural; lpm_widthu : natural; lpm_numwords : natural; lpm_showahead : string := "OFF"; lpm_type : string := "scfifo"; lpm_hint : string := "USE_EAB=ON"; intended_device_family : string := "Stratix"; underflow_checking : string := "ON"; overflow_checking : string := "ON"; allow_rwcycle_when_full : string := "OFF"; use_eab : string := "ON"; add_ram_output_register : string := "OFF"; almost_full_value : natural := 0; almost_empty_value : natural := 0; maximum_depth : natural := 0 ); PORT ( data : IN std_logic_vector(lpm_width-1 DOWNTO 0); clock : IN std_logic; wrreq : IN std_logic; rdreq : IN std_logic; aclr : IN std_logic := '0'; sclr : IN std_logic := '0'; q : OUT std_logic_vector(lpm_width-1 DOWNTO 0); usedw : OUT std_logic_vector(lpm_widthu-1 DOWNTO 0); full : OUT std_logic; empty : OUT std_logic; almost_full : OUT std_logic; almost_empty : OUT std_logic ); END COMPONENT; --********************************************************************* -- create procedure to read data out of fifo when not empty --********************************************************************* PROCEDURE procedure_example( SIGNAL clk_i : IN std_logic; SIGNAL reset_i : IN std_logic; SIGNAL empty : IN std_logic; SIGNAL rdreq : OUT std_logic) IS TYPE state_machine IS ( check_empty, clear_rdreq ); VARIABLE state : state_machine; BEGIN IF(reset_i = '1') THEN state := check_empty; rdreq <= '0'; ELSIF(clk_i = '1' AND clk_i'EVENT) THEN CASE state IS WHEN check_empty => IF(empty = '0') THEN rdreq <= '1'; state := clear_rdreq; END IF; WHEN clear_rdreq => rdreq <= '0'; state := check_empty; END CASE; END IF; END PROCEDURE; --********************************************************************* -- create signals for test --********************************************************************* SIGNAL clk : std_logic; SIGNAL reset : std_logic; SIGNAL data : std_logic_vector(15 DOWNTO 0); SIGNAL wrreq : std_logic; SIGNAL almost_full : std_logic; SIGNAL q : std_logic_vector(15 DOWNTO 0); SIGNAL rdreq_signal : std_logic; SIGNAL empty_signal : std_logic; BEGIN --********************************************************************* -- example fifo instance --********************************************************************* example_fifo : scfifo GENERIC MAP ( lpm_width => 16, lpm_widthu => 3, lpm_numwords => 8, almost_full_value => 4 ) PORT MAP ( clock => clk, aclr => reset, data => data, wrreq => wrreq, almost_full => almost_full, q => q, rdreq => rdreq_signal, empty => empty_signal ); --********************************************************************* -- create clk --********************************************************************* test_clk : PROCESS BEGIN clk <= '0', '1' AFTER 10 ns ; WAIT FOR 20 ns ; END PROCESS; --********************************************************************* -- drive reset --********************************************************************* test_reset : PROCESS BEGIN reset <= '1'; WAIT FOR 100 ns; reset <= '0'; WAIT FOR 100 us; END PROCESS; --********************************************************************* -- call procedure concurrently --********************************************************************* test_procedure : procedure_example ( clk_i => clk, reset_i => reset, empty => empty_signal, rdreq => rdreq_signal ); --********************************************************************* -- fill fifo with dummy data to test procedure --********************************************************************* test_fill_test_fifo : PROCESS(clk,reset) TYPE state_machine IS ( check_full, clear_wrreq ); VARIABLE state : state_machine; VARIABLE count : integer := 0; BEGIN IF(reset = '1') THEN state := check_full; count := 0; data <= (others => '0'); wrreq <= '0'; ELSIF(clk = '1' AND clk'EVENT) THEN CASE state IS WHEN check_full => IF(almost_full = '0') THEN data <= std_logic_vector(to_unsigned(count,16)); wrreq <= '1'; state := clear_wrreq; END IF; WHEN clear_wrreq => wrreq <= '0'; count := count + 1; state := check_full; END CASE; END IF; END PROCESS; END ARCHITECTURE;
0 Kudos
Altera_Forum
Honored Contributor II
962 Views

I have never used variable for state machine since it is updated instantly from current state to next state. So I don't know what happens in your case.

0 Kudos
Altera_Forum
Honored Contributor II
962 Views

Ok Fair enough, I have used state machines as variables in both simulation and in my build and I havent noticed any problems. Can you elaborate as to why you dont use them as variables? 

 

If I change the procedure to not use the state machine: 

 

PROCEDURE procedure_example ( SIGNAL clk_i : IN std_logic; SIGNAL reset_i : IN std_logic; SIGNAL empty : IN std_logic; SIGNAL rdreq : OUT std_logic) IS VARIABLE rdreq_ip : std_logic; BEGIN IF(reset_i = '1') THEN rdreq <= '0'; rdreq_ip := '0'; ELSIF(clk_i = '1' AND clk_i'EVENT) THEN IF(empty = '0' AND rdreq_ip = '0') THEN rdreq <= '1'; rdreq_ip := '1'; ELSIF(rdreq_ip = '1') THEN rdreq <= '0'; rdreq_ip := '0'; END IF; END IF; END PROCEDURE;  

 

If I call this like I did with the previous procedure rdreq never gets set. I took both procedures and put them in processes and they behaved as expected. I am not sure what but im obviously missing some feature of procedures or am using them for the wrong purpose. It would be great to work out what im missing! 

 

Thanks 

 

James
0 Kudos
Altera_Forum
Honored Contributor II
962 Views

I now see using variable for state machine is ok since it is tied up to clk edge anyway and new value will be read on next clk edge. 

I am still looking at your example. It does look there is an issue to explain.
0 Kudos
Altera_Forum
Honored Contributor II
962 Views

It looks like the procedure can drive its output correctly if there is a direct logic from its input to its output such as: 

BEGIN IF(reset_i = '1') THEN rdreq <= '0'; ELSIF rising_edge(clk_i) THEN rdreq <= not empty; END IF; END PROCEDURE;  

 

However any internal variable be it as state variable or just variable breaks the logic inside clked process and only first statement takes over??
0 Kudos
Altera_Forum
Honored Contributor II
962 Views

I also tried this example which I think is equivalent to your first procedure. It works 

PROCEDURE procedure_example(SIGNAL clk_i : IN std_logic; SIGNAL reset_i : IN std_logic; SIGNAL empty : IN std_logic; SIGNAL rdreq : INOUT std_logic) IS BEGIN IF(reset_i = '1') THEN rdreq <= '0'; ELSIF rising_edge(clk_i) THEN rdreq <= not empty; if rdreq = '1' then rdreq <= '0'; end if; END IF; END PROCEDURE;
0 Kudos
Altera_Forum
Honored Contributor II
962 Views

Hi Kaz, 

 

I tried your suggested procedure and it worked on my simulation. Setting the rdreq to INOUT was a good idea to work around needing a variable to assign to rdreq as you cant read an output.  

 

I was still confused as to why using a variable in the procedure was causing the simulation not to evaluate properly so I had a bit more of a search online and found a clue: 

 

 

--- Quote Start ---  

A procedure may declare local variables. These do not retain their values between successive calls, but are re-initialised each time. Array-type parameters may be unconstrained 

--- Quote End ---  

 

 

So from this im guessing that when I call the procedure concurrently, every time the inputs change and the procedure gets re-evaluated the variable is getting re-initialised. in the case of the state machine this is meaning that it keeps getting re-initialised to check_empty so the state machine isnt transitioning and in my second procedure rdreq_ip is always being re-intialised to 0 and is never able to clear rdreq.
0 Kudos
Altera_Forum
Honored Contributor II
962 Views

Well that explains it but then one would ask why use procedure as replacement for some block of code. It certainly needs extra care when using procedures not to declare any variable inside.

0 Kudos
Altera_Forum
Honored Contributor II
962 Views

 

--- Quote Start ---  

 

So from this im guessing that when I call the procedure concurrently, every time the inputs change and the procedure gets re-evaluated the variable is getting re-initialised. in the case of the state machine this is meaning that it keeps getting re-initialised to check_empty so the state machine isnt transitioning and in my second procedure rdreq_ip is always being re-intialised to 0 and is never able to clear rdreq. 

--- Quote End ---  

 

 

Its all a question of scope. Calling a procedure the way you have done, or any code outside a process, implicitly declares a process.  

Procedures and functions are supposed to be re-useble, but self contained bits of code.
0 Kudos
Altera_Forum
Honored Contributor II
962 Views

 

--- Quote Start ---  

Its all a question of scope. Calling a procedure the way you have done, or any code outside a process, implicitly declares a process.  

Procedures and functions are supposed to be re-useble, but self contained bits of code. 

--- Quote End ---  

 

 

But procedure does not behave like equivalent replacement with process. In this example if you replace the procedure with equivalent process then it works with process but not with procedure. 

 

try and replace the procedure with this code: 

process(reset,clk) TYPE state_machine IS (check_empty, clear_rdreq); VARIABLE state : state_machine; BEGIN IF(reset = '1') THEN state := check_empty; rdreq_signal <= '0'; ELSIF(clk = '1' AND clk'EVENT) THEN CASE state IS WHEN check_empty => IF(empty_signal = '0') THEN rdreq_signal <= '1'; state := clear_rdreq; END IF; WHEN clear_rdreq => rdreq_signal <= '0'; state := check_empty; END CASE; END IF; END PROCESS;
0 Kudos
Reply