------------------------------------------------------------ -- Copyright: 2010 Integrated Sytems Laboratory, ETH Zurich -- http://www.iis.ee.ethz.ch/~sha3 ------------------------------------------------------------ ------------------------------------------------------------------------------- -- File: simulstuff.vhd -- Authors: H.Kaeslin, M.Stadler, P.Luethi, F.Camarero, Th.Kuch, M.Braendli -- Copyright: 1995 - 2007 Microelectronics Design Center, ETH Zurich -- Platform: ModelSim 6.3 -- Last changes: 23.4.2008 ------------------------------------------------------------------------------- -- Mission: Take care of all those lower-level issues of testbench -- writing that make this activity slow and boring when starting -- from scratch. -- Description: A collection of universal types and subprograms that are -- helpful for simulation purposes, not intended for synthesis. -- By universal we mean independent of the model under test (MUT). ------------------------------------------------------------------------------- -- Modification history: -- 7.7.98: source code established. -- 16.7.98: new subprograms added for response evaluation and reporting. -- 4.9.98: failure annotation modified. -- 31.5.00: generator for random vectors added. -- 7.6.00: CheckResponse overloaded for std_logic as suggested by M.Braendli. -- 22.6.00: extraction of file entries (read) included for brevity of testb. -- 07.5.03: added GenerateRandomBit where probability can be controlled. -- 06.6.03: changed GenerateRandomVector for random numbers in open interval. -- 18.7.05: new global signal "EndOfSimxS". -- 25.7.05: new entry "mne" in (enumeration)type "respmatchtype". -- 26.7.05: procedure PutSimulationReportSummary changed. -- 09.8.05: CheckResponse renamed to CheckValue. -- 20.9.05: function AnnotateFailureMessage removed. -- 21.9.05: various changes and extensions by P.Luethi added: -- - extensions to stimuli/expresp file input: -- GetFileEntryXXX for signed/unsigned integer to -- signed/unsigned/std_logic_vector conversion -- - CheckValue and write() overload methods for signed & unsigned -- 23.4.08: some general cleanup ------------------------------------------------------------------------------- use std.textio.all; library ieee; use ieee.std_logic_textio.all; -- read and write overloaded for std_logic use ieee.std_logic_1164.all; use ieee.numeric_std.all; use ieee.math_real.Uniform; -- IEEE 1076.2 real math package use ieee.math_real.Trunc; ------------------------------------------------------------------------------- -- package declaration package simulstuff is -- signal to broadcast the end of simulation signal EndOfSimxS : boolean := false; -- clock generator procedure ClockGenerator ( signal ClkxC : out std_logic; constant clkphaselow : in time; constant clkphasehigh : in time); -- support for file handling function FileOpenMessage ( filename : string; status : file_open_status) return string; function FileReadMessage ( filename : string; read_ok : boolean; lineread : string) return string; -- purpose: get one entry from the stimuli or expected responses file -- binary character => std_logic procedure GetFileEntry ( fileentry : inout std_logic; in_line, in_line_tmp : inout line; filename : string); -- binary string => std_logic_vector procedure GetFileEntry ( fileentry : inout std_logic_vector; in_line, in_line_tmp : inout line; filename : string); -- binary string => unsigned procedure GetFileEntry ( val : inout unsigned; in_line, in_line_tmp : inout line; filename : string); -- binary string => signed procedure GetFileEntry ( val : inout signed; in_line, in_line_tmp : inout line; filename : string); -- unsigned integer string => unsigned procedure GetFileEntryInt2x ( val : inout unsigned; in_line, in_line_tmp : inout line; filename : string); -- signed/unsigned integer string => signed procedure GetFileEntryInt2x ( val : inout signed; in_line, in_line_tmp : inout line; filename : string); -- signed/unsigned integer string => (signed =>) std_logic_vector procedure GetFileEntryInt2x ( val : inout std_logic_vector; in_line, in_line_tmp : inout line; filename : string); -- unsigned integer string => (unsigned =>) std_logic_vector procedure GetFileEntryUInt2x ( val : inout std_logic_vector; in_line, in_line_tmp : inout line; filename : string); -- support for evaluating responses from the MUT type respmatchtype is ( mne, -- not evaluated = expected response has the value "don't care" mok, -- o.k. = both logic value and drive strength do match mlf, -- logic fail = logic value or tristate status does not match msf, -- strength fail = weak instead of strong drive or viceversa mil); -- illegal response = actual response has value "don't care" type vectorwise_matchtable is array (respmatchtype, respmatchtype) of respmatchtype; constant check_vectorwise : vectorwise_matchtable := ( -- --------------------------------------- -- | mne mok mlf msf mil | | -- --------------------------------------- ( mne, mok, mlf, msf, mil ), -- | mne | ( mok, mok, mlf, msf, mil ), -- | mok | ( mlf, mlf, mlf, mlf, mil ), -- | mlf | ( msf, msf, mlf, msf, mil ), -- | msf | ( mil, mil, mil, mil, mil ) -- | mil | ); -- symmetric, dimensions may be interchanged type respaccounttype is record numberof_mch, -- number of responses checked so far numberof_mne, -- see above for this and all other fields numberof_mok, numberof_mlf, numberof_msf, numberof_mil : natural; end record; -- checks if actual and expected response are equal procedure CheckValue ( actresp, expresp : in std_logic_vector; respmatch : out respmatchtype; respaccount : inout respaccounttype); procedure CheckValue ( actresp, expresp : in std_logic; respmatch : out respmatchtype; respaccount : inout respaccounttype); procedure CheckValue ( actresp, expresp : in unsigned; respmatch : out respmatchtype; respaccount : inout respaccounttype); procedure CheckValue ( actresp, expresp : in signed; respmatch : out respmatchtype; respaccount : inout respaccounttype); -- purpose: summarize simulation report and write it to report file procedure PutSimulationReportSummary (file simreptfile : text; respaccount : in respaccounttype); -- purpose: overload write for signed and unsigned procedure write (outline : inout line; val : in unsigned); procedure write (outline : inout line; val : in signed); -- support for generation of random test patterns procedure GenerateRandomVector ( randvectwidth : in natural; statevar1, statevar2 : inout integer; randvect : out std_logic_vector); -- unconstrained array type procedure GenerateRandomBit ( probability_of_1 : in real; statevar1, statevar2 : inout integer; randbit : out std_logic); procedure GenerateRandomInteger ( min, max : in integer; statevar1, statevar2 : inout integer; randinteger : out integer); end package simulstuff; --============================================================================= -- package body package body simulstuff is -- purpose: generate a periodic but stoppable clock signal, -- generator to be instantiated as a concurrent procedure. procedure ClockGenerator (signal ClkxC : out std_logic; constant clkphaselow : in time; constant clkphasehigh : in time) is begin ClkGen : loop ClkxC <= '1'; wait for clkphasehigh; ClkxC <= '0'; wait for clkphaselow; exit ClkGen when EndOfSimxS = true; end loop ClkGen; wait; -- forever end procedure ClockGenerator; ------------------------------------------------------------------------------- -- purpose: translate file open status into a human-readable text string. function FileOpenMessage (filename : string; status : file_open_status) return string is begin case status is when open_ok => return "File "& filename &" opened successfully."; when status_error => return "File "& filename &" already opened."; when name_error => return "File "& filename &" does not exist or can not be created."; when mode_error => return "File "& filename &" can not be opened in write or append mode."; end case; end FileOpenMessage; -- purpose: translate file read status into a human-readable text string. function FileReadMessage (filename : string; read_ok : boolean; lineread : string) return string is begin if read_ok = true then return "Line `"& lineread &"' sucessfully read from file " & filename &"."; else return "Missing or unsuitable entry found while reading line `" & lineread &"' from file "& filename &"."; end if; end FileReadMessage; ------------------------------------------------------------------------------- -- ### binary character => std_logic -- purpose: get one entry from the stimuli or expected responses file -- source (file entry) : binary -- target (VHDL signal): std_logic procedure GetFileEntry ( fileentry : inout std_logic; in_line, in_line_tmp : inout line; filename : string) is variable read_ok : boolean; begin -- extract next entry to obtain the value of formal variable fileentry read(in_line, fileentry, read_ok); -- binary read if not read_ok then report FileReadMessage(filename, read_ok, in_line_tmp.all) severity error; end if; end GetFileEntry; -- ### binary string => std_logic_vector -- purpose: get one entry from the stimuli or expected responses file -- source (file entry) : binary -- target (VHDL signal): std_logic_vector procedure GetFileEntry ( fileentry : inout std_logic_vector; in_line, in_line_tmp : inout line; filename : string) is variable read_ok : boolean; begin -- extract next entry to obtain the value of formal variable fileentry read(in_line, fileentry, read_ok); -- binary read if not read_ok then hread(in_line, fileentry, read_ok); -- try hex-read if not read_ok then report FileReadMessage(filename, read_ok, in_line_tmp.all) severity error; end if; end if; end GetFileEntry; -- ### binary string => unsigned -- purpose: get one entry from the stimuli or expected responses file -- source (file entry) : binary -- target (VHDL signal): unsigned procedure GetFileEntry ( val : inout unsigned; in_line, in_line_tmp : inout line; filename : string) is variable read_ok : boolean; variable fileentry : std_logic_vector(val'high downto val'low); begin -- extract next entry to obtain the value of formal variable fileentry read(in_line, fileentry, read_ok); -- binary read if not read_ok then hread(in_line, fileentry, read_ok); -- try hex-read if not read_ok then report FileReadMessage(filename, read_ok, in_line_tmp.all) severity error; end if; end if; val := unsigned(fileentry); end GetFileEntry; -- ### binary string => signed -- purpose: get one entry from the stimuli or expected responses file -- source (file entry) : binary -- target (VHDL signal): signed procedure GetFileEntry ( val : inout signed; in_line, in_line_tmp : inout line; filename : string) is variable read_ok : boolean; variable fileentry : std_logic_vector(val'high downto val'low); begin -- extract next entry to obtain the value of formal variable fileentry read(in_line, fileentry, read_ok); -- binary read if not read_ok then hread(in_line, fileentry, read_ok); -- try hex-read if not read_ok then report FileReadMessage(filename, read_ok, in_line_tmp.all) severity error; end if; end if; val := signed(fileentry); end GetFileEntry; -- ### unsigned integer string => unsigned -- purpose: get one entry from the stimuli or expected responses file -- source (file entry) : unsigned integer -- target (VHDL signal): unsigned procedure GetFileEntryInt2x ( val : inout unsigned; in_line, in_line_tmp : inout line; filename : string) is variable read_ok : boolean; variable fileentry : integer; begin -- extract next entry to obtain the value of formal variable fileentry read(in_line, fileentry, read_ok); if not read_ok then report FileReadMessage(filename, read_ok, in_line_tmp.all) severity error; end if; val := to_unsigned(fileentry, val'length); end GetFileEntryInt2x; -- ### signed/unsigned integer string => signed -- purpose: get one entry from the stimuli or expected responses file -- source (file entry) : signed/unsigned integer -- target (VHDL signal): signed procedure GetFileEntryInt2x ( val : inout signed; in_line, in_line_tmp : inout line; filename : string) is variable read_ok : boolean; variable fileentry : integer; begin -- extract next entry to obtain the value of formal variable fileentry read(in_line, fileentry, read_ok); if not read_ok then report FileReadMessage(filename, read_ok, in_line_tmp.all) severity error; end if; val := to_signed(fileentry, val'length); end GetFileEntryInt2x; -- ### signed/unsigned integer string => (signed =>) std_logic_vector -- purpose: get one entry from the stimuli or expected responses file -- source (file entry) : signed/unsigned integer -- target (VHDL signal): (signed) std_logic_vector procedure GetFileEntryInt2x ( val : inout std_logic_vector; in_line, in_line_tmp : inout line; filename : string) is variable tmp_signed : signed(1 to val'length); begin -- use overloaded procedure GetFileEntryInt2x(tmp_signed, in_line, in_line_tmp, filename); val := std_logic_vector(tmp_signed); end GetFileEntryInt2x; -- ### unsigned integer string => (unsigned =>) std_logic_vector -- purpose: get one entry from the stimuli or expected responses file -- source (file entry) : unsigned integer -- target (VHDL signal): (unsigned) std_logic_vector procedure GetFileEntryUInt2x ( val : inout std_logic_vector; in_line, in_line_tmp : inout line; filename : string) is variable tmp_unsigned : unsigned(1 to val'length); begin -- use overloaded procedure GetFileEntryInt2x(tmp_unsigned, in_line, in_line_tmp, filename); val := std_logic_vector(tmp_unsigned); end GetFileEntryUInt2x; ------------------------------------------------------------------------------- -- purpose: check to what extent actual and expected responses match, -- return a grade and update failure account record accordingly. procedure CheckValue ( actresp, expresp : in std_logic_vector; respmatch : out respmatchtype; respaccount : inout respaccounttype) is type bitwise_matchtable is array (std_logic, std_logic) of respmatchtype; constant check_bitwise : bitwise_matchtable := ( -- --------------------------------------------------------- -- exp | U X 0 1 Z W L H - |act| -- --------------------------------------------------------- ( mok, mlf, mlf, mlf, mlf, mlf, mlf, mlf, mne ), -- | U | ( mlf, mok, mlf, mlf, mlf, msf, mlf, mlf, mne ), -- | X | ( mlf, mlf, mok, mlf, mlf, mlf, msf, mlf, mne ), -- | 0 | ( mlf, mlf, mlf, mok, mlf, mlf, mlf, msf, mne ), -- | 1 | ( mlf, mlf, mlf, mlf, mok, mlf, mlf, mlf, mne ), -- | Z | ( mlf, msf, mlf, mlf, mlf, mok, mlf, mlf, mne ), -- | W | ( mlf, mlf, msf, mlf, mlf, mlf, mok, mlf, mne ), -- | L | ( mlf, mlf, mlf, msf, mlf, mlf, mlf, mok, mne ), -- | H | ( mil, mil, mil, mil, mil, mil, mil, mil, mil ) -- | - | ); -- act is the 1st and exp the 2nd dimension variable bitwise_match, vectorwise_match : respmatchtype := mne; begin assert expresp'length = actresp'length and expresp'length > 0 report " Cardinality of response does not match or is zero." severity warning; for i in expresp'range loop bitwise_match := check_bitwise(actresp(i), expresp(i)); vectorwise_match := check_vectorwise(vectorwise_match, bitwise_match); end loop; respmatch := vectorwise_match; case vectorwise_match is when mne => respaccount.numberof_mne := respaccount.numberof_mne + 1; when mok => respaccount.numberof_mok := respaccount.numberof_mok + 1; when mlf => respaccount.numberof_mlf := respaccount.numberof_mlf + 1; when msf => respaccount.numberof_msf := respaccount.numberof_msf + 1; when mil => respaccount.numberof_mil := respaccount.numberof_mil + 1; end case; respaccount.numberof_mch := respaccount.numberof_mch + 1; end CheckValue; -- purpose: above procedure overloaded for scalars rather than vectors. procedure CheckValue ( actresp, expresp : in std_logic; respmatch : out respmatchtype; respaccount : inout respaccounttype) is variable actrespvector, exprespvector : std_logic_vector(0 to 0); begin actrespvector(0) := actresp; exprespvector(0) := expresp; CheckValue(actrespvector, exprespvector, respmatch, respaccount); end CheckValue; -- purpose: above procedure overloaded for unsigned rather than std_logic_vector procedure CheckValue ( actresp, expresp : in unsigned; respmatch : out respmatchtype; respaccount : inout respaccounttype) is variable actrespvector, exprespvector : std_logic_vector(actresp'high downto actresp'low); begin actrespvector := std_logic_vector(actresp); exprespvector := std_logic_vector(expresp); CheckValue(actrespvector, exprespvector, respmatch, respaccount); end CheckValue; -- purpose: above procedure overloaded for signed rather than std_logic_vector procedure CheckValue ( actresp, expresp : in signed; respmatch : out respmatchtype; respaccount : inout respaccounttype) is variable actrespvector, exprespvector : std_logic_vector(actresp'high downto actresp'low); begin actrespvector := std_logic_vector(actresp); exprespvector := std_logic_vector(expresp); CheckValue(actrespvector, exprespvector, respmatch, respaccount); end CheckValue; ------------------------------------------------------------------------------- -- purpose: summarize simulation report and write it to report file. procedure PutSimulationReportSummary (file simreptfile : text; respaccount : in respaccounttype) is variable out_line : line; begin write(out_line, string'(" ")); writeline(simreptfile, out_line); write(out_line, string'("====== Simulation Report Summary ===============================")); writeline(simreptfile, out_line); write(out_line, string'(" Total of responses: ")); write(out_line, respaccount.numberof_mch); writeline(simreptfile, out_line); write(out_line, string'(" ")); writeline(simreptfile, out_line); write(out_line, string'(" ")); write(out_line, string'(" TOTAL VECTORS TESTED: ")); write(out_line, respaccount.numberof_mch - respaccount.numberof_mne); writeline(simreptfile, out_line); write(out_line, string'(" ---------------------------------")); writeline(simreptfile, out_line); write(out_line, string'(" ")); write(out_line, string'(" CORRECT RESPONSES: ")); write(out_line, respaccount.numberof_mok); writeline(simreptfile, out_line); write(out_line, string'(" ")); write(out_line, string'(" FALSE RESPONSES: ")); write(out_line, respaccount.numberof_mlf + respaccount.numberof_msf + respaccount.numberof_mil); writeline(simreptfile, out_line); writeline(simreptfile, out_line); write(out_line, string'(" ")); write(out_line, respaccount.numberof_mlf); write(out_line, string'(" responses failed logically (those with a 'l')")); writeline(simreptfile, out_line); write(out_line, string'(" ")); write(out_line, respaccount.numberof_msf); write(out_line, string'(" failed in drive strength (those with a 's')")); writeline(simreptfile, out_line); write(out_line, string'(" ")); write(out_line, respaccount.numberof_mil); write(out_line, string'(" had an illegal logic value (those with a 'i')")); writeline(simreptfile, out_line); write(out_line, string'(" ")); writeline(simreptfile, out_line); write(out_line, string'(" ")); write(out_line, respaccount.numberof_mne); write(out_line, string'(" responses haven't been checked (those with a '-'), because")); writeline(simreptfile, out_line); write(out_line, string'(" ")); write(out_line, string'("expected responses are unavailable or given as "&'"'&"don't care"&'"'&".")); writeline(simreptfile, out_line); end PutSimulationReportSummary; ------------------------------------------------------------------------------- -- purpose: overload write method for unsigned procedure write (outline : inout line; val : in unsigned) is begin write(outline, std_logic_vector(val)); end write; -- purpose: overload write method for signed procedure write (outline : inout line; val : in signed) is begin write(outline, std_logic_vector(val)); end write; ------------------------------------------------------------------------------- -- purpose: generate binary random vectors of parametrized word width that -- should be uniformly distributed over interval [0,2**randvectwidth-1]. -- limitation: mantissa of VHDL type reals has 23bits, so randvectwidth -- must not exceed this value as outcome is otherwise uncertain. -- note: state variables of procedure Uniform must be kept within the -- calling process because variables in a subprogram do not persist. -- findings: repeated calls of procedure ieee.math_real.Uniform with -- identical seeds indeed result in identical pseudo random numbers. procedure GenerateRandomVector ( randvectwidth : in natural; statevar1, statevar2 : inout integer; randvect : out std_logic_vector) -- unconstrained array type is -- upperbound is (2.0**randvectwidth)-1.0, spreadbound is 1.0 more constant spreadbound : real := 2.0**randvectwidth; variable randreal01 : real := 0.0; variable randscaled, randtruncd : real := 0.0; variable randinteger : integer := 0; begin -- obtain a random real in the open interval ]0,1[ Uniform(statevar1, statevar2, randreal01); -- scale open interval ]0,1[ to open interval ]0,spreadbound[ randscaled := randreal01*spreadbound; -- truncate to next smaller integer (still of type real, though) -- in the closed interval [0,upperbound] randtruncd := Trunc(randscaled); -- convert to a binary vector randinteger := integer(randtruncd); randvect := std_logic_vector(to_unsigned(randinteger, randvectwidth)); end GenerateRandomVector; -- purpose: generate a random bit with some given probability of being '1'. -- note: state variables of procedure Uniform must be kept within the -- calling process because variables in a subprogram do not persist. -- findings: repeated calls of procedure ieee.math_real.Uniform with -- identical seeds indeed result in identical pseudo random numbers. procedure GenerateRandomBit ( probability_of_1 : in real; statevar1, statevar2 : inout integer; randbit : out std_logic) is variable randreal01 : real := 0.0; begin -- obtain a random real in the open interval ]0,1[ Uniform(statevar1, statevar2, randreal01); -- set randbit according to threshold if (probability_of_1 > randreal01) then randbit := '1'; else randbit := '0'; end if; end GenerateRandomBit; -- purpose: generate random integer in the range [min, max], both included. -- note: state variables of procedure Uniform must be kept within the -- calling process because variables in a subprogram do not persist. -- findings: repeated calls of procedure ieee.math_real.Uniform with -- identical seeds indeed result in identical pseudo random numbers. procedure GenerateRandomInteger ( min, max : in integer; statevar1, statevar2 : inout integer; randinteger : out integer) is variable randreal01 : real := 0.0; constant spreadbound : real := real(max-min+1); variable randscaled : real := 0.0; begin -- obtain a random real in the open interval ]0,1[ Uniform(statevar1, statevar2, randreal01); -- scale open interval ]0,1[ to open interval ]0,spreadbound[ randscaled := randreal01*spreadbound; -- truncate to next smaller integer in the closed interval [0,max-min] and -- add the mininum value to get an integer in the closed interval [min,max] randinteger := integer(Trunc(randscaled)) + min; end GenerateRandomInteger; end package body simulstuff;