Browse Source

Add more checks, coverage & tests

* Add functional coverage for block & rmw cycles
* Add some more checks for WB rules
* Add tests of block cycles
master
T. Meissner 3 years ago
parent
commit
dbfe16af59
3 changed files with 294 additions and 59 deletions
  1. +122
    -0
      wishbone/wishbone_pkg.vhd
  2. +31
    -25
      wishbone/wishbone_tb.vhd
  3. +141
    -34
      wishbone/wishbone_vip.vhd

+ 122
- 0
wishbone/wishbone_pkg.vhd View File

@ -8,8 +8,12 @@ use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
package wishbone_pkg is
type t_slv_array is array (natural range <>) of std_logic_vector;
type t_wb_syscon is record
Reset : std_logic;
Clk : std_logic;
@ -36,4 +40,122 @@ package wishbone_pkg is
Tgd : std_logic_vector;
end record;
function to_string(wb : t_wb_master) return string;
function to_string(wb : t_wb_slave) return string;
procedure log_wishbone (signal clk : std_logic; master : t_wb_master; slave : t_wb_slave);
procedure cycle (signal syscon : in t_wb_syscon;
signal master : out t_wb_master;
signal slave : in t_wb_slave;
Wen : in std_logic;
Adr : in std_logic_vector;
WDat : in std_logic_vector;
RDat : out std_logic_vector);
procedure single_cycle (signal syscon : in t_wb_syscon;
signal master : out t_wb_master;
signal slave : in t_wb_slave;
Wen : in std_logic;
Adr : in std_logic_vector;
WDat : in std_logic_vector;
RDat : out std_logic_vector);
procedure block_cycle (signal syscon : in t_wb_syscon;
signal master : out t_wb_master;
signal slave : in t_wb_slave;
Wen : in std_logic;
Adr : in t_slv_array;
WDat : in t_slv_array;
RDat : out t_slv_array);
end package wishbone_pkg;
package body wishbone_pkg is
procedure cycle (signal syscon : in t_wb_syscon;
signal master : out t_wb_master;
signal slave : in t_wb_slave;
Wen : in std_logic;
Adr : in std_logic_vector;
WDat : in std_logic_vector;
RDat : out std_logic_vector) is
begin
master.Cyc <= '1';
master.Stb <= '1';
master.We <= Wen;
master.Adr <= Adr;
master.Dat <= WDat;
wait until rising_edge(syscon.Clk) and (slave.Ack or slave.Err or slave.Rty) = '1';
RDat := slave.Dat;
end procedure cycle;
procedure single_cycle (signal syscon : in t_wb_syscon;
signal master : out t_wb_master;
signal slave : in t_wb_slave;
Wen : in std_logic;
Adr : in std_logic_vector;
WDat : in std_logic_vector;
RDat : out std_logic_vector) is
begin
cycle(syscon, master, slave, Wen, Adr, WDat, RDat);
master.Cyc <= '0';
master.Stb <= '0';
master.We <= '0';
master.Adr <= (master.Adr'range => '0');
master.Dat <= (master.Dat'range => '0');
wait until rising_edge(syscon.Clk);
end procedure single_cycle;
procedure block_cycle (signal syscon : in t_wb_syscon;
signal master : out t_wb_master;
signal slave : in t_wb_slave;
Wen : in std_logic;
Adr : in t_slv_array;
WDat : in t_slv_array;
RDat : out t_slv_array) is
begin
assert Adr'length = WDat'length and WDat'length = RDat'length;
for i in Adr'low to Adr'high-1 loop
cycle(syscon, master, slave, Wen, Adr(i), WDat(i), RDat(i));
end loop;
single_cycle(syscon, master, slave, Wen, Adr(Adr'high), WDat(WDat'high), RDat(RDat'high));
end procedure block_cycle;
function to_string(wb : t_wb_master) return string is
begin
return "Cyc: " & to_string(wb.Cyc) & LF &
"Stb: " & to_string(wb.Stb) & LF &
"We: " & to_string(wb.We) & LF &
"Lock: " & to_string(wb.Lock) & LF &
"Adr: " & to_hstring(wb.Adr) & LF &
"Dat: " & to_hstring(wb.Dat) & LF &
"Sel: " & to_hstring(wb.Sel) & LF &
"Tgc: " & to_hstring(wb.Tgc) & LF &
"Tga: " & to_hstring(wb.Tga) & LF &
"Tgd: " & to_hstring(wb.Tgd);
end function to_string;
function to_string(wb : t_wb_slave) return string is
begin
return "Ack: " & to_string(wb.Ack) & LF &
"Err: " & to_string(wb.Err) & LF &
"Rty: " & to_string(wb.Rty) & LF &
"Dat: " & to_hstring(wb.Dat) & LF &
"Tgd: " & to_hstring(wb.Tgd);
end function to_string;
procedure log_wishbone (signal clk : std_logic; master : t_wb_master; slave : t_wb_slave) is
begin
wait until rising_edge(clk);
report "Wishbone master:" & LF & to_string(master);
report "Wishbone slave:" & LF & to_string(slave);
end procedure log_wishbone;
end package body wishbone_pkg;

+ 31
- 25
wishbone/wishbone_tb.vhd View File

@ -21,6 +21,7 @@ end entity wishbone_tb;
architecture testbench of wishbone_tb is
constant C_CLOCK_PERIOD : time := 2 ns;
constant C_WB_ADDR_WIDTH : positive := 32;
constant C_WB_DATA_WIDTH : positive := 32;
@ -39,53 +40,58 @@ architecture testbench of wishbone_tb is
signal s_wb_slave : t_wb_slave(Dat(C_WB_DATA_WIDTH-1 downto 0),
Tgd(C_WB_TGD_WIDTH-1 downto 0));
signal s_wb_slave_resp : std_logic_vector(2 downto 0);
alias s_clk is s_wb_syscon.Clk;
alias s_reset is s_wb_syscon.Reset;
begin
s_clk <= not s_clk after 1 ns;
s_clk <= not s_clk after C_CLOCK_PERIOD / 2;
s_reset <= '0' after 4 ns;
s_wb_slave_resp <= s_wb_slave.Rty & s_wb_slave.Err & s_wb_slave.Ack;
wb_master_p : process is
subtype t_wb_array is t_slv_array(0 to 7)(open);
variable v_wb_adr : t_wb_array(open)(C_WB_ADDR_WIDTH-1 downto 0);
variable v_wb_wdata : t_wb_array(open)(C_WB_DATA_WIDTH-1 downto 0);
variable v_wb_rdata : t_wb_array(open)(C_WB_DATA_WIDTH-1 downto 0);
begin
s_wb_master.Cyc <= '0';
s_wb_master.Stb <= '0';
wait until s_reset = '0';
-- simple reads
for i in 0 to 2 loop
wait until rising_edge(s_clk);
s_wb_master.Cyc <= '1';
s_wb_master.Stb <= '1';
s_wb_master.We <= '0';
wait until s_wb_slave_resp(i) = '1' and rising_edge(s_clk);
report "Master: end simple read cycle";
s_wb_master.Cyc <= '0';
s_wb_master.Stb <= '0';
-- single read cycles
for wait_cycles in 0 to 3 loop
single_cycle(s_wb_syscon, s_wb_master, s_wb_slave, '0', v_wb_adr(0), v_wb_wdata(0), v_wb_rdata(0));
end loop;
-- single write cycles
for wait_cycles in 0 to 3 loop
single_cycle(s_wb_syscon, s_wb_master, s_wb_slave, '1', v_wb_adr(0), v_wb_wdata(0), v_wb_rdata(0));
end loop;
-- block cycles
block_cycle(s_wb_syscon, s_wb_master, s_wb_slave, '0', v_wb_adr, v_wb_wdata, v_wb_rdata);
block_cycle(s_wb_syscon, s_wb_master, s_wb_slave, '1', v_wb_adr, v_wb_wdata, v_wb_rdata);
wait for 10 ns;
stop(0);
end process wb_master_p;
wb_slave_p : process is
variable v_resp : std_logic_vector(2 downto 0) := "000";
begin
(s_wb_slave.Rty, s_wb_slave.Err, s_wb_slave.Ack) <= v_resp;
s_wb_slave.Rty <= '0';
s_wb_slave.Err <= '0';
s_wb_slave.Ack <= '0';
wait until s_reset = '0';
-- simple read
for i in 0 to 2 loop
wait until (s_wb_master.Cyc and s_wb_master.Stb) = '1' and rising_edge(s_clk);
v_resp(i) := '1';
(s_wb_slave.Rty, s_wb_slave.Err, s_wb_slave.Ack) <= v_resp;
wait until not s_wb_master.Stb;
v_resp(i) := '0';
(s_wb_slave.Rty, s_wb_slave.Err, s_wb_slave.Ack) <= v_resp;
loop
for wait_cycles in 0 to 3 loop
wait until rising_edge(s_clk) and (s_wb_master.Cyc and s_wb_master.Stb) = '1';
if wait_cycles /= 0 then
wait for C_CLOCK_PERIOD * wait_cycles - 1 ps;
wait until rising_edge(s_clk);
end if;
s_wb_slave.Ack <= '1';
wait until rising_edge(s_clk);
s_wb_slave.Ack <= '0';
end loop;
end loop;
wait;
end process wb_slave_p;


+ 141
- 34
wishbone/wishbone_vip.vhd View File

@ -111,58 +111,165 @@ begin
end if;
end process;
sequence s_stb_not_term is {WbMaster.Stb = '1' and s_wb_slave_resp = "000"};
-- RULE 3.20
STB_RESET_a : assert always Reset -> not WbMaster.Stb;
CYC_RESET_a : assert always Reset -> not WbMaster.Cyc;
-- Synchonous reset
STB_RESET_a : assert always Reset -> next not WbMaster.Stb;
CYC_RESET_a : assert always Reset -> next not WbMaster.Cyc;
-- RULE 3.25
STB_CYC_a : assert always WbMaster.Stb -> WbMaster.Cyc;
STB_CYC_0_a : assert always WbMaster.Stb -> WbMaster.Cyc;
STB_CYC_1_a : assert always WbMaster.Stb and not s_wb_master.Stb ->
WbMaster.Cyc until not WbMaster.Stb;
-- RULE 3.35
ACK_ERR_RTY_CYC_STB_a : assert always one_hot(s_wb_slave_resp) ->
s_wb_master.Cyc and s_wb_master.Stb;
-- RULE 3.45
ACK_ERR_RTY_ONEHOT_a : assert always one_hot_0(s_wb_slave_resp);
-- RULE 3.50
ACK_ERR_RTY_STB_a : assert always or s_wb_slave_resp -> WbMaster.Stb;
ACK_ERR_RTY_STB_a : assert always one_hot(s_wb_slave_resp) -> WbMaster.Stb;
DAT_STABLE_STB_a : assert always WbMaster.Stb and s_wb_slave_resp = "000" ->
next (WbMaster.Dat = s_wb_master.Dat);
-- RULE 3.60
ADR_STABLE_STB_a : assert always s_stb_not_term |=> WbMaster.Adr = s_wb_master.Adr;
DAT_STABLE_STB_a : assert always s_stb_not_term |=> WbMaster.Dat = s_wb_master.Dat;
SEL_STABLE_STB_a : assert always s_stb_not_term |=> WbMaster.Sel = s_wb_master.Sel;
WE_STABLE_STB_a : assert always s_stb_not_term |=> WbMaster.We = s_wb_master.We;
TGC_STABLE_STB_a : assert always s_stb_not_term |=> WbMaster.Tgc = s_wb_master.Tgc;
TGA_STABLE_STB_a : assert always s_stb_not_term |=> WbMaster.Tga = s_wb_master.Tga;
TGD_STABLE_STB_a : assert always s_stb_not_term |=> WbMaster.Tgd = s_wb_master.Tgd;
end generate ASSERTS_G;
COVERAGE_G : if COVERAGE generate
sequence s_single_read (boolean resp) is {
not WbMaster.Cyc;
WbMaster.Cyc and not WbMaster.Stb[*];
{s_wb_slave_resp = "000"[*]; resp} &&
{WbMaster.Cyc and WbMaster.Stb and not WbMaster.We}[+]
};
sequence s_single_write (boolean resp) is {
not WbMaster.Cyc;
WbMaster.Cyc and not WbMaster.Stb[*];
{s_wb_slave_resp = "000"[*]; resp} &&
{WbMaster.Cyc and WbMaster.Stb and WbMaster.We}[+]
};
SINGLE_READ_ACKED_c : cover s_single_read(WbSlave.Ack)
report "Single read with ack finished";
SINGLE_READ_ERROR_c : cover s_single_read(WbSlave.Err)
report "Single read with error finished";
SINGLE_READ_RETRY_c : cover s_single_read(WbSlave.Rty)
report "Single read with retry finished";
CLASSIC_G : if MODE = "CLASSIC" generate
SINGLE_WRITE_ACKED_c : cover s_single_write(WbSlave.Ack)
report "Single read with ack finished";
-- Non waited single cycles, async slave termination or slave knows
-- address in advance or it doesn't matter
sequence s_single_read (boolean resp) is {
not WbMaster.Cyc;
WbMaster.Cyc and not WbMaster.Stb[*];
{resp} &&
{WbMaster.Cyc and WbMaster.Stb and not WbMaster.We}[+];
not WbMaster.Cyc
};
sequence s_single_write (boolean resp) is {
not WbMaster.Cyc;
WbMaster.Cyc and not WbMaster.Stb[*];
{resp} &&
{WbMaster.Cyc and WbMaster.Stb and WbMaster.We}[+];
not WbMaster.Cyc
};
SINGLE_READ_ACKED_c : cover s_single_read(WbSlave.Ack)
report "Single read with ack finished";
SINGLE_READ_ERROR_c : cover s_single_read(WbSlave.Err)
report "Single read with error finished";
SINGLE_READ_RETRY_c : cover s_single_read(WbSlave.Rty)
report "Single read with retry finished";
SINGLE_WRITE_ACKED_c : cover s_single_write(WbSlave.Ack)
report "Single write with ack finished";
SINGLE_WRITE_ERROR_c : cover s_single_write(WbSlave.Err)
report "Single write with error finished";
SINGLE_WRITE_RETRY_c : cover s_single_write(WbSlave.Rty)
report "Single write with retry finished";
-- Waited single cycles, slave termination at least one cycle after stb
-- is asswerted
sequence s_single_waited_read (boolean resp) is {
not WbMaster.Cyc;
WbMaster.Cyc and not WbMaster.Stb[*];
{s_wb_slave_resp = "000"[+]; resp} &&
{WbMaster.Cyc and WbMaster.Stb and not WbMaster.We}[+];
not WbMaster.Cyc
};
sequence s_single_waited_write (boolean resp) is {
not WbMaster.Cyc;
WbMaster.Cyc and not WbMaster.Stb[*];
{s_wb_slave_resp = "000"[+]; resp} &&
{WbMaster.Cyc and WbMaster.Stb and WbMaster.We}[+];
not WbMaster.Cyc
};
SINGLE_READ_WAITED_ACKED_c : cover s_single_waited_read(WbSlave.Ack)
report "Single waited read with ack finished";
SINGLE_READ_WAITED_ERROR_c : cover s_single_waited_read(WbSlave.Err)
report "Single waited read with error finished";
SINGLE_READ_WAITED_RETRY_c : cover s_single_waited_read(WbSlave.Rty)
report "Single waited read with retry finished";
SINGLE_WRITE_WAITED_ACKED_c : cover s_single_waited_write(WbSlave.Ack)
report "Single waited write with ack finished";
SINGLE_WRITE_WAITED_ERROR_c : cover s_single_waited_write(WbSlave.Err)
report "Single waited write with error finished";
SINGLE_WRITE_WAITED_RETRY_c : cover s_single_waited_write(WbSlave.Rty)
report "Single waited write with retry finished";
-- Block cycles, not distinguished between waited & non-waited
-- We don't cover each possible combination of slave termination
-- during block cycles, as there are a lot for longer block cycles
sequence s_block_read (boolean resp) is {
not WbMaster.Cyc;
{WbMaster.Cyc and not WbMaster.Stb[*];
{s_wb_slave_resp = "000"[*]; resp} &&
{WbMaster.Cyc and WbMaster.Stb and not WbMaster.We}[+]}[*2 to inf];
not WbMaster.Cyc
};
sequence s_block_write (boolean resp) is {
not WbMaster.Cyc;
{WbMaster.Cyc and not WbMaster.Stb[*];
{s_wb_slave_resp = "000"[*]; resp} &&
{WbMaster.Cyc and WbMaster.Stb and WbMaster.We}[+]}[*2 to inf];
not WbMaster.Cyc
};
BLOCK_READ_ACKED_c : cover s_block_read(one_hot(s_wb_slave_resp))
report "Block read finished";
BLOCK_WRITE_ACKED_c : cover s_block_write(one_hot(s_wb_slave_resp))
report "Block write finished";
-- Read-modified-write cycles, not distinguished between waited & non-waited
-- We don't cover each possible combination of slave termniation
-- during read-modified-write cycles
sequence s_rmw_read (boolean resp) is {
not WbMaster.Cyc;
{WbMaster.Cyc and not WbMaster.Stb[*];
{s_wb_slave_resp = "000"[*]; resp} &&
{WbMaster.Cyc and WbMaster.Stb and not WbMaster.We}[+]};
{WbMaster.Cyc and not WbMaster.Stb[*];
{s_wb_slave_resp = "000"[*]; resp} &&
{WbMaster.Cyc and WbMaster.Stb and WbMaster.We}[+]};
not WbMaster.Cyc
};
READ_MODIFIED_WRITE_ACKED_c : cover s_rmw_read(one_hot(s_wb_slave_resp))
report "Read-modified-write finished";
SINGLE_WRITE_ERROR_c : cover s_single_write(WbSlave.Err)
report "Single read with error finished";
end generate CLASSIC_G;
SINGLE_WRITE_RETRY_c : cover s_single_write(WbSlave.Rty)
report "Single read with retry finished";
end generate COVERAGE_G;


Loading…
Cancel
Save