diff options
author | Tristan Gingold <tgingold@free.fr> | 2020-01-02 18:22:19 +0100 |
---|---|---|
committer | Tristan Gingold <tgingold@free.fr> | 2020-01-02 18:22:19 +0100 |
commit | df5e32320e8284763705f0046b91d09caf1164fd (patch) | |
tree | 83e695da29fa7ab08e20e18b1ad883f6d6fc82cb | |
parent | 91faff646998925a81d57f8d5a6e1c5fcf1f7ce8 (diff) | |
download | ghdl-df5e32320e8284763705f0046b91d09caf1164fd.tar.gz ghdl-df5e32320e8284763705f0046b91d09caf1164fd.tar.bz2 ghdl-df5e32320e8284763705f0046b91d09caf1164fd.zip |
testsuite: add cases for #1051
-rw-r--r-- | testsuite/gna/issue1051/psi_common_array_pkg.vhd | 55 | ||||
-rw-r--r-- | testsuite/gna/issue1051/psi_common_bit_cc.vhd | 75 | ||||
-rw-r--r-- | testsuite/gna/issue1051/psi_common_i2c_master.vhd | 634 | ||||
-rw-r--r-- | testsuite/gna/issue1051/psi_common_i2c_master_tb.vhd | 679 | ||||
-rw-r--r-- | testsuite/gna/issue1051/psi_common_logic_pkg.vhd | 227 | ||||
-rw-r--r-- | testsuite/gna/issue1051/psi_common_math_pkg.vhd | 259 | ||||
-rw-r--r-- | testsuite/gna/issue1051/psi_tb_activity_pkg.vhd | 258 | ||||
-rw-r--r-- | testsuite/gna/issue1051/psi_tb_compare_pkg.vhd | 256 | ||||
-rw-r--r-- | testsuite/gna/issue1051/psi_tb_i2c_pkg.vhd | 740 | ||||
-rw-r--r-- | testsuite/gna/issue1051/psi_tb_txt_util.vhd | 583 | ||||
-rw-r--r-- | testsuite/gna/issue1051/repro.vhdl | 29 | ||||
-rw-r--r-- | testsuite/gna/issue1051/repro2.vhdl | 28 | ||||
-rwxr-xr-x | testsuite/gna/issue1051/testsuite.sh | 32 |
13 files changed, 3855 insertions, 0 deletions
diff --git a/testsuite/gna/issue1051/psi_common_array_pkg.vhd b/testsuite/gna/issue1051/psi_common_array_pkg.vhd new file mode 100644 index 000000000..97f3dc319 --- /dev/null +++ b/testsuite/gna/issue1051/psi_common_array_pkg.vhd @@ -0,0 +1,55 @@ +------------------------------------------------------------------------------ +-- Copyright (c) 2018 by Paul Scherrer Institute, Switzerland +-- All rights reserved. +-- Authors: Waldemar Koprek, Oliver Bruendler +------------------------------------------------------------------------------ + +------------------------------------------------------------------------------ +-- Libraries +------------------------------------------------------------------------------ +library ieee ; + use ieee.std_logic_1164.all; + +------------------------------------------------------------------------------ +-- Package Header +------------------------------------------------------------------------------ +package psi_common_array_pkg is + type t_aslv2 is array (natural range <>) of std_logic_vector( 1 downto 0); + type t_aslv3 is array (natural range <>) of std_logic_vector( 2 downto 0); + type t_aslv4 is array (natural range <>) of std_logic_vector( 3 downto 0); + type t_aslv5 is array (natural range <>) of std_logic_vector( 4 downto 0); + type t_aslv6 is array (natural range <>) of std_logic_vector( 5 downto 0); + type t_aslv7 is array (natural range <>) of std_logic_vector( 6 downto 0); + type t_aslv8 is array (natural range <>) of std_logic_vector( 7 downto 0); + type t_aslv9 is array (natural range <>) of std_logic_vector( 8 downto 0); + type t_aslv10 is array (natural range <>) of std_logic_vector( 9 downto 0); + type t_aslv11 is array (natural range <>) of std_logic_vector(10 downto 0); + type t_aslv12 is array (natural range <>) of std_logic_vector(11 downto 0); + type t_aslv13 is array (natural range <>) of std_logic_vector(12 downto 0); + type t_aslv14 is array (natural range <>) of std_logic_vector(13 downto 0); + type t_aslv15 is array (natural range <>) of std_logic_vector(14 downto 0); + type t_aslv16 is array (natural range <>) of std_logic_vector(15 downto 0); + type t_aslv17 is array (natural range <>) of std_logic_vector(16 downto 0); + type t_aslv18 is array (natural range <>) of std_logic_vector(17 downto 0); + type t_aslv19 is array (natural range <>) of std_logic_vector(18 downto 0); + type t_aslv20 is array (natural range <>) of std_logic_vector(19 downto 0); + type t_aslv21 is array (natural range <>) of std_logic_vector(20 downto 0); + type t_aslv22 is array (natural range <>) of std_logic_vector(21 downto 0); + type t_aslv23 is array (natural range <>) of std_logic_vector(22 downto 0); + type t_aslv24 is array (natural range <>) of std_logic_vector(23 downto 0); + type t_aslv25 is array (natural range <>) of std_logic_vector(24 downto 0); + type t_aslv26 is array (natural range <>) of std_logic_vector(25 downto 0); + type t_aslv27 is array (natural range <>) of std_logic_vector(26 downto 0); + type t_aslv28 is array (natural range <>) of std_logic_vector(27 downto 0); + type t_aslv29 is array (natural range <>) of std_logic_vector(28 downto 0); + type t_aslv30 is array (natural range <>) of std_logic_vector(29 downto 0); + type t_aslv32 is array (natural range <>) of std_logic_vector(31 downto 0); + type t_aslv36 is array (natural range <>) of std_logic_vector(35 downto 0); + type t_aslv48 is array (natural range <>) of std_logic_vector(47 downto 0); + type t_aslv64 is array (natural range <>) of std_logic_vector(63 downto 0); + + type t_ainteger is array (natural range <>) of integer; + type t_areal is array (natural range <>) of real; + type t_abool is array (natural range <>) of boolean; + +end psi_common_array_pkg; diff --git a/testsuite/gna/issue1051/psi_common_bit_cc.vhd b/testsuite/gna/issue1051/psi_common_bit_cc.vhd new file mode 100644 index 000000000..6078571aa --- /dev/null +++ b/testsuite/gna/issue1051/psi_common_bit_cc.vhd @@ -0,0 +1,75 @@ +------------------------------------------------------------------------------ +-- Copyright (c) 2018 by Paul Scherrer Institute, Switzerland +-- All rights reserved. +-- Authors: Oliver Bruendler +------------------------------------------------------------------------------ + +------------------------------------------------------------------------------ +-- Description +------------------------------------------------------------------------------ +-- This is a very basic clock crossing that allows passing multple independent +-- single-bit signals from one clock domain to another one. +-- Double stage synchronizers are implemeted for each bit, including then +-- required attributes. +-- +------------------------------------------------------------------------------ +-- Libraries +------------------------------------------------------------------------------ +library ieee ; + use ieee.std_logic_1164.all; + use ieee.numeric_std.all; + +------------------------------------------------------------------------------ +-- Entity Declaration +------------------------------------------------------------------------------ +entity psi_common_bit_cc is + generic ( + NumBits_g : positive := 1 + ); + port ( + -- Clock Domain A + BitsA : in std_logic_vector(NumBits_g-1 downto 0); + + -- Clock Domain B + ClkB : in std_logic; + BitsB : out std_logic_vector(NumBits_g-1 downto 0) + ); +end entity; + +------------------------------------------------------------------------------ +-- Architecture Declaration +------------------------------------------------------------------------------ +architecture rtl of psi_common_bit_cc is + + signal Reg0 : std_logic_vector(NumBits_g-1 downto 0) := (others => '0'); + signal Reg1 : std_logic_vector(NumBits_g-1 downto 0) := (others => '0'); + + attribute syn_srlstyle : string; + attribute syn_srlstyle of Reg0 : signal is "registers"; + attribute syn_srlstyle of Reg1 : signal is "registers"; + + attribute shreg_extract : string; + attribute shreg_extract of Reg0 : signal is "no"; + attribute shreg_extract of Reg1 : signal is "no"; + + attribute ASYNC_REG : string; + attribute ASYNC_REG of Reg0 : signal is "TRUE"; + attribute ASYNC_REG of Reg1 : signal is "TRUE"; + +begin + + -- Process + p : process(ClkB) + begin + if rising_edge(ClkB) then + Reg0 <= BitsA; + Reg1 <= Reg0; + end if; + end process; + BitsB <= Reg1; +end; + + + + + diff --git a/testsuite/gna/issue1051/psi_common_i2c_master.vhd b/testsuite/gna/issue1051/psi_common_i2c_master.vhd new file mode 100644 index 000000000..02e47d04b --- /dev/null +++ b/testsuite/gna/issue1051/psi_common_i2c_master.vhd @@ -0,0 +1,634 @@ +------------------------------------------------------------------------------ +-- Copyright (c) 2019 by Paul Scherrer Institute, Switzerland +-- All rights reserved. +-- Authors: Oliver Bruendler +------------------------------------------------------------------------------ + +------------------------------------------------------------------------------ +-- Description +------------------------------------------------------------------------------ +-- This entity implements a simple I2C-master (multi master capable) + +------------------------------------------------------------------------------ +-- Libraries +------------------------------------------------------------------------------ +library ieee; + use ieee.std_logic_1164.all; + use ieee.numeric_std.all; + use ieee.math_real.all; + +library work; + use work.psi_common_math_pkg.all; + use work.psi_common_logic_pkg.all; + +------------------------------------------------------------------------------ +-- Package for Interface Simplification +------------------------------------------------------------------------------ +package psi_common_i2c_master_pkg is + constant CMD_START : std_logic_vector(2 downto 0) := "000"; + constant CMD_STOP : std_logic_vector(2 downto 0) := "001"; + constant CMD_REPSTART : std_logic_vector(2 downto 0) := "010"; + constant CMD_SEND : std_logic_vector(2 downto 0) := "011"; + constant CMD_REC : std_logic_vector(2 downto 0) := "100"; +end package; + + +------------------------------------------------------------------------------ +-- Libraries +------------------------------------------------------------------------------ +library ieee; + use ieee.std_logic_1164.all; + use ieee.numeric_std.all; + use ieee.math_real.all; + +library work; + use work.psi_common_math_pkg.all; + use work.psi_common_logic_pkg.all; + use work.psi_common_i2c_master_pkg.all; + +------------------------------------------------------------------------------ +-- Entity Declaration +------------------------------------------------------------------------------ +-- $$ processes=stim,i2c $$ +-- $$ tbpkg=work.psi_tb_compare_pkg,work.psi_tb_activity_pkg,work.psi_tb_txt_util,work.psi_tb_i2c_pkg $$ +entity psi_common_i2c_master is + generic ( + ClockFrequency_g : real := 125.0e6; -- in Hz $$ constant=125.0e6 $$ + I2cFrequency_g : real := 100.0e3; -- in Hz $$ constant=1.0e6 $$ + BusBusyTimeout_g : real := 1.0e-3; -- in sec $$ constant=100.0e-6 $$ + CmdTimeout_g : real := 100.0e-6; -- in sec $$ constant=10.0e-6 $$ + InternalTriState_g : boolean := true; -- $$ constant=true $$ + DisableAsserts_g : boolean := false + ); + port ( + -- Control Signals + Clk : in std_logic; -- $$ type=clk; freq=125e6 $$ + Rst : in std_logic; -- $$ type=rst; clk=Clk $$ + + -- Command Interface + CmdRdy : out std_logic; + CmdVld : in std_logic; + CmdType : in std_logic_vector(2 downto 0); + CmdData : in std_logic_vector(7 downto 0); + CmdAck : in std_logic; + + -- Response Interface + RspVld : out std_logic; + RspType : out std_logic_vector(2 downto 0); + RspData : out std_logic_vector(7 downto 0); + RspAck : out std_logic; + RspArbLost : out std_logic; + RspSeq : out std_logic; + + -- Status Interface + BusBusy : out std_logic; + TimeoutCmd : out std_logic; + + -- I2c Interface with internal Tri-State (InternalTriState_g = true) + I2cScl : inout std_logic := 'Z'; + I2cSda : inout std_logic := 'Z'; + + -- I2c Interface with external Tri-State (InternalTriState_g = false) + I2cScl_I : in std_logic := '0'; + I2cScl_O : out std_logic; + I2cScl_T : out std_logic; + I2cSda_I : in std_logic := '0'; + I2cSda_O : out std_logic; + I2cSda_T : out std_logic + + ); +end entity; + +------------------------------------------------------------------------------ +-- Architecture Declaration +------------------------------------------------------------------------------ +architecture rtl of psi_common_i2c_master is + + -- *** Constants *** + constant BusyTimoutLimit_c : integer := integer(ClockFrequency_g*BusBusyTimeout_g)-1; + constant QuarterPeriodLimit_c : integer := integer(ceil(ClockFrequency_g/I2cFrequency_g/4.0))-1; + constant CmdTimeoutLimit_c : integer := integer(ClockFrequency_g*CmdTimeout_g)-1; + + -- *** Types *** + type Fsm_t is ( BusIdle_s, BusBusy_s, MinIdle_s, Start1_s, Start2_s, WaitCmd_s, WaitLowCenter_s, Stop1_s, Stop2_s, Stop3_s, RepStart1_s, + DataBit1_s, DataBit2_s, DataBit3_s, DataBit4_s, ArbitLost_s); + + -- *** Two Process Method *** + type two_process_r is record + BusBusy : std_logic; + CmdRdy : std_logic; + SclLast : std_logic; + SdaLast : std_logic; + BusBusyToCnt : unsigned(log2ceil(BusyTimoutLimit_c+1)-1 downto 0); + TimeoutCmdCnt : unsigned(log2ceil(CmdTimeoutLimit_c+1)-1 downto 0); + QuartPeriodCnt : unsigned(log2ceil(QuarterPeriodLimit_c+1)-1 downto 0); + QPeriodTick : std_logic; + CmdTypeLatch : std_logic_vector(CmdType'range); + CmdAckLatch : std_logic; + Fsm : Fsm_t; + SclOut : std_logic; + SdaOut : std_logic; + RspVld : std_logic; + RspAck : std_logic; + RspSeq : std_logic; + RspData : std_logic_vector(7 downto 0); + RspArbLost : std_logic; + BitCnt : unsigned(3 downto 0); -- 8 Data + 1 Ack = 9 = 4 bits + ShReg : std_logic_vector(8 downto 0); + CmdTimeout : std_logic; + TimeoutCmd : std_logic; + end record; + signal r, r_next : two_process_r; + attribute dont_touch : string; + attribute dont_touch of r : signal is "true"; -- Required to Fix Vivado 2018.2 Synthesis Bug! Is fixed in Vivado 2019.1 according to Xilinx. + + -- Tri-state buffer muxing + signal I2cScl_Input : std_logic; + signal I2cSda_Input : std_logic; + signal I2cScl_Sync : std_logic; + signal I2cSda_Sync : std_logic; + +begin + + -------------------------------------------------------------------------- + -- Combinatorial Proccess + -------------------------------------------------------------------------- + p_comb : process( Clk, r, I2cScl_Sync, I2cSda_Sync, + CmdVld, CmdType, CmdData, CmdAck) + variable v : two_process_r; + variable SclRe_v, SclFe_v, SdaRe_v, SdaFe_v : std_logic; + variable I2cStart_v, I2cStop_v : std_logic; + begin + -- *** hold variables stable *** + v := r; + + -- *** Edge Detection *** + SclRe_v := not r.SclLast and I2cScl_Sync; + SclFe_v := r.SclLast and not I2cScl_Sync; + SdaRe_v := not r.SdaLast and I2cSda_Sync; + SdaFe_v := r.SdaLast and not I2cSda_Sync; + v.SclLast := I2cScl_Sync; + v.SdaLast := I2cSda_Sync; + + -- *** Start/Stop Detection *** + I2cStart_v := r.SclLast and I2cScl_Sync and SdaFe_v; + I2cStop_v := r.SclLast and I2cScl_Sync and SdaRe_v; + + -- *** Quarter Period Counter *** + -- The FSM may overwrite the counter in some cases! + v.QPeriodTick := '0'; + if (r.Fsm = BusIdle_s) or (r.Fsm = BusBusy_s) then + v.QuartPeriodCnt := (others => '0'); + elsif r.QuartPeriodCnt = QuarterPeriodLimit_c then + v.QuartPeriodCnt := (others => '0'); + v.QPeriodTick := '1'; + else + v.QuartPeriodCnt := r.QuartPeriodCnt + 1; + end if; + + -- *** Command Timeout Detection *** + if r.Fsm = WaitCmd_s then + -- Timeout + if r.TimeoutCmdCnt = CmdTimeoutLimit_c then + v.CmdTimeout := '1'; + -- Count + else + v.TimeoutCmdCnt := r.TimeoutCmdCnt + 1; + end if; + -- In all states except waiting for command, reset the timer + else + v.TimeoutCmdCnt := (others => '0'); + end if; + + -- *** Latch Command *** + if (r.CmdRdy = '1') and (CmdVld = '1') then + v.CmdTypeLatch := CmdType; + v.CmdAckLatch := CmdAck; + end if; + + -- *** Default Values *** + v.RspVld := '0'; + v.RspAck := not r.ShReg(0); + v.RspData := r.ShReg(8 downto 1); + v.RspSeq := '0'; + v.RspArbLost := '0'; + v.TimeoutCmd := '0'; + v.CmdRdy := '0'; + + -- *** FSM *** + case r.Fsm is + + -- ********************************************************************************************** + -- Bus Idle + -- ********************************************************************************************** + when BusIdle_s => + -- Default Outputs + v.CmdRdy := '1'; + v.BusBusyToCnt := (others => '0'); + v.SclOut := '1'; + v.SdaOut := '1'; + v.CmdTimeout := '0'; + + -- Detect Bus Busy by Start Command + if (r.CmdRdy = '1') and (CmdVld = '1') then + -- Everyting else than START commands is ignored and an error is printed in this case + assert (CmdType = CMD_START) or DisableAsserts_g + report "###ERROR###: psi_common_i2c_master: In idle state, only CMD_START commands are allowed!" + severity error; + if CmdType = CMD_START then + v.Fsm := Start1_s; + v.CmdRdy := '0'; + v.CmdTypeLatch := CmdType; + else + v.RspVld := '1'; + v.RspSeq := '1'; + end if; + -- Detect Busy from other master + elsif (I2cScl_Sync = '0') or (I2cStart_v = '1') then + v.Fsm := BusBusy_s; + v.CmdRdy := '0'; + end if; + + -- ********************************************************************************************** + -- Bus Busy by other master + -- ********************************************************************************************** + when BusBusy_s => + -- Bus released + if I2cStop_v = '1' then + v.Fsm := MinIdle_s; + end if; + -- Timeout Handling + if I2cScl_Sync = '0' then + v.BusBusyToCnt := (others => '0'); + elsif r.BusBusyToCnt = BusyTimoutLimit_c then + v.Fsm := BusIdle_s; + else + v.BusBusyToCnt := r.BusBusyToCnt + 1; + end if; + + v.SclOut := '1'; + v.SdaOut := '1'; + + -- Ensure that SDA stays low for at least half a clock period + when MinIdle_s => + if r.QPeriodTick = '1' then + v.Fsm := BusIdle_s; + end if; + + v.SclOut := '1'; + v.SdaOut := '1'; + + -- ********************************************************************************************** + -- Start Condition + -- ********************************************************************************************** + -- State BusBusy_s Start1_s Start2_s WaitCmd_s + -- __________________________________ + -- Scl ... |___________ ... + -- _______________________ + -- SDA ... |______________________ ... + -- ********************************************************************************************** + + when Start1_s => + if r.QPeriodTick = '1' then + v.Fsm := Start2_s; + end if; + + -- Handle Clock Stretching in case of a repeated start (slave keeps SCL low) + if I2cScl_Sync = '0' and r.CmdTypeLatch = CMD_REPSTART then + v.QuartPeriodCnt := (others => '0'); + end if; + + -- Handle Arbitration (other master transmits start condition first) + if I2cSda_Sync = '0' then + v.Fsm := ArbitLost_s; + end if; + + v.SclOut := '1'; + v.SdaOut := '1'; + + when Start2_s => + if r.QPeriodTick = '1' then + v.Fsm := WaitCmd_s; + v.RspVld := '1'; + end if; + v.SclOut := '1'; + v.SdaOut := '0'; + + -- ********************************************************************************************** + -- Wait for user command (in first half of SCL low phase) + -- ********************************************************************************************** + when WaitCmd_s => + -- Default Outputs + v.CmdRdy := '1'; + v.SclOut := '0'; + + -- All commands except START are allowed, START commands are ignored + if (r.CmdRdy = '1') and (CmdVld = '1') then + assert (CmdType = CMD_STOP) or (CmdType = CMD_REPSTART) or (CmdType = CMD_SEND) or (CmdType = CMD_REC) or DisableAsserts_g + report "###ERROR###: psi_common_i2c_master: In WaitCmd_s state, CMD_START commands are not allowed!" + severity error; + if (CmdType = CMD_STOP) or (CmdType = CMD_REPSTART) or (CmdType = CMD_SEND) or (CmdType = CMD_REC) then + v.Fsm := WaitLowCenter_s; + v.CmdRdy := '0'; + else + v.RspVld := '1'; + v.RspSeq := '1'; + end if; + -- Latch data (used for SEND) + v.ShReg := CmdData & '0'; + -- Command timeout - In this case send a STOP to free the bus + elsif r.CmdTimeout = '1' then + v.Fsm := WaitLowCenter_s; + v.CmdRdy := '0'; + v.TimeoutCmd := '1'; + end if; + + -- ********************************************************************************************** + -- Wait for center of SCL low phase (after user command arrived) + -- ********************************************************************************************** + when WaitLowCenter_s => + -- State Handling + v.SclOut := '0'; + v.BitCnt := (others => '0'); + + -- Switch to commands + if r.QPeriodTick = '1' then + -- In timeout case, send a STOP to free the bus + if r.CmdTimeout = '1' then + v.Fsm := Stop1_s; + -- Else, go to requested command + else + case r.CmdTypeLatch is + when CMD_STOP => v.Fsm := Stop1_s; + when CMD_REPSTART => v.Fsm := RepStart1_s; + when CMD_SEND => v.Fsm := DataBit1_s; + when CMD_REC => v.Fsm := DataBit1_s; + when others => null; + end case; + end if; + end if; + + -- ********************************************************************************************** + -- Start Condition + -- ********************************************************************************************** + -- State RepStart1_s Start1_s Start2_s WaitCmd_s + -- _____________________ + -- Scl ..._________________| |___________ ... + -- __________________________ + -- SDA ...XXX |_____________________ ... + -- ********************************************************************************************** + -- States after RepStart1_s are shared with normal start condition + + when RepStart1_s => + if r.QPeriodTick = '1' then + -- The rest of the sequence is same as for START + v.Fsm := Start1_s; + + -- Handle Arbitration other master prvents repeating start by transmitting 0 + if I2cSda_Sync = '0' then + v.Fsm := ArbitLost_s; + end if; + end if; + v.SclOut := '0'; + v.SdaOut := '1'; + + -- ********************************************************************************************** + -- Start Condition + -- ********************************************************************************************** + -- State DataBit1_s DataBit2_s DataBit3_s WaitCmd_s / DataBit4_s + -- _________________________ + -- Scl ...___________| |___________ ... + -- + -- SDA ...XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX + -- ********************************************************************************************** + -- The DataBit1_s is the second half of the SCL low period. So the + -- SDA Line is set at the beginning of DataBit1_s. After the SCL high period + -- of the last bit, the state is changed to WaitCmd_s. Otherwise the first half of the SCL low + -- period is executed (DataBit4_s) before the next bit starts (DataBit1_s) + + when DataBit1_s => + if r.QPeriodTick = '1' then + v.Fsm := DataBit2_s; + end if; + v.SclOut := '0'; + + -- Send Operation + if r.CmdTypeLatch = CMD_SEND then + -- For Ack, receive data + if r.BitCnt = 8 then + v.SdaOut := '1'; + -- .. else send data + else + v.SdaOut := r.ShReg(8); + end if; + -- Receive Operatiom + else + -- Ack Handling + if r.BitCnt = 8 then + if r.CmdAckLatch = '1' then + v.SdaOut := '0'; + else + v.SdaOut := '1'; + end if; + -- .. else tri-state for receiving + else + v.SdaOut := '1'; + end if; + end if; + + when DataBit2_s => + if r.QPeriodTick = '1' then + v.Fsm := DataBit3_s; + -- Shift register in the middle of the CLK pulse + v.ShReg := r.ShReg(7 downto 0) & I2cSda_Sync; + end if; + v.SclOut := '1'; + + -- Handle Clock Stretching (slave keeps SCL low) + if I2cScl_Sync = '0' then + v.QuartPeriodCnt := (others => '0'); + end if; + + -- Handle Arbitration for Sending (only databits, not ack) + if (r.CmdTypeLatch = CMD_SEND) and (r.BitCnt /= 8) then + if I2cSda_Sync /= r.SdaOut then + v.Fsm := ArbitLost_s; + end if; + -- Receiving does not need arbitration since slave addresses are unique + end if; + + + when DataBit3_s => + if r.QPeriodTick = '1' then + -- Command Done after 9 bits (8 Data + 1 Ack) + if r.BitCnt = 8 then + v.Fsm := WaitCmd_s; + v.RspVld := '1'; + -- Else goto next bit + else + v.Fsm := DataBit4_s; + end if; + end if; + v.SclOut := '1'; + + -- Handle Arbitration for Sending (only databits, not ack) + if (r.CmdTypeLatch = CMD_SEND) and (r.BitCnt /= 8) then + if I2cSda_Sync /= r.SdaOut then + v.Fsm := ArbitLost_s; + end if; + -- Receiving does not need arbitration since slave addresses are unique + end if; + + when DataBit4_s => + if r.QPeriodTick = '1' then + v.Fsm := DataBit1_s; + v.BitCnt:= r.BitCnt + 1; + end if; + v.SclOut := '0'; + + -- ********************************************************************************************** + -- Stop Condition + -- ********************************************************************************************** + -- State WaitCmd_s Stop1_s Stop2_s Stop3_s BusIdle_s + -- _____________________ + -- Scl ..._____________________| |__________ ... + -- _____________________ + -- SDA ...XXXXXXXXXXXX____________________| ... + -- ********************************************************************************************** + + when Stop1_s => + if r.QPeriodTick = '1' then + v.Fsm := Stop2_s; + end if; + v.SclOut := '0'; + v.SdaOut := '0'; + + when Stop2_s => + if r.QPeriodTick = '1' then + v.Fsm := Stop3_s; + end if; + v.SclOut := '1'; + v.SdaOut := '0'; + + -- Handle Clock Stretching (slave keeps SCL low) + if I2cScl_Sync = '0' then + v.QuartPeriodCnt := (others => '0'); + end if; + + when Stop3_s => + if r.QPeriodTick = '1' then + -- Handle Arbitration + if I2cSda_Sync = '0' then + v.Fsm := ArbitLost_s; + -- Else the STOP was successful + else + v.Fsm := BusIdle_s; + v.RspVld := '1'; + end if; + end if; + v.SclOut := '1'; + v.SdaOut := '1'; + + -- ********************************************************************************************** + -- Send Response in case the arbitration was lost + -- ********************************************************************************************** + + when ArbitLost_s => + v.Fsm := BusBusy_s; + v.RspVld := '1'; + v.RspAck := '0'; + v.RspArbLost := '1'; + v.SclOut := '1'; + v.SdaOut := '1'; + + when others => null; + end case; + + -- TODO: FSM Stuck detection timeout! + + -- *** Bus Busy *** + if r.Fsm = BusIdle_s then + v.BusBusy := '0'; + else + v.BusBusy := '1'; + end if; + + -- *** assign signal *** + r_next <= v; + end process; + + -------------------------------------------------------------------------- + -- Outputs + -------------------------------------------------------------------------- + BusBusy <= r.BusBusy; + CmdRdy <= r.CmdRdy; + RspVld <= r.RspVld; + RspType <= r.CmdTypeLatch; + RspArbLost <= r.RspArbLost; + RspAck <= r.RspAck; + RspData <= r.RspData; + RspSeq <= r.RspSeq; + TimeoutCmd <= r.TimeoutCmd; + g_intTristate : if InternalTriState_g generate + I2cScl <= 'Z' when r.SclOut = '1' else '0'; + I2cSda <= 'Z' when r.SdaOut = '1' else '0'; + I2cScl_O <= '0'; + I2cSda_O <= '0'; + I2cScl_T <= '1'; + I2cSda_T <= '1'; + end generate; + g_extTristatte : if not InternalTriState_g generate + I2cScl_O <= r.SclOut; + I2cSda_O <= r.SdaOut; + I2cScl_T <= r.SclOut; + I2cSda_T <= r.SdaOut; + I2cScl <= 'Z'; + I2cSda <= 'Z'; + end generate; + + -------------------------------------------------------------------------- + -- Sequential Proccess + -------------------------------------------------------------------------- + p_seq : process(Clk) + begin + if rising_edge(Clk) then + r <= r_next; + if Rst = '1' then + r.BusBusy <= '0'; + r.CmdRdy <= '0'; + r.SclLast <= '1'; + r.SdaLast <= '1'; + r.BusBusyToCnt <= (others => '0'); + r.Fsm <= BusIdle_s; + r.SclOut <= '1'; + r.SdaOut <= '1'; + r.RspVld <= '0'; + end if; + end if; + end process; + + -------------------------------------------------------------------------- + -- Component Instantiations + -------------------------------------------------------------------------- + I2cScl_Input <= To01X(I2cScl) when InternalTriState_g else I2cScl_I; + I2cSda_Input <= To01X(I2cSda) when InternalTriState_g else I2cSda_I; + + i_sync : entity work.psi_common_bit_cc + generic map ( + NumBits_g => 2 + ) + port map ( + BitsA(0) => I2cScl_Input, + BitsA(1) => I2cSda_Input, + ClkB => Clk, + BitsB(0) => I2cScl_Sync, + BitsB(1) => I2cSda_Sync + ); + + +end; + + + + + diff --git a/testsuite/gna/issue1051/psi_common_i2c_master_tb.vhd b/testsuite/gna/issue1051/psi_common_i2c_master_tb.vhd new file mode 100644 index 000000000..0781d1d97 --- /dev/null +++ b/testsuite/gna/issue1051/psi_common_i2c_master_tb.vhd @@ -0,0 +1,679 @@ +------------------------------------------------------------ +-- Copyright (c) 2019 by Paul Scherrer Institute, Switzerland +-- All rights reserved. +------------------------------------------------------------ + +------------------------------------------------------------ +-- Testbench generated by TbGen.py +------------------------------------------------------------ +-- see Library/Python/TbGenerator + +------------------------------------------------------------ +-- Libraries +------------------------------------------------------------ +library ieee; + use ieee.std_logic_1164.all; + use ieee.numeric_std.all; + use ieee.math_real.all; + +library work; + use work.psi_common_math_pkg.all; + use work.psi_common_logic_pkg.all; + use work.psi_common_i2c_master_pkg.all; + +library work; + use work.psi_tb_compare_pkg.all; + use work.psi_tb_activity_pkg.all; + use work.psi_tb_txt_util.all; + use work.psi_tb_i2c_pkg.all; + +------------------------------------------------------------ +-- Entity Declaration +------------------------------------------------------------ +entity psi_common_i2c_master_tb is + generic ( + InternalTriState_g : boolean := true + ); +end entity; + +------------------------------------------------------------ +-- Architecture +------------------------------------------------------------ +architecture sim of psi_common_i2c_master_tb is + -- *** Fixed Generics *** + constant ClockFrequency_g : real := 125.0e6; + constant I2cFrequency_g : real := 1.0e6; + constant BusBusyTimeout_g : real := 50.0e-6; + constant CmdTimeout_g : real := 10.0e-6; + + -- *** Not Assigned Generics (default values) *** + + -- *** TB Control *** + signal TbRunning : boolean := True; + signal NextCase : integer := -1; + signal ProcessDone : std_logic_vector(0 to 1) := (others => '0'); + constant AllProcessesDone_c : std_logic_vector(0 to 1) := (others => '1'); + constant TbProcNr_stim_c : integer := 0; + constant TbProcNr_i2c_c : integer := 1; + signal StimCase : integer := -1; + signal I2cCase : integer := -1; + + -- *** DUT Signals *** + signal Clk : std_logic := '1'; + signal Rst : std_logic := '1'; + signal CmdRdy : std_logic := '0'; + signal CmdVld : std_logic := '0'; + signal CmdType : std_logic_vector(2 downto 0) := (others => '0'); + signal CmdData : std_logic_vector(7 downto 0) := (others => '0'); + signal CmdAck : std_logic := '0'; + signal RspVld : std_logic := '0'; + signal RspData : std_logic_vector(7 downto 0) := (others => '0'); + signal RspType : std_logic_vector(2 downto 0) := (others => '0'); + signal RspArbLost : std_logic := '0'; + signal RspAck : std_logic := '0'; + signal RspSeq : std_logic := '0'; + signal BusBusy : std_logic := '0'; + signal TimeoutCmd : std_logic := '0'; + signal I2cScl : std_logic := '0'; + signal I2cSda : std_logic := '0'; + signal I2cScl_I : std_logic := '0'; + signal I2cScl_O : std_logic := '0'; + signal I2cScl_T : std_logic := '0'; + signal I2cSda_I : std_logic := '0'; + signal I2cSda_O : std_logic := '0'; + signal I2cSda_T : std_logic := '0'; + + -- *** Helper Functions *** + procedure WaitForCase( signal TestCase : in integer; + Value : in integer) is + begin + if TestCase /= Value then + wait until TestCase = Value; + end if; + end procedure; + + procedure ApplyCmd( Command : in std_logic_vector(2 downto 0); + Data : in std_logic_vector(7 downto 0); + Ack : in std_logic; + signal CmdVld : out std_logic; + signal CmdRdy : in std_logic; + signal CmdType : out std_logic_vector(2 downto 0); + signal CmdData : out std_logic_vector(7 downto 0); + signal CmdAck : out std_logic) is + begin + wait until rising_edge(Clk); + CmdVld <= '1'; + CmdType <= Command; + CmdData <= Data; + CmdAck <= Ack; + wait until rising_edge(Clk) and CmdRdy = '1'; + CmdVld <= '0'; + CmdType <= (others => '0'); + CmdData <= (others => '0'); + CmdAck <= '0'; + end procedure; + + procedure CheckRsp( Command : in std_logic_vector(2 downto 0); + Data : in std_logic_vector; + Ack : in std_logic; + ArbLost : in std_logic; + signal RspVld : in std_logic; + signal RspData : in std_logic_vector(7 downto 0); + signal RspType : in std_logic_vector(2 downto 0); + signal RspArbLost : in std_logic; + signal RspAck : in std_logic; + signal RspSeq : in std_logic; + Msg : in string := "No Msg"; + Err : in std_logic := '0') is + begin + wait until rising_edge(Clk) and RspVld = '1'; + StdlvCompareStdlv(Command, RspType, "Response: Wrong Type - " & Msg); + if Data /= "X" then + StdlvCompareStdlv(Data, RspData, "Response: Wrong Data - " & Msg); + end if; + if Ack /= 'X' then + StdlCompare(choose(Ack = '1', 1, 0), RspAck, "Response: Wrong Ack - " & Msg); + end if; + if ArbLost /= 'X' then + StdlCompare(choose(ArbLost = '1', 1, 0), RspArbLost, "Response: Wrong ArbLost - " & Msg); + end if; + StdlCompare(choose(Err = '1', 1, 0), RspSeq, "Response: Wrong Err - " & Msg); + end procedure; + +begin + ------------------------------------------------------------ + -- DUT Instantiation + ------------------------------------------------------------ + i_dut : entity work.psi_common_i2c_master + generic map ( + ClockFrequency_g => ClockFrequency_g, + I2cFrequency_g => I2cFrequency_g, + BusBusyTimeout_g => BusBusyTimeout_g, + CmdTimeout_g => CmdTimeout_g, + InternalTriState_g => InternalTriState_g, + DisableAsserts_g => true + ) + port map ( + Clk => Clk, + Rst => Rst, + CmdRdy => CmdRdy, + CmdVld => CmdVld, + CmdType => CmdType, + CmdData => CmdData, + CmdAck => CmdAck, + RspVld => RspVld, + RspType => RspType, + RspArbLost => RspArbLost, + RspData => RspData, + RspAck => RspAck, + RspSeq => RspSeq, + BusBusy => BusBusy, + TimeoutCmd => TimeoutCmd, + I2cScl => I2cScl, + I2cSda => I2cSda, + I2cScl_I => I2cScl_I, + I2cScl_O => I2cScl_O, + I2cScl_T => I2cScl_T, + I2cSda_I => I2cSda_I, + I2cSda_O => I2cSda_O, + I2cSda_T => I2cSda_T + ); + + ------------------------------------------------------------ + -- I2C Emulation + ------------------------------------------------------------ + I2cPullup(I2cScl, I2cSda); + g_triState : if not InternalTriState_g generate + I2cScl <= 'Z' when I2cScl_T = '1' else I2cScl_O; + I2cScl_I <= To01X(I2cScl); + I2cSda <= 'Z' when I2cSda_T = '1' else I2cSda_O; + I2cSda_I <= To01X(I2cSda); + end generate; + + ------------------------------------------------------------ + -- Testbench Control !DO NOT EDIT! + ------------------------------------------------------------ + p_tb_control : process + begin + wait until Rst = '0'; + wait until ProcessDone = AllProcessesDone_c; + TbRunning <= false; + wait; + end process; + + ------------------------------------------------------------ + -- Clocks !DO NOT EDIT! + ------------------------------------------------------------ + p_clock_Clk : process + constant Frequency_c : real := real(125e6); + begin + while TbRunning loop + wait for 0.5*(1 sec)/Frequency_c; + Clk <= not Clk; + end loop; + wait; + end process; + + + ------------------------------------------------------------ + -- Resets + ------------------------------------------------------------ + p_rst_Rst : process + begin + wait for 1 us; + -- Wait for two clk edges to ensure reset is active for at least one edge + wait until rising_edge(Clk); + wait until rising_edge(Clk); + Rst <= '0'; + wait; + end process; + + + ------------------------------------------------------------ + -- Processes + ------------------------------------------------------------ + -- *** stim *** + p_stim : process + begin + I2cSetFrequency(I2cFrequency_g); + -- start of process !DO NOT EDIT + wait until Rst = '0'; + wait until rising_edge(Clk); + + -- *** Test Bus Busy *** + print(">> Test Bus Busy"); + StimCase <= 0; + wait until rising_edge(Clk); + WaitForCase(I2cCase, 0); + wait for 10 us; + + -- *** Test Start / Repeated-Start / Stop *** + print(">> Test Start / Repeated-Start / Stop"); + StimCase <= 1; + wait until rising_edge(Clk); + ApplyCmd(CMD_START, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_START, "X", 'X', '0', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start"); + ApplyCmd(CMD_REPSTART, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_REPSTART, "X", 'X', '0', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start"); + ApplyCmd(CMD_STOP, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_STOP, "X", 'X', '0', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Stop"); + WaitForCase(I2cCase, 1); + wait for 10 us; + + -- *** Test Write *** + print(">> Test Write"); + StimCase <= 2; + wait until rising_edge(Clk); + + -- 1Byte ACK + ApplyCmd(CMD_START, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_START, "X", 'X', 'X', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start 1b ACK"); + ApplyCmd(CMD_SEND, X"A3", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_SEND, "X", '1', '0', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start 1b ACK"); + ApplyCmd(CMD_STOP, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_STOP, "X", 'X', 'X', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Stop 1b ACK"); + + -- 2Byte ACK, then NACK + ApplyCmd(CMD_START, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_START, "X", 'X', 'X', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start 2b ACK -> NACK"); + ApplyCmd(CMD_SEND, X"12", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_SEND, "X", '1', '0', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start 2b ACK -> NACK 1"); + ApplyCmd(CMD_SEND, X"34", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_SEND, "X", '0', '0', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start 2b ACK -> NACK 2"); + ApplyCmd(CMD_STOP, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_STOP, "X", 'X', 'X', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Stop 2b ACK -> NACK"); + + WaitForCase(I2cCase, 2); + wait for 10 us; + + -- *** Test Read *** + print(">> Test Read"); + StimCase <= 3; + wait until rising_edge(Clk); + + -- 1Byte ACK + ApplyCmd(CMD_START, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_START, "X", 'X', 'X', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start 1b ACK"); + ApplyCmd(CMD_REC, X"00", '1', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_REC, X"67", '1', '0', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start 1b ACK"); + ApplyCmd(CMD_STOP, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_STOP, "X", 'X', 'X', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Stop 1b ACK"); + + -- 2Byte ACK, then NACK + ApplyCmd(CMD_START, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_START, "X", 'X', 'X', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start 2b ACK -> NACK"); + ApplyCmd(CMD_REC, X"00", '1', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_REC, X"34", 'X', '0', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start 2b ACK -> NACK 1"); + ApplyCmd(CMD_REC, X"00", '0', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_REC, X"56", 'X', '0', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start 2b ACK -> NACK 2"); + ApplyCmd(CMD_STOP, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_STOP, "X", 'X', 'X', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Stop 2b ACK -> NACK"); + + WaitForCase(I2cCase, 3); + wait for 10 us; + + -- *** Test Clock Stretching *** + print(">> Test Clock Stretching"); + StimCase <= 4; + wait until rising_edge(Clk); + + -- 1Byte Read ACK + ApplyCmd(CMD_START, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_START, "X", 'X', 'X', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start Read 1b ACK"); + ApplyCmd(CMD_REC, X"00", '1', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_REC, X"67", '1', '0', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start Read 1b ACK"); + ApplyCmd(CMD_STOP, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_STOP, "X", 'X', 'X', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Stop Read 1b ACK"); + + -- 2Byte ACK, then NACK + ApplyCmd(CMD_START, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_START, "X", 'X', 'X', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start 2b Write ACK -> NACK"); + ApplyCmd(CMD_SEND, X"12", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_SEND, "X", '1', '0', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start 2b Write ACK -> NACK 1"); + ApplyCmd(CMD_SEND, X"34", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_SEND, "X", '0', '0', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start 2b Write ACK -> NACK 2"); + ApplyCmd(CMD_STOP, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_STOP, "X", 'X', 'X', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Stop 2b Write ACK -> NACK"); + + -- Write / Read + ApplyCmd(CMD_START, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_START, "X", 'X', 'X', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start 2b W->R"); + ApplyCmd(CMD_SEND, X"12", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_SEND, "X", '1', '0', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Write 2b W->R"); + ApplyCmd(CMD_REPSTART, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_REPSTART, "X", 'X', '0', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "RepStart 2b W->R"); + ApplyCmd(CMD_REC, X"00", '1', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_REC, X"67", '1', '0', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Read 2b W->R"); + ApplyCmd(CMD_STOP, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_STOP, "X", 'X', 'X', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Stop 2b W->R"); + + WaitForCase(I2cCase, 4); + wait for 10 us; + + -- *** Test Delayed Command *** (clock is held low until command available) + print(">> Test Delayed Command"); + StimCase <= 5; + wait until rising_edge(Clk); + + -- 1Byte Read ACK, delay shorter than timeout + ApplyCmd(CMD_START, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_START, "X", 'X', 'X', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start Read 1b ACK"); + wait for CmdTimeout_g/2.0*(1 sec); + wait until rising_edge(Clk); + ApplyCmd(CMD_REC, X"00", '1', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_REC, X"67", '1', '0', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start Read 1b ACK"); + wait for CmdTimeout_g/2.0*(1 sec); + wait until rising_edge(Clk); + ApplyCmd(CMD_STOP, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_STOP, "X", 'X', 'X', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Stop Read 1b ACK"); + + -- Command Timeout (Timeout after start, other commands ignored) + ApplyCmd(CMD_START, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_START, "X", 'X', 'X', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start Read 1b ACK"); + wait for CmdTimeout_g*2.0*(1 sec); + wait until rising_edge(Clk); + ApplyCmd(CMD_REC, X"00", '1', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_REC, "X", '1', '0', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start Read 1b ACK", Err => '1'); + wait for CmdTimeout_g*2.0*(1 sec); + wait until rising_edge(Clk); + ApplyCmd(CMD_STOP, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_STOP, "X", 'X', 'X', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Stop Read 1b ACK", Err => '1'); + + WaitForCase(I2cCase, 5); + wait for 10 us; + + -- *** Test Arbitration *** + print(">> Test Arbitration"); + StimCase <= 6; + wait until rising_edge(Clk); + + -- Multi Master, Same Write + ApplyCmd(CMD_START, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_START, "X", 'X', 'X', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start 1b ACK"); + ApplyCmd(CMD_SEND, X"A3", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_SEND, "X", '1', '0', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start 1b ACK"); + ApplyCmd(CMD_STOP, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_STOP, "X", 'X', 'X', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Stop 1b ACK"); + + -- Arbitration Lost during Write + wait for 10 us; + ApplyCmd(CMD_START, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_START, "X", 'X', 'X', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start Lost Write"); + ApplyCmd(CMD_SEND, X"A3", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_SEND, "X", '0', '1', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start Lost Write"); + ApplyCmd(CMD_STOP, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_STOP, "X", 'X', 'X', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Lost Write", Err => '1'); + + -- Arbitration Lost during STOP (other master continues writing) + wait for 10 us; + ApplyCmd(CMD_START, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_START, "X", 'X', 'X', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start Lost Stop"); + ApplyCmd(CMD_SEND, X"A3", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_SEND, "X", '1', '0', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start Lost Stop"); + ApplyCmd(CMD_STOP, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_STOP, "X", 'X', '1', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Stop Lost Stop"); + + -- Arbitration Lost during repeated start (other master continues writing) + wait for 20 us; + ApplyCmd(CMD_START, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_START, "X", 'X', 'X', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start Lost RepStartA"); + ApplyCmd(CMD_SEND, X"A3", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_SEND, "X", '1', '0', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start Lost RepStartA"); + ApplyCmd(CMD_REPSTART, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_REPSTART, "X", 'X', '1', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Repstart Lost RepStartA"); + + -- Arbitration Lost during repeated start (other master stops) + wait for 20 us; + ApplyCmd(CMD_START, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_START, "X", 'X', 'X', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start Lost RepStartB"); + ApplyCmd(CMD_SEND, X"A3", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_SEND, "X", '1', '0', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start Lost RepStartB"); + ApplyCmd(CMD_REPSTART, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_REPSTART, "X", 'X', '1', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Repstart Lost RepStartB"); + + -- Arbitration lost due to stop (during first bit of data) + wait for 10 us; + ApplyCmd(CMD_START, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_START, "X", 'X', 'X', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start Lost DueStop"); + ApplyCmd(CMD_SEND, X"A3", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_SEND, "X", '1', '0', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start Lost DueStop 1"); + ApplyCmd(CMD_SEND, X"F0", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_SEND, "X", '0', '1', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start Lost DueStop 2"); + + -- Arbitration lost due to rep-start (during first bit of data) + wait for 10 us; + ApplyCmd(CMD_START, X"00", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_START, "X", 'X', 'X', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start Lost DueRepStart"); + ApplyCmd(CMD_SEND, X"A3", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_SEND, "X", '1', '0', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start Lost DueRepStart 1"); + ApplyCmd(CMD_SEND, X"F0", 'X', CmdVld, CmdRdy, CmdType, CmdData, CmdAck); + CheckRsp(CMD_SEND, "X", '0', '1', RspVld, RspData, RspType, RspArbLost, RspAck, RspSeq, "Start Lost DueRepStart 2"); + + WaitForCase(I2cCase, 6); + wait for 10 us; + + + -- end of process !DO NOT EDIT! + wait for 1 us; + ProcessDone(TbProcNr_stim_c) <= '1'; + wait; + end process; + + -- *** i2c slave *** + p_i2c_slave : process + begin + I2cBusFree(I2cScl, I2cSda); + + -- start of process !DO NOT EDIT + wait until Rst = '0'; + wait until rising_edge(Clk); + + -- *** Test Bus Busy *** + WaitForCase(StimCase, 0); + -- Not busy + wait for 1 us; + StdlCompare(0, BusBusy, "Busy 0"); + -- A transfer is goiong on + I2cScl <= '0'; + wait for 1 us; + StdlCompare(1, BusBusy, "Busy 1"); + -- busy is kept + I2cScl <= 'Z'; + wait for 10 us; + StdlCompare(1, BusBusy, "Busy 2"); + -- released after timeout + wait for BusBusyTimeout_g*(1 sec); + StdlCompare(0, BusBusy, "Busy 3"); + -- Asserted on start + I2cMasterSendStart(I2cScl, I2cSda, "Assert Start"); + wait for 1 us; + StdlCompare(1, BusBusy, "Busy 4"); + -- Released on stop + I2cMasterSendStop(I2cScl, I2cSda, "Assert Start"); + wait for 1 us; + StdlCompare(0, BusBusy, "Busy 4"); + I2cCase <= 0; + + -- *** Test Start / Stop *** + WaitForCase(StimCase, 1); + I2cSlaveWaitStart(I2cScl, I2cSda, "Start"); + I2cSlaveWaitRepeatedStart(I2cScl, I2cSda, "RepStart"); + I2cSlaveWaitStop(I2cScl, I2cSda, "Stop"); + I2cCase <= 1; + + -- *** Test Write *** + WaitForCase(StimCase, 2); + + -- 1 Byte Ack + I2cSlaveWaitStart(I2cScl, I2cSda, "Start 1b Ack"); + I2cSlaveExpectByte(16#A3#, I2cScl, I2cSda, "Data 1b Ack", '0'); + I2cSlaveWaitStop(I2cScl, I2cSda, "Stop"); + + -- 2 Byte Ack, then NACK + I2cSlaveWaitStart(I2cScl, I2cSda, "Start 2b ACK -> NACK"); + I2cSlaveExpectByte(16#12#, I2cScl, I2cSda, "Data 2b ACK -> NACK 1", '0'); + I2cSlaveExpectByte(16#34#, I2cScl, I2cSda, "Data 2b ACK -> NACK 2", '1'); + I2cSlaveWaitStop(I2cScl, I2cSda, "Stop"); + + I2cCase <= 2; + + -- *** Test Read *** + WaitForCase(StimCase, 3); + + -- 1 Byte Ack + I2cSlaveWaitStart(I2cScl, I2cSda, "Start 1b Ack"); + I2cSlaveSendByte(16#67#, I2cScl, I2cSda, "Data 1b Ack", '0'); + I2cSlaveWaitStop(I2cScl, I2cSda, "Stop"); + + -- 2 Byte Ack, then NACK + I2cSlaveWaitStart(I2cScl, I2cSda, "Start 2b ACK -> NACK"); + I2cSlaveSendByte(16#34#, I2cScl, I2cSda, "Data 2b ACK -> NACK 1", '0'); + I2cSlaveSendByte(16#56#, I2cScl, I2cSda, "Data 2b ACK -> NACK 2", '1'); + I2cSlaveWaitStop(I2cScl, I2cSda, "Stop"); + + I2cCase <= 3; + + -- *** Test Clock Stretching *** + WaitForCase(StimCase, 4); + + -- 1 Byte Read Ack + I2cSlaveWaitStart(I2cScl, I2cSda, "Start Read 1b Ack"); + I2cSlaveSendByte(16#67#, I2cScl, I2cSda, "Data Write 1b Ack", '0', ClkStretch => 1 us); + I2cSlaveWaitStop(I2cScl, I2cSda, "Stop", ClkStretch => 1 us); + + + -- 2 Byte Write Ack, then NACK + I2cSlaveWaitStart(I2cScl, I2cSda, "Start 2b Write ACK -> NACK"); + I2cSlaveExpectByte(16#12#, I2cScl, I2cSda, "Data 2b Write ACK -> NACK 1", '0', ClkStretch => 1 us); + I2cSlaveExpectByte(16#34#, I2cScl, I2cSda, "Data 2b Write ACK -> NACK 2", '1', ClkStretch => 1 us); + I2cSlaveWaitStop(I2cScl, I2cSda, "Stop", ClkStretch => 1 us); + + -- Write / Read + I2cSlaveWaitStart(I2cScl, I2cSda, "Start 2b W->R"); + I2cSlaveExpectByte(16#12#, I2cScl, I2cSda, "Write 2b W->R", '0', ClkStretch => 1 us); + I2cSlaveWaitRepeatedStart(I2cScl, I2cSda, "RepStart 2b W->R", ClkStretch => 1 us); + I2cSlaveSendByte(16#67#, I2cScl, I2cSda, "Read 2b W->R", '0', ClkStretch => 1 us); + I2cSlaveWaitStop(I2cScl, I2cSda, "Stop 2b W->R", ClkStretch => 1 us); + + I2cCase <= 4; + + -- *** Test Delayed Command *** + WaitForCase(StimCase, 5); + + -- 1 Byte Ack, delay shorter than timeout + I2cSlaveWaitStart(I2cScl, I2cSda, "Start 1b Ack"); + I2cSlaveSendByte(16#67#, I2cScl, I2cSda, "Data 1b Ack", '0'); + I2cSlaveWaitStop(I2cScl, I2cSda, "Stop"); + + -- Command Timeout (Timeout after start, stop generated internally) + I2cSlaveWaitStart(I2cScl, I2cSda, "Start 1b Ack"); + I2cSlaveWaitStop(I2cScl, I2cSda, "Stop"); + + I2cCase <= 5; + + -- *** Test Arbitration *** + WaitForCase(StimCase, 6); + + -- Multi Master, Same Write + I2cSlaveWaitStart(I2cScl, I2cSda, "S: Start 1b Ack"); + I2cSlaveExpectByte(16#A3#, I2cScl, I2cSda, "S: Data 1b Ack", '0'); + I2cSlaveWaitStop(I2cScl, I2cSda, "S: Stop"); + + -- Arbitration Lost during Write + I2cSlaveWaitStart(I2cScl, I2cSda, "S: Start Lost Write"); + I2cSlaveExpectByte(16#87#, I2cScl, I2cSda, "S: Stop Lost Write", '0'); + I2cSlaveWaitStop(I2cScl, I2cSda, "S: Lost Write Stop"); + + -- Arbitration Lost STOP (other master continues writing) + I2cSlaveWaitStart(I2cScl, I2cSda, "S: Start Lost Stop"); + I2cSlaveExpectByte(16#A3#, I2cScl, I2cSda, "S: Data Lost Stop 1", '0'); + I2cSlaveExpectByte(16#12#, I2cScl, I2cSda, "S: Data Lost Stop 2", '0'); + I2cSlaveWaitStop(I2cScl, I2cSda, "S: Stop Lost Stop"); + + -- Arbitration Lost during repeated start (other master continues writing) + I2cSlaveWaitStart(I2cScl, I2cSda, "S: Start Lost RepStartA"); + I2cSlaveExpectByte(16#A3#, I2cScl, I2cSda, "S: Data Lost RepStartA 1", '0'); + I2cSlaveExpectByte(16#12#, I2cScl, I2cSda, "S: Data Lost RepStartA 2", '0'); + I2cSlaveWaitStop(I2cScl, I2cSda, "S: Stop Lost RepStartA"); + + -- Arbitration Lost during repeated start (other master stops) + I2cSlaveWaitStart(I2cScl, I2cSda, "S: Start Lost RepStartB"); + I2cSlaveExpectByte(16#A3#, I2cScl, I2cSda, "S: Data Lost RepStartB 1", '0'); + I2cSlaveWaitStop(I2cScl, I2cSda, "S: Stop Lost RepStartB"); + + -- Arbitration lost due to stop (during first bit of data) + I2cSlaveWaitStart(I2cScl, I2cSda, "S: Start Lost DueStop"); + I2cSlaveExpectByte(16#A3#, I2cScl, I2cSda, "S: Data Lost DueStop 1", '0'); + I2cSlaveWaitStop(I2cScl, I2cSda, "S: Stop Lost DueStop"); + + -- Arbitration lost due to rep-start (during first bit of data) + I2cSlaveWaitStart(I2cScl, I2cSda, "S: Start Lost RepStart"); + I2cSlaveExpectByte(16#A3#, I2cScl, I2cSda, "S: Write Lost RepStart 1", '0'); + I2cSlaveWaitRepeatedStart(I2cScl, I2cSda, "S: Lost RepStart RepStart"); + I2cSlaveSendByte(16#34#, I2cScl, I2cSda, "S: Read Lost RepStart RepStart", '0'); + I2cSlaveWaitStop(I2cScl, I2cSda, "S: Stop Lost RepStart"); + + I2cCase <= 6; + + -- end of process !DO NOT EDIT! + ProcessDone(TbProcNr_i2c_c) <= '1'; + wait; + end process; + + -- *** i2c master *** + p_i2c_master : process + begin + I2cBusFree(I2cScl, I2cSda); + + -- *** Test Arbitration *** + WaitForCase(StimCase, 6); + + -- Multi Master, Same Write + I2cSlaveWaitStart(I2cScl, I2cSda, "M: Start 1b Ack"); + -- small delay + I2cScl <= '0'; + wait for 100 ns; + -- continue + I2cMasterSendByte(16#A3#, I2cScl, I2cSda, "M: Data 1b Ack"); + I2cMasterSendStop(I2cScl, I2cSda, "M: Stop"); + + -- Arbitration Lost during Write + I2cSlaveWaitStart(I2cScl, I2cSda, "M: Start Lost Write"); + I2cMasterSendByte(16#87#, I2cScl, I2cSda, "M: Data Lost Write"); + I2cMasterSendStop(I2cScl, I2cSda, "M: Stop Loast Read"); + + -- Arbitration Lost STOP (other master continues writing) + I2cSlaveWaitStart(I2cScl, I2cSda, "M: Start Lost Stop"); + I2cMasterSendByte(16#A3#, I2cScl, I2cSda, "M: Data Lost Stop 1"); + I2cMasterSendByte(16#12#, I2cScl, I2cSda, "M: Data Lost Stop 2"); + I2cMasterSendStop(I2cScl, I2cSda, "M: Stop Lost Stop"); + + -- Arbitration Lost during repeated start (other master continues writing) + I2cSlaveWaitStart(I2cScl, I2cSda, "M: Start Lost RepStartA"); + I2cMasterSendByte(16#A3#, I2cScl, I2cSda, "M: Data Lost RepStartA 1"); + I2cMasterSendByte(16#12#, I2cScl, I2cSda, "M: Data Lost RepStartA 2"); + I2cMasterSendStop(I2cScl, I2cSda, "M: Stop Lost RepStartA"); + + -- Arbitration Lost during repeated start (other master stops) + I2cSlaveWaitStart(I2cScl, I2cSda, "M: Start Lost RepStartB"); + I2cMasterSendByte(16#A3#, I2cScl, I2cSda, "M: Data Lost RepStartB 1"); + I2cMasterSendStop(I2cScl, I2cSda, "M: Stop Lost RepStartB"); + + -- Arbitration lost due to stop (during first bit of data) + I2cSlaveWaitStart(I2cScl, I2cSda, "M: Start Lost DueStop"); + I2cMasterSendByte(16#A3#, I2cScl, I2cSda, "M: Data Lost DueStop 1"); + I2cMasterSendStop(I2cScl, I2cSda, "M: Stop Lost DueStop"); + + -- Arbitration lost due to rep-start (during first bit of data) + I2cSlaveWaitStart(I2cScl, I2cSda, "M: Start Lost DueRepstart"); + I2cMasterSendByte(16#A3#, I2cScl, I2cSda, "M: write Lost DueRepstart 1"); + I2cMasterSendRepeatedStart(I2cScl, I2cSda, "M: Stop Lost DueRepstart"); + I2cMasterExpectByte(16#34#, I2cScl, I2cSda, "M: read Lost DueRepstart 1"); + I2cMasterSendStop(I2cScl, I2cSda, "M: Stop Lost DueRepstart"); + + wait; + + end process; + + +end; diff --git a/testsuite/gna/issue1051/psi_common_logic_pkg.vhd b/testsuite/gna/issue1051/psi_common_logic_pkg.vhd new file mode 100644 index 000000000..f2e15b4be --- /dev/null +++ b/testsuite/gna/issue1051/psi_common_logic_pkg.vhd @@ -0,0 +1,227 @@ +------------------------------------------------------------------------------ +-- Copyright (c) 2018 by Paul Scherrer Institute, Switzerland +-- All rights reserved. +-- Authors: Oliver Bruendler +------------------------------------------------------------------------------ + +------------------------------------------------------------------------------ +-- Libraries +------------------------------------------------------------------------------ +library ieee; + use ieee.std_logic_1164.all; + use ieee.numeric_std.all; + +library work; + use work.psi_common_math_pkg.all; + +------------------------------------------------------------------------------ +-- Package Header +------------------------------------------------------------------------------ +package psi_common_logic_pkg is + + function ZerosVector(size : in natural) return std_logic_vector; + + function OnesVector(size : in natural) return std_logic_vector; + + function ShiftLeft( arg : in std_logic_vector; + bits : in integer; + fill : in std_logic := '0') + return std_logic_vector; + + function ShiftRight(arg : in std_logic_vector; + bits : in integer; + fill : in std_logic := '0') + return std_logic_vector; + + function BinaryToGray( binary : in std_logic_vector) + return std_logic_vector; + + function GrayToBinary( gray : in std_logic_vector) + return std_logic_vector; + + -- Parallel Prefix Computation of the OR function + -- Input --> Output + -- 0100 --> 0111 + -- 0101 --> 0111 + -- 0011 --> 0011 + -- 0010 --> 0011 + function PpcOr( inp : in std_logic_vector) + return std_logic_vector; + + function IntToStdLogic( int : in integer) + return std_logic; + + function ReduceOr( vec : in std_logic_vector) + return std_logic; + + function ReduceAnd( vec : in std_logic_vector) + return std_logic; + + function To01X( inp : in std_logic) + return std_logic; + + function To01X( inp : in std_logic_vector) + return std_logic_vector; + +end psi_common_logic_pkg; + +------------------------------------------------------------------------------ +-- Package Body +------------------------------------------------------------------------------ +package body psi_common_logic_pkg is + + -- *** ZerosVector *** + function ZerosVector(size : in natural) return std_logic_vector is + constant c : std_logic_vector(size-1 downto 0) := (others => '0'); + begin + return c; + end function; + + -- *** OnesVector *** + function OnesVector(size : in natural) return std_logic_vector is + constant c : std_logic_vector(size-1 downto 0) := (others => '1'); + begin + return c; + end function; + + -- *** ShiftLeft *** + function ShiftLeft( arg : in std_logic_vector; + bits : in integer; + fill : in std_logic := '0') + return std_logic_vector is + constant argDt : std_logic_vector(arg'high downto 0) := arg; + variable v : std_logic_vector(argDt'range); + begin + if bits < 0 then + return ShiftRight(argDt, -bits, fill); + else + v(v'left downto bits) := argDt(argDt'left-bits downto 0); + v(bits-1 downto 0) := (others => fill); + return v; + end if; + end function; + + -- *** ShiftRight *** + function ShiftRight( arg : in std_logic_vector; + bits : in integer; + fill : in std_logic := '0') + return std_logic_vector is + constant argDt : std_logic_vector(arg'high downto 0) := arg; + variable v : std_logic_vector(argDt'range); + begin + if bits < 0 then + return ShiftLeft(argDt, -bits, fill); + else + v(v'left-bits downto 0) := argDt(argDt'left downto bits); + v(v'left downto v'left-bits+1) := (others => fill); + return v; + end if; + end function; + + -- *** BinaryToGray *** + function BinaryToGray( binary : in std_logic_vector) + return std_logic_vector is + variable Gray_v : std_logic_vector(binary'range); + begin + Gray_v := binary xor ('0' & binary(binary'left downto 1)); + return Gray_v; + end function; + + -- *** GrayToBinary *** + function GrayToBinary( gray : in std_logic_vector) + return std_logic_vector is + variable Binary_v : std_logic_vector(gray'range); + begin + Binary_v(Binary_v'left) := gray(gray'left); + for b in gray'left-1 downto 0 loop + Binary_v(b) := gray(b) xor Binary_v(b+1); + end loop; + return Binary_v; + end function; + + + -- *** PpcOr *** + function PpcOr( inp : in std_logic_vector) + return std_logic_vector is + constant Stages_c : integer := log2ceil(inp'length); + constant Pwr2Width_c : integer := 2**Stages_c; + type StageOut_t is array (natural range <>) of std_logic_vector(Pwr2Width_c-1 downto 0); + variable StageOut_v : StageOut_t(0 to Stages_c); + variable BinCnt_v : unsigned(Pwr2Width_c-1 downto 0); + begin + StageOut_v(0) := (others => '0'); + StageOut_v(0)(inp'length-1 downto 0) := inp; + for stage in 0 to Stages_c-1 loop + BinCnt_v := (others => '0'); + for idx in 0 to Pwr2Width_c-1 loop + if BinCnt_v(stage) = '0' then + StageOut_v(stage+1)(idx) := StageOut_v(stage)(idx) or StageOut_v(stage)((idx/(2**stage)+1)*2**stage); + else + StageOut_v(stage+1)(idx) := StageOut_v(stage)(idx); + end if; + BinCnt_v := BinCnt_v+1; + end loop; + end loop; + return StageOut_v(Stages_c)(inp'length-1 downto 0); + end function; + + function IntToStdLogic( int : in integer) + return std_logic is + begin + if int = 1 then + return '1'; + elsif int = 0 then + return '0'; + else + return 'X'; + end if; + end function; + + function ReduceOr( vec : in std_logic_vector) + return std_logic is + variable tmp : std_logic; + begin + tmp := '0'; + for i in 0 to vec'high loop + tmp := tmp or vec(i); + end loop; + return tmp; + end function; + + function ReduceAnd( vec : in std_logic_vector) + return std_logic is + variable tmp : std_logic; + begin + tmp := '1'; + for i in 0 to vec'high loop + tmp := tmp and vec(i); + end loop; + return tmp; + end function; + + function To01X( inp : in std_logic) + return std_logic is + begin + case inp is + when '0' | 'L' => return '0'; + when '1' | 'H' => return '1'; + when others => return 'X'; + end case; + end function; + + function To01X( inp : in std_logic_vector) + return std_logic_vector is + variable tmp : std_logic_vector(inp'range); + begin + for i in inp'low to inp'high loop + tmp(i) := to01X(inp(i)); + end loop; + return tmp; + end function; + +end psi_common_logic_pkg; + + + + + diff --git a/testsuite/gna/issue1051/psi_common_math_pkg.vhd b/testsuite/gna/issue1051/psi_common_math_pkg.vhd new file mode 100644 index 000000000..183cba97f --- /dev/null +++ b/testsuite/gna/issue1051/psi_common_math_pkg.vhd @@ -0,0 +1,259 @@ +------------------------------------------------------------------------------ +-- Copyright (c) 2018 by Paul Scherrer Institute, Switzerland +-- All rights reserved. +-- Authors: Oliver Bruendler +------------------------------------------------------------------------------ + +------------------------------------------------------------------------------ +-- Libraries +------------------------------------------------------------------------------ +library ieee ; + use ieee.std_logic_1164.all; + use ieee.numeric_std.all; + +library work; + use work.psi_common_array_pkg.all; + +------------------------------------------------------------------------------ +-- Package Header +------------------------------------------------------------------------------ +package psi_common_math_pkg is + + function log2(arg : in natural) return natural; + + function log2ceil(arg : in natural) return natural; + + function log2ceil(arg : in real) return natural; + + function isLog2(arg : in natural) return boolean; + + function max( a : in integer; + b : in integer) return integer; + + function min( a : in integer; + b : in integer) return integer; + + -- choose t if s=true else f + function choose( s : in boolean; + t : in std_logic; + f : in std_logic) return std_logic; + + function choose( s : in boolean; + t : in std_logic_vector; + f : in std_logic_vector) return std_logic_vector; + + function choose( s : in boolean; + t : in integer; + f : in integer) return integer; + + function choose( s : in boolean; + t : in string; + f : in string) return string; + + function choose( s : in boolean; + t : in real; + f : in real) return real; + + function choose( s : in boolean; + t : in unsigned; + f : in unsigned) return unsigned; + + -- count occurence of a value inside an array + function count( a : in t_ainteger; + v : in integer) return integer; + + function count( a : in t_abool; + v : in boolean) return integer; + + function count( a : in std_logic_vector; + v : in std_logic) return integer; + +end psi_common_math_pkg; + +------------------------------------------------------------------------------ +-- Package Body +------------------------------------------------------------------------------ +package body psi_common_math_pkg is + + -- *** Log2 integer *** + function log2(arg : in natural) return natural is + variable v : natural := arg; + variable r : natural := 0; + begin + while v > 1 loop + v := v/2; + r := r+1; + end loop; + return r; + end function; + + -- *** Log2Ceil integer *** + function log2ceil(arg : in natural) return natural is + begin + if arg = 0 then + return 0; + end if; + return log2(arg*2-1); + end function; + + -- *** Log2Ceil real *** + function log2ceil(arg : in real) return natural is + variable v : real := arg; + variable r : natural := 0; + begin + while v > 1.0 loop + v := v/2.0; + r := r+1; + end loop; + return r; + end function; + + -- *** isLog2 *** + function isLog2(arg : in natural) return boolean is + begin + if log2(arg) = log2ceil(arg) then + return true; + else + return false; + end if; + end function; + + -- *** Max *** + function max( a : in integer; + b : in integer) return integer is + begin + if a > b then + return a; + else + return b; + end if; + end function; + + -- *** Min *** + function min( a : in integer; + b : in integer) return integer is + begin + if a > b then + return b; + else + return a; + end if; + end function; + + -- *** Choose (std_logic) *** + function choose( s : in boolean; + t : in std_logic; + f : in std_logic) return std_logic is + begin + if s then + return t; + else + return f; + end if; + end function; + + -- *** Choose (std_logic_vector) *** + function choose( s : in boolean; + t : in std_logic_vector; + f : in std_logic_vector) return std_logic_vector is + begin + if s then + return t; + else + return f; + end if; + end function; + + -- *** Choose (integer) *** + function choose( s : in boolean; + t : in integer; + f : in integer) return integer is + begin + if s then + return t; + else + return f; + end if; + end function; + + -- *** Choose (string) *** + function choose( s : in boolean; + t : in string; + f : in string) return string is + begin + if s then + return t; + else + return f; + end if; + end function; + + -- *** Choose (real) *** + function choose( s : in boolean; + t : in real; + f : in real) return real is + begin + if s then + return t; + else + return f; + end if; + end function; + + -- *** Choose (unsigned) *** + function choose( s : in boolean; + t : in unsigned; + f : in unsigned) return unsigned is + begin + if s then + return t; + else + return f; + end if; + end function; + + -- *** count (integer) *** + function count( a : in t_ainteger; + v : in integer) return integer is + variable cnt_v : integer := 0; + begin + for idx in a'low to a'high loop + if a(idx) = v then + cnt_v := cnt_v+1; + end if; + end loop; + return cnt_v; + end function; + + -- *** count (bool) *** + function count( a : in t_abool; + v : in boolean) return integer is + variable cnt_v : integer := 0; + begin + for idx in a'low to a'high loop + if a(idx) = v then + cnt_v := cnt_v+1; + end if; + end loop; + return cnt_v; + end function; + + -- *** count (std_logic) *** + function count( a : in std_logic_vector; + v : in std_logic) return integer is + variable cnt_v : integer := 0; + begin + for idx in a'low to a'high loop + if a(idx) = v then + cnt_v := cnt_v+1; + end if; + end loop; + return cnt_v; + end function; + +end psi_common_math_pkg; + + + + + diff --git a/testsuite/gna/issue1051/psi_tb_activity_pkg.vhd b/testsuite/gna/issue1051/psi_tb_activity_pkg.vhd new file mode 100644 index 000000000..b99a40207 --- /dev/null +++ b/testsuite/gna/issue1051/psi_tb_activity_pkg.vhd @@ -0,0 +1,258 @@ +------------------------------------------------------------------------------ +-- Copyright (c) 2018 by Paul Scherrer Institute, Switzerland +-- All rights reserved. +-- Authors: Oliver Bruendler, Benoit Stef +------------------------------------------------------------------------------ + +------------------------------------------------------------------------------ +-- Libraries +------------------------------------------------------------------------------ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use ieee.math_real.all; + +library work; +use work.psi_tb_txt_util.all; +use work.psi_tb_compare_pkg.all; + +------------------------------------------------------------------------------ +-- Package Header +------------------------------------------------------------------------------ +package psi_tb_activity_pkg is + + -- Wait for a given time and check if the signal is idle + procedure CheckNoActivity(signal Sig : in std_logic; + IdleTime : in time; + Level : in integer range -1 to 1; -- -1 = don't check, 0 = low, 1 = high + Msg : in string := ""; + Prefix : in string := "###ERROR###: "); + + procedure CheckNoActivityStlv(signal Sig : in std_logic_vector; + IdleTime : in time; + Level : in integer range -1 to integer'high; -- -1 = don't check, otherwise interpreted unsigned + Msg : in string := ""; + Prefix : in string := "###ERROR###: "); + + -- Check when a signal had its last activity (without waiting) + procedure CheckLastActivity(signal Sig : in std_logic; + IdleTime : in time; + Level : in integer range -1 to 1; -- -1 = don't check, 0 = low, 1 = high + Msg : in string := ""; + Prefix : in string := "###ERROR###: "); + + procedure CheckLastActivityStlv(signal Sig : in std_logic_vector; + IdleTime : in time; + Level : in integer range -1 to integer'high; -- -1 = don't check, otherwise interpreted unsigned + Msg : in string := ""; + Prefix : in string := "###ERROR###: "); + + -- pulse a signal + procedure PulseSig(signal Sig : out std_logic; + signal Clk : in std_logic); + + -- Clocked wait for a signal + procedure ClockedWaitFor(Val : in std_logic; + signal Sig : in std_logic; + signal Clk : in std_logic); + + -- Wait for a number of clock cycles + procedure WaitClockCycles( Cycles : in integer; + signal Clk : in std_logic); + + -- Wait for a time and quit on rising edge + procedure ClockedWaitTime( Duration : in time; + signal Clk : in std_logic); + + -- Strobe generator + procedure GenerateStrobe( freq_clock : in real := 100.0E6; -- in Hz + freq_str : in real := 1.0E6; -- in Hz + rst_pol_g : in std_logic := '1'; -- reset polarity + signal rst : in std_logic; -- rst + signal clk : in std_logic; -- clk + signal str : out std_logic); -- str + + -- check if stdlv is arrived within a defined period of time + procedure WaitForValueStdlv(signal Sig : in std_logic_vector; -- Signal to check + ExpVal : in std_logic_vector; -- expected value + Timeout : in time; -- time to wait for + Msg : in string; -- msg to display + Prefix : in string := "###ERROR###: "); -- bool out to stop Tb for ex. + + -- check if std is arrived within a defined period of time + procedure WaitForValueStdl( signal Sig : in std_logic; -- Signal to check + ExpVal : in std_logic; -- expected value + Timeout : in time; -- time to wait for + Msg : in string; -- msg to display + Prefix : in string := "###ERROR###: "); -- bool out to stop Tb for ex. + +end psi_tb_activity_pkg; + +------------------------------------------------------------------------------ +-- Package Body +------------------------------------------------------------------------------ +package body psi_tb_activity_pkg is + + -- *** CheckNoActivity *** + procedure CheckNoActivity(signal Sig : in std_logic; + IdleTime : in time; + Level : in integer range -1 to 1; -- -1 = don't check, 0 = low, 1 = high + Msg : in string := ""; + Prefix : in string := "###ERROR###: ") is + begin + wait for IdleTime; + assert Sig'last_event >= IdleTime + report Prefix & Msg & "[Unexpected Activity]" + severity error; + if Level /= -1 then + StdlCompare(Level, Sig, "CheckNoActivity: " & Msg, Prefix); + end if; + end procedure; + + -- *** CheckNoActivityStlv *** + procedure CheckNoActivityStlv(signal Sig : in std_logic_vector; + IdleTime : in time; + Level : in integer range -1 to integer'high; -- -1 = don't check, otherwise interpreted unsigned + Msg : in string := ""; + Prefix : in string := "###ERROR###: ") is + begin + wait for IdleTime; + assert Sig'last_event >= IdleTime + report Prefix & Msg & "[Unexpected Activity]" + severity error; + if Level /= -1 then + StdlvCompareInt(Level, Sig, "CheckNoActivityStlv: " & Msg, false, 0, Prefix); + end if; + end procedure; + + -- *** CheckLastActivity *** + procedure CheckLastActivity(signal Sig : in std_logic; + IdleTime : in time; + Level : in integer range -1 to 1; -- -1 = don't check, 0 = low, 1 = high + Msg : in string := ""; + Prefix : in string := "###ERROR###: ") is + begin + assert Sig'last_event >= IdleTime + report Prefix & Msg & "Unexpected activity, " & + "[Expeced idle " & time'image(IdleTime) & + ", Actual idle " & time'image(Sig'last_event) & "]" + severity error; + if Level /= -1 then + StdlCompare(Level, Sig, "CheckLastActivity: " & Msg, Prefix); + end if; + end procedure; + + -- *** CheckLastActivityStlv *** + procedure CheckLastActivityStlv(signal Sig : in std_logic_vector; + IdleTime : in time; + Level : in integer range -1 to integer'high; -- -1 = don't check, otherwise interpreted unsigned + Msg : in string := ""; + Prefix : in string := "###ERROR###: ") is + begin + assert Sig'last_event >= IdleTime + report Prefix & Msg & "Unexpected activity, " & + "[Expeced idle " & time'image(IdleTime) & + ", Actual idle " & time'image(Sig'last_event) & "]" + severity error; + if Level /= -1 then + StdlvCompareInt(Level, Sig, "CheckLastActivityStlv: " & Msg, false, 0, Prefix); + end if; + end procedure; + + -- *** PulseSig *** + procedure PulseSig(signal Sig : out std_logic; + signal Clk : in std_logic) is + begin + wait until rising_edge(Clk); + Sig <= '1'; + wait until rising_edge(Clk); + Sig <= '0'; + end procedure; + + -- *** ClockedWaitFor *** + procedure ClockedWaitFor(Val : in std_logic; + signal Sig : in std_logic; + signal Clk : in std_logic) is + begin + wait until rising_edge(Clk) and Sig = Val; + end procedure; + + -- *** ClockedWaitFor *** + procedure WaitClockCycles( Cycles : in integer; + signal Clk : in std_logic) is + begin + for i in 0 to Cycles-1 loop + wait until rising_edge(Clk); + end loop; + end procedure; + + -- *** ClockedWaitTime *** + procedure ClockedWaitTime( Duration : in time; + signal Clk : in std_logic) is + begin + wait for Duration; + wait until rising_edge(Clk); + end procedure; + + -- *** GenerateStrobe *** + procedure GenerateStrobe( freq_clock : in real := 100.0E6; + freq_str : in real := 1.0E6; + rst_pol_g : in std_logic := '1'; + signal rst : in std_logic; + signal clk : in std_logic; + signal str : out std_logic) is + + variable count_v : integer range 0 to (integer(ceil(freq_clock/freq_str))) := 0; + begin + while true loop + wait until rising_edge(clk); + if rst = rst_pol_g then + count_v := 0; + str <= '0'; + else + if count_v /= integer(ceil(freq_clock/freq_str)) - 1 then + str <= '0'; + count_v := count_v + 1; + else + str <= '1'; + count_v := 0; + end if; + end if; + end loop; + end procedure; + + -- *** Wait for Standard logic vector to happen *** + procedure WaitForValueStdlv(signal Sig : in std_logic_vector; + ExpVal : in std_logic_vector; + Timeout : in time; + Msg : in string; + Prefix : in string := "###ERROR###: ") is + begin + wait until ExpVal = Sig for timeout; + if ExpVal /= Sig then + report Prefix & Msg & + " Target state not reached" & + " [Expected " & str(ExpVal) & "(0x" & hstr(ExpVal) & ")" & + ", Received " & str(Sig) & "(0x" & hstr(Sig) & ")" & "]" + severity error; + end if; + end procedure; + + -- *** Wait for Standard logic to happen *** + procedure WaitForValueStdl( signal Sig : in std_logic; + ExpVal : in std_logic; + Timeout : in time; + Msg : in string; + Prefix : in string := "###ERROR###: ") is + begin + wait until ExpVal = Sig for timeout; + if ExpVal /= Sig then + report Prefix & msg & + " Target state not reached" & + " [Expected " & str(ExpVal) & + ", Received " & str(Sig) & "]" + severity error; + end if; + end procedure; + +end psi_tb_activity_pkg; diff --git a/testsuite/gna/issue1051/psi_tb_compare_pkg.vhd b/testsuite/gna/issue1051/psi_tb_compare_pkg.vhd new file mode 100644 index 000000000..94c9f6d7c --- /dev/null +++ b/testsuite/gna/issue1051/psi_tb_compare_pkg.vhd @@ -0,0 +1,256 @@ +------------------------------------------------------------------------------ +-- Copyright (c) 2018 by Paul Scherrer Institute, Switzerland +-- All rights reserved. +-- Authors: Oliver Bruendler, Benoit Stef +------------------------------------------------------------------------------ + +------------------------------------------------------------------------------ +-- Libraries +------------------------------------------------------------------------------ +library ieee; + use ieee.std_logic_1164.all; + use ieee.numeric_std.all; + +library work; + use work.psi_tb_txt_util.all; + +------------------------------------------------------------------------------ +-- Package Header +------------------------------------------------------------------------------ +package psi_tb_compare_pkg is + + -- returns an index string in the form "[3]" + function IndexString( Index : integer) return string; + + -- std_logic_vector compare to integer + procedure StdlvCompareInt ( Expected : in integer; + Actual : in std_logic_vector; + Msg : in string; + IsSigned : in boolean := true; + Tolerance : in integer := 0; + Prefix : in string := "###ERROR###: "); + + -- std_logic_vector compare to std_logic_vector + procedure StdlvCompareStdlv (Expected : in std_logic_vector; + Actual : in std_logic_vector; + Msg : in string; + Prefix : in string := "###ERROR###: "); + + -- std_logic compare std_logic + procedure StdlCompare( Expected : in integer range 0 to 1; + Actual : in std_logic; + Msg : in string; + Prefix : in string := "###ERROR###: "); + + -- integer compare to integer + procedure IntCompare( Expected : in integer; + Actual : in integer; + Msg : in string; + Tolerance : in integer := 0; + Prefix : in string := "###ERROR###: "); + + -- real compare to real + procedure RealCompare( Expected : in real; + Actual : in real; + Msg : in string; + Tolerance : in real := 0.0; + Prefix : in string := "###ERROR###: "); + + -- signed compare to signed + procedure SignCompare ( Expected : in signed; + Actual : in signed; + Msg : in string; + Tolerance : in integer := 0; + Prefix : in string := "###ERROR###: "); + + -- unsigned compare to unsigned + procedure UsignCompare (Expected : in unsigned; + Actual : in unsigned; + Msg : in string; + Tolerance : in integer := 0; + Prefix : in string := "###ERROR###: "); + + -- signed compare to integer + procedure SignCompareInt ( Expected : in integer; + Actual : in signed; + Msg : in string; + Tolerance : in integer := 0; + Prefix : in string := "###ERROR###: "); + + -- unsigned compare to integer + procedure UsignCompareInt ( Expected : in integer; + Actual : in unsigned; + Msg : in string; + Tolerance : in integer := 0; + Prefix : in string := "###ERROR###: "); + +end psi_tb_compare_pkg; + +------------------------------------------------------------------------------ +-- Package Body +------------------------------------------------------------------------------ +package body psi_tb_compare_pkg is + + -- *** IndexString *** + function IndexString( Index : integer) return string is + begin + return "[" & to_string(Index) & "]"; + end function; + + -- *** StdlvCompareInt *** + procedure StdlvCompareInt ( Expected : in integer; + Actual : in std_logic_vector; + Msg : in string; + IsSigned : in boolean := true; + Tolerance : in integer := 0; + Prefix : in string := "###ERROR###: ") is + variable ActualInt_v : integer; + variable ExpectedStdlv32_v : std_logic_vector(31 downto 0); + variable ActualStdlv32_v : std_logic_vector(31 downto 0); + begin + -- Convert Input + if IsSigned then + ActualInt_v := to_integer(signed(Actual)); + ExpectedStdlv32_v := std_logic_vector(to_signed(Expected, 32)); + ActualStdlv32_v := std_logic_vector(to_signed(ActualInt_v, 32)); + else + ActualInt_v := to_integer(unsigned(Actual)); + ExpectedStdlv32_v := std_logic_vector(to_unsigned(Expected, 32)); + ActualStdlv32_v := std_logic_vector(to_unsigned(ActualInt_v, 32)); + end if; + -- Assertion + assert (ActualInt_v >= Expected-Tolerance) and (ActualInt_v <= Expected+Tolerance) + report Prefix & Msg & + " [Expected " & integer'image(Expected) & "(0x" & hstr(ExpectedStdlv32_v) & ")" & + ", Received " & integer'image(ActualInt_v) & "(0x" & hstr(ActualStdlv32_v) & ")" & + ", Tolerance " & integer'image(Tolerance) & "]" + severity error; + end procedure; + + -- *** StdlvCompareStdlv *** + procedure StdlvCompareStdlv ( Expected : in std_logic_vector; + Actual : in std_logic_vector; + Msg : in string; + Prefix : in string := "###ERROR###: ") is + constant Expected_c : std_logic_vector(Expected'length-1 downto 0) := Expected; + constant Actual_c : std_logic_vector(Actual'length-1 downto 0) := Actual; + begin + -- Assertion + assert Actual_c = Expected_c + report Prefix & Msg & + " [Expected " & str(Expected_c) & "(0x" & hstr(Expected_c) & ")" & + ", Received " & str(Actual_c) & "(0x" & hstr(Actual_c) & ")" & "]" + severity error; + end procedure; + + -- *** StdlCompare *** + procedure StdlCompare( Expected : in integer range 0 to 1; + Actual : in std_logic; + Msg : in string; + Prefix : in string := "###ERROR###: ") is + variable ExStdl_v : std_logic; + begin + if Expected = 0 then + ExStdl_v := '0'; + else + ExStdl_v := '1'; + end if; + assert Actual = ExStdl_v + report Prefix & Msg & + " [Expected " & str(ExStdl_v) & + ", Received " & str(Actual) & "]" + severity error; + end procedure; + + -- *** IntCompare *** + procedure IntCompare( Expected : in integer; + Actual : in integer; + Msg : in string; + Tolerance : in integer := 0; + Prefix : in string := "###ERROR###: ") is + begin + assert (Actual >= Expected-Tolerance) and (Actual <= Expected+Tolerance) + report Prefix & Msg & + " [Expected " & to_string(Expected) & + ", Received " & to_string(Actual) & + ", Tolerance " & to_string(Tolerance) & "]" + severity error; + end procedure; + + -- *** RealCompare *** + procedure RealCompare( Expected : in real; + Actual : in real; + Msg : in string; + Tolerance : in real := 0.0; + Prefix : in string := "###ERROR###: ") is + begin + assert (Actual >= Expected-Tolerance) and (Actual <= Expected+Tolerance) + report Prefix & Msg & + " [Expected " & to_string(Expected) & + ", Received " & to_string(Actual) & + ", Tolerance " & to_string(Tolerance) & "]" + severity error; + end procedure; + + -- *** SignCompare *** + procedure SignCompare( Expected : in signed; + Actual : in signed; + Msg : in string; + Tolerance : in integer := 0; + Prefix : in string := "###ERROR###: ") is + begin + assert (Actual >= Expected-Tolerance) and (Actual <= Expected+Tolerance) + report Prefix & Msg & + " [Expected " & to_string(Expected) & + ", Received " & to_string(Actual) & + ", Tolerance " & to_string(Tolerance) & "]" + severity error; + end procedure; + + -- *** UsignCompare *** + procedure UsignCompare( Expected : in unsigned; + Actual : in unsigned; + Msg : in string; + Tolerance : in integer := 0; + Prefix : in string := "###ERROR###: ") is + begin + assert (Actual >= Expected-Tolerance) and (Actual <= Expected+Tolerance) + report Prefix & Msg & + " [Expected " & to_string(Expected) & + ", Received " & to_string(Actual) & + ", Tolerance " & to_string(Tolerance) & "]" + severity error; + end procedure; + + -- *** SignCompareInt *** + procedure SignCompareInt ( Expected : in integer; + Actual : in signed; + Msg : in string; + Tolerance : in integer := 0; + Prefix : in string := "###ERROR###: ") is + begin + StdlvCompareInt ( Expected => Expected, + Actual => std_logic_vector(Actual), + Msg => Msg, + IsSigned => true, + Tolerance => Tolerance, + Prefix => Prefix); + end procedure; + + -- *** UsignCompareInt *** + procedure UsignCompareInt ( Expected : in integer; + Actual : in unsigned; + Msg : in string; + Tolerance : in integer := 0; + Prefix : in string := "###ERROR###: ") is + begin + StdlvCompareInt ( Expected => Expected, + Actual => std_logic_vector(Actual), + Msg => Msg, + IsSigned => false, + Tolerance => Tolerance, + Prefix => Prefix); + end procedure; + +end psi_tb_compare_pkg; + diff --git a/testsuite/gna/issue1051/psi_tb_i2c_pkg.vhd b/testsuite/gna/issue1051/psi_tb_i2c_pkg.vhd new file mode 100644 index 000000000..285cacdf6 --- /dev/null +++ b/testsuite/gna/issue1051/psi_tb_i2c_pkg.vhd @@ -0,0 +1,740 @@ +------------------------------------------------------------------------------ +-- Copyright (c) 2019 by Paul Scherrer Institute, Switzerland +-- All rights reserved. +-- Authors: Oliver Bruendler +------------------------------------------------------------------------------ + +------------------------------------------------------------------------------ +-- Libraries +------------------------------------------------------------------------------ +library ieee; + use ieee.std_logic_1164.all; + use ieee.numeric_std.all; + +library work; + use work.psi_tb_compare_pkg.all; + use work.psi_tb_activity_pkg.all; + use work.psi_tb_txt_util.all; + use work.psi_common_logic_pkg.all; + use work.psi_common_math_pkg.all; + +------------------------------------------------------------------------------ +-- Package Header +------------------------------------------------------------------------------ +package psi_tb_i2c_pkg is + -- ----------------------------------------------------------------------- + -- Constants + -- ----------------------------------------------------------------------- + constant I2c_ACK : std_logic := '0'; + constant I2c_NACK : std_logic := '1'; + + type I2c_Transaction_t is (I2c_READ, I2c_WRITE); + + -- ----------------------------------------------------------------------- + -- Functions + -- ----------------------------------------------------------------------- + function I2cGetAddr( Addr : in integer; + Trans : in I2c_Transaction_t) return integer; + + -- ----------------------------------------------------------------------- + -- Initialization + -- ----------------------------------------------------------------------- + procedure I2cPullup(signal scl : inout std_logic; + signal sda : inout std_logic); + + procedure I2cBusFree( signal scl : inout std_logic; + signal sda : inout std_logic); + + procedure I2cSetFrequency( frequencyHz : in real); + + -- ----------------------------------------------------------------------- + -- Master Side Transactions + -- ----------------------------------------------------------------------- + procedure I2cMasterSendStart( signal Scl : inout std_logic; + signal Sda : inout std_logic; + Msg : in string := "No Msg"; + Prefix : in string := "###ERROR###: "); + + procedure I2cMasterSendRepeatedStart( signal Scl : inout std_logic; + signal Sda : inout std_logic; + Msg : in string := "No Msg"; + Prefix : in string := "###ERROR###: "); + + procedure I2cMasterSendStop( signal Scl : inout std_logic; + signal Sda : inout std_logic; + Msg : in string := "No Msg"; + Prefix : in string := "###ERROR###: "); + + procedure I2cMasterSendAddr( Address : in integer; + IsRead : in boolean; + signal Scl : inout std_logic; + signal Sda : inout std_logic; + Msg : in string := "No Msg"; + AddrBits : in integer := 7; -- 7 or 10 + ExpectedAck : in std_logic := '0'; -- '0' for ack, '1' for nack, anything else for "don't check" + Prefix : in string := "###ERROR###: "); + + procedure I2cMasterSendByte( Data : in integer range -128 to 255; + signal Scl : inout std_logic; + signal Sda : inout std_logic; + Msg : in string := "No Msg"; + ExpectedAck : in std_logic := '0'; -- '0' for ack, '1' for nack, anything else for "don't check" + Prefix : in string := "###ERROR###: "); + + procedure I2cMasterExpectByte( ExpData : in integer range -128 to 255; + signal Scl : inout std_logic; + signal Sda : inout std_logic; + Msg : in string := "No Msg"; + AckOutput : in std_logic := '0'; -- '0' for ack, '1' for nack + Prefix : in string := "###ERROR###: "); + + -- ----------------------------------------------------------------------- + -- Slave Side Transactions + -- ----------------------------------------------------------------------- + procedure I2cSlaveWaitStart( signal Scl : inout std_logic; + signal Sda : inout std_logic; + Msg : in string := "No Msg"; + Timeout : in time := 1 ms; + Prefix : in string := "###ERROR###: "); + + procedure I2cSlaveWaitRepeatedStart( signal Scl : inout std_logic; + signal Sda : inout std_logic; + Msg : in string := "No Msg"; + Timeout : in time := 1 ms; + ClkStretch : in time := 0 ns; -- hold clock-low for at least this time + Prefix : in string := "###ERROR###: "); + + procedure I2cSlaveWaitStop( signal Scl : inout std_logic; + signal Sda : inout std_logic; + Msg : in string := "No Msg"; + Timeout : in time := 1 ms; + ClkStretch : in time := 0 ns; -- hold clock-low for at least this time + Prefix : in string := "###ERROR###: "); + + procedure I2cSlaveExpectAddr( Address : in integer; + IsRead : in boolean; + signal Scl : inout std_logic; + signal Sda : inout std_logic; + Msg : in string := "No Msg"; + AddrBits : in integer := 7; -- 7 or 10 + AckOutput : in std_logic := '0'; -- '0' for ack, '1' for nack + Timeout : in time := 1 ms; + ClkStretch : in time := 0 ns; -- hold clock-low for at least this time + Prefix : in string := "###ERROR###: "); + + procedure I2cSlaveExpectByte( ExpData : in integer range -128 to 255; + signal Scl : inout std_logic; + signal Sda : inout std_logic; + Msg : in string := "No Msg"; + AckOutput : in std_logic := '0'; -- '0' for ack, '1' for nack + Timeout : in time := 1 ms; + ClkStretch : in time := 0 ns; -- hold clock-low for at least this time + Prefix : in string := "###ERROR###: "); + + procedure I2cSlaveSendByte( Data : in integer range -128 to 255; + signal Scl : inout std_logic; + signal Sda : inout std_logic; + Msg : in string := "No Msg"; + ExpectedAck : in std_logic := '0'; -- '0' for ack, '1' for nack, anything else for "don't check" + Timeout : in time := 1 ms; + ClkStretch : in time := 0 ns; -- hold clock-low for at least this time + Prefix : in string := "###ERROR###: "); + +end psi_tb_i2c_pkg; + +------------------------------------------------------------------------------ +-- Package Body +------------------------------------------------------------------------------ +package body psi_tb_i2c_pkg is + + -- ----------------------------------------------------------------------- + -- Local Types + -- ----------------------------------------------------------------------- + type MsgInfo_r is record + Prefix : string; + Func : string; + User : string; + end record; + + -- ----------------------------------------------------------------------- + -- Local Variables + -- ----------------------------------------------------------------------- + shared variable FreqClk_v : real := 100.0e3; + + -- ----------------------------------------------------------------------- + -- Private Procedures + -- ----------------------------------------------------------------------- + -- *** Message Handling *** + function GenMessage( Prefix : in string; + Func : in string; + General : in string; + User : in string) + return string is + begin + return Prefix & "- " & Func & " - " & General & " - " & User; + end function; + + function GenMessageNoPrefix( Func : in string; + General : in string; + User : in string) + return string is + begin + return Func & " - " & General & " - " & User; + end function; + + -- *** Level Check *** + procedure LevelCheck( Expected : in std_logic; + signal Sig : in std_logic; + Msg : in MsgInfo_r; + GeneralMsg : in string) is + begin + -- Do not check for other inputs than 1 or 0 + if (Expected = '0') or (Expected = '1') then + assert ((Expected = '0') and (Sig = '0')) or ((Expected = '1') and ((Sig = '1') or (Sig = 'H'))) + report GenMessage(Msg.Prefix, Msg.Func, GeneralMsg, Msg.User) + severity error; + end if; + end procedure; + + procedure LevelWait( Expected : in std_logic; + signal Sig : in std_logic; + Timeout : in time; + Msg : in MsgInfo_r; + GeneralMsg : in string) is + variable Correct_v : boolean; + begin + if Sig /= Expected then + if Expected = '0' then + wait until Sig = '0' for Timeout; + Correct_v := (Sig = '0'); + else + wait until (Sig = '1') or (Sig = 'H') for Timeout; + Correct_v := ((Sig = '1') or (Sig = 'H')); + end if; + assert Correct_v + report GenMessage(Msg.Prefix, Msg.Func, GeneralMsg, Msg.User) + severity error; + end if; + end procedure; + + + -- *** Time Calculations *** + impure function ClkPeriod return time is + begin + return (1 sec) / FreqClk_v; + end function; + + impure function ClkHalfPeriod return time is + begin + return (0.5 sec) / FreqClk_v; + end function; + + impure function ClkQuartPeriod return time is + begin + return (0.25 sec) / FreqClk_v; + end function; + + -- *** Bit Transfers *** + procedure SendBitInclClock( Data : in std_logic; + signal Scl : inout std_logic; + signal Sda : inout std_logic; + BitInfo : in string; + Msg : in MsgInfo_r) is + begin + -- Initial Check + LevelCheck('0', Scl, Msg, "SCL must be 0 before SendBitInclClock is called [" & BitInfo & "]"); + + -- Assert Data + if Data = '0' then + Sda <= '0'; + else + Sda <= 'Z'; + end if; + wait for ClkQuartPeriod; + + -- Send Clk Pulse + Scl <= 'Z'; + LevelWait('1', Scl, 1 ms, Msg, "SCL held low by other device"); + wait for ClkHalfPeriod; + CheckLastActivity(Scl, ClkHalfPeriod*0.9, -1, GenMessageNoPrefix(Msg.Func, "SCL high period too short [" & BitInfo & "]", Msg.User), Msg.Prefix); + LevelCheck(Data, Sda, Msg, "SDA readback does not match SDA transmit value during SCL pulse [" & BitInfo & "]"); + CheckLastActivity(Sda, ClkHalfPeriod, -1, GenMessageNoPrefix(Msg.Func, "SDA not stable during SCL pulse [" & BitInfo & "]", Msg.User), Msg.Prefix); + Scl <= '0'; + wait for ClkQuartPeriod; + end procedure; + + procedure CheckBitInclClock( Data : in std_logic; + signal Scl : inout std_logic; + signal Sda : inout std_logic; + BitInfo : in string; + Msg : in MsgInfo_r) is + begin + -- Initial Check + LevelCheck('0', Scl, Msg, "SCL must be 0 before CheckBitInclClock is called"); + + -- Wait for assertion + wait for ClkQuartPeriod; + + -- Send Clk Pulse + Scl <= 'Z'; + LevelWait('1', Scl, 1 ms, Msg, "SCL held low by other device"); + wait for ClkHalfPeriod; + CheckLastActivity(Scl, ClkHalfPeriod*0.9, -1, GenMessageNoPrefix(Msg.Func, "SCL high period too short [" & BitInfo & "]", Msg.User), Msg.Prefix); + LevelCheck(Data, Sda, Msg, "Received wrong data [" & BitInfo & "]"); + CheckLastActivity(Sda, ClkHalfPeriod, -1, GenMessageNoPrefix(Msg.Func, "SDA not stable during SCL pulse [" & BitInfo & "]", Msg.User), Msg.Prefix); + Scl <= '0'; + wait for ClkQuartPeriod; + end procedure; + + procedure SendBitExclClock( Data : in std_logic; + signal Scl : inout std_logic; + signal Sda : inout std_logic; + Timeout : in time; + BitInfo : in string; + Msg : in MsgInfo_r; + ClockStretch : in time) is + variable Stretched_v : boolean := false; + begin + -- Initial Check + LevelCheck('0', Scl, Msg, "SCL must be 0 before SendBitExclClock is called"); + + -- Clock stretching + if ClockStretch > 0 ns then + Scl <= '0'; + wait for ClockStretch; + Stretched_v := true; + end if; + + -- Assert Data + if Data = '0' then + Sda <= '0'; + else + Sda <= 'Z'; + end if; + if Stretched_v then + wait for ClkQuartPeriod; + Scl <= 'Z'; + end if; + + -- Wait clock rising edge + LevelWait('1', Scl, Timeout, Msg, "SCL did not go high"); + + -- wait clock falling edge + LevelWait('0', Scl, Timeout, Msg, "SCL did not go low"); + LevelCheck(Data, Sda, Msg, "Received wrong data [" & BitInfo & "]"); + CheckLastActivity(Sda, ClkHalfPeriod, -1, GenMessageNoPrefix(Msg.Func, "SDA not stable during SCL pulse [" & BitInfo & "]", Msg.User), Msg.Prefix); + + -- wait until center of low + wait for ClkQuartPeriod; + end procedure; + + procedure CheckBitExclClock( Data : in std_logic; + signal Scl : inout std_logic; + signal Sda : inout std_logic; + Timeout : in time; + BitInfo : in string; + Msg : in MsgInfo_r; + ClockStretch : in time) is + begin + -- Initial Check + LevelCheck('0', Scl, Msg, "SCL must be 0 before CheckBitExclClock is called"); + + -- Wait clock rising edge + if ClockStretch > 0 ns then + Scl <= '0'; + wait for ClockStretch; + Scl <= 'Z'; + end if; + LevelWait('1', Scl, Timeout, Msg, "SCL did not go high"); + + -- wait clock falling edge + LevelWait('0', Scl, Timeout, Msg, "SCL did not go low"); + LevelCheck(Data, Sda, Msg, "Received wrong data [" & BitInfo & "]"); + CheckLastActivity(Sda, ClkHalfPeriod, -1, GenMessageNoPrefix(Msg.Func, "SDA not stable during SCL pulse [" & BitInfo & "]", Msg.User), Msg.Prefix); + + -- wait until center of low + wait for ClkQuartPeriod; + end procedure; + + + -- *** Byte Transfers *** + procedure SendByteInclClock( Data : in std_logic_vector(7 downto 0); + signal Scl : inout std_logic; + signal Sda : inout std_logic; + Msg : in MsgInfo_r) is + begin + -- Do bits + for i in 7 downto 0 loop + SendBitInclClock(Data(i), Scl, Sda, to_string(i), Msg); + end loop; + end procedure; + + + + procedure ExpectByteExclClock( ExpData : in std_logic_vector(7 downto 0); + signal Scl : inout std_logic; + signal Sda : inout std_logic; + Msg : in MsgInfo_r; + Timeout : in time; + ClkStretch : in time) is + begin + -- Do bits + for i in 7 downto 0 loop + CheckBitExclClock(ExpData(i), Scl, Sda, Timeout, to_string(i), Msg, ClkStretch); + end loop; + end procedure; + + -- ----------------------------------------------------------------------- + -- Functions + -- ----------------------------------------------------------------------- + function I2cGetAddr( Addr : in integer; + Trans : in I2c_Transaction_t) return integer is + begin + return Addr*2+choose(Trans=I2c_READ, 1, 0); + end function; + + -- ----------------------------------------------------------------------- + -- Master Side Transactions + -- ----------------------------------------------------------------------- + procedure I2cPullup(signal Scl : inout std_logic; + signal Sda : inout std_logic) is + begin + Scl <= 'H'; + Sda <= 'H'; + end procedure; + + procedure I2cBusFree( signal Scl : inout std_logic; + signal Sda : inout std_logic) is + begin + Scl <= 'Z'; + Sda <= 'Z'; + end procedure; + + procedure I2cSetFrequency( FrequencyHz : in real) is + begin + FreqClk_v := FrequencyHz; + end procedure; + + procedure I2cMasterSendStart( signal Scl : inout std_logic; + signal Sda : inout std_logic; + Msg : in string := "No Msg"; + Prefix : in string := "###ERROR###: ") is + constant MsgInfo : MsgInfo_r := (Prefix, "I2cMasterSendStart", Msg); + begin + -- Initial check + LevelCheck('1', Scl, MsgInfo, "SCL must be 1 before procedure is called"); + LevelCheck('1', Sda, MsgInfo, "SDA must be 1 before procedure is called"); + + -- Do start condition + wait for ClkQuartPeriod; + Sda <= '0'; + LevelCheck('1', Scl, MsgInfo, "SCL must be 1 during SDA falling edge"); + wait for ClkQuartPeriod; + + -- Go to center of clk low period + Scl <= '0'; + wait for ClkQuartPeriod; + end procedure; + + procedure I2cMasterSendRepeatedStart( signal Scl : inout std_logic; + signal Sda : inout std_logic; + Msg : in string := "No Msg"; + Prefix : in string := "###ERROR###: ") is + constant MsgInfo : MsgInfo_r := (Prefix, "I2cMasterSendRepeatedStart", Msg); + begin + -- Initial check + if to01X(Scl) = '1' then + LevelCheck('1', Sda, MsgInfo, "SDA must be 1 before procedure is called if SCL = 1"); + end if; + + -- Do repeated start + if Scl = '0' then + Sda <= 'Z'; + wait for ClkQuartPeriod; + LevelCheck('1', Sda, MsgInfo, "SDA held low by other device"); + Scl <= 'Z'; + wait for ClkQuartPeriod; + LevelCheck('1', Scl, MsgInfo, "SCL held low by other device"); + end if; + wait for ClkQuartPeriod; + Sda <= '0'; + LevelCheck('1', Scl, MsgInfo, "SCL must be 1 during SDA falling edge"); + wait for ClkQuartPeriod; + + -- Go to center of clk low period + Scl <= '0'; + wait for ClkQuartPeriod; + end procedure; + + procedure I2cMasterSendStop( signal Scl : inout std_logic; + signal Sda : inout std_logic; + Msg : in string := "No Msg"; + Prefix : in string := "###ERROR###: ") is + constant MsgInfo : MsgInfo_r := (Prefix, "I2cMasterSendStop", Msg); + begin + -- Initial check + if to01X(Scl) = '1' then + LevelCheck('0', Sda, MsgInfo, "SDA must be 0 before procedure is called if SCL = 1"); + end if; + + -- Do stop + if Scl = '0' then + Sda <= '0'; + wait for ClkQuartPeriod; + Scl <= 'Z'; + wait for ClkQuartPeriod; + LevelCheck('1', Scl, MsgInfo, "SCL held low by other device"); + else + wait for ClkQuartPeriod; + end if; + Sda <= 'Z'; + LevelCheck('1', Scl, MsgInfo, "SCL must be 1 during SDA rising edge"); + + -- Go to center of clk high period + wait for ClkQuartPeriod; + + end procedure; + + procedure I2cMasterSendAddr( Address : in integer; + IsRead : in boolean; + signal Scl : inout std_logic; + signal Sda : inout std_logic; + Msg : in string := "No Msg"; + AddrBits : in integer := 7; -- 7 or 10 + ExpectedAck : in std_logic := '0'; -- '0' for ack, '1' for nack, anything else for "don't check" + Prefix : in string := "###ERROR###: ") is + constant AddrSlv_c : std_logic_vector(9 downto 0) := std_logic_vector(to_unsigned(Address, 10)); + constant Rw_c : std_logic := choose(IsRead, '1', '0'); + begin + -- 7 Bit addressing + if AddrBits = 7 then + SendByteInclClock(AddrSlv_c(6 downto 0) & Rw_c, Scl, Sda, (Prefix, "I2cMasterSendAddr 7b", Msg)); + Sda <= 'Z'; + CheckBitInclClock(ExpectedAck, Scl, Sda, "ACK", (Prefix, "I2cMasterSendAddr 7b", Msg)); + -- 10 Bit addressing + elsif AddrBits = 10 then + SendByteInclClock("11110" & AddrSlv_c(9 downto 8) & Rw_c, Scl, Sda, (Prefix, "I2cMasterSendAddr 10b 9:8", Msg)); + Sda <= 'Z'; + CheckBitInclClock(ExpectedAck, Scl, Sda, "ACK", (Prefix, "I2cMasterSendAddr 10b 9:8", Msg)); + SendByteInclClock(AddrSlv_c(7 downto 0), Scl, Sda, (Prefix, "I2cMasterSendAddr 10b 7:0", Msg)); + Sda <= 'Z'; + CheckBitInclClock(ExpectedAck, Scl, Sda, "ACK", (Prefix, "I2cMasterSendAddr 10b 7:0", Msg)); + else + report Prefix & "I2cMasterSendAddr - Illegal addrBits (must be 7 or 10) - " & Msg severity error; + end if; + end procedure; + + procedure I2cMasterSendByte( Data : in integer range -128 to 255; + signal Scl : inout std_logic; + signal Sda : inout std_logic; + Msg : in string := "No Msg"; + ExpectedAck : in std_logic := '0'; -- '0' for ack, '1' for nack, anything else for "don't check" + Prefix : in string := "###ERROR###: ") is + variable DataSlv_v : std_logic_vector(7 downto 0); + begin + -- Do data + if Data < 0 then + DataSlv_v := std_logic_vector(to_signed(Data, 8)); + else + DataSlv_v := std_logic_vector(to_unsigned(Data, 8)); + end if; + SendByteInclClock(DataSlv_v, Scl, Sda, (Prefix, "I2cMasterSendByte", Msg)); + Sda <= 'Z'; + CheckBitInclClock(ExpectedAck, Scl, Sda, "ACK", (Prefix, "I2cMasterSendByte", Msg)); + end procedure; + + + procedure I2cMasterExpectByte( ExpData : in integer range -128 to 255; + signal Scl : inout std_logic; + signal Sda : inout std_logic; + Msg : in string := "No Msg"; + AckOutput : in std_logic := '0'; -- '0' for ack, '1' for nack + Prefix : in string := "###ERROR###: ") is + variable Data_v : std_logic_vector(7 downto 0); + begin + -- Convert data + if ExpData < 0 then + Data_v := std_logic_vector(to_signed(ExpData, 8)); + else + Data_v := std_logic_vector(to_unsigned(ExpData, 8)); + end if; + + -- do bits + Sda <= 'Z'; + for i in 7 downto 0 loop + CheckBitInclClock(Data_v(i), Scl, Sda, to_string(i), (Prefix, "I2cMasterExpectByte", Msg)); + end loop; + SendBitInclClock(AckOutput, Scl, Sda, "ACK", (Prefix, "I2cMasterExpectByte", Msg)); + end procedure; + + -- ----------------------------------------------------------------------- + -- Slave Side Transactions + -- ----------------------------------------------------------------------- + procedure I2cSlaveWaitStart( signal Scl : inout std_logic; + signal Sda : inout std_logic; + Msg : in string := "No Msg"; + Timeout : in time := 1 ms; + Prefix : in string := "###ERROR###: ") is + constant MsgInfo : MsgInfo_r := (Prefix, "I2cSlaveWaitStart", Msg); + begin + -- Initial check + LevelCheck('1', Scl, MsgInfo, "SCL must be 1 before procedure is called"); + LevelCheck('1', Sda, MsgInfo, "SDA must be 1 before procedure is called"); + + -- Do start checking + LevelWait('0', Sda, Timeout, MsgInfo, "SDA did not go low"); + LevelCheck('1', Scl, MsgInfo, "SCL must be 1 during SDA falling edge"); + LevelWait('0', Scl, Timeout, MsgInfo, "SCL did not go low"); + LevelCheck('0', Sda, MsgInfo, "SDA must be 0 during SCL falling edge"); + + -- Wait for center of SCL low + wait for ClkQuartPeriod; + end procedure; + + procedure I2cSlaveWaitRepeatedStart( signal Scl : inout std_logic; + signal Sda : inout std_logic; + Msg : in string := "No Msg"; + Timeout : in time := 1 ms; + ClkStretch : in time := 0 ns; -- hold clock-low for at least this time + Prefix : in string := "###ERROR###: ") is + constant MsgInfo : MsgInfo_r := (Prefix, "I2cSlaveWaitRepeatedStart", Msg); + begin + -- Initial Check + if to01X(Scl) = '1' then + LevelCheck('1', Sda, MsgInfo, "SDA must be 1 before procedure is called if SCL = 1"); + end if; + + -- Do Check + if to01X(Scl) = '0' then + -- Clock stretching + if ClkStretch > 0 ns then + Scl <= '0'; + wait for ClkStretch; + Scl <= 'Z'; + end if; + LevelWait('1', Scl, Timeout, MsgInfo, "SCL did not go high"); + LevelCheck('1', Sda, MsgInfo, "SDA must be 1 before SCL goes high"); + end if; + LevelWait('0', Sda, Timeout, MsgInfo, "SDA did not go low"); + LevelCheck('1', Scl, MsgInfo, "SCL must be 1 during SDA falling edge"); + LevelWait('0', Scl, Timeout, MsgInfo, "SCL did not go low"); + LevelCheck('0', Sda, MsgInfo, "SDA must be 0 during SCL falling edge"); + + -- Wait for center of SCL low + wait for ClkQuartPeriod; + end procedure; + + procedure I2cSlaveWaitStop( signal Scl : inout std_logic; + signal Sda : inout std_logic; + Msg : in string := "No Msg"; + Timeout : in time := 1 ms; + ClkStretch : in time := 0 ns; -- hold clock-low for at least this time + Prefix : in string := "###ERROR###: ") is + constant MsgInfo : MsgInfo_r := (Prefix, "I2cSlaveWaitStop", Msg); + begin + -- Initial check + if to01X(Scl) = '1' then + LevelCheck('0', Sda, MsgInfo, "SDA must be 0 before procedure is called if SCL = 1"); + end if; + + -- Do Check + if Scl = '0' then + -- Clock stretching + if ClkStretch > 0 ns then + Scl <= '0'; + wait for ClkStretch; + Scl <= 'Z'; + end if; + LevelWait('1', Scl, Timeout, MsgInfo, "SCL did not go high"); + LevelCheck('0', Sda, MsgInfo, "SDA must be 0 before SCL goes high"); + end if; + LevelWait('1', Sda, Timeout, MsgInfo, "SDA did not go high"); + LevelCheck('1', Scl, MsgInfo, "SCL must be 1 during SDA rising edge"); + + -- Wait for center of SCL low + wait for ClkQuartPeriod; + end procedure; + + procedure I2cSlaveExpectAddr( Address : in integer; + IsRead : in boolean; + signal Scl : inout std_logic; + signal Sda : inout std_logic; + Msg : in string := "No Msg"; + AddrBits : in integer := 7; -- 7 or 10 + AckOutput : in std_logic := '0'; -- '0' for ack, '1' for nack + Timeout : in time := 1 ms; + ClkStretch : in time := 0 ns; -- hold clock-low for at least this time + Prefix : in string := "###ERROR###: ") is + constant AddrSlv_c : std_logic_vector(9 downto 0) := std_logic_vector(to_unsigned(Address, 10)); + constant Rw_c : std_logic := choose(IsRead, '1', '0'); + begin + -- 7 Bit addressing + if AddrBits = 7 then + ExpectByteExclClock(AddrSlv_c(6 downto 0) & Rw_c, Scl, Sda, (Prefix, "I2cSlaveExpectAddr 7b", Msg), Timeout, ClkStretch); + SendBitExclClock(AckOutput, Scl, Sda, Timeout, "ACK", (Prefix, "I2cSlaveExpectAddr 7b ack", Msg), ClkStretch); + I2cBusFree(Scl, Sda); + -- 10 Bit addressing + elsif AddrBits = 10 then + ExpectByteExclClock("11110" & AddrSlv_c(9 downto 8) & Rw_c, Scl, Sda, (Prefix, "I2cSlaveExpectAddr 10b 9:8" , Msg), Timeout, ClkStretch); + SendBitExclClock(AckOutput, Scl, Sda, Timeout, "ACK", (Prefix, "I2cSlaveExpectAddr 10b 9:8 ack", Msg), ClkStretch); + I2cBusFree(Scl, Sda); + ExpectByteExclClock(AddrSlv_c(7 downto 0), Scl, Sda, (Prefix, "I2cSlaveExpectAddr 10b 7:0", Msg), Timeout, ClkStretch); + SendBitExclClock(AckOutput, Scl, Sda, Timeout, "ACK", (Prefix, "I2cSlaveExpectAddr 10b 7:0 ack", Msg), ClkStretch); + I2cBusFree(Scl, Sda); + else + report Prefix & "I2cSlaveExpectAddr - Illegal addrBits (must be 7 or 10) - " & Msg severity error; + end if; + end procedure; + + procedure I2cSlaveExpectByte( ExpData : in integer range -128 to 255; + signal Scl : inout std_logic; + signal Sda : inout std_logic; + Msg : in string := "No Msg"; + AckOutput : in std_logic := '0'; -- '0' for ack, '1' for nack + Timeout : in time := 1 ms; + ClkStretch : in time := 0 ns; -- hold clock-low for at least this time + Prefix : in string := "###ERROR###: ") is + variable Data_v : std_logic_vector(7 downto 0); + begin + if ExpData < 0 then + Data_v := std_logic_vector(to_signed(ExpData, 8)); + else + Data_v := std_logic_vector(to_unsigned(ExpData, 8)); + end if; + ExpectByteExclClock(Data_v, Scl, Sda, (Prefix, "I2cSlaveExpectByte", Msg), Timeout, ClkStretch); + SendBitExclClock(AckOutput, Scl, Sda, Timeout, "ACK", (Prefix, "I2cSlaveExpectByte ack", Msg), ClkStretch); + I2cBusFree(Scl, Sda); + end procedure; + + + procedure I2cSlaveSendByte( Data : in integer range -128 to 255; + signal Scl : inout std_logic; + signal Sda : inout std_logic; + Msg : in string := "No Msg"; + ExpectedAck : in std_logic := '0'; -- '0' for ack, '1' for nack, anything else for "don't check" + Timeout : in time := 1 ms; + ClkStretch : in time := 0 ns; -- hold clock-low for at least this time + Prefix : in string := "###ERROR###: ") is + variable Data_v : std_logic_vector(7 downto 0); + begin + -- Convert Data + if Data < 0 then + Data_v := std_logic_vector(to_signed(Data, 8)); + else + Data_v := std_logic_vector(to_unsigned(Data, 8)); + end if; + + -- Send data + for i in 7 downto 0 loop + SendBitExclClock(Data_v(i), Scl, Sda, Timeout, to_string(i), (Prefix, "I2cSlaveSendByte", Msg), ClkStretch); + end loop; + + -- Check ack + I2cBusFree(Scl, Sda); + CheckBitExclClock(ExpectedAck, Scl, Sda, Timeout, "ACK", (Prefix, "I2cSlaveSendByte", Msg), ClkStretch); + + end procedure; + + +end psi_tb_i2c_pkg; + diff --git a/testsuite/gna/issue1051/psi_tb_txt_util.vhd b/testsuite/gna/issue1051/psi_tb_txt_util.vhd new file mode 100644 index 000000000..626b7373b --- /dev/null +++ b/testsuite/gna/issue1051/psi_tb_txt_util.vhd @@ -0,0 +1,583 @@ +------------------------------------------------------------------------------ +---- ---- +---- Text Utils ---- +---- ---- +---- http://www.opencores.org/ ---- +---- ---- +---- Description: ---- +---- Utils to handle text. Used for the testbenches. ---- +---- ---- +---- To Do: ---- +---- - ---- +---- ---- +---- Authors: ---- +---- - Oyvind Harboe, oyvind.harboe zylin.com ---- +---- - Oliver Bründler, Paul Scherrer Instititute ---- +---- ---- +------------------------------------------------------------------------------ +---- ---- +---- Copyright (c) 2008 Oyvind Harboe <oyvind.harboe zylin.com> ---- +---- Copyright (c) Paul Scherrer Institute (www.psi.ch) ---- +---- ---- +---- Distributed under the BSD license ---- +---- ---- +------------------------------------------------------------------------------ +---- ---- +---- Design unit: txt_util (Package) ---- +---- File name: txt_util.vhdl ---- +---- Note: None ---- +---- Limitations: None known ---- +---- Errors: None known ---- +---- Library: zpu ---- +---- Dependencies: IEEE.std_logic_1164 ---- +---- IEEE.numeric_std ---- +---- std.textio ---- +---- Target FPGA: N/A ---- +---- Language: VHDL ---- +---- Wishbone: No ---- +---- Synthesis tools: Xilinx Release 9.2.03i - xst J.39 ---- +---- Simulation tools: GHDL [Sokcho edition] (0.2x) ---- +---- Text editor: SETEdit 0.5.x ---- +---- ---- +------------------------------------------------------------------------------ + +library IEEE; +use IEEE.std_logic_1164.all; +use IEEE.numeric_std.all; + +use std.textio.all; + +-- library zpu; + +package psi_tb_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; + function hstr(slv: unsigned) return string; + + -- convert integer to string, VHDL2008 built-in equivalent + function to_string(int : integer) return string; + + -- convert real to string, VHDL2008 built-in equivalent + function to_string(num : real) return string; + + -- convert signed to string, VHDL2008 built-in equivalent + function to_string(num : signed) return string; + + -- convert unsigned to string, VHDL2008 built-in equivalent + function to_string(num : unsigned) return string; + + -- convert std_logic_vector to string, VHDL2008 built-in equivalent + function to_string(num : std_logic_vector) return string; + + + -- functions to manipulate strings + ----------------------------------- + + -- 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); + + procedure str_write(file out_file: TEXT; + new_string: in 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 package psi_tb_txt_util; + + + + +package body psi_tb_txt_util is + -- prints text to the screen + procedure print(text: string) is + variable msg_line: line; + begin + --synopsys translate off + write(msg_line, text); + writeline(output, msg_line); + --synopsys translate on + end procedure print; + + -- prints text to the screen when active + procedure print(active: boolean; text: string) is + begin + if active then + print(text); + end if; + end procedure 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'; + when 'L' => c:= 'L'; + when 'H' => c:= 'H'; + when '-' => c:= '-'; + end case; + return c; + end function 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 function 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 function str; + + + function str(b: boolean) return string is + begin + if b then + return "true"; + else + return "false"; + end if; + end function 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'; + 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 function 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. + + -- 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 function str; + + -- convert integer to string, using base 10 + function str(int: integer) return string is + begin + return str(int, 10) ; + end function 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'; + 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 function hstr; + + function hstr(slv: unsigned) return string is + begin + return hstr(std_logic_vector(slv)); + end function hstr; + + -- VHDL2008 to_string built-in equivalents + function to_string(int : integer) return string is + begin + return str(int); + end function; + + function to_string(num : real) return string is + begin + return real'image(num); + end function; + + function to_string(num : signed) return string is + begin + return integer'image(to_integer(num)); + end function; + + function to_string(num : unsigned) return string is + begin + return integer'image(to_integer(num)); + end function; + + function to_string(num : std_logic_vector) return string is + begin + return str(num); + end function; + + -- 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'; + 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 function 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'; + 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 function 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; + + -- 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 '-' => + sl:='-'; + when others => + sl:='X'; + end case; + return sl; + end function 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 function 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; + 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 procedure 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 procedure print; + + -- print character to a file and start new line + procedure print(file out_file: TEXT; + char: in character) is + variable l: line; + begin + write(l,char); + writeline(out_file,l); + end procedure 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 package body psi_tb_txt_util; diff --git a/testsuite/gna/issue1051/repro.vhdl b/testsuite/gna/issue1051/repro.vhdl new file mode 100644 index 000000000..3ffa7571f --- /dev/null +++ b/testsuite/gna/issue1051/repro.vhdl @@ -0,0 +1,29 @@ +entity repro is +end repro; + +architecture behav of repro is + type msg_t is record + pfx : string; + func : string; + user : string; + end record; + + procedure report_msg (m : msg_t) is + begin + report m.pfx & "." & m.func & ": " & m.user; + end report_msg; + + procedure fill (pfx : string := "#err#"; usr : string := "none") is + constant m : msg_t := (pfx, "fill", usr); + begin + report_msg (m); + end fill; +begin + process + begin + fill; + fill (pfx => "my err"); + wait; + end process; +end behav; + diff --git a/testsuite/gna/issue1051/repro2.vhdl b/testsuite/gna/issue1051/repro2.vhdl new file mode 100644 index 000000000..76d31deb2 --- /dev/null +++ b/testsuite/gna/issue1051/repro2.vhdl @@ -0,0 +1,28 @@ +entity repro2 is +end repro2; + +architecture behav of repro2 is + type msg_t is record + pfx : string; + func : string; + user : string; + end record; + + procedure report_msg (m : msg_t) is + begin + report m.pfx & "." & m.func & ": " & m.user; + end report_msg; + + procedure fill (pfx : string := "#err#"; usr : string := "none") is + begin + report_msg (m => (pfx, "fill", usr)); + end fill; +begin + process + begin + fill; + fill (pfx => "my err"); + wait; + end process; +end behav; + diff --git a/testsuite/gna/issue1051/testsuite.sh b/testsuite/gna/issue1051/testsuite.sh new file mode 100755 index 000000000..ad45ae63a --- /dev/null +++ b/testsuite/gna/issue1051/testsuite.sh @@ -0,0 +1,32 @@ +#! /bin/sh + +. ../../testenv.sh + +FILES=" +psi_common_array_pkg.vhd +psi_common_math_pkg.vhd +psi_common_logic_pkg.vhd + +psi_tb_txt_util.vhd +psi_tb_compare_pkg.vhd +psi_tb_activity_pkg.vhd +psi_tb_i2c_pkg.vhd + +psi_common_bit_cc.vhd +psi_common_i2c_master.vhd +psi_common_i2c_master_tb.vhd +" + +export GHDL_STD_FLAGS="--std=08 -frelaxed" +analyze repro.vhdl +elab_simulate repro + +analyze repro2.vhdl +elab_simulate repro2 + +analyze $FILES +elab_simulate psi_common_i2c_master_tb + +clean + +echo "Test successful" |