From 8e6ec6ff565110672e28275fcda92c51181c8c43 Mon Sep 17 00:00:00 2001 From: c-thaler Date: Fri, 25 Oct 2024 00:38:39 +0200 Subject: [PATCH 01/10] initial checkin of APB master and slave VC --- .../src/apb_master.vhd | 147 ++++++++++++++++++ .../verification_components/src/apb_pkg.vhd | 53 +++++++ .../verification_components/src/apb_slave.vhd | 77 +++++++++ .../test/tb_apb_master.vhd | 138 ++++++++++++++++ 4 files changed, 415 insertions(+) create mode 100644 vunit/vhdl/verification_components/src/apb_master.vhd create mode 100644 vunit/vhdl/verification_components/src/apb_pkg.vhd create mode 100644 vunit/vhdl/verification_components/src/apb_slave.vhd create mode 100644 vunit/vhdl/verification_components/test/tb_apb_master.vhd diff --git a/vunit/vhdl/verification_components/src/apb_master.vhd b/vunit/vhdl/verification_components/src/apb_master.vhd new file mode 100644 index 000000000..cdd22ff77 --- /dev/null +++ b/vunit/vhdl/verification_components/src/apb_master.vhd @@ -0,0 +1,147 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2024, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +use work.bus_master_pkg.all; +use work.check_pkg.all; +use work.com_pkg.all; +use work.com_types_pkg.all; +use work.queue_pkg.all; +use work.sync_pkg.all; +use work.logger_pkg.all; +use work.log_levels_pkg.all; + +entity apb_master is + generic ( + bus_handle : bus_master_t; + drive_invalid : boolean := true; + drive_invalid_val : std_logic := 'X' + ); + port ( + clk : in std_logic; + reset : in std_logic; + psel_o : out std_logic; + penable_o : out std_logic; + paddr_o : out std_logic_vector(address_length(bus_handle) - 1 downto 0); + pwrite_o : out std_logic; + pwdata_o : out std_logic_vector(data_length(bus_handle) - 1 downto 0); + prdata_i : in std_logic_vector(data_length(bus_handle) - 1 downto 0); + pready_i : in std_logic + ); +end entity; + +architecture behav of apb_master is + constant message_queue : queue_t := new_queue; + signal idle_bus : boolean := true; + + impure function queues_empty return boolean is + begin + return is_empty(message_queue); + end function; + + impure function is_idle return boolean is + begin + return idle_bus; + end function; + +begin + + PROC_MAIN: process + variable request_msg : msg_t; + variable msg_type : msg_type_t; + begin + DISPATCH_LOOP : loop + receive(net, bus_handle.p_actor, request_msg); + msg_type := message_type(request_msg); + + if msg_type = bus_read_msg then + push(message_queue, request_msg); + elsif msg_type = bus_write_msg then + push(message_queue, request_msg); + elsif msg_type = wait_until_idle_msg then + if not is_idle or not queues_empty then + wait until is_idle and queues_empty and rising_edge(clk); + end if; + handle_wait_until_idle(net, msg_type, request_msg); + else + unexpected_msg_type(msg_type); + end if; + end loop; + end process; + + BUS_PROCESS: process + procedure drive_bus_invalid is + begin + if drive_invalid then + penable_o <= drive_invalid_val; + paddr_o <= (paddr_o'range => drive_invalid_val); + pwrite_o <= drive_invalid_val; + pwdata_o <= (pwdata_o'range => drive_invalid_val); + end if; + end procedure; + + variable request_msg, reply_msg : msg_t; + variable msg_type : msg_type_t; + variable addr_this_transaction : std_logic_vector(paddr_o'range) := (others => '0'); + variable data_this_transaction : std_logic_vector(prdata_i'range) := (others => '0'); + begin + loop + drive_bus_invalid; + psel_o <= '0'; + + if is_empty(message_queue) then + wait until rising_edge(clk) and not is_empty(message_queue); + end if; + idle_bus <= false; + wait for 0 ns; + + request_msg := pop(message_queue); + msg_type := message_type(request_msg); + + if msg_type = bus_write_msg then + addr_this_transaction := pop_std_ulogic_vector(request_msg); + data_this_transaction := pop_std_ulogic_vector(request_msg); + + psel_o <= '1'; + penable_o <= '0'; + pwrite_o <= '1'; + paddr_o <= addr_this_transaction; + pwdata_o <= data_this_transaction; + + wait until rising_edge(clk); + penable_o <= '1'; + wait until (pready_i and penable_o) = '1' and rising_edge(clk); + + if is_visible(bus_handle.p_logger, debug) then + debug(bus_handle.p_logger, + "Wrote 0x" & to_hstring(data_this_transaction) & + " to address 0x" & to_hstring(addr_this_transaction)); + end if; + + elsif msg_type = bus_read_msg then + addr_this_transaction := pop_std_ulogic_vector(request_msg); + + psel_o <= '1'; + penable_o <= '0'; + pwrite_o <= '0'; + paddr_o <= addr_this_transaction; + + wait until rising_edge(clk); + penable_o <= '1'; + wait until (pready_i and penable_o) = '1' and rising_edge(clk); + + reply_msg := new_msg; + push_std_ulogic_vector(reply_msg, prdata_i); + reply(net, request_msg, reply_msg); + end if; + + idle_bus <= true; + end loop; + end process; +end architecture; diff --git a/vunit/vhdl/verification_components/src/apb_pkg.vhd b/vunit/vhdl/verification_components/src/apb_pkg.vhd new file mode 100644 index 000000000..e9fa4fc12 --- /dev/null +++ b/vunit/vhdl/verification_components/src/apb_pkg.vhd @@ -0,0 +1,53 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2024, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +use work.com_pkg.all; +use work.com_types_pkg.all; +use work.logger_pkg.all; +use work.memory_pkg.memory_t; +use work.memory_pkg.to_vc_interface; + +package apb_pkg is + + type apb_slave_t is record + ready_high_probability : real range 0.0 to 1.0; + -- Private + p_actor : actor_t; + p_memory : memory_t; + p_logger : logger_t; + end record; + + constant apb_slave_logger : logger_t := get_logger("vunit_lib:apb_slave_pkg"); + impure function new_apb_slave( + memory : memory_t; + ready_high_probability : real := 1.0; + logger : logger_t := apb_slave_logger) + return apb_slave_t; + + constant slave_write_msg : msg_type_t := new_msg_type("apb slave write"); + constant slave_read_msg : msg_type_t := new_msg_type("apb slave read"); +end package; + +package body apb_pkg is + + impure function new_apb_slave( + memory : memory_t; + ready_high_probability : real := 1.0; + logger : logger_t := apb_slave_logger) + return apb_slave_t is + begin + return (p_actor => new_actor, + p_memory => to_vc_interface(memory, logger), + p_logger => logger, + ready_high_probability => ready_high_probability + ); + end; + +end package body; \ No newline at end of file diff --git a/vunit/vhdl/verification_components/src/apb_slave.vhd b/vunit/vhdl/verification_components/src/apb_slave.vhd new file mode 100644 index 000000000..7f698ec61 --- /dev/null +++ b/vunit/vhdl/verification_components/src/apb_slave.vhd @@ -0,0 +1,77 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2024, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +use work.memory_pkg.all; +use work.apb_pkg.all; +use work.logger_pkg.all; + +entity apb_slave is + generic ( + bus_handle : apb_slave_t; + drive_invalid : boolean := true; + drive_invalid_val : std_logic := 'X' + ); + port ( + clk : in std_logic; + reset : in std_logic; + psel_i : in std_logic; + penable_i : in std_logic; + paddr_i : in std_logic_vector; + pwrite_i : in std_logic; + pwdata_i : in std_logic_vector; + prdata_o : out std_logic_vector; + pready_o : out std_logic + ); +end entity; + +architecture a of apb_slave is + +begin + + PROC_MAIN: process + procedure drive_outputs_invalid is + begin + if drive_invalid then + prdata_o <= (prdata_o'range => drive_invalid_val); + pready_o <= drive_invalid_val; + end if; + end procedure; + + variable addr : integer; + begin + drive_outputs_invalid; + wait until rising_edge(clk); + + loop + -- IDLE/SETUP state + drive_outputs_invalid; + + wait until psel_i = '1' and rising_edge(clk); + -- ACCESS state + + pready_o <= '1'; + + addr := to_integer(unsigned(paddr_i)); + + if pwrite_i = '1' then + write_word(bus_handle.p_memory, addr, pwdata_i); + else + prdata_o <= read_word(bus_handle.p_memory, addr, prdata_o'length/8); + end if; + + wait until rising_edge(clk); + + if penable_i = '0' then + failure(bus_handle.p_logger, "penable_i must be active in the ACCESS phase."); + end if; + end loop; + end process; + +end architecture; \ No newline at end of file diff --git a/vunit/vhdl/verification_components/test/tb_apb_master.vhd b/vunit/vhdl/verification_components/test/tb_apb_master.vhd new file mode 100644 index 000000000..be8437b51 --- /dev/null +++ b/vunit/vhdl/verification_components/test/tb_apb_master.vhd @@ -0,0 +1,138 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2024, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +context work.vunit_context; +context work.com_context; +use work.memory_pkg.all; +use work.bus_master_pkg.all; +use work.apb_pkg.all; +use work.logger_pkg.all; + +library osvvm; +use osvvm.RandomPkg.all; + +entity tb_apb_master is + generic ( + runner_cfg : string + ); +end entity; + +architecture a of tb_apb_master is + + constant BUS_DATA_WIDTH : natural := 16; + constant BUS_ADDRESS_WIDTH : natural := 32; + + signal clk : std_logic := '0'; + signal reset : std_logic := '0'; + signal psel : std_logic; + signal penable : std_logic; + signal paddr : std_logic_vector(BUS_ADDRESS_WIDTH-1 downto 0); + signal pwrite : std_logic; + signal pwdata : std_logic_vector(BUS_DATA_WIDTH-1 downto 0); + signal prdata : std_logic_vector(BUS_DATA_WIDTH-1 downto 0); + signal pready : std_logic := '0'; + + constant bus_handle : bus_master_t := new_bus(data_length => pwdata'length, + address_length => paddr'length); + constant memory : memory_t := new_memory; + constant slave_handle : apb_slave_t := new_apb_slave(memory => memory, + logger => get_logger("apb slave")); + + signal start : boolean := false; +begin + + main_stim : process + variable buf : buffer_t; + variable data : std_logic_vector(prdata'range); + variable bus_ref1, bus_ref2 : bus_reference_t; + begin + show(get_logger("apb slave"), display_handler, debug); + + test_runner_setup(runner, runner_cfg); + start <= true; + wait for 0 ns; + + if run("single_write") then + buf := allocate(memory => memory, num_bytes => 2, permissions => write_only); + mock(get_logger(bus_handle), debug); + write_bus(net, bus_handle, base_address(buf), x"1122"); + wait_until_idle(net, bus_handle); + check_only_log(get_logger(bus_handle), "Wrote 0x1122 to address 0x00000000", debug); + unmock(get_logger(bus_handle)); + + elsif run("single_read") then + buf := allocate(memory => memory, num_bytes => 2, permissions => read_only); + write_word(memory, base_address(buf), x"1234"); + read_bus(net, bus_handle, base_address(buf), data); + check_equal(data, std_logic_vector'(x"1234"), "Check read data."); + + elsif run("consecutive_reads") then + buf := allocate(memory => memory, num_bytes => 4, permissions => read_only); + write_word(memory, base_address(buf), x"1234"); + write_word(memory, base_address(buf)+2, x"5678"); + read_bus(net, bus_handle, base_address(buf), bus_ref1); + read_bus(net, bus_handle, base_address(buf)+2, bus_ref2); + await_read_bus_reply(net, bus_ref1, data); + check_equal(data, std_logic_vector'(x"1234"), "Check read data."); + await_read_bus_reply(net, bus_ref2, data); + check_equal(data, std_logic_vector'(x"5678"), "Check read data."); + + elsif run("consecutive_writes") then + buf := allocate(memory => memory, num_bytes => 4, permissions => write_only); + set_expected_word(memory, base_address(buf), x"1234"); + set_expected_word(memory, base_address(buf)+2, x"5678"); + write_bus(net, bus_handle, base_address(buf), x"1234"); + write_bus(net, bus_handle, base_address(buf)+2, x"5678"); + wait_until_idle(net, bus_handle); + check_expected_was_written(memory); + + end if; + + wait for 100 ns; + + test_runner_cleanup(runner); + wait; + end process; + test_runner_watchdog(runner, 100 us); + + U_DUT_MASTER: entity work.apb_master + generic map ( + bus_handle => bus_handle + ) + port map ( + clk => clk, + reset => reset, + psel_o => psel, + penable_o => penable, + paddr_o => paddr, + pwrite_o => pwrite, + pwdata_o => pwdata, + prdata_i => prdata, + pready_i => pready + ); + + U_DUT_SLAVE: entity work.apb_slave + generic map ( + bus_handle => slave_handle + ) + port map ( + clk => clk, + reset => reset, + psel_i => psel, + penable_i => penable, + paddr_i => paddr, + pwrite_i => pwrite, + pwdata_i => pwdata, + prdata_o => prdata, + pready_o => pready + ); + + clk <= not clk after 5 ns; +end architecture; From 3ddb9878777eb5af69481b75f91e5a84706973b3 Mon Sep 17 00:00:00 2001 From: c-thaler Date: Fri, 25 Oct 2024 00:48:38 +0200 Subject: [PATCH 02/10] set expected word for single write test --- vunit/vhdl/verification_components/test/tb_apb_master.vhd | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vunit/vhdl/verification_components/test/tb_apb_master.vhd b/vunit/vhdl/verification_components/test/tb_apb_master.vhd index be8437b51..59ca40ae5 100644 --- a/vunit/vhdl/verification_components/test/tb_apb_master.vhd +++ b/vunit/vhdl/verification_components/test/tb_apb_master.vhd @@ -62,10 +62,12 @@ begin if run("single_write") then buf := allocate(memory => memory, num_bytes => 2, permissions => write_only); mock(get_logger(bus_handle), debug); + set_expected_word(memory, base_address(buf), x"1122"); write_bus(net, bus_handle, base_address(buf), x"1122"); wait_until_idle(net, bus_handle); check_only_log(get_logger(bus_handle), "Wrote 0x1122 to address 0x00000000", debug); unmock(get_logger(bus_handle)); + check_expected_was_written(memory); elsif run("single_read") then buf := allocate(memory => memory, num_bytes => 2, permissions => read_only); From b15d8228a5c49bed6027b869d37686c7d81fe1a8 Mon Sep 17 00:00:00 2001 From: c-thaler Date: Fri, 25 Oct 2024 02:07:01 +0200 Subject: [PATCH 03/10] implement wait states --- .../verification_components/src/apb_slave.vhd | 9 +++++++ .../test/tb_apb_master.vhd | 24 +++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/vunit/vhdl/verification_components/src/apb_slave.vhd b/vunit/vhdl/verification_components/src/apb_slave.vhd index 7f698ec61..8c1bd615a 100644 --- a/vunit/vhdl/verification_components/src/apb_slave.vhd +++ b/vunit/vhdl/verification_components/src/apb_slave.vhd @@ -8,6 +8,9 @@ library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; +library osvvm; +use osvvm.RandomPkg.RandomPType; + use work.memory_pkg.all; use work.apb_pkg.all; use work.logger_pkg.all; @@ -45,6 +48,7 @@ begin end procedure; variable addr : integer; + variable rnd : RandomPType; begin drive_outputs_invalid; wait until rising_edge(clk); @@ -56,6 +60,11 @@ begin wait until psel_i = '1' and rising_edge(clk); -- ACCESS state + while rnd.Uniform(0.0, 1.0) > bus_handle.ready_high_probability loop + pready_o <= '0'; + wait until rising_edge(clk); + end loop; + pready_o <= '1'; addr := to_integer(unsigned(paddr_i)); diff --git a/vunit/vhdl/verification_components/test/tb_apb_master.vhd b/vunit/vhdl/verification_components/test/tb_apb_master.vhd index 59ca40ae5..f2e24f55c 100644 --- a/vunit/vhdl/verification_components/test/tb_apb_master.vhd +++ b/vunit/vhdl/verification_components/test/tb_apb_master.vhd @@ -43,14 +43,15 @@ architecture a of tb_apb_master is address_length => paddr'length); constant memory : memory_t := new_memory; constant slave_handle : apb_slave_t := new_apb_slave(memory => memory, - logger => get_logger("apb slave")); + logger => get_logger("apb slave"), + ready_high_probability => 0.5); signal start : boolean := false; begin main_stim : process variable buf : buffer_t; - variable data : std_logic_vector(prdata'range); + variable data, data2 : std_logic_vector(prdata'range); variable bus_ref1, bus_ref2 : bus_reference_t; begin show(get_logger("apb slave"), display_handler, debug); @@ -95,6 +96,25 @@ begin wait_until_idle(net, bus_handle); check_expected_was_written(memory); + elsif run("many_reads") then + for i in 1 to 100 loop + buf := allocate(memory => memory, num_bytes => 2, permissions => read_only); + data := std_logic_vector(to_unsigned(i, BUS_DATA_WIDTH)); + write_word(memory, base_address(buf), data); + read_bus(net, bus_handle, base_address(buf), data2); + check_equal(data2, data, "Check read data."); + end loop; + + elsif run("many_writes") then + for i in 1 to 100 loop + buf := allocate(memory => memory, num_bytes => 2, permissions => write_only); + data := std_logic_vector(to_unsigned(i, BUS_DATA_WIDTH)); + set_expected_word(memory, base_address(buf), data); + write_bus(net, bus_handle, base_address(buf), data); + end loop; + wait_until_idle(net, bus_handle); + check_expected_was_written(memory); + end if; wait for 100 ns; From 4c9f5dc41872197f6c3401180207a00834761ad8 Mon Sep 17 00:00:00 2001 From: c-thaler Date: Mon, 28 Oct 2024 01:30:16 +0100 Subject: [PATCH 04/10] fix trailing whitespace --- vunit/vhdl/verification_components/test/tb_apb_master.vhd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vunit/vhdl/verification_components/test/tb_apb_master.vhd b/vunit/vhdl/verification_components/test/tb_apb_master.vhd index f2e24f55c..3bdc9a521 100644 --- a/vunit/vhdl/verification_components/test/tb_apb_master.vhd +++ b/vunit/vhdl/verification_components/test/tb_apb_master.vhd @@ -126,7 +126,7 @@ begin U_DUT_MASTER: entity work.apb_master generic map ( - bus_handle => bus_handle + bus_handle => bus_handle ) port map ( clk => clk, From b3ce11b1e43c8cc887f08f1483ccdd6b2b23899a Mon Sep 17 00:00:00 2001 From: c-thaler Date: Mon, 28 Oct 2024 01:32:30 +0100 Subject: [PATCH 05/10] fix another trailing whitespace --- vunit/vhdl/verification_components/src/apb_master.vhd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vunit/vhdl/verification_components/src/apb_master.vhd b/vunit/vhdl/verification_components/src/apb_master.vhd index cdd22ff77..9c46ccf7e 100644 --- a/vunit/vhdl/verification_components/src/apb_master.vhd +++ b/vunit/vhdl/verification_components/src/apb_master.vhd @@ -49,7 +49,7 @@ architecture behav of apb_master is begin return idle_bus; end function; - + begin PROC_MAIN: process From a3dd3617563a244486ccfb8e1741dc0dc1db99cc Mon Sep 17 00:00:00 2001 From: c-thaler Date: Tue, 29 Oct 2024 00:16:53 +0100 Subject: [PATCH 06/10] use single generic parameter All generics are now integrated into a single parameter of type apb_master_t. --- .../src/apb_master.vhd | 27 +- .../src/apb_master_pkg.vhd | 250 ++++++++++++++++++ .../verification_components/src/apb_slave.vhd | 2 +- .../src/{apb_pkg.vhd => apb_slave_pkg.vhd} | 4 +- .../test/tb_apb_master.vhd | 7 +- 5 files changed, 270 insertions(+), 20 deletions(-) create mode 100644 vunit/vhdl/verification_components/src/apb_master_pkg.vhd rename vunit/vhdl/verification_components/src/{apb_pkg.vhd => apb_slave_pkg.vhd} (96%) diff --git a/vunit/vhdl/verification_components/src/apb_master.vhd b/vunit/vhdl/verification_components/src/apb_master.vhd index 9c46ccf7e..70d3d7880 100644 --- a/vunit/vhdl/verification_components/src/apb_master.vhd +++ b/vunit/vhdl/verification_components/src/apb_master.vhd @@ -16,22 +16,21 @@ use work.queue_pkg.all; use work.sync_pkg.all; use work.logger_pkg.all; use work.log_levels_pkg.all; +use work.apb_master_pkg.all; entity apb_master is generic ( - bus_handle : bus_master_t; - drive_invalid : boolean := true; - drive_invalid_val : std_logic := 'X' + bus_handle : apb_master_t ); port ( clk : in std_logic; reset : in std_logic; psel_o : out std_logic; penable_o : out std_logic; - paddr_o : out std_logic_vector(address_length(bus_handle) - 1 downto 0); + paddr_o : out std_logic_vector(address_length(bus_handle.p_bus_handle) - 1 downto 0); pwrite_o : out std_logic; - pwdata_o : out std_logic_vector(data_length(bus_handle) - 1 downto 0); - prdata_i : in std_logic_vector(data_length(bus_handle) - 1 downto 0); + pwdata_o : out std_logic_vector(data_length(bus_handle.p_bus_handle) - 1 downto 0); + prdata_i : in std_logic_vector(data_length(bus_handle.p_bus_handle) - 1 downto 0); pready_i : in std_logic ); end entity; @@ -57,7 +56,7 @@ begin variable msg_type : msg_type_t; begin DISPATCH_LOOP : loop - receive(net, bus_handle.p_actor, request_msg); + receive(net, bus_handle.p_bus_handle.p_actor, request_msg); msg_type := message_type(request_msg); if msg_type = bus_read_msg then @@ -78,11 +77,11 @@ begin BUS_PROCESS: process procedure drive_bus_invalid is begin - if drive_invalid then - penable_o <= drive_invalid_val; - paddr_o <= (paddr_o'range => drive_invalid_val); - pwrite_o <= drive_invalid_val; - pwdata_o <= (pwdata_o'range => drive_invalid_val); + if bus_handle.p_drive_invalid then + penable_o <= bus_handle.p_drive_invalid_val; + paddr_o <= (paddr_o'range => bus_handle.p_drive_invalid_val); + pwrite_o <= bus_handle.p_drive_invalid_val; + pwdata_o <= (pwdata_o'range => bus_handle.p_drive_invalid_val); end if; end procedure; @@ -118,8 +117,8 @@ begin penable_o <= '1'; wait until (pready_i and penable_o) = '1' and rising_edge(clk); - if is_visible(bus_handle.p_logger, debug) then - debug(bus_handle.p_logger, + if is_visible(bus_handle.p_bus_handle.p_logger, debug) then + debug(bus_handle.p_bus_handle.p_logger, "Wrote 0x" & to_hstring(data_this_transaction) & " to address 0x" & to_hstring(addr_this_transaction)); end if; diff --git a/vunit/vhdl/verification_components/src/apb_master_pkg.vhd b/vunit/vhdl/verification_components/src/apb_master_pkg.vhd new file mode 100644 index 000000000..f7bff7fd3 --- /dev/null +++ b/vunit/vhdl/verification_components/src/apb_master_pkg.vhd @@ -0,0 +1,250 @@ +-- This Source Code Form is subject to the terms of the Mozilla Public +-- License, v. 2.0. If a copy of the MPL was not distributed with this file, +-- You can obtain one at http://mozilla.org/MPL/2.0/. +-- +-- Copyright (c) 2014-2024, Lars Asplund lars.anders.asplund@gmail.com + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +use work.bus_master_pkg.all; +use work.com_pkg.all; +use work.com_types_pkg.all; +use work.logger_pkg.all; +use work.memory_pkg.memory_t; +use work.memory_pkg.to_vc_interface; + +package apb_master_pkg is + + type apb_master_t is record + -- Private + p_bus_handle : bus_master_t; + p_drive_invalid : boolean; + p_drive_invalid_val : std_logic; + p_ready_high_probability : real range 0.0 to 1.0; + end record; + + impure function new_apb_master( + data_length : natural; + address_length : natural; + logger : logger_t := bus_logger; + actor : actor_t := null_actor; + drive_invalid : boolean := true; + drive_invalid_val : std_logic := 'X'; + ready_high_probability : real := 1.0 + ) return apb_master_t; + + function get_logger(bus_handle : apb_master_t) return logger_t; + + -- Blocking: Write the bus + procedure write_bus(signal net : inout network_t; + constant bus_handle : apb_master_t; + constant address : std_logic_vector; + constant data : std_logic_vector; + -- default byte enable is all bytes + constant byte_enable : std_logic_vector := ""); + procedure write_bus(signal net : inout network_t; + constant bus_handle : apb_master_t; + constant address : natural; + constant data : std_logic_vector; + -- default byte enable is all bytes + constant byte_enable : std_logic_vector := ""); + + procedure wait_until_idle(signal net : inout network_t; + bus_handle : apb_master_t); + + -- Non blocking: Read the bus returning a reference to the future reply + procedure read_bus(signal net : inout network_t; + constant bus_handle : apb_master_t; + constant address : std_logic_vector; + variable reference : inout bus_reference_t); + + procedure read_bus(signal net : inout network_t; + constant bus_handle : apb_master_t; + constant address : natural; + variable reference : inout bus_reference_t); + + -- Blocking: read bus with immediate reply + procedure read_bus(signal net : inout network_t; + constant bus_handle : apb_master_t; + constant address : std_logic_vector; + variable data : inout std_logic_vector); + + procedure read_bus(signal net : inout network_t; + constant bus_handle : apb_master_t; + constant address : natural; + variable data : inout std_logic_vector); + + -- Blocking: Read bus and check result against expected data + procedure check_bus(signal net : inout network_t; + constant bus_handle : apb_master_t; + constant address : std_logic_vector; + constant expected : std_logic_vector; + constant msg : string := ""); + + procedure check_bus(signal net : inout network_t; + constant bus_handle : apb_master_t; + constant address : natural; + constant expected : std_logic_vector; + constant msg : string := ""); + + -- Blocking: Wait until a read from address equals the value using + -- std_match If timeout is reached error with msg + procedure wait_until_read_equals( + signal net : inout network_t; + bus_handle : apb_master_t; + addr : std_logic_vector; + value : std_logic_vector; + timeout : delay_length := delay_length'high; + msg : string := ""); + + -- Blocking: Wait until a read from address has the bit with this + -- index set to value If timeout is reached error with msg + procedure wait_until_read_bit_equals( + signal net : inout network_t; + bus_handle : apb_master_t; + addr : std_logic_vector; + idx : natural; + value : std_logic; + timeout : delay_length := delay_length'high; + msg : string := ""); +end package; + +package body apb_master_pkg is + + impure function new_apb_master( + data_length : natural; + address_length : natural; + logger : logger_t := bus_logger; + actor : actor_t := null_actor; + drive_invalid : boolean := true; + drive_invalid_val : std_logic := 'X'; + ready_high_probability : real := 1.0 + ) return apb_master_t is + variable bus_handle : bus_master_t := new_bus( + data_length => data_length, + address_length => address_length, + logger => logger, + actor => actor + ); + begin + return ( + p_bus_handle => bus_handle, + p_drive_invalid => drive_invalid, + p_drive_invalid_val => drive_invalid_val, + p_ready_high_probability => ready_high_probability + ); + end; + + function get_logger(bus_handle : apb_master_t) return logger_t is + begin + return get_logger(bus_handle.p_bus_handle); + end function; + + -- Blocking: Write the bus + procedure write_bus(signal net : inout network_t; + constant bus_handle : apb_master_t; + constant address : std_logic_vector; + constant data : std_logic_vector; + -- default byte enable is all bytes + constant byte_enable : std_logic_vector := "") is + begin + write_bus(net, bus_handle.p_bus_handle, address, data, byte_enable); + end procedure; + + procedure write_bus(signal net : inout network_t; + constant bus_handle : apb_master_t; + constant address : natural; + constant data : std_logic_vector; + -- default byte enable is all bytes + constant byte_enable : std_logic_vector := "") is + begin + write_bus(net, bus_handle.p_bus_handle, address, data, byte_enable); + end procedure; + + procedure wait_until_idle(signal net : inout network_t; + bus_handle : apb_master_t) is + begin + wait_until_idle(net, bus_handle.P_bus_handle); + end procedure; + + -- Blocking: read bus with immediate reply + procedure read_bus(signal net : inout network_t; + constant bus_handle : apb_master_t; + constant address : std_logic_vector; + variable data : inout std_logic_vector) is + begin + read_bus(net, bus_handle.p_bus_handle, address, data); + end procedure; + + procedure read_bus(signal net : inout network_t; + constant bus_handle : apb_master_t; + constant address : natural; + variable data : inout std_logic_vector) is + begin + read_bus(net, bus_handle.p_bus_handle, address, data); + end procedure; + + procedure read_bus(signal net : inout network_t; + constant bus_handle : apb_master_t; + constant address : natural; + variable reference : inout bus_reference_t) is + begin + read_bus(net, bus_handle.p_bus_handle, address, reference); + end procedure; + + procedure read_bus(signal net : inout network_t; + constant bus_handle : apb_master_t; + constant address : std_logic_vector; + variable reference : inout bus_reference_t) is + begin + read_bus(net, bus_handle.p_bus_handle, address, reference); + end procedure; + + -- Blocking: Read bus and check result against expected data + procedure check_bus(signal net : inout network_t; + constant bus_handle : apb_master_t; + constant address : std_logic_vector; + constant expected : std_logic_vector; + constant msg : string := "") is + begin + check_bus(net, bus_handle.p_bus_handle, address, expected, msg); + end procedure; + + procedure check_bus(signal net : inout network_t; + constant bus_handle : apb_master_t; + constant address : natural; + constant expected : std_logic_vector; + constant msg : string := "") is + begin + check_bus(net, bus_handle.p_bus_handle, address, expected, msg); + end procedure; + + -- Blocking: Wait until a read from address equals the value using + -- std_match If timeout is reached error with msg + procedure wait_until_read_equals( + signal net : inout network_t; + bus_handle : apb_master_t; + addr : std_logic_vector; + value : std_logic_vector; + timeout : delay_length := delay_length'high; + msg : string := "") is + begin + wait_until_read_equals(net, bus_handle.p_bus_handle, addr, value, timeout, msg); + end procedure; + + -- Blocking: Wait until a read from address has the bit with this + -- index set to value If timeout is reached error with msg + procedure wait_until_read_bit_equals( + signal net : inout network_t; + bus_handle : apb_master_t; + addr : std_logic_vector; + idx : natural; + value : std_logic; + timeout : delay_length := delay_length'high; + msg : string := "") is + begin + wait_until_read_bit_equals(net, bus_handle.p_bus_handle, addr, idx, value, timeout, msg); + end procedure; +end package body; \ No newline at end of file diff --git a/vunit/vhdl/verification_components/src/apb_slave.vhd b/vunit/vhdl/verification_components/src/apb_slave.vhd index 8c1bd615a..3dfba6885 100644 --- a/vunit/vhdl/verification_components/src/apb_slave.vhd +++ b/vunit/vhdl/verification_components/src/apb_slave.vhd @@ -12,7 +12,7 @@ library osvvm; use osvvm.RandomPkg.RandomPType; use work.memory_pkg.all; -use work.apb_pkg.all; +use work.apb_slave_pkg.all; use work.logger_pkg.all; entity apb_slave is diff --git a/vunit/vhdl/verification_components/src/apb_pkg.vhd b/vunit/vhdl/verification_components/src/apb_slave_pkg.vhd similarity index 96% rename from vunit/vhdl/verification_components/src/apb_pkg.vhd rename to vunit/vhdl/verification_components/src/apb_slave_pkg.vhd index e9fa4fc12..c89a36945 100644 --- a/vunit/vhdl/verification_components/src/apb_pkg.vhd +++ b/vunit/vhdl/verification_components/src/apb_slave_pkg.vhd @@ -14,7 +14,7 @@ use work.logger_pkg.all; use work.memory_pkg.memory_t; use work.memory_pkg.to_vc_interface; -package apb_pkg is +package apb_slave_pkg is type apb_slave_t is record ready_high_probability : real range 0.0 to 1.0; @@ -35,7 +35,7 @@ package apb_pkg is constant slave_read_msg : msg_type_t := new_msg_type("apb slave read"); end package; -package body apb_pkg is +package body apb_slave_pkg is impure function new_apb_slave( memory : memory_t; diff --git a/vunit/vhdl/verification_components/test/tb_apb_master.vhd b/vunit/vhdl/verification_components/test/tb_apb_master.vhd index 3bdc9a521..719e96f30 100644 --- a/vunit/vhdl/verification_components/test/tb_apb_master.vhd +++ b/vunit/vhdl/verification_components/test/tb_apb_master.vhd @@ -12,7 +12,8 @@ context work.vunit_context; context work.com_context; use work.memory_pkg.all; use work.bus_master_pkg.all; -use work.apb_pkg.all; +use work.apb_slave_pkg.all; +use work.apb_master_pkg.all; use work.logger_pkg.all; library osvvm; @@ -39,8 +40,8 @@ architecture a of tb_apb_master is signal prdata : std_logic_vector(BUS_DATA_WIDTH-1 downto 0); signal pready : std_logic := '0'; - constant bus_handle : bus_master_t := new_bus(data_length => pwdata'length, - address_length => paddr'length); + constant bus_handle : apb_master_t := new_apb_master(data_length => pwdata'length, + address_length => paddr'length); constant memory : memory_t := new_memory; constant slave_handle : apb_slave_t := new_apb_slave(memory => memory, logger => get_logger("apb slave"), From 5aaf471f4af59d1724b6fd81e03a2bd7e9584302 Mon Sep 17 00:00:00 2001 From: c-thaler Date: Tue, 29 Oct 2024 01:15:36 +0100 Subject: [PATCH 07/10] reduce to single generic parameter in apb slave --- .../verification_components/src/apb_slave.vhd | 12 +++---- .../src/apb_slave_pkg.vhd | 32 +++++++++++++------ 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/vunit/vhdl/verification_components/src/apb_slave.vhd b/vunit/vhdl/verification_components/src/apb_slave.vhd index 3dfba6885..b4b61a7a4 100644 --- a/vunit/vhdl/verification_components/src/apb_slave.vhd +++ b/vunit/vhdl/verification_components/src/apb_slave.vhd @@ -17,9 +17,7 @@ use work.logger_pkg.all; entity apb_slave is generic ( - bus_handle : apb_slave_t; - drive_invalid : boolean := true; - drive_invalid_val : std_logic := 'X' + bus_handle : apb_slave_t ); port ( clk : in std_logic; @@ -41,9 +39,9 @@ begin PROC_MAIN: process procedure drive_outputs_invalid is begin - if drive_invalid then - prdata_o <= (prdata_o'range => drive_invalid_val); - pready_o <= drive_invalid_val; + if bus_handle.p_drive_invalid then + prdata_o <= (prdata_o'range => bus_handle.p_drive_invalid_val); + pready_o <= bus_handle.p_drive_invalid_val; end if; end procedure; @@ -60,7 +58,7 @@ begin wait until psel_i = '1' and rising_edge(clk); -- ACCESS state - while rnd.Uniform(0.0, 1.0) > bus_handle.ready_high_probability loop + while rnd.Uniform(0.0, 1.0) > bus_handle.p_ready_high_probability loop pready_o <= '0'; wait until rising_edge(clk); end loop; diff --git a/vunit/vhdl/verification_components/src/apb_slave_pkg.vhd b/vunit/vhdl/verification_components/src/apb_slave_pkg.vhd index c89a36945..5e00046e4 100644 --- a/vunit/vhdl/verification_components/src/apb_slave_pkg.vhd +++ b/vunit/vhdl/verification_components/src/apb_slave_pkg.vhd @@ -8,6 +8,7 @@ library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; +use work.bus_master_pkg.all; use work.com_pkg.all; use work.com_types_pkg.all; use work.logger_pkg.all; @@ -17,18 +18,23 @@ use work.memory_pkg.to_vc_interface; package apb_slave_pkg is type apb_slave_t is record - ready_high_probability : real range 0.0 to 1.0; -- Private p_actor : actor_t; p_memory : memory_t; p_logger : logger_t; + p_drive_invalid : boolean; + p_drive_invalid_val : std_logic; + p_ready_high_probability : real range 0.0 to 1.0; end record; constant apb_slave_logger : logger_t := get_logger("vunit_lib:apb_slave_pkg"); impure function new_apb_slave( memory : memory_t; - ready_high_probability : real := 1.0; - logger : logger_t := apb_slave_logger) + logger : logger_t := bus_logger; + actor : actor_t := null_actor; + drive_invalid : boolean := true; + drive_invalid_val : std_logic := 'X'; + ready_high_probability : real := 1.0) return apb_slave_t; constant slave_write_msg : msg_type_t := new_msg_type("apb slave write"); @@ -39,15 +45,21 @@ package body apb_slave_pkg is impure function new_apb_slave( memory : memory_t; - ready_high_probability : real := 1.0; - logger : logger_t := apb_slave_logger) + logger : logger_t := bus_logger; + actor : actor_t := null_actor; + drive_invalid : boolean := true; + drive_invalid_val : std_logic := 'X'; + ready_high_probability : real := 1.0) return apb_slave_t is begin - return (p_actor => new_actor, - p_memory => to_vc_interface(memory, logger), - p_logger => logger, - ready_high_probability => ready_high_probability - ); + return ( + p_memory => to_vc_interface(memory, logger), + p_logger => logger, + p_actor => new_actor, + p_drive_invalid => drive_invalid, + p_drive_invalid_val => drive_invalid_val, + p_ready_high_probability => ready_high_probability + ); end; end package body; \ No newline at end of file From 472d51430a1df907bb9bd3e9a9a714062fecd610 Mon Sep 17 00:00:00 2001 From: c-thaler Date: Tue, 29 Oct 2024 01:55:07 +0100 Subject: [PATCH 08/10] use null_actor/null_logger as defaults for master --- .../src/apb_master_pkg.vhd | 43 ++++++++++++------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/vunit/vhdl/verification_components/src/apb_master_pkg.vhd b/vunit/vhdl/verification_components/src/apb_master_pkg.vhd index f7bff7fd3..500ed0164 100644 --- a/vunit/vhdl/verification_components/src/apb_master_pkg.vhd +++ b/vunit/vhdl/verification_components/src/apb_master_pkg.vhd @@ -22,17 +22,15 @@ package apb_master_pkg is p_bus_handle : bus_master_t; p_drive_invalid : boolean; p_drive_invalid_val : std_logic; - p_ready_high_probability : real range 0.0 to 1.0; end record; impure function new_apb_master( data_length : natural; address_length : natural; - logger : logger_t := bus_logger; + logger : logger_t := null_logger; actor : actor_t := null_actor; drive_invalid : boolean := true; - drive_invalid_val : std_logic := 'X'; - ready_high_probability : real := 1.0 + drive_invalid_val : std_logic := 'X' ) return apb_master_t; function get_logger(bus_handle : apb_master_t) return logger_t; @@ -116,24 +114,37 @@ package body apb_master_pkg is impure function new_apb_master( data_length : natural; address_length : natural; - logger : logger_t := bus_logger; + logger : logger_t := null_logger; actor : actor_t := null_actor; drive_invalid : boolean := true; - drive_invalid_val : std_logic := 'X'; - ready_high_probability : real := 1.0 + drive_invalid_val : std_logic := 'X' ) return apb_master_t is - variable bus_handle : bus_master_t := new_bus( - data_length => data_length, - address_length => address_length, - logger => logger, - actor => actor - ); + impure function create_bus (logger : logger_t; actor : actor_t) return bus_master_t is + begin + return new_bus( + data_length => data_length, + address_length => address_length, + logger => logger, + actor => actor + ); + end function; + variable actor_tmp : actor_t := null_actor; + variable logger_tmp : logger_t := null_logger; begin + if actor = null_actor then + actor_tmp := new_actor; + else + actor_tmp := actor; + end if; + if logger = null_logger then + logger_tmp := bus_logger; + else + logger_tmp := logger; + end if; return ( - p_bus_handle => bus_handle, + p_bus_handle => create_bus(logger_tmp, actor_tmp), p_drive_invalid => drive_invalid, - p_drive_invalid_val => drive_invalid_val, - p_ready_high_probability => ready_high_probability + p_drive_invalid_val => drive_invalid_val ); end; From 8d74268a1704378505ee8161bd535a07d8ae3e67 Mon Sep 17 00:00:00 2001 From: c-thaler Date: Tue, 29 Oct 2024 01:58:56 +0100 Subject: [PATCH 09/10] use null_logger as default for apb slave --- .../src/apb_slave_pkg.vhd | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/vunit/vhdl/verification_components/src/apb_slave_pkg.vhd b/vunit/vhdl/verification_components/src/apb_slave_pkg.vhd index 5e00046e4..54adb5f71 100644 --- a/vunit/vhdl/verification_components/src/apb_slave_pkg.vhd +++ b/vunit/vhdl/verification_components/src/apb_slave_pkg.vhd @@ -30,7 +30,7 @@ package apb_slave_pkg is constant apb_slave_logger : logger_t := get_logger("vunit_lib:apb_slave_pkg"); impure function new_apb_slave( memory : memory_t; - logger : logger_t := bus_logger; + logger : logger_t := null_logger; actor : actor_t := null_actor; drive_invalid : boolean := true; drive_invalid_val : std_logic := 'X'; @@ -45,17 +45,29 @@ package body apb_slave_pkg is impure function new_apb_slave( memory : memory_t; - logger : logger_t := bus_logger; + logger : logger_t := null_logger; actor : actor_t := null_actor; drive_invalid : boolean := true; drive_invalid_val : std_logic := 'X'; ready_high_probability : real := 1.0) return apb_slave_t is + variable actor_tmp : actor_t := null_actor; + variable logger_tmp : logger_t := null_logger; begin + if actor = null_actor then + actor_tmp := new_actor; + else + actor_tmp := actor; + end if; + if logger = null_logger then + logger_tmp := bus_logger; + else + logger_tmp := logger; + end if; return ( p_memory => to_vc_interface(memory, logger), - p_logger => logger, - p_actor => new_actor, + p_logger => logger_tmp, + p_actor => actor_tmp, p_drive_invalid => drive_invalid, p_drive_invalid_val => drive_invalid_val, p_ready_high_probability => ready_high_probability From b1331b04672865d7137a00324e899d8bbbb462b4 Mon Sep 17 00:00:00 2001 From: c-thaler Date: Tue, 29 Oct 2024 02:01:31 +0100 Subject: [PATCH 10/10] let the apb_master ctor be handled by bus_master The bus_master implementation will create an actor if null_actor is given as parameter. So we don't have to do it in apb_master. --- .../verification_components/src/apb_master_pkg.vhd | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/vunit/vhdl/verification_components/src/apb_master_pkg.vhd b/vunit/vhdl/verification_components/src/apb_master_pkg.vhd index 500ed0164..3f73745c4 100644 --- a/vunit/vhdl/verification_components/src/apb_master_pkg.vhd +++ b/vunit/vhdl/verification_components/src/apb_master_pkg.vhd @@ -119,7 +119,7 @@ package body apb_master_pkg is drive_invalid : boolean := true; drive_invalid_val : std_logic := 'X' ) return apb_master_t is - impure function create_bus (logger : logger_t; actor : actor_t) return bus_master_t is + impure function create_bus (logger : logger_t) return bus_master_t is begin return new_bus( data_length => data_length, @@ -128,21 +128,15 @@ package body apb_master_pkg is actor => actor ); end function; - variable actor_tmp : actor_t := null_actor; variable logger_tmp : logger_t := null_logger; begin - if actor = null_actor then - actor_tmp := new_actor; - else - actor_tmp := actor; - end if; if logger = null_logger then logger_tmp := bus_logger; else logger_tmp := logger; end if; return ( - p_bus_handle => create_bus(logger_tmp, actor_tmp), + p_bus_handle => create_bus(logger_tmp), p_drive_invalid => drive_invalid, p_drive_invalid_val => drive_invalid_val );