Is it possible to define looping functions in a test bench - vhdl

I am doing a past paper in preparation for an exam and one of the questions shows this waveform:
Now I know of course that you could just write the code out line by line like so:
sig1 <= '1'; sig2 <= '1'; y <= '1'; wait for 20 ns;
y <= '0'; wait for 5 ns;
-- etc.
or by utilising arrays. I was wondering if it was possible to streamline the process by setting up looping functions that can be called and run simultaneously.
-- Some pseudocode
function sig1 is
sig1 <= '1'; wait for 25 ns;
sig1 <= '0'; wait for 50 ns;
end sig1;
-- Definition for the other waves goes here
function waveform is
while n=1 loop
sig1, sig2, y run;
end loop;
end waveform;
I've already had a poke around the documentation for VHDL and here on Stack Overflow but I must apologise in advance, I haven't a clue as to what you would call something like this so my searches haven't yielded any results close to what I am thinking. This is just a curiosity of course, I already expect that because of the nature of the hardware and language in question it just might not be possible.
Thank you though in advance for any help!

VHDL for test bench can be more software like, since it does not need to be synthesizable.
The ´y´ part can be written with loop as shown below, and the other parts can be made in a similar fashion.
library ieee;
use ieee.std_logic_1164.all;
entity tb is
end entity;
architecture sim of tb is
signal y : std_logic;
begin
process is
constant period : time := 80 ns;
constant steps : natural := 8;
begin
for i in 0 to steps - 1 loop
case i is
when 0 | 1 | 3 => y <= '1';
when others => y <= '0';
end case;
wait for period / steps;
end loop;
end process;
end architecture;
The above makes use of the VHDL feature, that a process without sensitivity list will restart when the end is reached.
Waveform will be:

VHDL signal assignment allows for waveforms with multiple elements:
IEEE Std 1076-2008
10.5.2 Simple signal assignments
10.5.2.1 General
simple_signal_assignment ::=
simple_waveform_assignment
| simple_force_assignment
| simple_release_assignment
simple_waveform_assignment ::=
target <= [ delay_mechanism ] waveform ;
delay_mechanism ::=
transport
| [ reject time_expression ] inertial
target ::=
name
| aggregate
waveform ::=
waveform_element { , waveform_element }
| unaffected
10.5.2.2 Executing a simple assignment statement
waveform_element ::=
value_expression [ after time_expression ]
| null [ after time_expression ]
Evaluation of a waveform element produces a single transaction. The time component of the transaction is determined by the current time added to the value of the time expression in the waveform element. For the first form of waveform element, the value component of the transaction is determined by the value expression in the waveform element. For the second form of waveform element, the value component is not defined by the language, but it is defined to be of the type of the target. A transaction produced by the evaluation of the second form of waveform element is called a null transaction.
This emulates waveform descriptions found in pattern generators for test languages and IC testers. Multiple waveform elements are intended for simulation typically in testbenches and are not supported by synthesis.
The projected output waveform is a queue of waveform elements that must occur in ascending time order.
Also process statements inherently loop:
11.3 Process statement
The execution of a process statement consists of the repetitive execution of its sequence of statements. After the last statement in the sequence of statements of a process statement is executed, execution will immediately continue with the first statement in the sequence of statements.
These two features allow writing compact independent pattern generators in testbenches:
library ieee;
use ieee.std_logic_1164.all;
entity sig1sig2y_tb is
end entity;
architecture foo of sig1sig2y_tb is
signal sig1, sig2, y: std_ulogic;
begin
sig1_process:
process
begin
sig1 <= '1', '0' after 25 ns;
wait for 75 ns;
end process;
sig2_process:
process
begin
sig2 <= '1', '0' after 25 ns, '1' after 75 ns, '0' after 100 ns,
'1' after 125 ns, '0' after 175 ns;
wait for 200 ns;
end process;
y_process:
process
begin
y <= '1', '0' after 20 ns, '1' after 30 ns, '0' after 40 ns;
wait for 80 ns;
end process;
end architecture;
Note the waveform element delay is relative to the current simulation time.
This testbench produces the target waveforms:

Related

VHDL Generic delay - test bench and config

I have been modelling a few simple VHDL gates, but I can't seem to get the time delay rightI have the following code:
LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
ENTITY AND_4 IS
GENERIC (delay : delay_length := 0 ns);
PORT (a, b, c, d : IN std_logic;
x : OUT STD_logic);
END ENTITY AND_4;
ARCHITECTURE dflow OF AND_4 IS
BEGIN
x <= ( a and b and c and d) AFTER delay;
END ARCHITECTURE dflow;
LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
ENTITY TEST_AND_4 IS
END ENTITY TEST_AND_4;
ARCHITECTURE IO OF TEST_AND_4 IS
COMPONENT AND_4 IS
GENERIC (delay : delay_length := 0 ns);
PORT (a, b, c, d : IN std_logic;
x : OUT STD_logic);
END COMPONENT AND_4;
SIGNAL a,b,c,d,x : std_logic := '0';
BEGIN
G1 : AND_4 GENERIC MAP (delay => 5ns) PORT MAP (a,b,c,d,x);
PROCESS
VARIABLE error_count : integer:= 0;
BEGIN
WAIT FOR 1 NS;
a <= '1';
b <= '0';
c <= '0';
d <= '0';
ASSERT (x = '1') REPORT "output error" SEVERITY error;
IF (x /= '1') THEN
error_count := error_count + 1;
END IF;
--Repeated test vector -- omitted
END PROCESS;
END ARCHITECTURE IO;
CONFIGURATION TESTER1 OF TEST_AND_4 IS
FOR IO
FOR G1 : AND_4
USE ENTITY work.AND_4(dflow)
GENERIC MAP (delay);
END FOR;
END FOR;
END CONFIGURATION TESTER1;
When I simulate the model I only get the 1 ns delay that I added to each test vector. I'm guessing the problem is how I pass the delay to the component declaration in the test bench. I've tried a few things and reread the topic in the book I have but still no joy. Any help ?
Many thanks
D
modifying the unlabelled stimulus process in your testbench:
process
variable error_count : integer:= 0;
begin
wait for 1 ns;
a <= '1';
-- b <= '0';
-- c <= '0';
-- d <= '0';
-- assert (x = '1') report "output error" severity error;
-- if (x /= '1') then
-- error_count := error_count + 1;
-- end if;
--repeated test vector -- omitted
b <= '1';
c <= '1';
d <= '1';
wait for 5 ns;
wait for 5 ns;
wait;
end process;
to simply demonstrated the delay shows that the generic delay is being passed to the instantiated component:
If you get something different perhaps you could convert your question to a Minimal, Complete, and Verifiable example by ensuring that the example actually reproduces the problem and that we know your results:
Describe the problem. "It doesn't work" is not a problem statement. Tell us what the expected behavior should be. Tell us what the exact wording of the error message is, and which line of code is producing it. Put a brief summary of the problem in the title of your question.
The little bit of stimulus you left in your testbench doesn't appear properly test the and_4.
In the event there was more stimulus and you weren't waiting the pulse rejection limit implied by your signal assignment delay mechanism, you'd get nothing but those annoying assertions.
See IEEE Std 1076-2008 10.5. Simple signal assignment statements, 5.2.1 General, paragraphs 5 and 6:
The right-hand side of a simple waveform assignment may optionally specify a delay mechanism. A delay mechanism consisting of the reserved word transport specifies that the delay associated with the first waveform element is to be construed as transport delay. Transport delay is characteristic of hardware devices (such as transmission lines) that exhibit nearly infinite frequency response: any pulse is transmitted, no matter how short its duration. If no delay mechanism is present, or if a delay mechanism including the reserved word inertial is present, the delay is construed to be inertial delay. Inertial delay is characteristic of switching circuits: a pulse whose duration is shorter than the switching time of the circuit will not be transmitted, or in the case that a pulse rejection limit is specified, a pulse whose duration is shorter than that limit will not be transmitted.
Every inertially delayed signal assignment has a pulse rejection limit. If the delay mechanism specifies inertial delay, and if the reserved word reject followed by a time expression is present, then the time expression specifies the pulse rejection limit. In all other cases, the pulse rejection limit is specified by the time expression associated with the first waveform element.
(Note you can go to 10.5.2.2 Executing a simple assignment statement and see the after time_expression is part of the waveform_element and not the delay mechanism).
Sure
ENTITY TEST_AND_4 IS
END ENTITY TEST_AND_4;
ARCHITECTURE IO OF TEST_AND_4 IS
COMPONENT AND_4 IS
GENERIC (delay : delay_length := 0 ns);
PORT (a, b, c, d : IN std_logic;
x : OUT STD_logic);
END COMPONENT AND_4;
SIGNAL a,b,c,d,x : std_logic := '0';
BEGIN
G1 : AND_4 GENERIC MAP (delay => 5 NS) PORT MAP (a,b,c,d,x);
PROCESS
VARIABLE error_count : integer:= 0;
BEGIN
WAIT FOR 1 NS; -- Changed to 6 ns so that the wait is longer then the
-- generic gate propagation delay
a <= '1';
b <= '1';
c <= '1';
d <= '1';
ASSERT (x = '1') REPORT "output error" SEVERITY error;
IF (x /= '1') THEN
error_count := error_count + 1;
END IF;
I have noted the change I made to the test bench model above, seems kinda obvious now but yesterday it had me pulling my hair out.
Cheers
D
The 'fix' was to change the WAIT value in the sequential test bench model from 1 ns to 6 ns. This gives the gate the time to change state because it has a 5 ns inertial delay.
WAIT FOR 6 NS; -- Changed to 6 ns so that the wait is longer then the
-- generic gate propagation delay
Thanks for the help, but I spotted the problem this morning after reading USER115520's post. The delay I set was 'inertial' and set generically at 5 ns. In my test bench process I had only set 1 ns wait statements in between input signal changes. Thus the gate would not perform the transition when the correct stimuli and introduced.
I inserted a 6 ns delay after a=1 b=1 c=1 d=1 and got the correct response from the gate

For the following VHDL Code

For the following VHDL code, assume that D changes to '1' at time 5 ns. Give the values of A, B, C, D, E, and F each time a change occurs. That is, give the values at time 5 ns, 5 + delta, 5 + 2(delta), etc. Carry this out until either 20 steps have occurred, until no further change occurs, or until a repetitive pattern emerges.
entity prob4 is
port (D: inout bit);
end prob4;
architecture q1 of prob4 is
signal A,B,C,E,F: bit;
begin
C <= A;
A <= (B and not E) or D;
P1: proecess (A)
begin
B <= A;
end prcoess P1;
P2: process
wait until A <= '1';
wait for 0 ns;
E <= B after 5 ns;
D < = '0';
F <= E;
end process P2;
end architecture q1;
The problem as presented
There are some obvious syntax errors:
entity prob4 is
port (D: inout bit);
end prob4;
architecture q1 of prob4 is
signal A,B,C,E,F: bit;
begin
C <= A;
A <= (B and not E) or D;
P1: proecess (A) -- misspelled process
begin
B <= A;
end prcoess P1; -- misspelled process
P2: process
-- begin -- missing begin
wait until A <= '1';
wait for 0 ns;
E <= B after 5 ns;
D < = '0'; -- should be "<=" (a single delimiter token signifying assignment)
F <= E;
end process P2;
end architecture q1;
Answering this problem appears to require you've paid attention to lectures, taken notes and/or done the required reading.
Note that there are two drivers one in process P2 assigning D, the other the mode inout port D. There is no resolution for type BIT, implying some event not shown by the VHDL design specification is responsible for D to be assigned a value of 1 at 5 ns and that actual for port D isn't driven. A resolution function can be associated with any subtype declaration (and a port declaration declares a subtype).
That isn't the case here:
entity tb_prob4 is
end entity;
architecture foo of tb_prob4 is
signal D: bit;
begin
DUT:
entity work.prob4 port map (D);
STIMULUS:
process
begin
D <= '1' after 5 ns;
wait;
end process;
end architecture;
ghdl -a prob4 .vhdl
ghdl -e tb_prob4
ghdl -r tb_prob4
./tb_prob4:error: several sources for unresolved signal
for signal: .tb_prob4(foo).ghdl: compilation error
You could theoretically answer the 'problem' supplied in your 'question' by stepping the simulation of an analyzed version of prob4 with the appropriate VHDL tool. This would require forcing D to '1' at 5 ns and releasing it in the next delta cycle (after a step) in the device under test. Otherwise it's a nonsense problem, the VHDL design specification is invalid (see above). You could surmise the bit about "assume that D changes to '1'" was added to stave off questions about the validity.
Solving the problem
The problem can also also be done by hand (paper and pencil).
"... assume that D changes to '1' at time 5 ns" sounds like it implies a single event (no lasting force).
In VHDL an uninitialized signal will take the left most value, which for an enumerated value of type bit is '0' (See package std.standard). This tells you what everything is up to 5 ns. (What's the initial value of A,B,C,D,E,F ?)
Delta cycles are inferred by assignment to the current simulation time. Simulation time advances when further signal assignments are scheduled for the current simulation time. Simulation time is then advanced to the next time a scheduled transaction is present in a projected output waveform for the modeled design hierarchy.
An after schedules a signal event in the projected output waveform for it's target. In the example that would be the current value of B that is scheduled to be assigned to D.
Simulation time advances to the next time with an event (wait, after), wait for 0 ns refers to the current simulation time, will that cause a delta cycle?

DFF Testbench confusing

So i saw this VHDL code for a testbench for a DFF somewhere and i don't quite get a few things.
1) Why are there 5 cases? Why aren't there just two? when the input is 0 and when it is 1;
2) Why did he pick those waiting periods so randomly? It seems that 12,28,2,10,20 ns seem very randomly chosen. What was the logic behind that?
architecture testbench of dff_tb is
signal T_din: std_logic;
signal T_dclk: std_logic;
signal T_qout: std_logic;
signal T_nqout: std_logic;
component dff
port ( din: in std_logic;
dclk: in std_logic;
qout: out std_logic;
nqout: out std_logic
);
end component;
begin
dut_dff: dff port map (T_din,T_dclk,T_qout,T_nqout);
process
begin
T_dclk <= '0';
wait for 5 ns;
T_dclk <= '1';
wait for 5 ns;
end process;
process
variable err_cnt: integer := 0;
begin
--case1
T_din <= '1';
wait for 12 ns;
assert (T_qout='1') report "Error1!" severity error;
-- case 2
T_din <= '0';
wait for 28 ns;
assert (T_qout='0') report "Error2!" severity error;
-- case 3
T_din <= '1';
wait for 2 ns;
assert (T_qout='0') report "Error3!" severity error;
-- case 4
T_din <= '0';
wait for 10 ns;
assert (T_qout='0') report "Error4!" severity error;
-- case 5
T_din <= '1';
wait for 20 ns;
assert (T_qout='1') report "Error5!" severity error;
wait;
end process;
end testbench;
In the following the case 1, case 2, through case 5 are represented by named markers A through E:
Case 1 checks to see that T_qout doesn't get updated on the falling edge of T_clk with T_din = '1'. See marker A.
Case 2 (marker B) checks to see T_qout doesn't get updated on the falling edge of T_clk with T_din = '0'.
(And about now you get the impression a student was supposed to do a gate level implementation of dff).
Case 3 (marker C) checks to see if T_qout remains a '0' (an assertion occurs when the condition is False), that the dff is clocked. That a '1' on T_din doesn't cause the output to change.
Case 4 (marker D) checks to see if T_qout remains a '1' the for the opposite value of T_din.
(These all appear to be checking gate level dff implementations).
Case 5 (marker E) appears to be checking the that a Master–slave edge-triggered D flip-flop isn't oscillating or 'relaxing' to the original state.
The testbench appears to be specific to a class assignment for implementing a DFF as a gate level model.
Now the question is, did the instructor cover all possible cases for a student to get it wrong?
You have to look closer on the clock. The clock switches every 5 ns.
Case 1
So in the beginning the DFF should be '1'.
Case 2
After 15 ns since the start, the output should be '0', not after 12 ns, thats, what you have to check.
Case 3
You set the input to '1' but the DFF should never react on it, because the duration is too short.
Case 4 & Case 5
They just ensure you, that after Case 3, there is everything alright. Again, like in Case 2 here you can check after which amount of time the DFF really switches.
This testbench IS a little bit large, if you consider testing a DFF. But if you are learning about hardware description and testbenching, it is good to know what you have to look after, before you start to implement more complicated and complex designs. Especially if you are going to make a silicon out of it, there is never enough testing done :)

vhdl DFF assert statments

Description:
I want to include vhdl assert statements to report when set_delay and hold_delay time violations occur. I am not sure how to do this with my code and I have been to many places on the web and I don't understand. Please give examples with my code.
Code:
LIBRARY ieee;
USE ieee.std_logic_1164.all;
ENTITY dff IS
GENERIC (set_delay : TIME := 3 NS; prop_delay : TIME := 12 NS;
hold_delay : TIME := 5 NS);
PORT (d, set, rst, clk : IN BIT; q : OUT BIT; nq : OUT BIT := '1');
END dff;
--
ARCHITECTURE dff OF dff IS
SIGNAL state : BIT := '0';
BEGIN
dff: PROCESS
BEGIN
wait until rst;
wait until set;
wait until clk;
IF set = '1' THEN
q <= '1' AFTER set_delay;
nq <= '0' AFTER set_delay;
ELSIF rst = '1' THEN
q <= '0' AFTER prop_delay;
nq <= '1' AFTER prop_delay;
ELSIF clk = '1' AND clk'EVENT THEN
q <= d AFTER hold_delay;
nq <= NOT d AFTER hold_delay;
END IF;
END PROCESS dff;
END dff;
I do understand that the general assert syntax is:
ASSERT
condition
REPORT
"message"
SEVERITY
severity level;
Part of my problem is that I don't know where to put these assert statements and I am not sure how I would write them.
I would introduce additional signals in which you store the time of the last manipulation. Then I'd add other process which manage the signals and check the times:.
time_debug : block
signal t_setup, t_hold : time := 0 ns;
begin
setup_check : process (clk)
begin
if clk'event and clk = '1' then
t_hold <= now;
assert (t_setup - now)>set_delay REPORT "Setup time violated." SEVERITY note;
end if;
end process setup_check;
hold_check: process (d)
begin
if d'event then
t_setup <= now;
assert (t_hold - now)>hold_delay REPORT "Hold time violated." SEVERITY note;
end if;
end process hold_check;
end block time_debug;
What this does is it saves the time of the last positive clock edge and the time of the last input change. Now every time either d changes or the clock rises the delays are checked. I couldn't verify this in a compiler because I don't have one set up here, but I'll gladly do so if there are problems with this solution.
I personally like to keep debug stuff in a dedicated block, so I can easily keep track of which signals I only use for debugging and can later easily remove them. It also makes it easier to add all debug signals to e.g. modelsim's wave screen.
Also note that these asserts and reports will only work in simulation.

Make a signal wait until falling edge

I have this signal that should be zero until another signal Start = 0. How can I accomplish this? Here is the relevant code:
din<=0;
wait until falling_edge(start);
for i in 0 to 63 loop
wait until clk = '1' and clk'event;
if i = 0 then
Start <= '1','0' after clk_period;
end if;
if (i < 24) then
din <= 255;
elsif (i > 40) then
din <= 255;
else
din <= 0;
end if;
end loop;
wait;
I thought I could just make din = 0 until the falling edge of start but it stops at the rising edge of start. I want to start reading the din values when start =0. Before that din = 0.
Here is a pic:
EDIT: Actually I got it to start at the correct signal values but the dout value always has an intermediate value that isn't necessary. In this case its 78450. I know this has to do with the testbench code but I can't get it to just calculate the correct value at the correct time. What changes can be made to the code below to get rid of the intermediate value?
din<=0;
for i in 0 to 63 loop
wait until clk = '1' and clk'event;
if i = 0 then
Start <= '1','0' after clk_period;
elsif (i < 24) then
din <= 255;
elsif (i > 40) then
din <= 255;
else
din <= 0;
end if;
end loop;
First of all I assume (and hope) you are writing a testbench. If not, you should avoid using wait statements, as these have very limited support in synthesis tools.
Even in a testbench, it is best to use time-based wait or after statements only to generate the clock, and make all other signals dependent on an event (e.g. rising_edge(clk)). This avoids the problem of having multiple signals changing during delta cycle 0 along with the clock.
Consider the following code for a typical register:
process(clk) begin
if(rising_edge(clk)) then
a <= b;
end if;
end process;
and assume that clk and b are generated in a testbench as follows:
clk <= not clock after 1 ns;
process begin
b <= '1', '0' after 10 ns;
wait;
end process;
At time 0 delta 0, clk changes to '1' and b would change to '1'.
At time 0 delta 1, the register process would run since clk changed, and a would change to '1'.
No further sensitivity exists, so time would update to the next event at 1 ns.
At time 1 delta 0, clk changes to '0'.
At time 1 delta 1, the register process is run since clk changed, but nothing happens because rising_edge(clk) is false.
The above repeats for time 2-9 ns.
At time 10 delta 0, clk changes to '1' and b changes to '0'. Note that clk and b change in the same delta cycle.
At time 10 delta 1, the register process runs and a changes to '0'! As far as the result is concerned, this means that b changed before the rising clock edge!
Even if this behavior is understandable in this simple system, it can lead to some incredibly difficult to find simulation bugs. It is therefore better to base all signals off of the appropriate clock.
process begin
-- Initialize b to 1.
b <= '1';
-- Wait for 5 cycles.
for i in 1 to 5 loop
wait for rising_edge(clk);
end loop;
-- Set b to 0.
b <= '0';
-- Done.
wait;
end process;
This avoids unexpected behavior, since all signals will change at least one delta cycle after the associated clock, meaning causality is maintained throughout all of your processes.
I have this signal that should be zero until another signal Start = 0. How can I accomplish this?
Maybe you can use a handshake signal and put it in the sensitive list of the process. It will behave like a reset signal.
process (handshake_s, ...)
begin
if (handshake_s = '1') then -- failing edge of start
din <= 0;
else
-- do something
end if;
end process;
Use another process to update handshake_s.
process (start, ...)
begin
if failing_edge(start) then
handshake_s <= '1', '0' after 10 ns; -- produce a pulse
end if;
-- do something
end process;
Would you mind post all your code here so that we could understand the waveform better?
Testbench or RTL code?
For a testbench, your coding style is mostly ok, however, your signal Start has a problem and will never be '1' during a rising edge of clock. It goes to '1' just after the rising edge of clock and will return to '0' either simultaneously with clock or 1 delta cycle before clock (depending on your clock setup). Either way, anything running on rising_edge clock, such as your design, will not see it as a '1'.
A simple way to avoid this is to use nominal delays (25% of tperiod_Clk) on all of your testbench outputs that go to the DUT (Device Under Test). The pattern for a pulse is as follows.
wait until clk = '1' ; -- I recommend using rising_edge(Clk) for readability
Start <= '1' after tpd, '0' after tpd + tperiod_clk ;
Alternately, you can avoid this issue by not using waveform assignments. Such as the following. In this case, you don't need the tpd, however, if it really is a testbench, I recommend using it.
wait until clk = '1' ;
if i = 0 then
Start <= '1' after tpd ;
else
Start <= '0' after tpd ;
end if ;
For RTL code, you need to explore a different approach. Very briefly one way to approach it is as follows. Note do not use any delays, waveform assignments, or loops.
-- Counter to count from 0 to 63. Use "+ 1". Use "mod 64" if using type integer.
-- Start logic = decoder (can be coded separately)
-- Din Logic = decoder (can be coded separately)

Resources