I have manageg to implement a simulation timeout in VHDL. If processes are running longer the MaxRuntime they get 'killed'.
Unfortunately, this does not work the other way around. If my simulation is finished before MaxRuntime, everything is waiting for the last wait statement on MaxRuntime.
I found that it's possible to combine wait on, wait for and wait until statements into one.
My current code in snippets. A full example is quite to long...
package sim is
shared variable IsFinalized : BOOLEAN := FALSE;
procedure initialize(MaxRuntime : TIME := TIME'high);
procedure finalize;
end package;
package body sim is
procedure initialize(MaxRuntime : TIME := TIME'high) is
begin
-- do init stuff
if (MaxRuntime = TIME'high) then
wait on IsFinalized for MaxRuntime;
finalize;
end if;
end procedure;
procedure finalize;
begin
if (not IsFinalized) then
IsFinalized := TRUE;
-- do finalize stuff:
-- -> stop all clocks
-- write a report
end if;
end procedure;
end package body;
entity test is
end entity;
architecture tb of test is
begin
initialize(200 us);
process
begin
-- simulate work
wait for 160 us;
finalize;
end process;
end architecture;
The wait statement is not exited if IsFinalized changed. My simulation runs for circa 160 us. If I set MaxRuntime to 50 us, the simulation stops at circa 50 us (plus some extra cycles until each process noticed the stop condition). When I set MaxRuntime to 200 us, the simulation exits at 200 us and not after 162 us.
How can I exit/abort the wait statement?
Why can't I wait on a variable?
I don't want to use a command line switch for a simulator to set the max execution time.
You cannot wait on a variable for the reasons given by user1155120. So, instead you need to use a signal. (A signal in a package is a global signal).
Unfortunately, even though the global signal is in scope, it still needs to be an output parameter of the procedure, which is ugly. Not only that, in your code, you will then be driving the signal from more than one place, this global signal needs to be a resolved type, eg std_logic. Which is also a bit ugly.
Here is a version of your code with the shared variable replaced by a signal, the boolean type replaced by a std_logic and the global signal added as output parameters:
library IEEE;
use IEEE.std_logic_1164.all;
package sim is
signal IsFinalized : std_logic := '0';
procedure initialize(signal f : out std_logic; MaxRuntime : TIME := TIME'high);
procedure finalize (signal f : out std_logic);
end package;
package body sim is
procedure initialize(signal f : out std_logic; MaxRuntime : TIME := TIME'high) is
begin
-- do init stuff
if (MaxRuntime = TIME'high) then
wait on IsFinalized for MaxRuntime;
finalize(f);
end if;
end procedure;
procedure finalize (signal f : out std_logic) is
begin
if (IsFinalized = '0') then
f <= '1';
-- do finalize stuff:
-- -> stop all clocks
-- write a report
report "Finished!";
end if;
end procedure;
end package body;
use work.sim.all;
entity test is
end entity;
architecture tb of test is
begin
initialize(IsFinalized, 200 us);
process
begin
-- simulate work
wait for 160 us;
finalize(IsFinalized);
wait;
end process;
end architecture;
http://www.edaplayground.com/x/VBK
So it sounds like you want to terminate the simulation, either at time out, or at finished test prior to timeout. It is correct that the simulator will stop when the event queue is empty, but that is pretty high do achieve, as you also experience.
VHDL-2008 has introduced stop and finish in the std.env package for stop or termination of simulation.
In VHDL-2002, a common way is to stop simulation is by an assert with severity FAILURE:
report "OK ### Sim end: OK :-) ### (not actual failure)" severity FAILURE;
This method for simulation stop is based on the fact that simulators (e.g. ModelSim) will usually stop simulation when an assert with severity FAILURE occurs.
How can I exit/abort the wait statement?
Why can't I wait on a variable?
All sequential statements in VHDL are atomic.
A wait statement happens to wait until a resumption condition is met, waiting on signal events, signal expressions or simulation time.
What would you use to select the statement a process resumed at if you could exit/abort it? Would it resume? You'd invalidate the execution of the sequence of statements. VHDL doesn't do exception handling, it's event driven.
And here it's worth remembering that everything that executes in simulation is a process, function calls are expressions and concurrent statements (including procedure calls) are devolved into processes potentially with super-imposed block statements limiting scope, during elaboration.
There's this basic difference between variables and signals (IEEE Std 1076-2008 Appendix I Glossary):
signal: An object with a past history of values. A signal may have multiple drivers, each with a current value and projected future values. The term signal refers to objects declared by signal declarations or port declarations. (6.4.2.3)
variable: An object with a single current value. (6.4.2.4)
You can't wait on something that doesn't have a future value, or a little more simply variables don't signal (from a dictionary - an event or statement that provides the impulse or occasion for something specified to happen).
Simulation is driven by signal events depending on future values. It defines the progression of simulation time. Time is the basis for discrete event simulation.
And about now you could wonder if someone would be telling you the truth if they were to claim VHDL is a general purpose programming language. How can it be while maintaining the ability formally specify the operation of hardware by discrete time events if you have the ability to break and resume a process arbitrarily?
And all this tells you is you might consider using signals instead of share variables.
Morton's stop and finish procedures are found in std.env.
From a test automation point of view it's important to have a time-out that can force a test to a stop using stop/finish/assert failure. Even if your intention is to make everything terminate by not producing more events there is a risk that there is a bug causing everything to hang. If that happens in the first of your tests in a longer nightly test run you're wasting a lot of time.
If you use VUnit it would work like this
library vunit_lib;
context vunit_lib.vunit_context;
entity tb_test is
generic (runner_cfg : runner_cfg_t);
end tb_test;
architecture tb of tb_test is
begin
test_runner : process
begin
test_runner_setup(runner, runner_cfg);
while test_suite loop
if run("Test that hangs") then
-- Simulate hanging test case
wait;
elsif run("Test that goes well") then
-- Simulate test case running for 160 us
wait for 160 us;
end if;
end loop;
test_runner_cleanup(runner); -- Normal forced exit point
end process test_runner;
test_runner_watchdog(runner, 200 us);
end;
The first test case which hangs is terminated by the watchdog after 200 us such that the second can run.
A problem that you may encounter when forcing the testbench to stop with the test_runner_cleanup procedure is if you have one or more additional test processes that have more things to do when the test_runner process reaches the test_runner_cleanup procedure call. For example, if you have a process like this
another_test_process: process is
begin
for i in 1 to 4 loop
wait for 45 us;
report "Testing something";
end loop;
wait;
end process;
it will not be allowed to run all four iterations. The last iteration is supposed to run at 180 us but test_runner_cleanup is called at 160 us.
So there is a need to synchronize the test_runner and another_test_process processes. You can create a package with a global resolved signal to fix this (as discussed before) but VUnit already provide such a signal which you can use to save some time. runner is a record containing, among other things, the current phase of the VUnit testbench. When calling test_runner_cleanup it enters the phase with the same name and then it moves to the test_runner_exit phase in which the simulation is forced to a stop. Any process can prevent VUnit from entering or exiting its phases by placing a temporary lock. The updated another_test_process will prevent VUnit from exiting the test_runner_cleanup phase until it's done with all its iterations.
another_test_process: process is
begin
lock_exit(runner, test_runner_cleanup);
for i in 1 to 4 loop
wait for 45 us;
report "Testing something";
end loop;
unlock_exit(runner, test_runner_cleanup);
wait;
end process;
Any number of processes can place a lock on a phase and the phase transition is prevented until all locks have been unlocked. You can also get the current phase with the get_phase function which may be useful in some cases.
If you're developing reusable components you should probably not use this technique. It saves you some code but it also makes your components dependent on VUnit and not everyone uses VUnit. Working on that :-)
DISCLAIMER: I'm one of the authors for VUnit.
Related
I have a flag that activates in one process and I would like that a concurret process respond to this activation.
send_data is a bit signal:
signal send_data:std_logic:='0';
process
begin
wait until send_data='1';
...
end
process(enumer)
begin
...
send_data<='1';
...
end process;
Generally:
You can write a Signal in one process and read it in multiple other processes.
For your application:
You can set send_data to '1' whilst waiting for it in another process, then set a signal like send_flag_encountered to '1', then reset send_data in the first process and then reset the send_flag_encountered in the second process.
This is even synthesizable. When doing this, I would recommend you to use FSMs.
I just want to make my point (SQ_X2,SQ_Y2) move in 2D plane(in TFT-LCD).
following is part of my code.
If I Push PSW(Push button switch), and point moves 5 in x or y. and there is problem, when I merge following code in one process statement, there is an error "that bad synchronous description".
but to avoid this problem, I declare 4 Process statements, there is another error that Signal SQ_X2 or SQ_Y2 is connected Multiple drivers.
I am in now dilemma.. plz help me and I will buy a pizza
PROCESS (PSW(3))
BEGIN
IF (rising_edge(PSW(3))) then
SQ_Y2 <= SQ_Y2 - std_logic_vector(to_unsigned(5,10));
END IF;
END PROCESS;
PROCESS (PSW(2))
BEGIN
IF (rising_edge(PSW(2))) then
SQ_Y2 <= SQ_Y2 + std_logic_vector(to_unsigned(5,10));
END IF;
END PROCESS;
PROCESS (PSW(1))
BEGIN
IF (rising_edge(PSW(1))) then
SQ_X2 <= SQ_X2 - std_logic_vector(to_unsigned(5,10));
END IF;
END PROCESS;
PROCESS (PSW(0))
BEGIN
IF (rising_edge(PSW(0))) then
SQ_X2 <= SQ_X2 + std_logic_vector(to_unsigned(5,10));
END IF;
END PROCESS;
You cannot assign one signal in multiple processes(more accurately, there are ways you could do that, but you don't want to do it here). Also, one process can only contain one if rising_edge() then statement, because it's not possible to synthesize hardware that reacts to multiple rising edges...
What you need to do is learn about synchronous design, add a clock to your system and do it all in one process. You might want to take a look at this tutorial, especially the sequential logic design section.
I apologize for the cumbersome wording of the title question, but can't think of any other way to ask it concisely. I'm aware that this is quite different from, yet analogous to, the pass-by-value vs reference dichotomy. I am wondering which of the following snippets of code would behave identically:
Declarations:
Port (source : in STD_LOGIC);
...
signal destination : STD_LOGIC := '0';
#1 - Sets the "value" on clock
process (clk) begin
if falling_edge(clk) then
if source = '1' then
destination <= '1';
else
destination <= '0';
end if;
#2 - Sets a "reference" universally
-- Top-Level
destination <= source;
#3 - Sets the (value? reference?) on clock
process (clk) begin
if falling_edge(clk) then
destination <= source;
Snippet #1 will change the value of the destination to match the source every falling edge of the clock cycle. In #2, the destination will become 'connected' to the source, and changes in the source will be followed by the destination regardless of the clock cycle. I am unsure about #3; does it behave like #1 or #2, taking the value of the source and putting it in the destination, or linking the two together as in the top-level assignment?
#1
This process has a sensitivity list. When there is an event (a change in value) on any signal in a sensitivity list, the process starts executing. This process has one signal in the sensitivity list - clk - so when there is a change on that signal, the process starts executing. If that change were a rising edge, then the condition in the if statement evaluates to FALSE and so no more lines of code are executed and the process suspends (goes to sleep). If that change were a falling edge, however, then the condition in the if statement evaluates to TRUE and so, depending on the value of source, one of two lines of code is executed that assigns a value to destination. Here's the really important bit:
When a line of code containing a signal assignment (<=) is executed in VHDL (and the effect is to change the value of the target signal - the signal on the LHS of the assignment), an event is placed on the what I shall call the event queue.
The event queue is the simulator's ToDo list and events placed on it will be actioned at some future time. If there is no explicit delay specified then that event with be actioned on the next simulation cycle (or delta cycle). Bear with...
So, assuming that the effect of executing the lines containing signal assignments is to change the value of destination, then the effect of executing those lines is to place an event on the event queue. The process then suspends.
Once all the processes have suspended, the simulator takes a look at its event queue and moves the simulation time forward to the time of the next event on the queue. In this case, that event will have been scheduled for the next simulation cycle, so the simulator time advances one simulation cycle and the events for that cycle are actioned. If any sensitivity list contains a signal that has been caused to change by actioning one of those events, then the whole cycle starts again: processes get executed, lines of code containing signal assignments get executed, new events get place on the event queue for some future time...
#3
From the point of view of this discussion, case #3 is exactly the same as case #1. A falling edge on clk will cause a line of code containing a signal assignment to be executed and, if a change of value on the target signal (destination) is required then an event will get placed on the event queue to action that change on the next simulation cycle.
#2
Case #2 clearly does not depend on clk, so is different. However, #2 is an example of a concurrent signal assignment. This is effectively and implicit process: you get an implicit sensitivity list, which contains any signal on the RHS of the signal assignment. In this case, the implicit sensitivity list contains one signal - source. So, if there is an event on source then the (implicit) process starts executing resulting in a line of code containing a signal assignment being executed and hence resulting in an event being placed on the event queue (which will be scheduled for the next simulation cycle).
So, to answer your question: "do VHDL signal assignments set destination value or reference?"
The value that is placed on the event queue (ie the value to which the target signal is to be driven) is the value that was evaluated at the time the line of code containing the signal assignment was executed.
So, in case #3, the value to assign to destination which is placed on the event queue is the value that source signal had at the time the line of code containing the signal assignment was executed. If you want to cal that 'pass by copy' then do so, but I wouldn't take that analogy very far.
Note: The LRM - the VHDL standard - uses the terminology Projected Output Waveform for what I call the event queue.
I am trying to understand how signal handlers attach to a process and their scope for a process.
class Main
Signal.trap('USR1') do
Process2.kill
end
def main_process
#do something
p = Process2.new
output = p.give_output
#then again do something
end
end
class Process2
Signal.trap('USR1') do
Process2.kill
end
def self.kill
if ##static_object.blank?
#do nothing
else
#do something
end
end
def give_output
#do something
##static_object = true
#do something
end
end
Now if I issue a SIGUSR1 to the process while give_output is getting executed and ##static_object is not nil, the handler should behave as expected. However, even after give_output has finished execution, if a signal is sent, the handler inside Process2 will catch it. From what I understand, the handler gets attached to the process. Can we have two signal handlers for the same process for the same signal? For example - while give_output is executing and a signal is issued, the handler inside Process2 should get control otherwise another signal handler defined in Main should get control.
I was curious about this myself and came across the following resources: http://timuruski.net/blog/2014/signals-traps-and-rescues/, https://www.ruby-forum.com/topic/87221, and http://www.ibm.com/developerworks/library/l-reent/. That second link has some resources which seem very relevant to your question.
In short, you can only reliably trap one interrupt type per process. Signal interrupts are handled by the host OS. As its name suggests, interrupts halt the normal execution of your program until the interrupt is handled.
Summarizing the information given in the links posted by #Anthony E. Using signal trap it's not possible to trap a signal from multiple places in your code. If you want to handle signals differently at multiple places, use rescue blocks.
Signal Trap -
Signal.trap("USR1") do
#do something
end
Rescue Interrupt -
begin
#some functions
rescue Interrupt
#handle the exception
exit
end
Difference in the way signals are handled by rescue and trap -
Trap defines the handler at the top of the code, if a signal is triggered, it would be caught by the trap. On the other hand, rescue is defined at the bottom of the code, that is when there is no trap available, it will raise a Signal Exception which will be caught by the rescue block
The signal trap code gets associated with the process and cannot be replaced. If one wants to handle signals at different places of the program in a different way, the best way is to raise an exception and handle it
For example I have a code:
BEGIN
BEGIN
-- First Part
call_1_1();
call_1_2();
...
call_1_N();
END;
BEGIN
-- Second Part
call_2_1();
call_2_2();
...
call_2_M();
END;
END;
This code placed at package and running in a job. Execution of this code (job) can be stopped from the outside by stopping the job. Interrupting could crach execution in the middle of the each block. And the question is how secure the execution of the blocks First Part or Second Part when someone interrupting execution from the outside.
Either all the transaction will complete or none of the transaction will complete. Guaranteed. You have to manage your transactions. For example don't commit before the end of the "block" if you want the block to be in one transaction.