From bf25339ec5dae3da0cadb13c803706693924e7bf Mon Sep 17 00:00:00 2001 From: Reinier van der Walle <walle@astron.nl> Date: Thu, 6 Apr 2023 11:02:19 +0200 Subject: [PATCH] Added testbench + removed ready-latency adapters removed ready-latency adapters from axi4-lite_mm_bridge as these are not needed. Placed functions in package. Also added testbench. --- libraries/base/axi4/hdllib.cfg | 4 +- .../axi4/src/vhdl/axi4_lite_mm_bridge.vhd | 138 ++-------------- .../base/axi4/src/vhdl/axi4_lite_pkg.vhd | 62 ++++++++ .../axi4/tb/vhdl/tb_axi4_lite_mm_bridge.vhd | 147 ++++++++++++++++++ 4 files changed, 227 insertions(+), 124 deletions(-) create mode 100644 libraries/base/axi4/tb/vhdl/tb_axi4_lite_mm_bridge.vhd diff --git a/libraries/base/axi4/hdllib.cfg b/libraries/base/axi4/hdllib.cfg index 6cef475d8b..0b31ec4667 100644 --- a/libraries/base/axi4/hdllib.cfg +++ b/libraries/base/axi4/hdllib.cfg @@ -1,7 +1,7 @@ hdl_lib_name = axi4 hdl_library_clause_name = axi4_lib hdl_lib_uses_synth = common dp -hdl_lib_uses_sim = +hdl_lib_uses_sim = mm hdl_lib_technology = synth_files = @@ -11,11 +11,13 @@ synth_files = src/vhdl/axi4_lite_mm_bridge.vhd test_bench_files = + tb/vhdl/tb_axi4_lite_mm_bridge.vhd tb/vhdl/tb_axi4_stream_dp_bridge.vhd tb/vhdl/tb_tb_axi4_stream_dp_bridge.vhd regression_test_vhdl = tb/vhdl/tb_tb_axi4_stream_dp_bridge.vhd + tb/vhdl/tb_axi4_lite_mm_bridge.vhd [modelsim_project_file] diff --git a/libraries/base/axi4/src/vhdl/axi4_lite_mm_bridge.vhd b/libraries/base/axi4/src/vhdl/axi4_lite_mm_bridge.vhd index a763e876a9..443f690a74 100644 --- a/libraries/base/axi4/src/vhdl/axi4_lite_mm_bridge.vhd +++ b/libraries/base/axi4/src/vhdl/axi4_lite_mm_bridge.vhd @@ -23,18 +23,18 @@ -- Description: -- . This core consists of: -- . Combinatorial translation of one interface to the other. --- . Ready latency adapters as AXI4-Lite has RL = 0 and MM has RL = 1. -- . Details: -- . g_active_low_rst should be set to TRUE when in_rst is active low. This is useful as an -- AXI4 interface often comes with an active-low reset while DP comes with an active-high -- reset. +-- . Remark: +-- . The read latency is not adapted. Ensure that the Controller an Peripheral use the same +-- read-latency. -LIBRARY IEEE, common_lib, dp_lib, technology_lib, mm_lib; +LIBRARY IEEE, common_lib; USE IEEE.STD_LOGIC_1164.ALL; USE common_lib.common_pkg.ALL; USE common_lib.common_mem_pkg.ALL; -USE dp_lib.dp_stream_pkg.ALL; -USE work.axi4_stream_pkg.ALL; USE work.axi4_lite_pkg.ALL; ENTITY axi4_lite_mm_bridge IS @@ -65,27 +65,9 @@ ENTITY axi4_lite_mm_bridge IS END axi4_lite_mm_bridge; ARCHITECTURE str OF axi4_lite_mm_bridge IS --- Sum of all t_mem_copi fields widths (synthesis will optimize away unused address and data bits) - CONSTANT c_data_w : NATURAL := c_mem_address_w + c_mem_data_w + 2; -- 32 + 72 + 1 (wr) + 1 (rd) = 106 - SIGNAL i_rst : STD_LOGIC := '0'; - SIGNAL axi4_from_mm_copi : t_mem_copi; - SIGNAL axi4_from_mm_cipo : t_mem_cipo; - SIGNAL mm_from_axi4_copi : t_mem_copi; - SIGNAL mm_from_axi4_cipo : t_mem_cipo; - - SIGNAL rl_decr_snk_ready : STD_LOGIC := '0'; - SIGNAL rl_decr_snk_dat : STD_LOGIC_VECTOR(c_data_w-1 DOWNTO 0); - SIGNAL rl_decr_snk_val : STD_LOGIC := '0'; - SIGNAL rl_decr_src_ready : STD_LOGIC := '0'; - SIGNAL rl_decr_src_dat : STD_LOGIC_VECTOR(c_data_w-1 DOWNTO 0); - - SIGNAL rl_incr_snk_ready : STD_LOGIC := '0'; - SIGNAL rl_incr_snk_dat : STD_LOGIC_VECTOR(c_data_w-1 DOWNTO 0); - SIGNAL rl_incr_snk_val : STD_LOGIC := '0'; - SIGNAL rl_incr_src_ready : STD_LOGIC := '0'; - SIGNAL rl_incr_src_dat : STD_LOGIC_VECTOR(c_data_w-1 DOWNTO 0); + SIGNAL i_mm_out_copi : t_mem_copi; SIGNAL d_bvalid : STD_LOGIC := '0'; SIGNAL q_bvalid : STD_LOGIC := '0'; @@ -96,83 +78,25 @@ BEGIN mm_rst <= i_rst; ----------------------------------------------- - -- Translate MM to AXI4 Lite and RL 1 to RL 0 + -- Translate MM to AXI4 Lite ----------------------------------------------- - -- Decrease ready latency - rl_decr_snk_dat <= func_slv_concat(mm_in_copi.address, mm_in_copi.wrdata, slv(mm_in_copi.wr), slv(mm_in_copi.rd)); - rl_decr_snk_val <= mm_in_copi.wr OR mm_in_copi.rd; - - u_common_rl_decrease : ENTITY common_lib.common_rl_decrease - GENERIC MAP ( - g_dat_w => c_axi4_lite_data_w - ) - PORT MAP ( - rst => i_rst, - clk => in_clk, - -- Sink RL = 1 - snk_out_ready => rl_decr_snk_ready, - snk_in_dat => rl_decr_snk_dat, - snk_in_val => rl_decr_snk_val, - -- Source RL = 0 - src_in_ready => rl_decr_src_ready, - src_out_dat => rl_decr_src_dat, - src_out_val => OPEN - ); - - -- Account for opposite meaning of waitrequest and ready - mm_in_cipo.waitrequest <= NOT rl_decr_snk_ready; - rl_decr_src_ready <= NOT axi4_from_mm_cipo.waitrequest; - - -- Wire remaining copi/cipo signals - mm_from_axi4_cipo.rddata <= mm_out_cipo.rddata; - mm_from_axi4_cipo.rdval <= mm_out_cipo.rdval; - axi4_from_mm_copi.address <= func_slv_extract(c_mem_address_w, c_mem_data_w, 1, 1, rl_decr_src_dat, 0); - axi4_from_mm_copi.wrdata <= func_slv_extract(c_mem_address_w, c_mem_data_w, 1, 1, rl_decr_src_dat, 1); - axi4_from_mm_copi.wr <= sl(func_slv_extract(c_mem_address_w, c_mem_data_w, 1, 1, rl_decr_src_dat, 2)); - axi4_from_mm_copi.rd <= sl(func_slv_extract(c_mem_address_w, c_mem_data_w, 1, 1, rl_decr_src_dat, 3)); - - -- MM to AXI4 Lite - axi4_out_copi.awaddr <= axi4_from_mm_copi.address; - axi4_out_copi.awprot <= (OTHERS => '0'); - axi4_out_copi.awvalid <= axi4_from_mm_copi.wr; - axi4_out_copi.wdata <= axi4_from_mm_copi.wrdata(c_axi4_lite_data_w-1 DOWNTO 0); - axi4_out_copi.wstrb <= (OTHERS => '1'); -- Either ignored or all bytes selected. - axi4_out_copi.wvalid <= axi4_from_mm_copi.wr; - axi4_out_copi.bready <= '1'; -- Unsupported by MM, assuming always ready. - axi4_out_copi.araddr <= axi4_from_mm_copi.address; - axi4_out_copi.arprot <= (OTHERS => '0'); - axi4_out_copi.arvalid <= axi4_from_mm_copi.rd; - axi4_out_copi.rready <= '1'; -- Unsupported by MM, assuming always ready. - - axi4_from_mm_cipo.rddata(c_axi4_lite_data_w-1 DOWNTO 0) <= axi4_out_cipo.rdata; - axi4_from_mm_cipo.rdval <= axi4_out_cipo.rvalid; - axi4_from_mm_cipo.waitrequest <= NOT (axi4_out_cipo.awready AND axi4_out_cipo.wready AND axi4_out_cipo.arready); + axi4_out_copi <= func_axi4_lite_from_mm_copi(mm_in_copi); + mm_in_cipo <= func_axi4_lite_to_mm_cipo(axi4_out_cipo); ------------------------------------------- - -- Translate AXI4 to MM and RL 0 to RL 1 + -- Translate AXI4 to MM ------------------------------------------- -- AXI4 Lite to MM - mm_from_axi4_copi.address <= axi4_in_copi.awaddr WHEN axi4_in_copi.awvalid = '1' ELSE axi4_in_copi.araddr; - mm_from_axi4_copi.wrdata(c_axi4_lite_data_w-1 DOWNTO 0) <= axi4_in_copi.wdata; - mm_from_axi4_copi.wr <= axi4_in_copi.awvalid; - mm_from_axi4_copi.rd <= axi4_in_copi.arvalid; - - axi4_in_cipo.awready <= NOT mm_from_axi4_cipo.waitrequest; - axi4_in_cipo.wready <= NOT mm_from_axi4_cipo.waitrequest; - axi4_in_cipo.bresp <= c_axi4_lite_resp_okay; - axi4_in_cipo.bvalid <= q_bvalid; - axi4_in_cipo.arready <= NOT mm_from_axi4_cipo.waitrequest; - axi4_in_cipo.rdata <= mm_from_axi4_cipo.rddata(c_axi4_lite_data_w-1 DOWNTO 0); - axi4_in_cipo.rresp <= c_axi4_lite_resp_okay; - axi4_in_cipo.rvalid <= mm_from_axi4_cipo.rdval; + i_mm_out_copi <= func_axi4_lite_to_mm_copi(axi4_in_copi); + axi4_in_cipo <= func_axi4_lite_from_mm_cipo(mm_out_cipo, q_bvalid); -- Generate bvalid q_bvalid <= d_bvalid WHEN rising_edge(in_clk); - p_bvalid : PROCESS(i_rst, q_bvalid, mm_from_axi4_cipo, mm_from_axi4_copi, axi4_in_copi) + p_bvalid : PROCESS(i_rst, q_bvalid, mm_out_cipo, i_mm_out_copi, axi4_in_copi) BEGIN d_bvalid <= q_bvalid; - IF mm_from_axi4_cipo.waitrequest = '0' AND mm_from_axi4_copi.wr = '1' THEN + IF mm_out_cipo.waitrequest = '0' AND i_mm_out_copi.wr = '1' THEN d_bvalid <= '1'; ELSIF axi4_in_copi.bready = '1' THEN d_bvalid <= '0'; @@ -181,39 +105,7 @@ BEGIN d_bvalid <= '0'; END IF; END PROCESS; - - -- Increase ready latency - rl_incr_snk_dat <= func_slv_concat(mm_from_axi4_copi.address, mm_from_axi4_copi.wrdata, slv(mm_from_axi4_copi.wr), slv(mm_from_axi4_copi.rd)); - rl_incr_snk_val <= mm_from_axi4_copi.wr OR mm_from_axi4_copi.rd; - - u_common_rl_increase : ENTITY common_lib.common_rl_increase - GENERIC MAP ( - g_dat_w => c_axi4_lite_data_w - ) - PORT MAP ( - rst => i_rst, - clk => in_clk, - -- Sink RL = 0 - snk_out_ready => rl_incr_snk_ready, - snk_in_dat => rl_incr_snk_dat, - snk_in_val => rl_incr_snk_val, - -- Source RL = 1 - src_in_ready => rl_incr_src_ready, - src_out_dat => rl_incr_src_dat, - src_out_val => OPEN - ); - - -- Account for opposite meaning of waitrequest and ready - mm_from_axi4_cipo.waitrequest <= NOT rl_incr_snk_ready; - rl_incr_src_ready <= NOT mm_out_cipo.waitrequest; - - -- Wire remaining copi/cipo signals - mm_from_axi4_cipo.rddata <= mm_out_cipo.rddata; - mm_from_axi4_cipo.rdval <= mm_out_cipo.rdval; - mm_out_copi.address <= func_slv_extract(c_mem_address_w, c_mem_data_w, 1, 1, rl_incr_src_dat, 0); - mm_out_copi.wrdata <= func_slv_extract(c_mem_address_w, c_mem_data_w, 1, 1, rl_incr_src_dat, 1); - mm_out_copi.wr <= sl(func_slv_extract(c_mem_address_w, c_mem_data_w, 1, 1, rl_incr_src_dat, 2)); - mm_out_copi.rd <= sl(func_slv_extract(c_mem_address_w, c_mem_data_w, 1, 1, rl_incr_src_dat, 3)); - + + mm_out_copi <= i_mm_out_copi; END str; diff --git a/libraries/base/axi4/src/vhdl/axi4_lite_pkg.vhd b/libraries/base/axi4/src/vhdl/axi4_lite_pkg.vhd index 497c8c9068..7214af9bff 100644 --- a/libraries/base/axi4/src/vhdl/axi4_lite_pkg.vhd +++ b/libraries/base/axi4/src/vhdl/axi4_lite_pkg.vhd @@ -93,8 +93,70 @@ PACKAGE axi4_lite_pkg IS CONSTANT c_axi4_lite_resp_slverr : STD_LOGIC_VECTOR(c_axi4_lite_resp_w-1 DOWNTO 0) := "10"; -- peripheral error CONSTANT c_axi4_lite_resp_decerr : STD_LOGIC_VECTOR(c_axi4_lite_resp_w-1 DOWNTO 0) := "11"; -- decode error + -- Functions to convert axi4-lite to MM. + FUNCTION func_axi4_lite_to_mm_copi(axi4_copi : t_axi4_lite_copi) RETURN t_mem_copi; + FUNCTION func_axi4_lite_to_mm_cipo(axi4_cipo : t_axi4_lite_cipo) RETURN t_mem_cipo; + + -- Functions to convert MM to axi4-lite. + FUNCTION func_axi4_lite_from_mm_copi(mm_copi : t_mem_copi) RETURN t_axi4_lite_copi; + FUNCTION func_axi4_lite_from_mm_cipo(mm_cipo : t_mem_cipo; bvalid : STD_LOGIC) RETURN t_axi4_lite_cipo; + END axi4_lite_pkg; PACKAGE BODY axi4_lite_pkg IS + FUNCTION func_axi4_lite_to_mm_copi(axi4_copi : t_axi4_lite_copi) RETURN t_mem_copi IS + VARIABLE v_mm_copi : t_mem_copi := c_mem_copi_rst; + BEGIN + IF axi4_copi.awvalid = '1' THEN + v_mm_copi.address := axi4_copi.awaddr; + ELSE + v_mm_copi.address := axi4_copi.araddr; + END IF; + v_mm_copi.wrdata(c_axi4_lite_data_w-1 DOWNTO 0) := axi4_copi.wdata; + v_mm_copi.wr := axi4_copi.awvalid; + v_mm_copi.rd := axi4_copi.arvalid; + RETURN v_mm_copi; + END; + + FUNCTION func_axi4_lite_to_mm_cipo(axi4_cipo : t_axi4_lite_cipo) RETURN t_mem_cipo IS + VARIABLE v_mm_cipo : t_mem_cipo := c_mem_cipo_rst; + BEGIN + v_mm_cipo.rddata(c_axi4_lite_data_w-1 DOWNTO 0) := axi4_cipo.rdata; + v_mm_cipo.rdval := axi4_cipo.rvalid; + v_mm_cipo.waitrequest := NOT (axi4_cipo.awready AND axi4_cipo.wready AND axi4_cipo.arready); + RETURN v_mm_cipo; + END; + + FUNCTION func_axi4_lite_from_mm_copi(mm_copi : t_mem_copi) RETURN t_axi4_lite_copi IS + VARIABLE v_axi4_copi : t_axi4_lite_copi := c_axi4_lite_copi_rst; + BEGIN + v_axi4_copi.awaddr := mm_copi.address; + v_axi4_copi.awprot := (OTHERS => '0'); + v_axi4_copi.awvalid := mm_copi.wr; + v_axi4_copi.wdata := mm_copi.wrdata(c_axi4_lite_data_w-1 DOWNTO 0); + v_axi4_copi.wstrb := (OTHERS => '1'); -- Either ignored or all bytes selected. + v_axi4_copi.wvalid := mm_copi.wr; + v_axi4_copi.bready := '1'; -- Unsupported by MM, assuming always ready. + v_axi4_copi.araddr := mm_copi.address; + v_axi4_copi.arprot := (OTHERS => '0'); + v_axi4_copi.arvalid := mm_copi.rd; + v_axi4_copi.rready := '1'; -- Unsupported by MM, assuming always ready. + RETURN v_axi4_copi; + END; + + FUNCTION func_axi4_lite_from_mm_cipo(mm_cipo : t_mem_cipo; bvalid : STD_LOGIC) RETURN t_axi4_lite_cipo IS + VARIABLE v_axi4_cipo : t_axi4_lite_cipo := c_axi4_lite_cipo_rst; + BEGIN + v_axi4_cipo.awready := NOT mm_cipo.waitrequest; + v_axi4_cipo.wready := NOT mm_cipo.waitrequest; + v_axi4_cipo.bresp := c_axi4_lite_resp_okay; + v_axi4_cipo.bvalid := bvalid; + v_axi4_cipo.arready := NOT mm_cipo.waitrequest; + v_axi4_cipo.rdata := mm_cipo.rddata(c_axi4_lite_data_w-1 DOWNTO 0); + v_axi4_cipo.rresp := c_axi4_lite_resp_okay; + v_axi4_cipo.rvalid := mm_cipo.rdval; + RETURN v_axi4_cipo; + END; + END axi4_lite_pkg; diff --git a/libraries/base/axi4/tb/vhdl/tb_axi4_lite_mm_bridge.vhd b/libraries/base/axi4/tb/vhdl/tb_axi4_lite_mm_bridge.vhd new file mode 100644 index 0000000000..7ae57fa96c --- /dev/null +++ b/libraries/base/axi4/tb/vhdl/tb_axi4_lite_mm_bridge.vhd @@ -0,0 +1,147 @@ +------------------------------------------------------------------------------- +-- +-- Copyright 2023 +-- ASTRON (Netherlands Institute for Radio Astronomy) <http://www.astron.nl/> +-- P.O.Box 2, 7990 AA Dwingeloo, The Netherlands +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +------------------------------------------------------------------------------- + +------------------------------------------------------------------------------- +-- Author : R vd Walle +-- Purpose: +-- TB for testing axi4_lite_mm_bridge using common_ram_r_w +-- Description: +------------------------------------------------------------------------------- + +LIBRARY IEEE, common_lib, mm_lib; +USE IEEE.std_logic_1164.ALL; +USE IEEE.numeric_std.ALL; +USE IEEE.std_logic_textio.ALL; +USE STD.textio.ALL; +USE common_lib.common_pkg.ALL; +USE common_lib.common_mem_pkg.ALL; +USE common_lib.tb_common_mem_pkg.ALL; +USE common_lib.tb_common_pkg.ALL; +USE common_lib.common_str_pkg.ALL; +USE work.axi4_lite_pkg.ALL; + +ENTITY tb_axi4_lite_mm_bridge IS +END tb_axi4_lite_mm_bridge; + +ARCHITECTURE tb OF tb_axi4_lite_mm_bridge IS + + CONSTANT c_mm_clk_period : TIME := 40 ns; + CONSTANT c_reset_len : NATURAL := 4; + + CONSTANT c_mm_usr_ram : t_c_mem := (latency => 1, + adr_w => 5, + dat_w => 8, + nof_dat => 32, + init_sl => '0'); + CONSTANT c_offset : NATURAL := 57; --Some value to offset the counter data written to ram. + + SIGNAL mm_rst : STD_LOGIC; + SIGNAL mm_clk : STD_LOGIC := '0'; + SIGNAL sim_finished : STD_LOGIC := '0'; + SIGNAL tb_end : STD_LOGIC := '0'; + + SIGNAL mm_in_copi : t_mem_copi := c_mem_copi_rst; + SIGNAL mm_in_cipo : t_mem_cipo := c_mem_cipo_rst; + SIGNAL mm_out_copi : t_mem_copi := c_mem_copi_rst; + SIGNAL mm_out_cipo : t_mem_cipo := c_mem_cipo_rst; + SIGNAL ram_copi : t_mem_copi := c_mem_copi_rst; + SIGNAL ram_cipo : t_mem_cipo := c_mem_cipo_rst; + + SIGNAL axi_copi : t_axi4_lite_copi; + SIGNAL axi_cipo : t_axi4_lite_cipo; + +BEGIN + + mm_clk <= NOT mm_clk OR sim_finished AFTER c_mm_clk_period/2; + mm_rst <= '1', '0' AFTER c_mm_clk_period*c_reset_len; + + u_axi4_lite_mm_bridge : ENTITY work.axi4_lite_mm_bridge + PORT MAP ( + in_clk => mm_clk, + in_rst => mm_rst, + + axi4_in_copi => axi_copi, + axi4_in_cipo => axi_cipo, + mm_out_copi => mm_out_copi, + mm_out_cipo => mm_out_cipo, + mm_in_copi => mm_in_copi, + mm_in_cipo => mm_in_cipo, + axi4_out_copi => axi_copi, + axi4_out_cipo => axi_cipo + ); + + u_waitrequest_model : ENTITY mm_lib.mm_waitrequest_model + GENERIC MAP ( + g_waitrequest => TRUE, + g_seed => c_offset + ) + PORT MAP ( + mm_clk => mm_clk, + bus_mosi => mm_out_copi, + bus_miso => mm_out_cipo, + slave_mosi => ram_copi, + slave_miso => ram_cipo + ); + + u_ram : ENTITY common_lib.common_ram_r_w + GENERIC MAP ( + g_ram => c_mm_usr_ram + ) + PORT MAP ( + rst => mm_rst, + clk => mm_clk, + clken => '1', + wr_en => ram_copi.wr, + wr_dat => ram_copi.wrdata(c_mm_usr_ram.dat_w-1 DOWNTO 0), + wr_adr => ram_copi.address(c_mm_usr_ram.adr_w-1 DOWNTO 0), + rd_en => ram_copi.rd, + rd_adr => ram_copi.address(c_mm_usr_ram.adr_w-1 DOWNTO 0), + rd_dat => ram_cipo.rddata(c_mm_usr_ram.dat_w-1 DOWNTO 0), + rd_val => ram_cipo.rdval + ); + + -- Testbench writes a number of words to memory and then reads them back trough the AXI interface. + -- It uses the axi_lite_transaction to write, read and verify the data. + tb : PROCESS + BEGIN + proc_common_wait_until_low(mm_clk, mm_rst); + proc_common_wait_some_cycles(mm_clk, 3); + -- write a number of words to memory + FOR I IN 0 TO 10 LOOP + proc_mem_mm_bus_wr(I, (c_offset + I), mm_clk, mm_in_cipo, mm_in_copi); + END LOOP; + + proc_common_wait_some_cycles(mm_clk, 10); + -- read the words from memory + FOR i IN 0 TO 10 LOOP + proc_mem_mm_bus_rd(I, mm_clk, mm_in_cipo, mm_in_copi); + proc_mem_mm_bus_rd_latency(1, mm_clk); + ASSERT TO_UINT(mm_in_cipo.rddata(c_mm_usr_ram.dat_w-1 DOWNTO 0)) = (c_offset + I) REPORT + "Wrong value read from RAM at address " & int_to_str(I) & " expected " & int_to_str(c_offset + I) + & " but received " & int_to_str(TO_UINT(mm_in_cipo.rddata(c_mm_usr_ram.dat_w-1 DOWNTO 0))) SEVERITY ERROR; + END LOOP; + + sim_finished <= '1'; + tb_end <= '1'; + WAIT FOR 1 us; + REPORT "Finished Simulation" SEVERITY FAILURE; + WAIT; + END PROCESS tb; +END tb; -- GitLab