------------------------------------------------------------
-- Copyright: 2011 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;

Generated on Tue Nov 22 15:16:34 CET 2011
Home