diff --git a/wishbone/Makefile b/wishbone/Makefile new file mode 100644 index 0000000..8774660 --- /dev/null +++ b/wishbone/Makefile @@ -0,0 +1,13 @@ +.PHONY: sim clean + +wishbone_tb: wishbone_pkg.vhd wishbone_vip.vhd wishbone_tb.vhd + ghdl -a --std=08 --work=wishbone wishbone_pkg.vhd wishbone_vip.vhd + ghdl -a --std=08 wishbone_tb.vhd + ghdl -e --std=08 wishbone_tb + +sim: wishbone_tb + ./wishbone_tb --wave=$<.ghw --psl-report=$<.json + +clean: + rm -rf *.o *.cf *.ghw *.json + rm -rf wishbone_tb diff --git a/wishbone/wishbone_pkg.vhd b/wishbone/wishbone_pkg.vhd new file mode 100644 index 0000000..f19d574 --- /dev/null +++ b/wishbone/wishbone_pkg.vhd @@ -0,0 +1,39 @@ +-- Simple wishbone verification IP +-- For use with GHDL only +-- Suitable for simulation & formal verification +-- Copyright 2021 by Torsten Meissner (programming@goodcleanfun.de) + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + + +package wishbone_pkg is + + type t_wb_syscon is record + Reset : std_logic; + Clk : std_logic; + end record; + + type t_wb_master is record + Cyc : std_logic; + Stb : std_logic; + We : std_logic; + Lock : std_logic; + Adr : std_logic_vector; + Dat : std_logic_vector; + Sel : std_logic_vector; + Tgc : std_logic_vector; + Tga : std_logic_vector; + Tgd : std_logic_vector; + end record; + + type t_wb_slave is record + Ack : std_logic; + Err : std_logic; + Rty : std_logic; + Dat : std_logic_vector; + Tgd : std_logic_vector; + end record; + +end package wishbone_pkg; diff --git a/wishbone/wishbone_tb.vhd b/wishbone/wishbone_tb.vhd new file mode 100644 index 0000000..769dc3e --- /dev/null +++ b/wishbone/wishbone_tb.vhd @@ -0,0 +1,105 @@ +-- Simple wishbone verification IP +-- For use with GHDL only +-- Suitable for simulation & formal verification +-- Copyright 2021 by Torsten Meissner (programming@goodcleanfun.de) + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +use std.env.all; + +library wishbone; +use wishbone.wishbone_pkg.all; + + + +entity wishbone_tb is +end entity wishbone_tb; + + + +architecture testbench of wishbone_tb is + + + constant C_WB_ADDR_WIDTH : positive := 32; + constant C_WB_DATA_WIDTH : positive := 32; + constant C_WB_SEL_WIDTH : positive := 4; + constant C_WB_TGA_WIDTH : positive := 4; + constant C_WB_TGC_WIDTH : positive := 4; + constant C_WB_TGD_WIDTH : positive := 4; + + signal s_wb_syscon : t_wb_syscon := ('1', '1'); + signal s_wb_master : t_wb_master(Adr(C_WB_ADDR_WIDTH-1 downto 0), + Dat(C_WB_DATA_WIDTH-1 downto 0), + Sel(C_WB_SEL_WIDTH-1 downto 0), + Tgc(C_WB_TGC_WIDTH-1 downto 0), + Tga(C_WB_TGA_WIDTH-1 downto 0), + Tgd(C_WB_TGD_WIDTH-1 downto 0)); + 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_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 + 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'; + end loop; + 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; + 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; + end loop; + wait; + end process wb_slave_p; + + + i_wishbone_vip : entity wishbone.wishbone_vip + generic map ( + MODE => "CLASSIC" + ) + port map ( + WbSysCon => s_wb_syscon, + WbMaster => s_wb_master, + WbSlave => s_wb_slave + ); + + +end testbench; \ No newline at end of file diff --git a/wishbone/wishbone_vip.vhd b/wishbone/wishbone_vip.vhd new file mode 100644 index 0000000..8489dbe --- /dev/null +++ b/wishbone/wishbone_vip.vhd @@ -0,0 +1,170 @@ +-- Simple wishbone verification IP +-- For use with GHDL only +-- Suitable for simulation & formal verification +-- Copyright 2021 by Torsten Meissner (programming@goodcleanfun.de) + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library wishbone; +use wishbone.wishbone_pkg.all; + + + +entity wishbone_vip is + generic ( + MODE : string := "CLASSIC"; + ASSERTS : boolean := true; + COVERAGE : boolean := true + ); + port ( + -- syscon signals + WbSysCon : in t_wb_syscon; + -- master signals + WbMaster : in t_wb_master; + -- slave signals + WbSlave : in t_wb_slave + ); +end entity wishbone_vip; + + + +architecture verification of wishbone_vip is + + + function count_ones (data : in std_logic_vector) return natural is + variable v_return : natural := 0; + begin + for i in data'range loop + if (to_x01(data(i)) = '1') then + v_return := v_return + 1; + end if; + end loop; + return v_return; + end function count_ones; + + function one_hot (data : in std_logic_vector) return boolean is + begin + return count_ones(data) = 1; + end function one_hot; + + function one_hot_0 (data : in std_logic_vector) return boolean is + begin + return count_ones(data) <= 1; + end function one_hot_0; + + alias Clk is WbSysCon.Clk; + alias Reset is WbSysCon.Reset; + + signal s_wb_slave_resp : std_logic_vector(2 downto 0); + + +begin + + + s_wb_slave_resp <= WbSlave.Rty & WbSlave.Err & WbSlave.Ack; + + -- Static interface checks + -- Always enabled, regardless of generic ASSERTS setting + + MODE_a : assert MODE = "CLASSIC" + report "ERROR: Unsupported mode" + severity failure; + + DATA_MS_WIDTH_a : assert WbMaster.Dat'length = 8 or WbMaster.Dat'length = 16 or + WbMaster.Dat'length = 32 or WbMaster.Dat'length = 64 + report "ERROR: Invalid Master Data length" + severity failure; + + DATA_SM_WIDTH_a : assert WbSlave.Dat'length = 8 or WbSlave.Dat'length = 16 or + WbSlave.Dat'length = 32 or WbSlave.Dat'length = 64 + report "ERROR: Invalid Slave Data length" + severity failure; + + DATA_EQUAL_WIDTH_a : assert WbMaster.Dat'length = WbSlave.Dat'length + report "ERROR: Master & Slave Data don't have equal length" + severity failure; + + + default clock is rising_edge(Clk); + + ASSERTS_G : if ASSERTS generate + + signal s_wb_master : t_wb_master(Adr(WbMaster.Adr'range), + Dat(WbMaster.Dat'range), + Sel(WbMaster.Sel'range), + Tgc(WbMaster.Tgc'range), + Tga(WbMaster.Tga'range), + Tgd(WbMaster.Tgd'range)); + signal s_wb_slave : t_wb_slave(Dat(WbSlave.Dat'range), + Tgd(WbSlave.Tgd'range)); + + begin + + -- Create copies of bus signals + process (Clk) is + begin + if rising_edge(Clk) then + s_wb_master <= WbMaster; + s_wb_slave <= WbSlave; + end if; + end process; + + -- RULE 3.20 + STB_RESET_a : assert always Reset -> not WbMaster.Stb; + CYC_RESET_a : assert always Reset -> not WbMaster.Cyc; + + -- RULE 3.25 + STB_CYC_a : assert always WbMaster.Stb -> WbMaster.Cyc; + + -- 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; + + DAT_STABLE_STB_a : assert always WbMaster.Stb and s_wb_slave_resp = "000" -> + next (WbMaster.Dat = s_wb_master.Dat); + + 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"; + + SINGLE_WRITE_ACKED_c : cover s_single_write(WbSlave.Ack) + report "Single read with ack finished"; + + SINGLE_WRITE_ERROR_c : cover s_single_write(WbSlave.Err) + report "Single read with error finished"; + + SINGLE_WRITE_RETRY_c : cover s_single_write(WbSlave.Rty) + report "Single read with retry finished"; + + end generate COVERAGE_G; + + +end architecture verification;