You are on page 1of 133

VHDL Verification Course

Verification is an important part of any ASIC design cycle. It's important that complex designs are
simulated fully before prototypes are built, as it's difficult to find bugs in silicon and going through
additional layout cycles is costly and time consuming.

VHDL is well suited for verification. This course is an introduction to VHDL verification techniques. It
assumes some familiarity with VHDL.

© Stefan Doll, vc3@stefanVHDL.com

Table of Contents

Basic Stimulus Generation Approaches to Test Generation


Testbench Structure File Read Method
Definition of Terms VHDL pre-processing Method
Writing to Files Test-specific Entities
Reading from Files Configuration controlled Test Selection
More Reading from Files Using Transaction Logs
The World of Perl Using Transaction Logs II
SRAM modeling Using Behavioural Models
Passive SRAM Model Recommended Directory Structure
Signal Monitors Test Strategy
Generating Clock and Reset Stimulus The End

Sponsor: BIO-SMG

http://www.stefanvhdl.com/vhdl/html/index.html [1/13/2009 3:54:26 PM]


VHDL Verification Course

Basic stimulus generation and verification


First basic stimulus generation is demonstrated. A simple XOR build from AND and OR gates is used as
an example. An extra term is added to deliberately introduce an error.

y <= (x1 and not x2) or (x2 and not x1) or (x1 and x2);

It's possible to generate stimulus using a process with signal assignments and wait statements as shown
below:

test_seq: process

begin

x1 <= '0';
x2 <= '0';
wait for 10 ns;

x1 <= '1';
x2 <= '0';
wait for 10 ns;

x1 <= '0';
x2 <= '1';
wait for 10 ns;

x1 <= '1';
x2 <= '1';
wait for 10 ns;

x1 <= 'X';
x2 <= 'X';

...

end process test_seq;

http://www.stefanvhdl.com/vhdl/html/basic_stim_gen.html (1 of 4) [1/13/2009 3:54:30 PM]


VHDL Verification Course

This code can now be simulated and it's possible to identify the design error by observing the
waveforms:

However it's more efficient to build in checks which automatically verify the result of a simulation. This
can be accomplished with the use of assert statements. An assert statement verifies that a certain
condition holds true. If the condition is violated it will generate the associated message and will attach
the specified severity level to it. The example VHDL code could be extended like this:

....

wait for 10 ns;

x1 <= '1';
x2 <= '1';
assert y = (x1 xor x2)
report "E@TB: circuit failed"
severity Error;

wait for 10 ns;

...

A simulator executing the code will produce output similar to this:

# ** Error: E@TB: circuit failed


# Time: 40 ns Iteration: 0 Instance: /tb1

Ideally the assert statement should provide information about the condition in which the error occurred.

http://www.stefanvhdl.com/vhdl/html/basic_stim_gen.html (2 of 4) [1/13/2009 3:54:30 PM]


VHDL Verification Course

This will aid debugging substantially. Below are two alternatives to the previous assert statements. One
uses the 'image attribute from the VHDL 93 standard. The other uses the conversion function str() from
package txt_util.vhd which is used extensively throughout this course. The package provides a
somewhat less cumbersome way to handle string processing and similar tasks.

assert y = (x1 xor x2)


report "E@TB: failure at: x1="& std_logic'image(x1)&
" x2="& std_logic'image(x2)
severity Error;

assert y = (x1 xor x2)


report "E@TB: failure at: x1="& str(x1)& " x2="& str(x2)
severity Error;

Shown below is the output of the two assert statements. It's now much easier to identify the cause of the
problem.

# ** Error: E@TB: failure at: x1='1' x2='1'


# Time: 40 ns Iteration: 0 Instance: /tb1

# ** Error: E@TB: failure at: x1=1 x2=1


# Time: 40 ns Iteration: 0 Instance: /tb1

A standard format for error messages will also help to identify the location of a bug. The standard
adopted here uses the first letter to indicate the severity (I=Information, W=Warning, E=Error,
F=Failure) followed by "@" and the entity name of the unit which generated the message. The "E@"
notation makes it very easy to grep long logfiles and identify problems. Knowing the entity which
detected a bug will aid fixing it, too.

Below are the files which have been simulated in this section:

txt_util. tb1.
vhd vhd

Note: The txt_util package requires a VHDL 93 compatible simulator.

http://www.stefanvhdl.com/vhdl/html/basic_stim_gen.html (3 of 4) [1/13/2009 3:54:30 PM]


VHDL Verification Course

http://www.stefanvhdl.com/vhdl/html/basic_stim_gen.html (4 of 4) [1/13/2009 3:54:30 PM]


VHDL Verification Course

Testbench Structure
In the previous section both the design and the test code were located in the same file. This approach
was only taken for demonstration purposes and is not recommended in the general case. Typical
testbench code - such as text output and assert statements - can not be synthesized and will at the very
least create a number of unnecessary warnings from the tool. Also testbench code can have similar
complexity as design code and if it's well structured, is quite likely to be reusable.

The following diagram shows a typical testbench structure:

In this example the top level entity (testbench) instantiates four components: the processor transactor,
the RAM model, the I2C-Controller transactor and the design which needs to be tested (DUT). Each
testbench component and the top level testbench are in their own files, the DUT is also in a separate file.

http://www.stefanvhdl.com/vhdl/html/tb_structure.html (1 of 2) [1/13/2009 3:54:31 PM]


VHDL Verification Course

The testbench components produce stimulus and verify the response from the DUT.

http://www.stefanvhdl.com/vhdl/html/tb_structure.html (2 of 2) [1/13/2009 3:54:31 PM]


VHDL Verification Course

Definition of Terms
The following terms are used frequently in literature. Despite of the statement in the heading, there are
no hard definitions, but the following should reflect common usage:

● Model
A model is a description of the device which behaves just like the device does. Generally the
behaviour can not be controlled by any other way then applying stimulus to it's input pins. RAMs
are usually described in this way.

● Transactor
Transactors have additional control mechanisms: they can read control data from a file or contain
test specific code. They control which test is run in the testbench in which they are instantiated.

● Bus Functional Model (BFM)


A BFM is a model of a device as it appears on a bus. E.g. a processor model coded in this fashion
would only contain read and write processes, and none of the internal processing (no ALU,
registers etc).

http://www.stefanvhdl.com/vhdl/html/definitions.html [1/13/2009 3:54:32 PM]


VHDL Verification Course

Writing to Files
Writing to files can be useful in a VHDL simulation. This section will illustrate how to implement that
with VHDL 93.

Here is the header of an example program:

library ieee;
use ieee.std_logic_1164.all;

use std.textio.all;
use work.txt_util.all;

entity FILE_LOG is
generic (
log_file: string := "res.log"
);
port(
CLK : in std_logic;
RST : in std_logic;
x1 : in std_logic;
x2 : in std_logic_vector(7 downto 0)
);
end FILE_LOG;

For the purpose of this example two signals x1 and x2 shall be logged to a file on every rising clock
edge.

To operate on files they need to be declared:

architecture log_to_file of FILE_LOG is

file l_file: TEXT open write_mode is log_file;

begin

Here l_file is the file, it's of type TEXT and opened in write mode. The log_file parameter is of type

http://www.stefanvhdl.com/vhdl/html/file_write.html (1 of 4) [1/13/2009 3:54:34 PM]


VHDL Verification Course

string and usually assigned with a generic as shown above.

The following loop will log x1 and x2 into the file specified by log_file:

while true loop


write(l, str(x1)&" "& hstr(x2)& "h");
writeline(l_file, l);
end loop;

As can be seen VHDL writes to a file in two steps: first a string is written to a variable of type line (that's
what the write command does), then the line is appended to a file with the writeline command.

In the example shown here x1 is of type std_logic and x2 of type std_logic_vector. The function str()
will convert x1 in a string, the function hstr() will convert x2 in a string in hex format. (Both functions
are in txt_util.vhd). Strings and characters can be combined into a larger string with the & operator.

The txt_util package provides a simpler way to write to a file, e.g. with:

print(l_file, str(x1)& " "& hstr(x2)& "h");

Which fits into a single line and doesn't require a line type variable. Also since the input to print is
always a string no type casting is necessary.

The usage can be illustrated by inserting the lines below in front of the while-loop. (The code will
generate a header for the log file.)

print(l_file, "# x1 x2 ");


print(l_file, "#----------");
print(l_file, " ");

wait until RST='1';


wait until RST='0';

while true loop


. . .

If the waveforms shown below are applied to the entity:

http://www.stefanvhdl.com/vhdl/html/file_write.html (2 of 4) [1/13/2009 3:54:34 PM]


VHDL Verification Course

Then the following will be the contents of res.log:

# x1 x2
#----------

0 01h
0 02h
0 03h
...
0 0Fh
1 10h
1 11h
...

Below are the files which have been simulated in this section:

txt_util. file_log. stim_gen2. tb_file_log.


vhd vhd vhd vhd

http://www.stefanvhdl.com/vhdl/html/file_write.html (3 of 4) [1/13/2009 3:54:34 PM]


VHDL Verification Course

http://www.stefanvhdl.com/vhdl/html/file_write.html (4 of 4) [1/13/2009 3:54:34 PM]


VHDL Verification Course

Reading from Files


Reading from files is very important for VHDL simulation. Apart from using it in self-designed
testbenches, many commercially available testbench components make use of this method, too. This
section will illustrate how to read file using VHDL 93 syntax.

Here is an example entity header:

entity FILE_READ is
generic(
stim_file: string :="sim.dat"
);
port(
CLK : in std_logic;
RST : in std_logic;
Y : out std_logic_vector(4 downto 0);
EOG : out std_logic
);
end FILE_READ;

In this example data is read from a file sim.dat at every rising clock edge and applied to the output
vector Y. Once every line of the file is read the EOG (End Of Generation) flag is set.

The declaration of the input file is shown below:

architecture read_from_file of FILE_READ is

file stimulus: TEXT open read_mode is stim_file;

begin

The file is stimulus, it's of type TEXT and opened in read mode. The file name is defined in the string
stim_file. (stim_file is a generic, defined in the entity header).

Just as a file write, a file read is done in two steps. The first step fetches a line from a file and stores it in

http://www.stefanvhdl.com/vhdl/html/file_read.html (1 of 3) [1/13/2009 3:54:35 PM]


VHDL Verification Course

a line type variable (readline command) the second reads a string from the line (read command):

EOG <= '0';

-- wait for Reset to complete


wait until RST='1';
wait until RST='0';

while not endfile(stimulus) loop

-- read digital data from input file


readline(stimulus, l);
read(l, s);
Y <= to_std_logic_vector(s);

wait until CLK = '1';

end loop;

print("I@FILE_READ: reached end of "& stim_file);


EOG <= '1';

wait;

Since a string is read from the input file, a conversion function is required to obtain a std_logic_vector.
The function to_std_logic_vector(s) achieves that, it is part of the txt_util package.

With the following contents of sim.dat:

00010
00011
11100
1UXZW
HL111
11111

...file_read.vhd will generate these waveforms:

http://www.stefanvhdl.com/vhdl/html/file_read.html (2 of 3) [1/13/2009 3:54:35 PM]


VHDL Verification Course

Below are the files which have been simulated in this section:

txt_util. file_read. sim. tb_file_read.


vhd vhd dat vhd

http://www.stefanvhdl.com/vhdl/html/file_read.html (3 of 3) [1/13/2009 3:54:35 PM]


VHDL Verification Course

More Reading from Files


In addition to reading data, it's also possible to read commands from files. This will be discussed in this section by
extending the already introduced file_read.vhd.

The new version will read hex data and will also understand a command: #count. Each time it is found in the input file the
file reader shall shall count from 1 to 5 in binary format and present that count on the output port. Unfortunately the file I/O
of VHDL is not very sophisticated. It's not allowed to read a string from a file where the string is longer than the number
of characters in that line.

Here is what needs to be done in order to read variable length strings from an input file:

readline(stimulus, l);
s := (others => ' ');
for i in s'range loop
read(l, c, in_string);
s(i) := c;
if not in_string then -- found end of line
exit;
end if;
end loop;

The read function will return false for in_string once the last character of the line has been read.

The above function has been placed in txt_util.vhd and named str_read(stimulus, s). The length of s determines the
maximum number of characters in a line which can be evaluated.

Using this function the following code will implement the set task:

while not endfile(stimulus) loop


str_read(stimulus, s);

if s(1 to 6) = "#count" then -- check for command "count"


for i in 1 to 5 loop
Y <= conv_std_logic_vector(i,5);
wait until CLK = '1';
end loop;
else
-- if it's not a command -> process data normally
Y <= to_std_logic_vector(s to 5);
wait until CLK = '1';
end if;

end loop;

http://www.stefanvhdl.com/vhdl/html/file_read2.html (1 of 2) [1/13/2009 3:54:37 PM]


VHDL Verification Course

print("I@FILE_READ: reached end of "& stim_file);


EOG <= '1';

wait;

Note that the appropriate sub-section of the string s needs to be compared with #count as comparing different length
strings will always yield the result false.

Here is an input file making use of the #count-command.

00010
00011
#count
11100
1UXZW
HL111
11111

The resulting waveforms are thus:

Below are the files which have been simulated in this section:

txt_util. file_read2. sim2. tb_file_read2.


vhd vhd dat vhd

http://www.stefanvhdl.com/vhdl/html/file_read2.html (2 of 2) [1/13/2009 3:54:37 PM]


VHDL Verification Course

The World of Perl


As it could be seen in the last section, it's possible to read more from files than just data. Many
commercially available testbenches support sophisticated commands. There are limits however: in most
cases structural elements like loops and procedures are missing. It's theoretically possible to extend the
file reader into a proper parser and add these language elements, however VHDL is not really suited for
these tasks and access to the source code may not always be possible. A way to get around these
problems is to generate the input files with a different language such as perl.

The perl script below will generate an input file which can be read by file_read.vhd.

print "00011\n";
print "11100\n";

for ($i=0;$i<10;$i++) {
print num2binary($i,5)."\n";
}

print "1UXZW\n";
print "11111\n";

Resulting Stimulus File:

00011
11100
00000
00001
00010
00011
00100
00101
00110
00111
01000
01001
01010
1UXZW

http://www.stefanvhdl.com/vhdl/html/perl.html (1 of 2) [1/13/2009 3:54:38 PM]


VHDL Verification Course

HL111
11111

It's straightforward to extend this approach e.g. for 256 iterations if all values of a 8 bit word are to be
covered. Entering these values manually would be very cumbersome. The script actually calls a
procedure num2binary which can be found in the complete script. More complex procedures like PRBS
patterns or CRC generators could be used in a similar fashion.

Here is the complete perl script:

tgen.
pl

http://www.stefanvhdl.com/vhdl/html/perl.html (2 of 2) [1/13/2009 3:54:38 PM]


VHDL Verification Course

SRAM modeling
SRAM models are used quite frequently and many devices have bus interfaces which are similar to SRAMs. It's therefore
valuable to have a standard approach for modeling these interfaces.

Below is the data for a simplified SRAM:

Parameter Description Min Max Unit


tSU A,D valid to WE_L asserted 4 - ns
tH WE_L deasserted to A,D invalid 3 - ns

tW_WE WE_L asserted to WE_L


40 - ns
deasserted

For the purpose of this example reading from the SRAM is ignored, and only writes are implemented. It's easier to code
transactors rather than models, so initially this approach is taken.

Core of the implementation is test_prg, a process which contains the write procedure and the test program. The purpose of
the write procedure is to verify the timing of a write access to the SRAM as well as to verify whether data and address are as
expected.

procedure write(wadd: std_logic_vector(7 downto 0);


wdat: std_logic_vector(7 downto 0)
) is

variable start_cycle: time;

The parameters wadd and wdat specify address and data for the expected write access.

begin

D <= (others => 'Z');

http://www.stefanvhdl.com/vhdl/html/sram.html (1 of 4) [1/13/2009 3:54:41 PM]


VHDL Verification Course

wait until WE_L = '0';

The data bus is assigned to 'Z' (=tristate) values. This means the SRAM model will not drive the data bus and therefore will
not corrupt the data input. The procedure will wait for the start of the write access which is equivalent to waiting for WE_L
to be asserted (active low).

start_cycle := now;

-- check setup times


assert A'last_event >= tSU
report "E@SIMPLE_SRAM: Address setup time violated"
severity Error;

assert D'last_event >= tSU


report "E@SIMPLE_SRAM: Data setup time violated"
severity Error;

The variable start_cycle will be assigned to the simulation time. This means it will contain the time at which WE_L was first
asserted in the access cycle. (The contents of that variable will be used later.) The attribute 'last_event is very useful for
testbenches, it returns the time which has expired since the last event of a signal. (Example: if WE_L was asserted at 35 ns,
and the current simulation time is 73 ns, then WE_L'last_event will return 73 ns - 35 ns = 38 ns.)

In this case the A'last_event returns the time A has been stable before WE_L was asserted. This is the actual setup time and
should be greater or equal to tSU. If this is not the case, the assert statement will issue the message "E@SIMPLE_SRAM:
Address setup time violated". The same mechanism is used to verify the setup time for the data lines.

-- report action for transaction log


print("I@SIMPLE_SRAM: "& hstr(D)& "h written to "& hstr(A)& "h");

In any case the access to the SRAM will be reported. The hstr function is part of the txt_util.vhd package, of course. It
returns the value of a std_logic_vector in hex format.

-- verify address
assert A = wadd
report "E@SIMPLE_SRAM: Address incorrect, expected "&
str(wadd)& " received "& str(A)
severity Error;

Next the address is verified, if it's incorrect a message is issued which reports the expected and the actual value. This time it's
in binary format, so that it's easier to identify which bit was corrupted.

-- verify data
for i in wdat'range loop
if wdat(i) /= '-' and wdat(i) /= D(i) then
print("E@SIMPLE_SRAM: Write Data Invalid, written data = "& str(D)&
" expected data = "& str(wdat) );

http://www.stefanvhdl.com/vhdl/html/sram.html (2 of 4) [1/13/2009 3:54:41 PM]


VHDL Verification Course

exit;
end if;
end loop;

Somewhat more effort is put into verifying the data bus. Bits which are marked with "-" (=don't care bits) in the expected
data are not compared with the actual data. This can be useful especially when modeling devices with a SRAM like interface
when not all control bits at a particular address are actually relevant. (Example: wdat="00010--" and D="0001011" => not
error would be indicated.)

The loop will go through all bits of the expected data word, compare or skip it and issue an error message when a
discrepancy is found. In that latter case the loop is exited, to avoid reporting the same error several times.

wait until WE_L = '1';

-- verify pulse width on WE_L


assert now - start_cycle >= tW_WE
report "E@SIMPLE_SRAM: WE_L pulse width violated"
severity Error;

-- verify address and data haven't changed during the cycle


assert A'last_event >= (now - start_cycle)
report "E@SIMPLE_SRAM: Address hold time violated"
severity Error;

assert D'last_event >= (now - start_cycle)


report "E@SIMPLE_SRAM: Data hold time violated"
severity Error;

The cycle completes when WE_L is deasserted. Now it's possible to verify the pulse width by comparing the current time
with the time at the beginning of the cycle (= start_cycle). The 'last_event attribute can not be used for this purpose since
that would now refer to the time expired since WE_L returned to '1' (= 0 ns).

It's now also possible to verify that A hasn't changed during the cycle. If that was the case then A'last_event would be smaller
than the time which has expired since the beginning of the cycle (= now - start_cycle). The same steps are taken to verify
that D hasn't changed.

-- now make sure the hold times are maintained


add_hold <= true, false after tH;
dat_hold <= true, false after tH;
add_val <= A;
dat_val <= D;

end write;

The cycle is not totally complete yet, as the address and data hold times have not been verified. The code above activates

http://www.stefanvhdl.com/vhdl/html/sram.html (3 of 4) [1/13/2009 3:54:41 PM]


VHDL Verification Course

two separate processes (the hold time monitors) to check the values of A and D.

-- hold time monitors


add_monitor: process
begin

wait until A'event;

assert not add_hold or A = add_val


report "E@SIMPLE_SRAM: Address hold time violated"
severity Error;

end process add_monitor;

As long as add_hold is true (which it will be for a time tH after WE_L was deasserted) the hold time memory will verify the
value of A, each time an event on A occurs. The simulation diagram below shows how the signals add_hold and dat_hold are
true (activating the hold time monitors) for 3 ns (the value of tH) and then return to false (deactivating the monitors).

Below are the files which have been simulated in this section:

txt_util. simple_sram. stim_gen. tb_simple.


vhd vhd vhd vhd

http://www.stefanvhdl.com/vhdl/html/sram.html (4 of 4) [1/13/2009 3:54:41 PM]


VHDL Verification Course

Passive SRAM Model


Modeling RAMs as models rather than as transactors has advantages and disadvantages. A transactor implementation
doesn't need to represent memory and can verify the correct address and data. In case of a write it's possible to identify the
problem at the time the write access occurs, rather than the time at which reading back occurs. However a passive
implementation does not need to know which test is executed and is in that respect more generic than the transactor
version.

One of the problems of implementing RAM models is representing internal memory. A large array of std_logic_vectors
(eg. 2Mx64 bits) needs a large chunk of memory on the machine it's simulated on.

A possible approach is to represent the memory cells with integers (of an appropriate range) rather than std_logic_vectors.
(One such approach can be found on Ben Cohen's website.) The approach taken here is based on the observation that most
tests only access a tiny fraction of the modeled memory. For each write access the data as well as the address is stored into
the internal array. For a subsequent access the model searches the array and identifies whether the address is already
present. So if there are only 32 different addresses which are accessed, then there is no reason to have more than that
number of memory locations in the model. However searching the array is more time intensive than using the address as an
index, therefore there is likely to be a cross over point, where searching requires more resources than having a large array.

Here is the VHDL for this memory representation:

-- Internal Memory
type mem_add_type is array (integer range <>) of std_logic_vector(A'range);
type mem_dat_type is array (integer range <>) of std_logic_vector(D'range);

variable mem_add: mem_add_type(mem_words-1 downto 0);


variable mem_dat: mem_dat_type(mem_words-1 downto 0);
variable used_pnt: integer := 0;

The parameter mem_words is a generic defined in the header, it can be set during instantiation for the required number of
memory locations.

One of the additional tasks for the passive memory model, is to decide whether to call the read or the write procedure. The
code below will take care of that:

D <= (others => 'Z');

wait until WE_L'event or RD_L'event;

assert (WE_L /= 'X' and WE_L /= 'Z' and WE_L /= 'U' and WE_L /= '-') or
no_reset_yet
report "E@SRAM2: WE_L="& str(WE_L)& " invalid value"
severity Error;

http://www.stefanvhdl.com/vhdl/html/sram_passive.html (1 of 3) [1/13/2009 3:54:42 PM]


VHDL Verification Course

assert (RD_L /= 'X' and RD_L /= 'Z' and RD_L /= 'U' and WE_L /= '-') or
no_reset_yet
report "E@SRAM2: RD_L="& str(RD_L)& " invalid value"
severity Error;

assert to_X01(RD_L) /= '0' or to_X01(WE_L) /= '0'


report "E@SRAM2: both read and write are asserted"&
"RD_L="& str(RD_L)& " WE_L="& str(WE_L)
severity Error;

-- decide whether read or write access

if to_X01(WE_L) = '0' then


write;
end if;

if to_X01(RD_L) = '0' then


read;
end if;

end process test_proc;

The process will wait until activity either on RD_L or WE_L occurs. Illegal values for these signals ('U', 'X', '-' and 'Z') are
reported as errors, they should never occur during simulation. Also if both signals are asserted simultaneously an error is
reported. The function to_X01 will convert 'H' and 'L' values to '1' and '0' respectively. This is useful on busses which are
pulled up or down and reflects the actual behaviour of the SRAM.

The write process is similar to the one for the transactor version. However the address and data verification can no longer
be performed. Also the write access now has to be stored in the memory array:

...
wait until to_X01(WE_L) = '1';

-- Store written data


for i in 0 to used_pnt loop
if i = used_pnt then -- access to a new address
mem_add(i) := A;
mem_dat(i) := D;
if used_pnt < mem_words - 1 then
used_pnt := used_pnt + 1;
else
print("W@SRAM2: Simulation model can't handle additional
addresses");
end if;

http://www.stefanvhdl.com/vhdl/html/sram_passive.html (2 of 3) [1/13/2009 3:54:42 PM]


VHDL Verification Course

end if;
if mem_add(i) = A then -- access to an existing address
mem_dat(i) := D;
exit;
end if;
end loop;

The code loops through the already written array until a match for the address is found. If no match can be found and there
is still room in the array, the new address is entered and the usage pointer is incremented. If there is no more space
available a warning is issued.

The counterpart of this code can be found in the read procedure:

-- Retrieve data from internal memory


for i in 0 to used_pnt+1 loop
if i = used_pnt+1 then -- access to a new address
print("W@SRAM2: Address has not been written to yet");
print("I@SIMPLE_SRAM: "& hstr(xx)& " provided for "&
hstr(A)& "h");
D <= (others => 'X');
exit;
end if;
if mem_add(i) = A then -- access to an existing address
D <= mem_dat(i) after tRD;
print("I@SIMPLE_SRAM: "& hstr(mem_dat(i))& "h provided for "&
hstr(A)& "h");
exit;
end if;
end loop;

The loop will investigate all written memory locations and try to establish a match. If successful it will take the data from
that location and drive it onto the bus, otherwise it will issue a warning and drive all X's on the data bus.

Below are the files which have been simulated in this section:

txt_util. sram2. mp. tb_simple2.


vhd vhd vhd vhd

http://www.stefanvhdl.com/vhdl/html/sram_passive.html (3 of 3) [1/13/2009 3:54:42 PM]


VHDL Verification Course

Signal Monitors
Often it's desirable to monitor the status of a signal and display messages whenever it changes. A good
example for this is an interrupt signal. Here presented are two possibilities for implementing this:

-- report changes of the interrupt signal

monitor: process(INT_L)

begin

print("I@TB: INT_L="& str(INT_L));

end process monitor;

The process will be executed each time there is an event on INT_L. Whenever that happens a message
will be printed.

Here is an alternative using an extensions of the print command which is available in txt_util.vhd:

-- report when interrupt is asserted


print(INT_L'event and INT_L = '0', "I@TB: INT_L="& str
(INT_L));

This function has as a first parameter a boolean expression and as the second parameter a message text.
The message text will be printed whenever the boolean expression is true. (In this case whenever INT_L
changes to '0'). The function does not need to be part of a process, it can be used as a concurrent
statement.

Below are the files which have been simulated in this section:

txt_util. simple_mon.
vhd vhd

http://www.stefanvhdl.com/vhdl/html/monitor.html (1 of 2) [1/13/2009 3:54:42 PM]


VHDL Verification Course

http://www.stefanvhdl.com/vhdl/html/monitor.html (2 of 2) [1/13/2009 3:54:42 PM]


VHDL Verification Course

Generating Clock and Reset Stimulus


Practically every testbench needs a clock and a reset signal. This section shows a simple way to
implement them:

. . .
signal clk: std_logic := '0';
signal rst: std_logic;

begin

rst <= '0', '1' after 10 ns, '0' after 30 ns;


clk <= not clk after 8 ns;
. . .

Note that the clk signal needs to be initialized in the declaration, as the inverse of 'U' (=uninitialized) is
also 'U'. Here is the simulation result:

Below are the files which have been simulated in this section:

http://www.stefanvhdl.com/vhdl/html/clk_rst.html (1 of 2) [1/13/2009 3:54:44 PM]


VHDL Verification Course

tb_clk_rst.
vhd

http://www.stefanvhdl.com/vhdl/html/clk_rst.html (2 of 2) [1/13/2009 3:54:44 PM]


VHDL Verification Course

Approaches to Test Generation


A variety of methods are available to set up transactors for specific tests, some of the more popular
methods are introduced here.

File Read Method


This method is very common, especially in commercially available testbenches.

The transactor reads commands from an input file and executes them, creating stimulus and verifying
responses for the testbench. For each simulation the input files for the simulated test need to be copied
into the working directory. Usually this method is combined with a pre-processor which generates the
input files, as described in the perl section.

http://www.stefanvhdl.com/vhdl/html/test_approach.html (1 of 3) [1/13/2009 3:54:46 PM]


VHDL Verification Course

Some problems of this approach are:

● Usually the input file format does not support sophisticated structural elements (loops,
procedures etc) and VHDL is not really suited to implement a sophisticated parser
● Syntactical errors in the input file are usually only found during simulation time (this may result
in the loss of valuable computing time)
● Lack of feedback: it's usually not possible to react flexibly on the response of the DUT by
examining the value of signals etc
● The input "language" is not standard VHDL and therefore not immediately understandable to
other designers

Some of the benefits are:

● The testbench VHDL can remain clean and should run on all platforms without any changes
● The testbench structure is straightforward, no configuration statements are required

http://www.stefanvhdl.com/vhdl/html/test_approach.html (2 of 3) [1/13/2009 3:54:46 PM]


VHDL Verification Course

http://www.stefanvhdl.com/vhdl/html/test_approach.html (3 of 3) [1/13/2009 3:54:46 PM]


VHDL Verification Course

VHDL pre-processing Method


In this approach the test specific code is written in VHDL. It is stored in a file separate from the
transactor code. To set up the transactor for a specific test, a pre-processor will insert that file into the
transactor code and then the transactor is recompiled. The place where the file is inserted is marked with
a special comment e.g. "--insert_file inp.cmd" where inp.cmd is the file name.

In Unix the pre-processor would be run like this:

awk -f insert.awk < transactor.vhd > precompiled_transactor.vhd

http://www.stefanvhdl.com/vhdl/html/pre_processing.html (1 of 2) [1/13/2009 3:54:47 PM]


VHDL Verification Course

vcom precompiled_transactor.vhd

The file test_code.vhd is assumed to be in the current directory. (The compilation command would be
appropriate for the MTI simulator, it needs to be replaced by an equivalent command for other
simulators.)

Some problems of this approach are:

● Reliance on OS specific tools (not strictly pure VHDL)


● Recompilation adds to simulation time

Some of the benefits are:

● Tests can be compiled before simulation, so syntax errors are detected before the simulation runs
● Tests can be readily understood by designers who are familiar with VHDL
● All structural elements of VHDL are available
● Tests can react flexibly on the response of the DUT, full access to all signals known to the
transactor is possible

Below are the files which have been executed in this section:

insert. test_code. transactor.


awk vhd vhd

http://www.stefanvhdl.com/vhdl/html/pre_processing.html (2 of 2) [1/13/2009 3:54:47 PM]


VHDL Verification Course

Test-specific Entities
For each test there is a separate testbench which uses test-specific transactors. For example there may be
a microprocessor transactor mp.vhd. The testbench tb_test1 would then instantiate a test-specific
microprocessor transactor mp_test1.vhd which would be coded by enhancing the mp.vhd template.

Some problems of this approach are:

● Bug fixes in the transactor code may have to be made in many files
● Results in a large number of files which are difficult to handle

Some of the benefits are:

● Pure VHDL, no dependency on OS specific tools


● Tests can be compiled before simulation, so syntax errors are detected before the simulation runs
● Tests can be readily understood by designers who are familiar with VHDL
● All structural elements of VHDL are available

http://www.stefanvhdl.com/vhdl/html/test_specific_entities.html (1 of 2) [1/13/2009 3:54:48 PM]


VHDL Verification Course

● Tests can react flexibly on the response of the DUT, full access to all signals known to the
transactor is possible

http://www.stefanvhdl.com/vhdl/html/test_specific_entities.html (2 of 2) [1/13/2009 3:54:48 PM]


VHDL Verification Course

Configuration controlled Test Selection


In this approach the transactors contain the code for all tests. The behaviour of the transactor can be
controlled by a generic. The value of the generic defines which test is run. For each test a configuration
statement exists which selects the test in the transactor by assigning the appropriate value to the generic
parameter. (For example: configuration tb_A selects test A in the transactor by assigning the value
"test_a" to the generic testselector of the transactor.)

Some problems of this approach are:

● Transactor code becomes very large and difficult to handle


● Modifying the transactor code for one test could potentially cause a problem in another test (e.g.
accidental editing)

Some of the benefits are:

http://www.stefanvhdl.com/vhdl/html/all_in_one.html (1 of 2) [1/13/2009 3:54:50 PM]


VHDL Verification Course

● Pure VHDL, no dependency on OS specific tools


● Tests can be compiled before simulation, so syntax errors are detected before the simulation runs
● Tests can be readily understood by designers who are familiar with VHDL
● All structural elements of VHDL are available
● Tests can react flexibly on the response of the DUT, full access to all signals known to the
transactor is possible

http://www.stefanvhdl.com/vhdl/html/all_in_one.html (2 of 2) [1/13/2009 3:54:50 PM]


VHDL Verification Course

Using Transaction Logs


The purpose of this section is to show how to use transaction logs and to demonstrate why they are useful. First
the usefulness shall be demonstrated.

Below are the requirements for two output signals of a device:

Parameter Description Min Max Unit


tSU_W Setup time for W
6 - ns
asserted
tSU_R Setup time for R asserted 5 - ns

In this example a circuit has been synthesized already, and the simulation shall verify the VHDL description of the
synthesized design. The following process shall check whether the timing requirements on the pins R and W are
fulfilled. (The assumption of this test is that the design will execute write and read accesses alternatingly.)

timing_check: process

variable w_asserted: time;


variable r_asserted: time;

begin

-- wait for DUT to be reset


wait until RST = '1';
wait until RST = '0';

-- verify write access


wait until W = '0';

http://www.stefanvhdl.com/vhdl/html/transaction_logs.html (1 of 3) [1/13/2009 3:54:51 PM]


VHDL Verification Course

w_asserted := now;
wait until W = '1';

assert (now - w_asserted) >= tSU_W


report "E@TB: W setup time too short"
severity Error;

-- verify read access


wait until R = '0';
r_asserted := now;
wait until R = '1';

assert (now - r_asserted) >= tSU_R


report "E@TB: R setup time too short"
severity Error;

end process timing_check;

Here is the description of the circuit's timing behaviour:

-- description of the timing behaviour


-- of the DUT implemenation
dut: process

begin

W <= '1';
R <= '1';

wait until RST = '1';


wait until RST = '0';

wait for 10 ns;

-- write access
W <= '0', '1' after 8 ns;
wait for 10 ns;

-- read access
R <= '0', '1' after 9 ns;
wait for 10 ns;

http://www.stefanvhdl.com/vhdl/html/transaction_logs.html (2 of 3) [1/13/2009 3:54:51 PM]


VHDL Verification Course

-- write access
W <= '0', '1' after 7 ns;
wait for 10 ns;

-- read access
R <= '0', '1' after 4 ns; -- this is a violation we want to detect
wait for 10 ns;

-- write access
W <= '0', '1' after 8 ns;
wait for 10 ns;

wait;

end process dut;

As can be seen, the circuit does not meet the requirements. The second read pulse is too short. A simulation results
in the following output, however:

VSIM 26> run -all


VSIM 27>

Closer examination of the timing_check process reveals that it contains an error causing it to verify only one write
and one read access, as it won't progress after the read unless a second reset pulse occurs. However the simulation
output is exactly as would be expected for a correctly functioning circuit.

This kind of error is not infrequent and can be avoided by using transaction logs. This is shown in the next section.

Below are the files which have been simulated in this section:

txt_util. hang.
vhd vhd

http://www.stefanvhdl.com/vhdl/html/transaction_logs.html (3 of 3) [1/13/2009 3:54:51 PM]


VHDL Verification Course

Using Transaction Logs II


Using the same synthesized circuit the timing checker will be enhanced with transaction reporting:

-- verify the setup time on W and R signals


-- we assume W and R are asserted alternatingly

timing_check: process

variable w_asserted: time;


variable r_asserted: time;

begin

-- wait for DUT to be reset


wait until RST = '1';
wait until RST = '0';

-- verify write access


wait until W = '0';
w_asserted := now;
wait until W = '1';

print("I@TB: detected W access");

assert (now - w_asserted) >= tSU_W


report "E@TB: W setup time too short"
severity Error;

-- verify read access


wait until R = '0';
r_asserted := now;
wait until R = '1';

print("I@TB: detected R access");

http://www.stefanvhdl.com/vhdl/html/transaction_logs2.html (1 of 3) [1/13/2009 3:54:52 PM]


VHDL Verification Course

assert (now - r_asserted) >= tSU_R


report "E@TB: R setup time too short"
severity Error;

end process timing_check;

Rerunning the simulation, the problem becomes immediately apparent:

VSIM 31> run -all


# I@TB: detected W access
# I@TB: detected R access
VSIM 32>

The transaction log contains an insufficient number of write and read cycles. The problem can be fixed
in the timing checker as shown:

-- wait for DUT to be reset


wait until RST = '1';
wait until RST = '0';

loop
-- verify write access
wait until W = '0';

...

end loop;

end process timing_check;

And now the problem in the circuit is detected:

VSIM 8> run -all


# I@TB: detected W access
# I@TB: detected R access
# I@TB: detected W access
# I@TB: detected R access
# ** Error: E@TB: R setup time too short
# Time: 64 ns Iteration: 0
# I@TB: detected W access
VSIM 9>

http://www.stefanvhdl.com/vhdl/html/transaction_logs2.html (2 of 3) [1/13/2009 3:54:52 PM]


VHDL Verification Course

In order to effectively work with transaction logs it's helpful to write them to files. Here is a way to do
this with the MTI simulator:

vsim tb1 -c -do "run 400 ns ; exit -f" > sim.log

These logs should be kept for each test, so that in a later simulation run the current log can be
automatically compared with the golden log.

Below are the files which have been simulated in this section:

txt_util. hang2. tloop.


vhd vhd vhd

http://www.stefanvhdl.com/vhdl/html/transaction_logs2.html (3 of 3) [1/13/2009 3:54:52 PM]


VHDL Verification Course

Using Behavioural Models


A behavioural model of a design can be used to verify that design. Such a model is used when there
exists a way to represent the design's behaviour in a significantly simplified manner (e.g. if the objective
is to build a very fast adder, the design's behaviour could be modeled with a simple "+"). Typically only
the main functionality of the design would be modeled while other parts (like processor interface and
RAM interfaces) would be ignored. Also in many cases there is no need to model the exact timing
relationship as occurs in the design.

In principle the behavioural model will receive the same stimulus as the design and produce the same
output. However often the stimulus and response can be represented in a more abstract format. For
example if the actual design works on data blocks which are received one byte at a time in four clock
cycles, the behavioural model could just operate on a simple hex number.

Often behavioural models are used to generate expected (golden) log files. In this case the the
behavioural model operates on a stimulus file and creates a result file. The design is stimulated with the
same input file via a file reader (see Reading from Files) and will protocol it's response into a transaction
log (see Using Transaction Logs). The transaction log can then be compared with the expected results
from the behavioural model (e.g. with Unix' diff utility).

http://www.stefanvhdl.com/vhdl/html/behavioural_models.html (1 of 2) [1/13/2009 3:54:53 PM]


VHDL Verification Course

http://www.stefanvhdl.com/vhdl/html/behavioural_models.html (2 of 2) [1/13/2009 3:54:53 PM]


VHDL Verification Course

Recommended Directory Structure


It's useful to have a standardized directory structure for every module in an ASIC project. It makes it
easier for designers to understand each others code and scripts can be shared more easily.

Below is a recommended directory structure which works well for the author. Many other structures are
of course just as useful. (Instead of module the name of the module should be used.)

Directory Contents
bin Scripts for running tests
doc Module documentation
Simulation directory,
sim contains the VHDL work library, simulator setup files,
current transaction log etc
Synthesis directory,
synth contains the work directory for intermediate files of the synthesis tool,
synthesis scripts, constraint files

http://www.stefanvhdl.com/vhdl/html/dir_struct.html (1 of 2) [1/13/2009 3:54:55 PM]


VHDL Verification Course

tbench VHDL testbench code


Test case directory,
contains a directory for each test that is run with the following
subdirectories
test
cmd: command files, any files which are required to run the test
exp: expected (golden) log files to compare the results with
res: result files of the last simulation run
vhdl VHDL design code (synthesizable VHDL)

http://www.stefanvhdl.com/vhdl/html/dir_struct.html (2 of 2) [1/13/2009 3:54:55 PM]


VHDL Verification Course

Test Strategy
Testing should normally be done in a bottom-up fashion, which means that blocks will be simulated in
separate testbenches before they are integrated. This has the advantage that simulating smaller blocks
requires less computing resources, which means the simulation runs faster and it's easier to find errors.
Also since there is less code it's easier to identify the problem area. Of course there are limits to splitting
the testing effort into sub-blocks. The designer needs choose the right level of hierarchies by comparing
the effort to write additional testbenches against the effort to locate bugs in a more complex testbench.
Usually if several designers are involved there should be at least one testbench for each designer's
module and a testbench for the complete device.

There should be a testplan for the device which makes sure that every part of the design is exercised.
Each test should have a unique identifier (ideally the directory name in which the test files are kept) and
a description of the test.

If several designers work on the same device it's useful to have a bug log, a file that is kept in a central
location and has an entry for every bug which is found. Here is a possible format:

Bug Test Fixed by /


Status Found by / Date Description
No Case Date
back to back cycles fail on MP
1 open Stefan / 3/16/99 mp_test_1 -
interface
2 closed Bryan / 3/17/99 sdr_1 CRC values inverted Paul / 3/19/99
3 ... ... ... ... ...

There are also tools which help to maintain these logs and (among other things) prevent that entries are
accidentally deleted.

Any small change in a minor file of the design could potentially cause the design to fail. It's therefore
important that all tests are run after the last change has been made to the design code (and before the
design is manufactured). To make this task feasible all tests should be automated and self-checking, so
that they can be run from a script instead of having to run every test manually and having to check every
log file line by line.

http://www.stefanvhdl.com/vhdl/html/test_strategy.html (1 of 2) [1/13/2009 3:54:56 PM]


VHDL Verification Course

http://www.stefanvhdl.com/vhdl/html/test_strategy.html (2 of 2) [1/13/2009 3:54:56 PM]


VHDL Verification Course

The End
I hope this course has been useful, I'm looking forward to your comments at: vc2@stefanVHDL.com

Thanks

© Stefan Doll

Below are the files which have been simulated in this section:

txt_util. finish.
vhd vhd

http://www.stefanvhdl.com/vhdl/html/end.html [1/13/2009 3:54:56 PM]


http://www.stefanvhdl.com/vhdl/vhdl/convert.vhd

function to_std_logic(c: character) return std_logic is


variable sl: std_logic;
begin
case c is
when 'U' =>
sl := 'U';
when 'X' =>
sl := 'X';
when '0' =>
sl := '0';
when '1' =>
sl := '1';
when 'Z' =>
sl := 'Z';
when 'W' =>
sl := 'W';
when 'L' =>
sl := 'L';
when 'H' =>
sl := 'H';
when '-' =>
sl := '-';
when others =>
sl := 'X';
end case;
return sl;
end to_std_logic;

-- converts a string into std_logic_vector

function to_std_logic_vector(s: string) return std_logic_vector is


variable slv: std_logic_vector(s'high-s'low downto 0);
variable k: integer;
begin
k := s'high-s'low;
for i in s'range loop
slv(k) := to_std_logic(s(i));
k := k - 1;
end loop;
return slv;
end to_std_logic_vector;

http://www.stefanvhdl.com/vhdl/vhdl/convert.vhd (1 of 2) [1/13/2009 3:54:57 PM]


http://www.stefanvhdl.com/vhdl/vhdl/convert.vhd

http://www.stefanvhdl.com/vhdl/vhdl/convert.vhd (2 of 2) [1/13/2009 3:54:57 PM]


http://www.stefanvhdl.com/vhdl/vhdl/txt_util.vhd

library ieee;
use ieee.std_logic_1164.all;
use std.textio.all;

package txt_util is

-- prints a message to the screen


procedure print(text: string);

-- prints the message when active


-- useful for debug switches
procedure print(active: boolean; text: string);

-- converts std_logic into a character


function chr(sl: std_logic) return character;

-- converts std_logic into a string (1 to 1)


function str(sl: std_logic) return string;

-- converts std_logic_vector into a string (binary base)


function str(slv: std_logic_vector) return string;

-- converts boolean into a string


function str(b: boolean) return string;

-- converts an integer into a single character


-- (can also be used for hex conversion and other bases)
function chr(int: integer) return character;

-- converts integer into string using specified base


function str(int: integer; base: integer) return string;

-- converts integer to string, using base 10


function str(int: integer) return string;

-- convert std_logic_vector into a string in hex format


function hstr(slv: std_logic_vector) return string;

-- functions to manipulate strings


-----------------------------------

http://www.stefanvhdl.com/vhdl/vhdl/txt_util.vhd (1 of 14) [1/13/2009 3:54:59 PM]


http://www.stefanvhdl.com/vhdl/vhdl/txt_util.vhd

-- convert a character to upper case


function to_upper(c: character) return character;

-- convert a character to lower case


function to_lower(c: character) return character;

-- convert a string to upper case


function to_upper(s: string) return string;

-- convert a string to lower case


function to_lower(s: string) return string;

-- functions to convert strings into other formats


--------------------------------------------------

-- converts a character into std_logic


function to_std_logic(c: character) return std_logic;

-- converts a string into std_logic_vector


function to_std_logic_vector(s: string) return std_logic_vector;

-- file I/O
-----------

-- read variable length string from input file


procedure str_read(file in_file: TEXT;
res_string: out string);

-- print string to a file and start new line


procedure print(file out_file: TEXT;
new_string: in string);

-- print character to a file and start new line


procedure print(file out_file: TEXT;
char: in character);

end txt_util;

http://www.stefanvhdl.com/vhdl/vhdl/txt_util.vhd (2 of 14) [1/13/2009 3:54:59 PM]


http://www.stefanvhdl.com/vhdl/vhdl/txt_util.vhd

package body txt_util is

-- prints text to the screen

procedure print(text: string) is


variable msg_line: line;
begin
write(msg_line, text);
writeline(output, msg_line);
end print;

-- prints text to the screen when active

procedure print(active: boolean; text: string) is


begin
if active then
print(text);
end if;
end print;

-- converts std_logic into a character

function chr(sl: std_logic) return character is


variable c: character;
begin
case sl is
when 'U' => c:= 'U';
when 'X' => c:= 'X';
when '0' => c:= '0';
when '1' => c:= '1';
when 'Z' => c:= 'Z';
when 'W' => c:= 'W';

http://www.stefanvhdl.com/vhdl/vhdl/txt_util.vhd (3 of 14) [1/13/2009 3:54:59 PM]


http://www.stefanvhdl.com/vhdl/vhdl/txt_util.vhd

when 'L' => c:= 'L';


when 'H' => c:= 'H';
when '-' => c:= '-';
end case;
return c;
end chr;

-- converts std_logic into a string (1 to 1)

function str(sl: std_logic) return string is


variable s: string(1 to 1);
begin
s(1) := chr(sl);
return s;
end str;

-- converts std_logic_vector into a string (binary base)


-- (this also takes care of the fact that the range of
-- a string is natural while a std_logic_vector may
-- have an integer range)

function str(slv: std_logic_vector) return string is


variable result : string (1 to slv'length);
variable r : integer;
begin
r := 1;
for i in slv'range loop
result(r) := chr(slv(i));
r := r + 1;
end loop;
return result;
end str;

function str(b: boolean) return string is

begin
if b then
return "true";

http://www.stefanvhdl.com/vhdl/vhdl/txt_util.vhd (4 of 14) [1/13/2009 3:54:59 PM]


http://www.stefanvhdl.com/vhdl/vhdl/txt_util.vhd

else
return "false";
end if;
end str;

-- converts an integer into a character


-- for 0 to 9 the obvious mapping is used, higher
-- values are mapped to the characters A-Z
-- (this is usefull for systems with base > 10)
-- (adapted from Steve Vogwell's posting in comp.lang.vhdl)

function chr(int: integer) return character is


variable c: character;
begin
case int is
when 0 => c := '0';
when 1 => c := '1';
when 2 => c := '2';
when 3 => c := '3';
when 4 => c := '4';
when 5 => c := '5';
when 6 => c := '6';
when 7 => c := '7';
when 8 => c := '8';
when 9 => c := '9';
when 10 => c := 'A';
when 11 => c := 'B';
when 12 => c := 'C';
when 13 => c := 'D';
when 14 => c := 'E';
when 15 => c := 'F';
when 16 => c := 'G';
when 17 => c := 'H';
when 18 => c := 'I';
when 19 => c := 'J';
when 20 => c := 'K';
when 21 => c := 'L';
when 22 => c := 'M';
when 23 => c := 'N';
when 24 => c := 'O';
when 25 => c := 'P';
when 26 => c := 'Q';

http://www.stefanvhdl.com/vhdl/vhdl/txt_util.vhd (5 of 14) [1/13/2009 3:54:59 PM]


http://www.stefanvhdl.com/vhdl/vhdl/txt_util.vhd

when 27 => c := 'R';


when 28 => c := 'S';
when 29 => c := 'T';
when 30 => c := 'U';
when 31 => c := 'V';
when 32 => c := 'W';
when 33 => c := 'X';
when 34 => c := 'Y';
when 35 => c := 'Z';
when others => c := '?';
end case;
return c;
end chr;

-- convert integer to string using specified base


-- (adapted from Steve Vogwell's posting in comp.lang.vhdl)

function str(int: integer; base: integer) return string is

variable temp: string(1 to 10);


variable num: integer;
variable abs_int: integer;
variable len: integer := 1;
variable power: integer := 1;

begin

-- bug fix for negative numbers


abs_int := abs(int);

num := abs_int;

while num >= base loop -- Determine how many


len := len + 1; -- characters required
num := num / base; -- to represent the
end loop ; -- number.

for i in len downto 1 loop -- Convert the number to


temp(i) := chr(abs_int/power mod base); -- a string starting
power := power * base; -- with the right hand
end loop ; -- side.

http://www.stefanvhdl.com/vhdl/vhdl/txt_util.vhd (6 of 14) [1/13/2009 3:54:59 PM]


http://www.stefanvhdl.com/vhdl/vhdl/txt_util.vhd

-- return result and add sign if required


if int < 0 then
return '-'& temp(1 to len);
else
return temp(1 to len);
end if;

end str;

-- convert integer to string, using base 10


function str(int: integer) return string is

begin

return str(int, 10) ;

end str;

-- converts a std_logic_vector into a hex string.


function hstr(slv: std_logic_vector) return string is
variable hexlen: integer;
variable longslv : std_logic_vector(67 downto 0) := (others => '0');
variable hex : string(1 to 16);
variable fourbit : std_logic_vector(3 downto 0);
begin
hexlen := (slv'left+1)/4;
if (slv'left+1) mod 4 /= 0 then
hexlen := hexlen + 1;
end if;
longslv(slv'left downto 0) := slv;
for i in (hexlen -1) downto 0 loop
fourbit := longslv(((i*4)+3) downto (i*4));
case fourbit is
when "0000" => hex(hexlen -I) := '0';
when "0001" => hex(hexlen -I) := '1';
when "0010" => hex(hexlen -I) := '2';
when "0011" => hex(hexlen -I) := '3';
when "0100" => hex(hexlen -I) := '4';
when "0101" => hex(hexlen -I) := '5';

http://www.stefanvhdl.com/vhdl/vhdl/txt_util.vhd (7 of 14) [1/13/2009 3:54:59 PM]


http://www.stefanvhdl.com/vhdl/vhdl/txt_util.vhd

when "0110" => hex(hexlen -I) := '6';


when "0111" => hex(hexlen -I) := '7';
when "1000" => hex(hexlen -I) := '8';
when "1001" => hex(hexlen -I) := '9';
when "1010" => hex(hexlen -I) := 'A';
when "1011" => hex(hexlen -I) := 'B';
when "1100" => hex(hexlen -I) := 'C';
when "1101" => hex(hexlen -I) := 'D';
when "1110" => hex(hexlen -I) := 'E';
when "1111" => hex(hexlen -I) := 'F';
when "ZZZZ" => hex(hexlen -I) := 'z';
when "UUUU" => hex(hexlen -I) := 'u';
when "XXXX" => hex(hexlen -I) := 'x';
when others => hex(hexlen -I) := '?';
end case;
end loop;
return hex(1 to hexlen);
end hstr;

-- functions to manipulate strings


-----------------------------------

-- convert a character to upper case

function to_upper(c: character) return character is

variable u: character;

begin

case c is
when 'a' => u := 'A';
when 'b' => u := 'B';
when 'c' => u := 'C';
when 'd' => u := 'D';
when 'e' => u := 'E';
when 'f' => u := 'F';
when 'g' => u := 'G';
when 'h' => u := 'H';
when 'i' => u := 'I';

http://www.stefanvhdl.com/vhdl/vhdl/txt_util.vhd (8 of 14) [1/13/2009 3:54:59 PM]


http://www.stefanvhdl.com/vhdl/vhdl/txt_util.vhd

when 'j' => u := 'J';


when 'k' => u := 'K';
when 'l' => u := 'L';
when 'm' => u := 'M';
when 'n' => u := 'N';
when 'o' => u := 'O';
when 'p' => u := 'P';
when 'q' => u := 'Q';
when 'r' => u := 'R';
when 's' => u := 'S';
when 't' => u := 'T';
when 'u' => u := 'U';
when 'v' => u := 'V';
when 'w' => u := 'W';
when 'x' => u := 'X';
when 'y' => u := 'Y';
when 'z' => u := 'Z';
when others => u := c;
end case;

return u;

end to_upper;

-- convert a character to lower case

function to_lower(c: character) return character is

variable l: character;

begin

case c is
when 'A' => l := 'a';
when 'B' => l := 'b';
when 'C' => l := 'c';
when 'D' => l := 'd';
when 'E' => l := 'e';
when 'F' => l := 'f';
when 'G' => l := 'g';
when 'H' => l := 'h';
when 'I' => l := 'i';

http://www.stefanvhdl.com/vhdl/vhdl/txt_util.vhd (9 of 14) [1/13/2009 3:54:59 PM]


http://www.stefanvhdl.com/vhdl/vhdl/txt_util.vhd

when 'J' => l := 'j';


when 'K' => l := 'k';
when 'L' => l := 'l';
when 'M' => l := 'm';
when 'N' => l := 'n';
when 'O' => l := 'o';
when 'P' => l := 'p';
when 'Q' => l := 'q';
when 'R' => l := 'r';
when 'S' => l := 's';
when 'T' => l := 't';
when 'U' => l := 'u';
when 'V' => l := 'v';
when 'W' => l := 'w';
when 'X' => l := 'x';
when 'Y' => l := 'y';
when 'Z' => l := 'z';
when others => l := c;
end case;

return l;

end to_lower;

-- convert a string to upper case

function to_upper(s: string) return string is

variable uppercase: string (s'range);

begin

for i in s'range loop


uppercase(i):= to_upper(s(i));
end loop;
return uppercase;

end to_upper;

http://www.stefanvhdl.com/vhdl/vhdl/txt_util.vhd (10 of 14) [1/13/2009 3:54:59 PM]


http://www.stefanvhdl.com/vhdl/vhdl/txt_util.vhd

-- convert a string to lower case

function to_lower(s: string) return string is

variable lowercase: string (s'range);

begin

for i in s'range loop


lowercase(i):= to_lower(s(i));
end loop;
return lowercase;

end to_lower;

-- functions to convert strings into other types

-- converts a character into a std_logic

function to_std_logic(c: character) return std_logic is


variable sl: std_logic;
begin
case c is
when 'U' =>
sl := 'U';
when 'X' =>
sl := 'X';
when '0' =>
sl := '0';
when '1' =>
sl := '1';
when 'Z' =>
sl := 'Z';
when 'W' =>
sl := 'W';
when 'L' =>
sl := 'L';
when 'H' =>
sl := 'H';
when '-' =>

http://www.stefanvhdl.com/vhdl/vhdl/txt_util.vhd (11 of 14) [1/13/2009 3:54:59 PM]


http://www.stefanvhdl.com/vhdl/vhdl/txt_util.vhd

sl := '-';
when others =>
sl := 'X';
end case;
return sl;
end to_std_logic;

-- converts a string into std_logic_vector

function to_std_logic_vector(s: string) return std_logic_vector is


variable slv: std_logic_vector(s'high-s'low downto 0);
variable k: integer;
begin
k := s'high-s'low;
for i in s'range loop
slv(k) := to_std_logic(s(i));
k := k - 1;
end loop;
return slv;
end to_std_logic_vector;

----------------
-- file I/O --
----------------

-- read variable length string from input file

procedure str_read(file in_file: TEXT;


res_string: out string) is

variable l: line;
variable c: character;
variable is_string: boolean;

http://www.stefanvhdl.com/vhdl/vhdl/txt_util.vhd (12 of 14) [1/13/2009 3:54:59 PM]


http://www.stefanvhdl.com/vhdl/vhdl/txt_util.vhd

begin

readline(in_file, l);
-- clear the contents of the result string
for i in res_string'range loop
res_string(i) := ' ';
end loop;
-- read all characters of the line, up to the length
-- of the results string
for i in res_string'range loop
read(l, c, is_string);
res_string(i) := c;
if not is_string then -- found end of line
exit;
end if;
end loop;

end str_read;

-- print string to a file


procedure print(file out_file: TEXT;
new_string: in string) is

variable l: line;

begin

write(l, new_string);
writeline(out_file, l);

end print;

-- print character to a file and start new line


procedure print(file out_file: TEXT;
char: in character) is

variable l: line;

begin

http://www.stefanvhdl.com/vhdl/vhdl/txt_util.vhd (13 of 14) [1/13/2009 3:54:59 PM]


http://www.stefanvhdl.com/vhdl/vhdl/txt_util.vhd

write(l, char);
writeline(out_file, l);

end print;

-- appends contents of a string to a file until line feed occurs


-- (LF is considered to be the end of the string)

procedure str_write(file out_file: TEXT;


new_string: in string) is
begin

for i in new_string'range loop


print(out_file, new_string(i));
if new_string(i) = LF then -- end of string
exit;
end if;
end loop;

end str_write;

end txt_util;

http://www.stefanvhdl.com/vhdl/vhdl/txt_util.vhd (14 of 14) [1/13/2009 3:54:59 PM]


http://www.stefanvhdl.com/vhdl/vhdl/tb1.vhd

library ieee;
use ieee.std_logic_1164.all;
use std.textio.all;
use ieee.std_logic_arith.all;

use work.txt_util.all;

entity TB1 is
end TB1;

architecture test of TB1 is

signal x1: std_logic;


signal x2: std_logic;
signal y: std_logic;

begin

test_seq: process

variable cnt: integer := 0;


--*variable slv: std_logic_vector(X2'range);

begin

x1 <= '0';
x2 <= '0';

wait for 10 ns;

x1 <= '1';
x2 <= '0';

wait for 10 ns;

x1 <= '0';
x2 <= '1';

http://www.stefanvhdl.com/vhdl/vhdl/tb1.vhd (1 of 3) [1/13/2009 3:55:00 PM]


http://www.stefanvhdl.com/vhdl/vhdl/tb1.vhd

wait for 10 ns;

x1 <= '1';
x2 <= '1';

wait for 10 ns;

assert y = (x1 xor x2)


report "E@TB: circuit failed"
severity Error;

assert y = (x1 xor x2)


report "E@TB: failure at: x1="& std_logic'image(x1)&
" x2="& std_logic'image(x2)
severity Error;

assert y = (x1 xor x2)


report "E@TB: failure at: x1="& str(x1)& " x2="& str(x2)
severity Error;

x1 <= 'X';
x2 <= 'X';

wait for 30 ns;

x1 <= '1', '0' after 10 ns, '1' after 20 ns, '0' after 30 ns;
x2 <= '1', '0' after 20 ns;

wait; -- stop process

end process test_seq;

-- this is supposed to be an xor ... but it isn't


y <= (x1 and not x2) or (x2 and not x1) or (x1 and x2);

http://www.stefanvhdl.com/vhdl/vhdl/tb1.vhd (2 of 3) [1/13/2009 3:55:00 PM]


http://www.stefanvhdl.com/vhdl/vhdl/tb1.vhd

end test;

http://www.stefanvhdl.com/vhdl/vhdl/tb1.vhd (3 of 3) [1/13/2009 3:55:00 PM]


http://www.stefanvhdl.com/vhdl/vhdl/file_log.vhd

library ieee;
use ieee.std_logic_1164.all;

use std.textio.all;
use work.txt_util.all;

entity FILE_LOG is
generic (
log_file: string := "res.log"
);
port(
CLK : in std_logic;
RST : in std_logic;
x1 : in std_logic;
x2 : in std_logic_vector(7 downto 0)
);
end FILE_LOG;

architecture log_to_file of FILE_LOG is

file l_file: TEXT open write_mode is log_file;

begin

-- write data and control information to a file

receive_data: process

variable l: line;

begin

-- print header for the logfile


print(l_file, "# x1 x2 ");
print(l_file, "#----------");
print(l_file, " ");

http://www.stefanvhdl.com/vhdl/vhdl/file_log.vhd (1 of 2) [1/13/2009 3:55:00 PM]


http://www.stefanvhdl.com/vhdl/vhdl/file_log.vhd

wait until RST='1';


wait until RST='0';

while true loop

-- write digital data into log file


--* write(l, str(x1)& " "& hstr(x2)& "h");
--* writeline(l_file, l);
print(l_file, str(x1)& " "& hstr(x2)& "h");

wait until CLK = '1';

end loop;

end process receive_data;

end log_to_file;

http://www.stefanvhdl.com/vhdl/vhdl/file_log.vhd (2 of 2) [1/13/2009 3:55:00 PM]


http://www.stefanvhdl.com/vhdl/vhdl/stim_gen2.vhd

library ieee;
use ieee.std_logic_1164.all;
use std.textio.all;
use ieee.std_logic_arith.all;

use work.txt_util.all;

entity STIM_GEN2 is
port(
RST: out std_logic;
CLK: out std_logic;
X1: out std_logic;
X2: out std_logic_vector(7 downto 0)
);
end STIM_GEN2;

architecture test of STIM_GEN2 is

signal i_clk: std_logic := '0';

begin

RST <= '0', '1' after 10 ns, '0' after 30 ns;


i_clk <= not i_clk after 8 ns;
CLK <= i_clk;

test_seq: process

variable cnt: integer := 0;


variable slv: std_logic_vector(X2'range);

begin

wait until i_clk = '1';

slv := conv_std_logic_vector(cnt, 8);


X2 <= slv;
X1 <= slv(4);

http://www.stefanvhdl.com/vhdl/vhdl/stim_gen2.vhd (1 of 2) [1/13/2009 3:55:01 PM]


http://www.stefanvhdl.com/vhdl/vhdl/stim_gen2.vhd

cnt := cnt + 1;

end process test_seq;

end test;

http://www.stefanvhdl.com/vhdl/vhdl/stim_gen2.vhd (2 of 2) [1/13/2009 3:55:01 PM]


http://www.stefanvhdl.com/vhdl/vhdl/tb_file_log.vhd

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;

entity tb_file_log is
end tb_file_log;

architecture structure of tb_file_log is

component file_log
generic (
log_file: string := "res.log"
);
port(
CLK : in std_logic;
RST : in std_logic;
x1 : in std_logic;
x2 : in std_logic_vector(7 downto 0)
);
end component;

component stim_gen2
port(
RST: out std_logic;
CLK: out std_logic;
X1: out std_logic;
X2: out std_logic_vector(7 downto 0)
);
end component;

signal RST: std_logic;


signal CLK: std_logic;
signal X1: std_logic;
signal X2: std_logic_vector(7 downto 0);

http://www.stefanvhdl.com/vhdl/vhdl/tb_file_log.vhd (1 of 2) [1/13/2009 3:55:03 PM]


http://www.stefanvhdl.com/vhdl/vhdl/tb_file_log.vhd

begin

U_FILE_LOG: FILE_LOG
port map (
CLK => clk,
RST => rst,
x1 => x1,
x2 => x2
);

U_STIM_GEN2: STIM_GEN2
port map (
RST => rst,
CLK => clk,
X1 => x1,
X2 => x2
);

end structure;

http://www.stefanvhdl.com/vhdl/vhdl/tb_file_log.vhd (2 of 2) [1/13/2009 3:55:03 PM]


http://www.stefanvhdl.com/vhdl/vhdl/file_read.vhd

library ieee;
use ieee.std_logic_1164.all;

use std.textio.all;
use work.txt_util.all;

entity FILE_READ is
generic (
stim_file: string := "sim2.dat"
);
port(
CLK : in std_logic;
RST : in std_logic;
Y : out std_logic_vector(4 downto 0);
EOG : out std_logic
);
end FILE_READ;

-- I/O Dictionary
--
-- Inputs:
--
-- CLK: new cell needed
-- RST: reset signal, wait with reading till reset seq complete
--
-- Outputs:
--
-- Y: Output vector
-- EOG: End Of Generation, all lines have been read from the file
--

architecture read_from_file of FILE_READ is

file stimulus: TEXT open read_mode is stim_file;

begin

http://www.stefanvhdl.com/vhdl/vhdl/file_read.vhd (1 of 2) [1/13/2009 3:55:03 PM]


http://www.stefanvhdl.com/vhdl/vhdl/file_read.vhd

-- read data and control information from a file

receive_data: process

variable l: line;
variable s: string(y'range);

begin

EOG <= '0';

-- wait for Reset to complete


wait until RST='1';
wait until RST='0';

while not endfile(stimulus) loop

-- read digital data from input file


readline(stimulus, l);
read(l, s);
Y <= to_std_logic_vector(s);

wait until CLK = '1';

end loop;

print("I@FILE_READ: reached end of "& stim_file);


EOG <= '1';

wait;

end process receive_data;

end read_from_file;

http://www.stefanvhdl.com/vhdl/vhdl/file_read.vhd (2 of 2) [1/13/2009 3:55:03 PM]


http://www.stefanvhdl.com/vhdl/vhdl/sim.dat

00010
00011
11100
1UXZW
HL111
11111

http://www.stefanvhdl.com/vhdl/vhdl/sim.dat [1/13/2009 3:55:04 PM]


http://www.stefanvhdl.com/vhdl/vhdl/tb_file_read.vhd

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;

entity TB_FILE_READ is
end TB_FILE_READ;

architecture test of TB_FILE_READ is

component FILE_READ
generic (
stim_file: string := "sim.dat"
);
port(
CLK : in std_logic;
RST : in std_logic;
Y : out std_logic_vector(4 downto 0);
EOG : out std_logic
);
end component;

signal rst: std_logic;


signal clk: std_logic := '0';
signal eog: std_logic;
signal y: std_logic_vector(4 downto 0);

begin

rst <= '0', '1' after 40 ns, '0' after 100 ns;
clk <= not clk after 10 ns;

input_stim: FILE_READ
port map(
CLK => clk,
RST => rst,
Y => y,
EOG => eog

http://www.stefanvhdl.com/vhdl/vhdl/tb_file_read.vhd (1 of 2) [1/13/2009 3:55:04 PM]


http://www.stefanvhdl.com/vhdl/vhdl/tb_file_read.vhd

);

end test;

http://www.stefanvhdl.com/vhdl/vhdl/tb_file_read.vhd (2 of 2) [1/13/2009 3:55:04 PM]


http://www.stefanvhdl.com/vhdl/vhdl/file_read2.vhd

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;

use std.textio.all;
use work.txt_util.all;

entity FILE_READ2 is
generic (
stim_file: string := "sim.dat"
);
port(
CLK : in std_logic;
RST : in std_logic;
Y : out std_logic_vector(4 downto 0);
EOG : out std_logic
);
end FILE_READ2;

-- I/O Dictionary
--
-- Inputs:
--
-- CLK: new cell needed
-- RST: reset signal, wait with reading till reset seq complete
--
-- Outputs:
--
-- Y: Output vector
-- EOG: End Of Generation, all lines have been read from the file
--

architecture read_from_file of FILE_READ2 is

file stimulus: TEXT open read_mode is stim_file;

begin

http://www.stefanvhdl.com/vhdl/vhdl/file_read2.vhd (1 of 3) [1/13/2009 3:55:05 PM]


http://www.stefanvhdl.com/vhdl/vhdl/file_read2.vhd

-- read data and control information from a file

receive_data: process

variable l: line;
variable s: string(1 to 80);
variable c: character;
variable in_string: boolean;

begin

EOG <= '0';

wait until RST='1';


wait until RST='0';

while not endfile(stimulus) loop

--*-- read variable length string


--*-- from input file
--*readline(stimulus, l);
--*s := (others => ' ');
--*for i in s'range loop
--* read(l, c, in_string);
--* s(i) := c;
--* if not in_string then -- found end of line
--* exit;
--* end if;
--*end loop;
str_read(stimulus, s);

if s(1 to 6) = "#count" then -- check for command "count"

for i in 1 to 5 loop
Y <= conv_std_logic_vector(i,5);
wait until CLK = '1';
end loop;

http://www.stefanvhdl.com/vhdl/vhdl/file_read2.vhd (2 of 3) [1/13/2009 3:55:05 PM]


http://www.stefanvhdl.com/vhdl/vhdl/file_read2.vhd

else

-- if it's not a command -> process data normally


Y <= to_std_logic_vector(s(1 to 5));
wait until CLK = '1';

end if;

end loop;

print("I@FILE_READ: reached end of "& stim_file);


EOG <= '1';

wait;

end process receive_data;

end read_from_file;

http://www.stefanvhdl.com/vhdl/vhdl/file_read2.vhd (3 of 3) [1/13/2009 3:55:05 PM]


http://www.stefanvhdl.com/vhdl/vhdl/sim2.dat

00010
00011
#count
11100
1UXZW
HL111
11111

http://www.stefanvhdl.com/vhdl/vhdl/sim2.dat [1/13/2009 3:55:06 PM]


http://www.stefanvhdl.com/vhdl/vhdl/tb_file_read2.vhd

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;

entity TB_FILE_READ2 is
end TB_FILE_READ2;

architecture test of TB_FILE_READ2 is

component FILE_READ2
generic (
stim_file: string := "sim2.dat"
);
port(
CLK : in std_logic;
RST : in std_logic;
Y : out std_logic_vector(4 downto 0);
EOG : out std_logic
);
end component;

signal rst: std_logic;


signal clk: std_logic := '0';
signal eog: std_logic;
signal y: std_logic_vector(4 downto 0);

begin

rst <= '0', '1' after 40 ns, '0' after 100 ns;
clk <= not clk after 10 ns;

input_stim: FILE_READ2
port map(
CLK => clk,
RST => rst,
Y => y,
EOG => eog

http://www.stefanvhdl.com/vhdl/vhdl/tb_file_read2.vhd (1 of 2) [1/13/2009 3:55:07 PM]


http://www.stefanvhdl.com/vhdl/vhdl/tb_file_read2.vhd

);

end test;

http://www.stefanvhdl.com/vhdl/vhdl/tb_file_read2.vhd (2 of 2) [1/13/2009 3:55:07 PM]


http://www.stefanvhdl.com/vhdl/vhdl/simple_sram.vhd

library ieee;
use ieee.std_logic_1164.all;
use std.textio.all;
use ieee.std_logic_arith.all;

use work.txt_util.all;

entity SIMPLE_SRAM is
port(
A: in std_logic_vector(7 downto 0);
WE_L: in std_logic;

D: inout std_logic_vector(7 downto 0)


);
end SIMPLE_SRAM;

-- I/O Dictionary
--
-- A: Address bus
-- WE_L: Write Enable
-- D: Data bus
--
--

architecture transactor of SIMPLE_SRAM is

constant tSU : time := 4 ns;


constant tH : time := 3 ns;
constant tW_WE : time := 40 ns;

signal add_hold: boolean := false;


signal dat_hold: boolean := false;
signal add_val: std_logic_vector(A'range);
signal dat_val: std_logic_vector(D'range);

begin

http://www.stefanvhdl.com/vhdl/vhdl/simple_sram.vhd (1 of 5) [1/13/2009 3:55:08 PM]


http://www.stefanvhdl.com/vhdl/vhdl/simple_sram.vhd

-- hold time monitors


add_monitor: process
begin

wait until A'event;

assert not add_hold or A = add_val


report "E@SIMPLE_SRAM: Address hold time violated"
severity Error;

end process add_monitor;

dat_monitor: process
begin

wait until D'event;

assert not dat_hold or D = dat_val


report "E@SIMPLE_SRAM: Data hold time violated"
severity Error;

end process dat_monitor;

test_prg: process

procedure write(wadd: std_logic_vector(7 downto 0);


wdat: std_logic_vector(7 downto 0)
) is

variable start_cycle: time;

begin

D <= (others => 'Z');

wait until WE_L = '0';

http://www.stefanvhdl.com/vhdl/vhdl/simple_sram.vhd (2 of 5) [1/13/2009 3:55:08 PM]


http://www.stefanvhdl.com/vhdl/vhdl/simple_sram.vhd

start_cycle := now;

-- check setup times


assert A'last_event >= tSU
report "E@SIMPLE_SRAM: Address setup time violated"
severity Error;

assert D'last_event >= tSU


report "E@SIMPLE_SRAM: Data setup time violated"
severity Error;

-- report action for transaction log


print("I@SIMPLE_SRAM: "& hstr(D)& "h written to "& hstr(A)& "h");

-- verify address
assert A = wadd
report "E@SIMPLE_SRAM: Address incorrect, expected "&
str(wadd)& " received "& str(A)
severity Error;

-- verify data
for i in wdat'range loop
if wdat(i) /= '-' and wdat(i) /= D(i) then
print("E@SIMPLE_SRAM: Write Data Invalid, written data = "& str(D)&
" expected data = "& str(wdat) );
exit;
end if;
end loop;

wait until WE_L = '1';

-- verify pulse width on WE_L


assert now - start_cycle >= tW_WE
report "E@SIMPLE_SRAM: WE_L pulse width violated"
severity Error;

-- verify address and data haven't changed during the cycle


assert A'last_event >= (now - start_cycle)
report "E@SIMPLE_SRAM: Address hold time violated"

http://www.stefanvhdl.com/vhdl/vhdl/simple_sram.vhd (3 of 5) [1/13/2009 3:55:08 PM]


http://www.stefanvhdl.com/vhdl/vhdl/simple_sram.vhd

severity Error;

assert D'last_event >= (now - start_cycle)


report "E@SIMPLE_SRAM: Data hold time violated"
severity Error;

-- now make sure the hold times are maintained


add_hold <= true, false after tH;
dat_hold <= true, false after tH;
add_val <= A;
dat_val <= D;

end write;

procedure read(radd: std_logic_vector(A'range);


rdat: std_logic_vector(D'range)) is
begin

end read;

begin

-- Test Program
----------------

write("00000000", "11110000");
write("00000001", "00001111");

------------
-- End Test

end process test_prg;

end transactor;

http://www.stefanvhdl.com/vhdl/vhdl/simple_sram.vhd (4 of 5) [1/13/2009 3:55:08 PM]


http://www.stefanvhdl.com/vhdl/vhdl/simple_sram.vhd

http://www.stefanvhdl.com/vhdl/vhdl/simple_sram.vhd (5 of 5) [1/13/2009 3:55:08 PM]


http://www.stefanvhdl.com/vhdl/vhdl/stim_gen.vhd

library ieee;
use ieee.std_logic_1164.all;
use std.textio.all;

use work.txt_util.all;

entity STIM_GEN is
port(
A: out std_logic_vector(7 downto 0);
WE_L: out std_logic;
D: out std_logic_vector(7 downto 0)
);
end STIM_GEN;

architecture test of STIM_GEN is

constant tSU : time := 4 ns;


constant tH : time := 3 ns;
constant tW_WE : time := 40 ns;

signal clk: std_logic := '0';


signal rst: std_logic;

begin

rst <= '0', '1' after 10 ns, '0' after 30 ns;


clk <= not clk after 8 ns;

test_seq: process
begin

A <= (others => 'X');


D <= (others => 'Z');
WE_L <= '1';

wait for 20 ns;

http://www.stefanvhdl.com/vhdl/vhdl/stim_gen.vhd (1 of 7) [1/13/2009 3:55:09 PM]


http://www.stefanvhdl.com/vhdl/vhdl/stim_gen.vhd

-----------------------------

print(" ");
print(" one correct access");

A <= "00000000";
D <= "11110000";

wait for 4 ns;

WE_L <= '0';

wait for 40 ns;

WE_L <= '1';

wait for 3 ns;

A <= (others => 'X');


D <= (others => 'Z');

wait for 10 ns;

-----------------------------

print(" ");
print(" wrong address and wrong data");

A <= "00000000";
D <= "11110000";

wait for 4 ns;

WE_L <= '0';

wait for 40 ns;

WE_L <= '1';

wait for 3 ns;

A <= (others => 'X');


D <= (others => 'Z');

http://www.stefanvhdl.com/vhdl/vhdl/stim_gen.vhd (2 of 7) [1/13/2009 3:55:09 PM]


http://www.stefanvhdl.com/vhdl/vhdl/stim_gen.vhd

wait for 10 ns;

-----------------------------

print(" ");
print(" violate address setup and data hold time");

D <= "11110000";

wait for 1 ns;

A <= "00000000";

wait for 3 ns;

WE_L <= '0';

wait for 40 ns;

WE_L <= '1';

wait for 2 ns;

D <= (others => 'Z');

wait for 1 ns;

A <= (others => 'X');

wait for 10 ns;

-----------------------------

print(" ");
print(" violate data setup and address hold time");

A <= "00000001";

wait for 1 ns;

D <= "00001111";

http://www.stefanvhdl.com/vhdl/vhdl/stim_gen.vhd (3 of 7) [1/13/2009 3:55:09 PM]


http://www.stefanvhdl.com/vhdl/vhdl/stim_gen.vhd

wait for 3 ns;

WE_L <= '0';

wait for 40 ns;

WE_L <= '1';

wait for 2 ns;

A <= (others => 'X');

wait for 1 ns;

D <= (others => 'Z');

wait for 10 ns;

-----------------------------

print(" ");
print(" pulse width too short");

A <= "00000000";
D <= "11110000";

wait for 4 ns;

WE_L <= '0';

wait for 30 ns;

WE_L <= '1';

wait for 3 ns;

A <= (others => 'X');


D <= (others => 'Z');

wait for 10 ns;

http://www.stefanvhdl.com/vhdl/vhdl/stim_gen.vhd (4 of 7) [1/13/2009 3:55:09 PM]


http://www.stefanvhdl.com/vhdl/vhdl/stim_gen.vhd

-----------------------------

print(" ");
print(" unstable address");

A <= "00000001";
D <= "00001111";

wait for 4 ns;

WE_L <= '0';

wait for 30 ns;

A <= "00000000", "00000001" after 2 ns;

wait for 10 ns;

WE_L <= '1';

wait for 3 ns;

A <= (others => 'X');


D <= (others => 'Z');

wait for 10 ns;

-----------------------------

print(" ");
print(" unstable data");

A <= "00000000";
D <= "11110000";

wait for 4 ns;

WE_L <= '0';

wait for 30 ns;

http://www.stefanvhdl.com/vhdl/vhdl/stim_gen.vhd (5 of 7) [1/13/2009 3:55:09 PM]


http://www.stefanvhdl.com/vhdl/vhdl/stim_gen.vhd

D <= "00000000";

wait for 10 ns;

WE_L <= '1';

wait for 3 ns;

A <= (others => 'X');


D <= (others => 'Z');

wait for 10 ns;

-----------------------------

print(" ");
print(" one correct access");

A <= "00000001";
D <= "00001111";

wait for 4 ns;

WE_L <= '0';

wait for 40 ns;

WE_L <= '1';

wait for 3 ns;

A <= (others => 'X');


D <= (others => 'Z');

wait for 10 ns;

wait;

http://www.stefanvhdl.com/vhdl/vhdl/stim_gen.vhd (6 of 7) [1/13/2009 3:55:09 PM]


http://www.stefanvhdl.com/vhdl/vhdl/stim_gen.vhd

end process test_seq;

end test;

http://www.stefanvhdl.com/vhdl/vhdl/stim_gen.vhd (7 of 7) [1/13/2009 3:55:09 PM]


http://www.stefanvhdl.com/vhdl/vhdl/tb_simple.vhd

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;

entity tb_simple is
end tb_simple;

architecture structure of tb_simple is

component simple_sram
port(
A: in std_logic_vector(7 downto 0);
WE_L: in std_logic;

D: inout std_logic_vector(7 downto 0)


);
end component;

component stim_gen
port(
A: out std_logic_vector(7 downto 0);
WE_L: out std_logic;
D: out std_logic_vector(7 downto 0)
);
end component;

signal D: std_logic_vector(7 downto 0);


signal A: std_logic_vector(7 downto 0);
signal WE_L: std_logic;

begin

U_SIMPLE_SRAM: SIMPLE_SRAM
port map (
A => a,
WE_L => we_l,
D => d

http://www.stefanvhdl.com/vhdl/vhdl/tb_simple.vhd (1 of 2) [1/13/2009 3:55:10 PM]


http://www.stefanvhdl.com/vhdl/vhdl/tb_simple.vhd

);

U_STIM_GEN: STIM_GEN
port map (
A => a,
WE_L => we_l,
D => d
);

end structure;

http://www.stefanvhdl.com/vhdl/vhdl/tb_simple.vhd (2 of 2) [1/13/2009 3:55:10 PM]


http://www.stefanvhdl.com/vhdl/vhdl/sram2.vhd

library ieee;
use ieee.std_logic_1164.all;
use std.textio.all;
use ieee.std_logic_arith.all;

use work.txt_util.all;

entity SRAM2 is
generic(
mem_words: integer := 32
);
port(
RST: in std_logic; -- doesn't go to RAM, but is useful for testing
A: in std_logic_vector(7 downto 0);
WE_L: in std_logic;
RD_L: in std_logic;

D: inout std_logic_vector(7 downto 0)


);
end SRAM2;

-- I/O Dictionary
--
-- A: Address bus
-- WE_L: Write Enable
-- RD_L: Read Enable
-- D: Data bus
--
--

architecture model of SRAM2 is

constant tSU : time := 4 ns;


constant tH : time := 3 ns;
constant tW_WE : time := 40 ns;
constant tRD : time := 6 ns;
constant tW_RD : time := 40 ns;

http://www.stefanvhdl.com/vhdl/vhdl/sram2.vhd (1 of 7) [1/13/2009 3:55:11 PM]


http://www.stefanvhdl.com/vhdl/vhdl/sram2.vhd

signal add_hold: boolean := false;


signal dat_hold: boolean := false;
signal add_val: std_logic_vector(A'range);
signal dat_val: std_logic_vector(D'range);

signal no_reset_yet: boolean := true;

begin

wait_for_reset: process
begin
wait until RST = '1';
wait until RST = '0';
no_reset_yet <= false;
end process wait_for_reset;

-- hold time monitors


add_monitor: process
begin

wait until A'event;

assert not add_hold or A = add_val


report "E@SIMPLE_SRAM: Address hold time violated"
severity Error;

end process add_monitor;

dat_monitor: process
begin

wait until D'event;

assert not dat_hold or D = dat_val


report "E@SIMPLE_SRAM: Data hold time violated"
severity Error;

end process dat_monitor;

http://www.stefanvhdl.com/vhdl/vhdl/sram2.vhd (2 of 7) [1/13/2009 3:55:11 PM]


http://www.stefanvhdl.com/vhdl/vhdl/sram2.vhd

test_proc: process

-- Internal Memory
type mem_add_type is array (integer range <>) of std_logic_vector(A'range);
type mem_dat_type is array (integer range <>) of std_logic_vector(D'range);

variable mem_add: mem_add_type(mem_words-1 downto 0);


variable mem_dat: mem_dat_type(mem_words-1 downto 0);
variable used_pnt: integer := 0;

procedure write is

variable start_cycle: time;

begin

start_cycle := now;

-- check setup times


assert A'last_event >= tSU
report "E@SIMPLE_SRAM: Address setup time violated"
severity Error;

assert D'last_event >= tSU


report "E@SIMPLE_SRAM: Data setup time violated"
severity Error;

-- report action for transaction log


print("I@SIMPLE_SRAM: "& hstr(D)& "h written to "& hstr(A)& "h");

-- can't do this anymore:


--*-- verify address
--*assert A = wadd
--* report "E@SIMPLE_SRAM: Address incorrect, expected "&
--* str(wadd)& " received "& str(A)
--* severity Error;

http://www.stefanvhdl.com/vhdl/vhdl/sram2.vhd (3 of 7) [1/13/2009 3:55:11 PM]


http://www.stefanvhdl.com/vhdl/vhdl/sram2.vhd

-- can't do this anymore:


--*-- verify data
--*for i in wdat'range loop
--* if wdat(i) /= '-' and wdat(i) /= D(i) then
--* print("E@SIMPLE_SRAM: Write Data Invalid, written data = "& str(D)&
--* " expected data = "& str(wdat) );
--* exit;
--* end if;
--*end loop;

wait until to_X01(WE_L) = '1';

-- Store written data


for i in 0 to used_pnt loop
if i = used_pnt then -- access to a new address
mem_add(i) := A;
mem_dat(i) := D;
if used_pnt < mem_words - 1 then
used_pnt := used_pnt + 1;
else
print("W@SRAM2: Simulation model can't handle additional addresses");
end if;
end if;
if mem_add(i) = A then -- access to an existing address
mem_dat(i) := D;
exit;
end if;
end loop;

-- verify pulse width on WE_L


assert now - start_cycle >= tW_WE
report "E@SIMPLE_SRAM: WE_L pulse width violated"
severity Error;

-- verify address and data haven't changed during the cycle


assert A'last_event >= (now - start_cycle)
report "E@SIMPLE_SRAM: Address hold time violated"
severity Error;

http://www.stefanvhdl.com/vhdl/vhdl/sram2.vhd (4 of 7) [1/13/2009 3:55:11 PM]


http://www.stefanvhdl.com/vhdl/vhdl/sram2.vhd

assert D'last_event >= (now - start_cycle)


report "E@SIMPLE_SRAM: Data hold time violated"
severity Error;

-- now make sure the hold times are maintained


add_hold <= true, false after tH;
dat_hold <= true, false after tH;
add_val <= A;
dat_val <= D;

end write;

procedure read is

constant xx: std_logic_vector(D'range) := (others => 'X');


variable start_cycle: time;

begin

start_cycle := now;

-- check setup times


assert A'last_event >= tSU
report "E@SIMPLE_SRAM: Address setup time violated"
severity Error;

assert D = (D'range => 'Z')


report "E@SIMPLE_SRAM: Data bus is driven"
severity Error;

-- can't do this anymore:


--*-- verify address
--*assert A = wadd
--* report "E@SIMPLE_SRAM: Address incorrect, expected "&
--* str(wadd)& " received "& str(A)
--* severity Error;

-- Retrieve data from internal memory

http://www.stefanvhdl.com/vhdl/vhdl/sram2.vhd (5 of 7) [1/13/2009 3:55:11 PM]


http://www.stefanvhdl.com/vhdl/vhdl/sram2.vhd

for i in 0 to used_pnt+1 loop


if i = used_pnt+1 then -- access to a new address
print("W@SRAM2: Address has not been written to yet");
print("I@SIMPLE_SRAM: "& hstr(xx)& " provided for "&
hstr(A)& "h");
D <= (others => 'X');
exit;
end if;
if mem_add(i) = A then -- access to an existing address
D <= mem_dat(i) after tRD;
print("I@SIMPLE_SRAM: "& hstr(mem_dat(i))& "h provided for "&
hstr(A)& "h");
exit;
end if;
end loop;

wait until to_X01(RD_L) = '1';

-- verify pulse width on RD_L


assert now - start_cycle >= tW_RD
report "E@SIMPLE_SRAM: RD_L pulse width violated"
severity Error;

-- verify address and data haven't changed during the cycle


assert A'last_event >= (now - start_cycle)
report "E@SIMPLE_SRAM: Address hold time violated"
severity Error;

-- now make sure the hold times are maintained


add_hold <= true, false after tH;
dat_hold <= false;
add_val <= A;

end read;

begin

http://www.stefanvhdl.com/vhdl/vhdl/sram2.vhd (6 of 7) [1/13/2009 3:55:11 PM]


http://www.stefanvhdl.com/vhdl/vhdl/sram2.vhd

D <= (others => 'Z');

wait until WE_L'event or RD_L'event;

assert (WE_L /= 'X' and WE_L /= 'Z' and WE_L /= 'U' and WE_L /= '-') or no_reset_yet
report "E@SRAM2: WE_L="& str(WE_L)& " invalid value"
severity Error;

assert (RD_L /= 'X' and RD_L /= 'Z' and RD_L /= 'U' and WE_L /= '-') or no_reset_yet
report "E@SRAM2: RD_L="& str(RD_L)& " invalid value"
severity Error;

assert to_X01(RD_L) /= '0' or to_X01(WE_L) /= '0'


report "E@SRAM2: both read and write are asserted"&
"RD_L="& str(RD_L)& " WE_L="& str(WE_L)
severity Error;

-- decide whether read or write access

if to_X01(WE_L) = '0' then


write;
end if;

if to_X01(RD_L) = '0' then


read;
end if;

end process test_proc;

end model;

http://www.stefanvhdl.com/vhdl/vhdl/sram2.vhd (7 of 7) [1/13/2009 3:55:11 PM]


http://www.stefanvhdl.com/vhdl/vhdl/mp.vhd

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.conv_integer;

use std.textio.all;
use work.txt_util.all;

entity MP is
port(
RST: in std_logic;
A: out std_logic_vector(7 downto 0);
WE_L: out std_logic;
RD_L: out std_logic;
D: inout std_logic_vector(7 downto 0)
);
end MP;

architecture test of MP is

constant tSU : time := 4 ns;


constant tH : time := 3 ns;
constant tW_WE : time := 40 ns;
constant tRD : time := 6 ns;
constant tW_RD : time := 40 ns;

begin

transactor: process

procedure write(wadd: std_logic_vector(A'range);


wdat: std_logic_vector(D'range);
flag: string) is
begin

print("I@MP: write "& hstr(wdat)& "h to "& hstr(wadd)& "h");

http://www.stefanvhdl.com/vhdl/vhdl/mp.vhd (1 of 5) [1/13/2009 3:55:12 PM]


http://www.stefanvhdl.com/vhdl/vhdl/mp.vhd

A <= wadd;
D <= wdat;

wait for tSU;

WE_L <= '0';


if flag = "weak" then
WE_L <= 'L';
end if;

wait for tW_WE;

WE_L <= '1';


if flag = "weak" then
WE_L <= 'H';
end if;

wait for tH;

A <= (others => 'X');


D <= (others => 'Z');

end procedure write;

procedure write(wadd: std_logic_vector(A'range);


wdat: std_logic_vector(D'range)) is
begin
write(wadd, wdat, "none");
end procedure write;

procedure read(radd: std_logic_vector(A'range);


rdat: std_logic_vector(D'range);
flag: string) is
begin

A <= radd;
D <= (others => 'Z');

http://www.stefanvhdl.com/vhdl/vhdl/mp.vhd (2 of 5) [1/13/2009 3:55:12 PM]


http://www.stefanvhdl.com/vhdl/vhdl/mp.vhd

wait for tSU;

RD_L <= '0';


if flag = "weak" then
RD_L <= 'L';
end if;

wait for tW_RD;

RD_L <= '1';


if flag = "weak" then
RD_L <= 'H';
end if;
print("I@MP: read "& hstr(D)& "h from "& hstr(radd)& "h");
assert D = rdat
report "E@MP: read incorrect value"
severity Error;

wait for tH;

A <= (others => 'X');

end procedure read;

procedure read(radd: std_logic_vector(A'range);


rdat: std_logic_vector(D'range)) is
begin
read(radd, rdat, "none");
end procedure read;

begin

A <= (others => 'X');


D <= (others => 'Z');
WE_L <= '1';
RD_L <= '1';

http://www.stefanvhdl.com/vhdl/vhdl/mp.vhd (3 of 5) [1/13/2009 3:55:12 PM]


http://www.stefanvhdl.com/vhdl/vhdl/mp.vhd

wait until RST='1';


wait until RST='0';

write("00001111","10101111");
wait for 5 ns;
read("00001111","10101111");
wait for 5 ns;

-- fill up model space


for i in 0 to 32 loop
write(conv_std_logic_vector(i,8),conv_std_logic_vector(i,8));
end loop;

-- read back all locations


for i in 0 to 33 loop
read(conv_std_logic_vector(i,8),conv_std_logic_vector(i,8));
end loop;

print(" ");
print("change the values of existing addresses");
print(" ");

write("00000011","10101111");
write("00000010","00000000");

print(" ");
print("and verify the changes");
print(" ");
read("00000011","10101111");
read("00000010","00000000");

print(" ");
print("do two weak writes");
print(" ");
write("00000100","10101010","weak");
write("00000101","01010101","weak");

print(" ");
print("do two weak reads");
print(" ");
read("00000100","10101010","weak");
read("00000101","01010101","weak");

http://www.stefanvhdl.com/vhdl/vhdl/mp.vhd (4 of 5) [1/13/2009 3:55:12 PM]


http://www.stefanvhdl.com/vhdl/vhdl/mp.vhd

wait;

end process transactor;

end test;

http://www.stefanvhdl.com/vhdl/vhdl/mp.vhd (5 of 5) [1/13/2009 3:55:12 PM]


http://www.stefanvhdl.com/vhdl/vhdl/tb_simple2.vhd

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;

entity tb_simple2 is
end tb_simple2;

architecture structure of tb_simple2 is

component sram2
generic(
mem_words: integer := 32
);
port(
RST: in std_logic; -- doesn't go to RAM, but is useful for testing
A: in std_logic_vector(7 downto 0);
WE_L: in std_logic;
RD_L: in std_logic;

D: inout std_logic_vector(7 downto 0)


);
end component;

component mp
port(
RST: in std_logic;
A: out std_logic_vector(7 downto 0);
WE_L: out std_logic;
RD_L: out std_logic;
D: inout std_logic_vector(7 downto 0)
);
end component;

signal D: std_logic_vector(7 downto 0);


signal A: std_logic_vector(7 downto 0);

http://www.stefanvhdl.com/vhdl/vhdl/tb_simple2.vhd (1 of 2) [1/13/2009 3:55:13 PM]


http://www.stefanvhdl.com/vhdl/vhdl/tb_simple2.vhd

signal WE_L: std_logic;


signal RD_L: std_logic;
signal RST: std_logic;

begin

RST <= '0', '1' after 1 ns, '0' after 2 ns;

U_SIMPLE_SRAM: SRAM2
port map (
RST => rst,
A => a,
WE_L => we_l,
RD_L => rd_l,
D => d
);

U_MP: MP
port map (
RST => rst,
A => a,
WE_L => we_l,
RD_L => rd_l,
D => d
);

end structure;

http://www.stefanvhdl.com/vhdl/vhdl/tb_simple2.vhd (2 of 2) [1/13/2009 3:55:13 PM]


http://www.stefanvhdl.com/vhdl/vhdl/simple_mon.vhd

library ieee;
use ieee.std_logic_1164.all;
use std.textio.all;
use ieee.std_logic_arith.all;

use work.txt_util.all;

entity SIMPLE_MON is
end SIMPLE_MON;

architecture test of SIMPLE_MON is

signal INT_L: std_logic;

begin

-- example stimulus
INT_L <= '1', '0' after 30 ns, '1' after 200 ns;

-- report changes of the interrupt signal

monitor: process(INT_L)

begin

print("I@TB: INT_L="& str(INT_L));

end process monitor;

-- report when interrupt is asserted


print(INT_L'event and INT_L = '0', "I@TB: INT_L="& str(INT_L));

end test;

http://www.stefanvhdl.com/vhdl/vhdl/simple_mon.vhd [1/13/2009 3:55:14 PM]


http://www.stefanvhdl.com/vhdl/vhdl/tb_clk_rst.vhd

library ieee;
use ieee.std_logic_1164.all;

entity TB_CLK_RST is
end TB_CLK_RST;

architecture test of TB_CLK_RST is

signal clk: std_logic := '0';


signal rst: std_logic;

begin

rst <= '0', '1' after 10 ns, '0' after 30 ns;


clk <= not clk after 8 ns;

end test;

http://www.stefanvhdl.com/vhdl/vhdl/tb_clk_rst.vhd [1/13/2009 3:55:14 PM]


http://www.stefanvhdl.com/vhdl/vhdl/insert.awk

#
# File insertion script
#

BEGIN {skip=0}

/--insert_file/ {
print "-- inserted file: " $2;
system("cat " $2);
print "-- -----------------------------------------------------------";
print "";
skip = 1}

// {
if(skip==0) {print $0};
skip = 0
}

http://www.stefanvhdl.com/vhdl/vhdl/insert.awk [1/13/2009 3:55:15 PM]


http://www.stefanvhdl.com/vhdl/vhdl/test_code.vhd

Y <= '0', '1' after 30 ns, '0' after 80 ns;

http://www.stefanvhdl.com/vhdl/vhdl/test_code.vhd [1/13/2009 3:55:15 PM]


http://www.stefanvhdl.com/vhdl/vhdl/transactor.vhd

library ieee;
use ieee.std_logic_1164.all;
use std.textio.all;

entity TRANSACTOR is
port(
Y: out std_logic
);
end TRANSACTOR;

architecture test of TRANSACTOR is

begin

--insert_file test_code.vhd

end test;

http://www.stefanvhdl.com/vhdl/vhdl/transactor.vhd [1/13/2009 3:55:16 PM]


http://www.stefanvhdl.com/vhdl/vhdl/hang.vhd

library ieee;
use ieee.std_logic_1164.all;
use std.textio.all;
use ieee.std_logic_arith.all;

use work.txt_util.all;

entity HANG is
end HANG;

architecture test of HANG is

constant tSU_R: time := 5 ns;


constant tSU_W: time := 6 ns;

signal W: std_logic;
signal R: std_logic;
signal RST: std_logic;

begin

RST <= '0', '1' after 12 ns, '0' after 20 ns;

-- verify the setup time on W and R signals


-- we assume W and R are asserted alternatingly

timing_check: process

variable w_asserted: time;


variable r_asserted: time;

begin

-- wait for DUT to be reset


wait until RST = '1';
wait until RST = '0';

-- verify write access


wait until W = '0';
w_asserted := now;

http://www.stefanvhdl.com/vhdl/vhdl/hang.vhd (1 of 3) [1/13/2009 3:55:17 PM]


http://www.stefanvhdl.com/vhdl/vhdl/hang.vhd

wait until W = '1';

assert (now - w_asserted) >= tSU_W


report "E@TB: W setup time too short"
severity Error;

-- verify read access


wait until R = '0';
r_asserted := now;
wait until R = '1';

assert (now - r_asserted) >= tSU_R


report "E@TB: R setup time too short"
severity Error;

end process timing_check;

-- description of the timing behaviour


-- of the DUT implemenation
dut: process

begin

W <= '1';
R <= '1';

wait until RST = '1';


wait until RST = '0';

wait for 10 ns;

-- write access
W <= '0', '1' after 8 ns;
wait for 10 ns;

-- read access
R <= '0', '1' after 9 ns;
wait for 10 ns;

http://www.stefanvhdl.com/vhdl/vhdl/hang.vhd (2 of 3) [1/13/2009 3:55:17 PM]


http://www.stefanvhdl.com/vhdl/vhdl/hang.vhd

-- write access
W <= '0', '1' after 7 ns;
wait for 10 ns;

-- read access
R <= '0', '1' after 4 ns; -- this is a violation we want to detect
wait for 10 ns;

-- write access
W <= '0', '1' after 8 ns;
wait for 10 ns;

wait;

end process dut;

end test;

http://www.stefanvhdl.com/vhdl/vhdl/hang.vhd (3 of 3) [1/13/2009 3:55:17 PM]


http://www.stefanvhdl.com/vhdl/vhdl/hang2.vhd

library ieee;
use ieee.std_logic_1164.all;
use std.textio.all;
use ieee.std_logic_arith.all;

use work.txt_util.all;

entity HANG2 is
end HANG2;

architecture test of HANG2 is

constant tSU_R: time := 5 ns;


constant tSU_W: time := 6 ns;

signal W: std_logic;
signal R: std_logic;
signal RST: std_logic;

begin

RST <= '0', '1' after 12 ns, '0' after 20 ns;

-- verify the setup time on W and R signals


-- we assume W and R are asserted alternatingly

timing_check: process

variable w_asserted: time;


variable r_asserted: time;

begin

-- wait for DUT to be reset


wait until RST = '1';
wait until RST = '0';

-- verify write access


wait until W = '0';
w_asserted := now;

http://www.stefanvhdl.com/vhdl/vhdl/hang2.vhd (1 of 3) [1/13/2009 3:55:18 PM]


http://www.stefanvhdl.com/vhdl/vhdl/hang2.vhd

wait until W = '1';

print("I@TB: detected W access");

assert (now - w_asserted) >= tSU_W


report "E@TB: W setup time too short"
severity Error;

-- verify read access


wait until R = '0';
r_asserted := now;
wait until R = '1';

print("I@TB: detected R access");

assert (now - r_asserted) >= tSU_R


report "E@TB: R setup time too short"
severity Error;

end process timing_check;

-- description of the timing behaviour


-- of the DUT implemenation
dut: process

begin

W <= '1';
R <= '1';

wait until RST = '1';


wait until RST = '0';

wait for 10 ns;

-- write access
W <= '0', '1' after 8 ns;
wait for 10 ns;

http://www.stefanvhdl.com/vhdl/vhdl/hang2.vhd (2 of 3) [1/13/2009 3:55:18 PM]


http://www.stefanvhdl.com/vhdl/vhdl/hang2.vhd

-- read access
R <= '0', '1' after 9 ns;
wait for 10 ns;

-- write access
W <= '0', '1' after 7 ns;
wait for 10 ns;

-- read access
R <= '0', '1' after 4 ns; -- this is a violation we want to detect
wait for 10 ns;

-- write access
W <= '0', '1' after 8 ns;
wait for 10 ns;

wait;

end process dut;

end test;

http://www.stefanvhdl.com/vhdl/vhdl/hang2.vhd (3 of 3) [1/13/2009 3:55:18 PM]


http://www.stefanvhdl.com/vhdl/vhdl/tloop.vhd

library ieee;
use ieee.std_logic_1164.all;
use std.textio.all;
use ieee.std_logic_arith.all;

use work.txt_util.all;

entity TLOOP is
end TLOOP;

architecture test of TLOOP is

constant tSU_R: time := 5 ns;


constant tSU_W: time := 6 ns;

signal W: std_logic;
signal R: std_logic;
signal RST: std_logic;

begin

RST <= '0', '1' after 12 ns, '0' after 20 ns;

-- verify the setup time on W and R signals


-- we assume W and R are asserted alternatingly

timing_check: process

variable w_asserted: time;


variable r_asserted: time;

begin

-- wait for DUT to be reset


wait until RST = '1';
wait until RST = '0';

loop
wait until W = '0';
w_asserted := now;

http://www.stefanvhdl.com/vhdl/vhdl/tloop.vhd (1 of 3) [1/13/2009 3:55:18 PM]


http://www.stefanvhdl.com/vhdl/vhdl/tloop.vhd

wait until W = '1';

print("I@TB: detected W access");

assert (now - w_asserted) >= tSU_W


report "E@TB: W setup time too short"
severity Error;

-- verify read access


wait until R = '0';
r_asserted := now;
wait until R = '1';

print("I@TB: detected R access");

assert (now - r_asserted) >= tSU_R


report "E@TB: R setup time too short"
severity Error;

end loop;

end process timing_check;

-- description of the timing behaviour


-- of the DUT implemenation
dut: process

begin

W <= '1';
R <= '1';

wait until RST = '1';


wait until RST = '0';

wait for 10 ns;

-- write access
W <= '0', '1' after 8 ns;
wait for 10 ns;

http://www.stefanvhdl.com/vhdl/vhdl/tloop.vhd (2 of 3) [1/13/2009 3:55:18 PM]


http://www.stefanvhdl.com/vhdl/vhdl/tloop.vhd

-- read access
R <= '0', '1' after 9 ns;
wait for 10 ns;

-- write access
W <= '0', '1' after 7 ns;
wait for 10 ns;

-- read access
R <= '0', '1' after 4 ns; -- this is a violation we want to detect
wait for 10 ns;

-- write access
W <= '0', '1' after 8 ns;
wait for 10 ns;

wait;

end process dut;

end test;

http://www.stefanvhdl.com/vhdl/vhdl/tloop.vhd (3 of 3) [1/13/2009 3:55:18 PM]


http://www.stefanvhdl.com/vhdl/vhdl/finish.vhd

use std.textio.all;
use work.txt_util.all;

entity finish is
end finish;

architecture text of finish is

begin

pr_proc: process
begin

print(" ");
print("I hope this course has been useful, I'm looking forward");
print("to your comments at: sdoll@intrinsix.com");
print(" ");
print("Thanks");
print(" ");
print("© Stefan Doll");

wait;

end process;

end text;

http://www.stefanvhdl.com/vhdl/vhdl/finish.vhd [1/13/2009 3:55:19 PM]


###########################################################################

# Generates some test patterns


# Stefan Doll, sdoll@intrinsix.com
#

# Usage:
#
# perl tgen.pl > _output_file_
#
#

###########################################################################

print "00010\n";
print "00011\n";
print "11100\n";

for ($i=0;$i<10;$i++) {
print num2binary($i,5)."\n";
}

print "1UXZW\n";
print "HL111\n";
print "11111\n";

sub num2binary {
my($num) = @_;
my $binary = $num ? '' : '0'; # in case $num is zero
my $len = $_[1];
my $result;

while ($num) {
$binary .= $num & 1 ? 1 : 0; # do the LSB
$num >>= 1; # on to the next bit
}

$result = scalar reverse $binary;


while (length($result)<$len) {
$result = "0".$result;
}

return $result;
}

You might also like