-- 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;
|
|
|
|
|
|
sequence s_stb_not_term is {WbMaster.Stb = '1' and s_wb_slave_resp = "000"};
|
|
|
|
-- RULE 3.20
|
|
-- 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_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 one_hot(s_wb_slave_resp) -> WbMaster.Stb;
|
|
|
|
-- 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
|
|
|
|
CLASSIC_G : if MODE = "CLASSIC" generate
|
|
|
|
-- 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";
|
|
|
|
end generate CLASSIC_G;
|
|
|
|
|
|
end generate COVERAGE_G;
|
|
|
|
|
|
end architecture verification;
|