From dbfe16af5919679741be4f162a4840cb7a27b70d Mon Sep 17 00:00:00 2001 From: tmeissner Date: Thu, 28 Oct 2021 20:18:17 +0200 Subject: [PATCH] Add more checks, coverage & tests * Add functional coverage for block & rmw cycles * Add some more checks for WB rules * Add tests of block cycles --- wishbone/wishbone_pkg.vhd | 122 ++++++++++++++++++++++++++ wishbone/wishbone_tb.vhd | 56 ++++++------ wishbone/wishbone_vip.vhd | 175 ++++++++++++++++++++++++++++++-------- 3 files changed, 294 insertions(+), 59 deletions(-) diff --git a/wishbone/wishbone_pkg.vhd b/wishbone/wishbone_pkg.vhd index f19d574..a94e97f 100644 --- a/wishbone/wishbone_pkg.vhd +++ b/wishbone/wishbone_pkg.vhd @@ -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; diff --git a/wishbone/wishbone_tb.vhd b/wishbone/wishbone_tb.vhd index 769dc3e..63e57f8 100644 --- a/wishbone/wishbone_tb.vhd +++ b/wishbone/wishbone_tb.vhd @@ -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; diff --git a/wishbone/wishbone_vip.vhd b/wishbone/wishbone_vip.vhd index 8489dbe..292cec3 100644 --- a/wishbone/wishbone_vip.vhd +++ b/wishbone/wishbone_vip.vhd @@ -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;