Skip to content

Commit

Permalink
[sim] updates and cleanups
Browse files Browse the repository at this point in the history
  • Loading branch information
stnolting committed Jan 4, 2025
1 parent f50a28d commit fb73c50
Showing 1 changed file with 68 additions and 139 deletions.
207 changes: 68 additions & 139 deletions sim/neorv32_riscof_tb.vhd
Original file line number Diff line number Diff line change
@@ -1,52 +1,19 @@
-- #################################################################################################
-- # << neorv32-riscof - Testbench for running RISCOF >> #
-- # ********************************************************************************************* #
-- # Minimal NEORV32 CPU testbench for running the RISCOF-based architecture test framework. #
-- # #
-- # A processor-external memory is initialized by a plain ASCII HEX file that contains the #
-- # executable and all relevant data. The memory is split into four submodules of 512kB each #
-- # using variables of type bit_vector to minimize simulation memory footprint. These hacks are #
-- # required since GHDL has problems with handling very large objects: #
-- # https://github.com/ghdl/ghdl/issues/1592 #
-- # #
-- # Test signature data is dumped to a file "DUT-neorv32.signature" by writing to address #
-- # 0xF0000004. Additional simulation triggers are implemented as memory-mapped registers: #
-- # - trigger end of simulation using VHDL08's "finish" statement #
-- # - trigger machine software interrupt (MSI) #
-- # - trigger machine external interrupt (MEI) #
-- # #
-- # This testbench uses VHDL2008! #
-- # ********************************************************************************************* #
-- # BSD 3-Clause License #
-- # #
-- # Copyright (c) 2024, Stephan Nolting. All rights reserved. #
-- # #
-- # Redistribution and use in source and binary forms, with or without modification, are #
-- # permitted provided that the following conditions are met: #
-- # #
-- # 1. Redistributions of source code must retain the above copyright notice, this list of #
-- # conditions and the following disclaimer. #
-- # #
-- # 2. Redistributions in binary form must reproduce the above copyright notice, this list of #
-- # conditions and the following disclaimer in the documentation and/or other materials #
-- # provided with the distribution. #
-- # #
-- # 3. Neither the name of the copyright holder nor the names of its contributors may be used to #
-- # endorse or promote products derived from this software without specific prior written #
-- # permission. #
-- # #
-- # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS #
-- # OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF #
-- # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE #
-- # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, #
-- # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE #
-- # GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED #
-- # AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING #
-- # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED #
-- # OF THE POSSIBILITY OF SUCH DAMAGE. #
-- # ********************************************************************************************* #
-- # https://github.com/stnolting/neorv32-riscof (c) Stephan Nolting #
-- #################################################################################################
-- ================================================================================ --
-- neorv32_riscof_tb.vhd - Testbench for running RISCOF --
-- -------------------------------------------------------------------------------- --
-- A processor-external memory is initialized by a plain ASCII HEX file that --
-- contains the executable and all relevant data. The memory is split into four --
-- sub-modules using variables of type bit_vector to minimize the host's simulation --
-- RAM footprint. Test signature data is dumped to a file "DUT-neorv32.signature" --
-- by writing to address 0xF0000004. The simulation is terminated by writing --
-- 0xCAFECAFE to address 0xF0000000. Note that this testbench requires VHDL2008. --
-- -------------------------------------------------------------------------------- --
-- The NEORV32 RISC-V Processor - https://github.com/stnolting/neorv32 --
-- Copyright (c) NEORV32 contributors. --
-- Copyright (c) 2020 - 2025 Stephan Nolting. All rights reserved. --
-- Licensed under the BSD-3-Clause license, see LICENSE for details. --
-- SPDX-License-Identifier: BSD-3-Clause --
-- ================================================================================ --

library std;
use std.textio.all;
Expand All @@ -61,31 +28,29 @@ use neorv32.neorv32_package.all;

entity neorv32_riscof_tb is
generic (
MEM_FILE : string; -- memory initialization file
MEM_SIZE : natural := 8*1024 -- total memory size in bytes
MEM_FILE : string := "" -- memory initialization file (max 4MB)
);
end neorv32_riscof_tb;

architecture neorv32_riscof_tb_rtl of neorv32_riscof_tb is

-- maximum memory size in bytes --
constant mem_size_max_c : natural := 4*1024*1024;
constant mem_size_c : natural := cond_sel_natural_f(boolean(MEM_SIZE >= mem_size_max_c), mem_size_max_c, MEM_SIZE);
-- total memory size in bytes --
constant mem_size_c : natural := 4*1024*1024;

-- memory type --
type mem8_bv_t is array (natural range <>) of bit_vector(7 downto 0); -- bit_vector type for optimized system storage

-- initialize mem8_bv_t array from plain ASCII HEX file --
impure function mem8_bv_init_f(file_name : string; num_bytes : natural; byte_sel : natural) return mem8_bv_t is
impure function mem8_bv_init_f(file_name : string; num_words : natural; byte_sel : natural) return mem8_bv_t is
file text_file : text open read_mode is file_name;
variable text_line_v : line;
variable mem8_bv_v : mem8_bv_t(0 to num_bytes-1);
variable mem8_bv_v : mem8_bv_t(0 to num_words-1);
variable index_v : natural;
variable word_v : bit_vector(31 downto 0);
begin
mem8_bv_v := (others => (others => '0')); -- initialize to all-zero
index_v := 0;
while (endfile(text_file) = false) and (index_v < num_bytes) loop
while (endfile(text_file) = false) and (index_v < num_words) loop
readline(text_file, text_line_v);
hread(text_line_v, word_v);
case byte_sel is
Expand All @@ -99,14 +64,14 @@ architecture neorv32_riscof_tb_rtl of neorv32_riscof_tb is
return mem8_bv_v;
end function mem8_bv_init_f;

-- memory address --
signal addr : integer range 0 to (mem_size_c/4)-1;
-- memory word address --
signal mem_addr : integer range 0 to (mem_size_c/4)-1;

-- generators/triggers --
signal clk_gen, rst_gen, msi, mei, mti : std_ulogic := '0';
-- generators --
signal clk_gen, rst_gen : std_ulogic := '0';

-- wishbone bus --
type wishbone_t is record
-- external bus interface --
type xbus_t is record
addr : std_ulogic_vector(31 downto 0);
wdata : std_ulogic_vector(31 downto 0);
rdata : std_ulogic_vector(31 downto 0);
Expand All @@ -116,18 +81,13 @@ architecture neorv32_riscof_tb_rtl of neorv32_riscof_tb is
cyc : std_ulogic;
ack : std_ulogic;
end record;
signal wb_cpu : wishbone_t;
signal xbus : xbus_t;

signal xmem_rdata, trig_rdata, dump_rdata : std_ulogic_vector(31 downto 0);
signal xmem_ack, trig_ack, dump_ack : std_ulogic;
signal xmem_rdata, dump_rdata : std_ulogic_vector(31 downto 0);
signal xmem_ack, dump_ack, trig_ack : std_ulogic;

begin

-- Debug Info -----------------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
assert false report "[RISCOF-TB] actual memory size = " & integer'image(mem_size_c) & " bytes" severity note;


-- Clock/Reset Generator ------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
clk_gen <= not clk_gen after 5 ns;
Expand All @@ -147,6 +107,7 @@ begin
RISCV_ISA_C => true,
RISCV_ISA_M => true,
RISCV_ISA_U => true,
RISCV_ISA_Zaamo => true,
RISCV_ISA_Zba => true,
RISCV_ISA_Zbb => true,
RISCV_ISA_Zbkb => true,
Expand All @@ -163,9 +124,8 @@ begin
-- Tuning Options --
CPU_FAST_MUL_EN => true,
CPU_FAST_SHIFT_EN => true,
-- Internal Instruction memory --
-- Internal memories --
MEM_INT_IMEM_EN => false,
-- Internal Data memory --
MEM_INT_DMEM_EN => false,
-- External bus interface --
XBUS_EN => true,
Expand All @@ -174,27 +134,23 @@ begin
)
port map (
-- Global control --
clk_i => clk_gen,
rstn_i => rst_gen,
clk_i => clk_gen,
rstn_i => rst_gen,
-- External bus interface (available if XBUS_EN = true) --
xbus_adr_o => wb_cpu.addr,
xbus_dat_i => wb_cpu.rdata,
xbus_dat_o => wb_cpu.wdata,
xbus_we_o => wb_cpu.we,
xbus_sel_o => wb_cpu.sel,
xbus_stb_o => wb_cpu.stb,
xbus_cyc_o => wb_cpu.cyc,
xbus_ack_i => wb_cpu.ack,
xbus_err_i => '0',
-- CPU Interrupts --
mtime_irq_i => mti,
msw_irq_i => msi,
mext_irq_i => mei
xbus_adr_o => xbus.addr,
xbus_dat_i => xbus.rdata,
xbus_dat_o => xbus.wdata,
xbus_we_o => xbus.we,
xbus_sel_o => xbus.sel,
xbus_stb_o => xbus.stb,
xbus_cyc_o => xbus.cyc,
xbus_ack_i => xbus.ack,
xbus_err_i => '0'
);

-- bus feedback --
wb_cpu.rdata <= xmem_rdata or trig_rdata or dump_rdata;
wb_cpu.ack <= xmem_ack or trig_ack or dump_ack;
xbus.rdata <= xmem_rdata or dump_rdata;
xbus.ack <= xmem_ack or dump_ack or trig_ack;


-- External Main Memory [rwx] - Constructed from four parallel byte-wide memories ---------
Expand All @@ -210,70 +166,43 @@ begin
xmem_rdata <= (others => '0');
xmem_ack <= '0';
-- bus access --
if (wb_cpu.cyc = '1') and (wb_cpu.stb = '1') and (wb_cpu.addr(31 downto 28) = "0000") then
if (xbus.cyc = '1') and (xbus.stb = '1') and (xbus.addr(31 downto 28) = "0000") then
xmem_ack <= '1';
if (wb_cpu.we = '1') then
if (wb_cpu.sel(0) = '1') then mem8_bv_b0_v(addr) := to_bitvector(wb_cpu.wdata(07 downto 00)); end if;
if (wb_cpu.sel(1) = '1') then mem8_bv_b1_v(addr) := to_bitvector(wb_cpu.wdata(15 downto 08)); end if;
if (wb_cpu.sel(2) = '1') then mem8_bv_b2_v(addr) := to_bitvector(wb_cpu.wdata(23 downto 16)); end if;
if (wb_cpu.sel(3) = '1') then mem8_bv_b3_v(addr) := to_bitvector(wb_cpu.wdata(31 downto 24)); end if;
if (xbus.we = '1') then
if (xbus.sel(0) = '1') then mem8_bv_b0_v(mem_addr) := to_bitvector(xbus.wdata(07 downto 00)); end if;
if (xbus.sel(1) = '1') then mem8_bv_b1_v(mem_addr) := to_bitvector(xbus.wdata(15 downto 08)); end if;
if (xbus.sel(2) = '1') then mem8_bv_b2_v(mem_addr) := to_bitvector(xbus.wdata(23 downto 16)); end if;
if (xbus.sel(3) = '1') then mem8_bv_b3_v(mem_addr) := to_bitvector(xbus.wdata(31 downto 24)); end if;
else
xmem_rdata(07 downto 00) <= to_stdulogicvector(mem8_bv_b0_v(addr));
xmem_rdata(15 downto 08) <= to_stdulogicvector(mem8_bv_b1_v(addr));
xmem_rdata(23 downto 16) <= to_stdulogicvector(mem8_bv_b2_v(addr));
xmem_rdata(31 downto 24) <= to_stdulogicvector(mem8_bv_b3_v(addr));
xmem_rdata(07 downto 00) <= to_stdulogicvector(mem8_bv_b0_v(mem_addr));
xmem_rdata(15 downto 08) <= to_stdulogicvector(mem8_bv_b1_v(mem_addr));
xmem_rdata(23 downto 16) <= to_stdulogicvector(mem8_bv_b2_v(mem_addr));
xmem_rdata(31 downto 24) <= to_stdulogicvector(mem8_bv_b3_v(mem_addr));
end if;
end if;
end if;
end process ext_mem_rw;

-- read/write address --
addr <= to_integer(unsigned(wb_cpu.addr(index_size_f(mem_size_c/4)+1 downto 2)));
mem_addr <= to_integer(unsigned(xbus.addr(index_size_f(mem_size_c/4)+1 downto 2)));


-- Simulation Triggers --------------------------------------------------------------------
-- Terminate Simulation -------------------------------------------------------------------
-- -------------------------------------------------------------------------------------------
sim_triggers: process(rst_gen, clk_gen)
sim_terminate: process(rst_gen, clk_gen)
begin
if (rst_gen = '0') then
msi <= '0';
mei <= '0';
mti <= '0';
trig_ack <= '0';
elsif rising_edge(clk_gen) then
-- defaults --
trig_rdata <= (others => '0');
trig_ack <= '0';
-- bus access --
if (wb_cpu.cyc = '1') and (wb_cpu.stb = '1') and (wb_cpu.we = '1') and (wb_cpu.addr = x"F0000000") then
trig_ack <= '0';
if (xbus.cyc = '1') and (xbus.stb = '1') and (xbus.we = '1') and
(xbus.addr = x"F0000000") and (xbus.wdata = x"CAFECAFE") then
trig_ack <= '1';
case wb_cpu.wdata is
when x"CAFECAFE" => -- end simulation
assert false report "Finishing simulation." severity note;
finish;
when x"11111111" => -- set machine software interrupt
assert false report "Set MSI." severity note;
msi <= '1';
when x"22222222" => -- clear machine software interrupt
assert false report "Clear MSI." severity note;
msi <= '0';
when x"33333333" => -- set machine external interrupt
assert false report "Set MEI." severity note;
mei <= '1';
when x"44444444" => -- clear machine external interrupt
assert false report "Clear MEI." severity note;
mei <= '0';
when x"55555555" => -- set machine timer interrupt
assert false report "Set MTI." severity note;
mti <= '1';
when x"66666666" => -- clear machine timer interrupt
assert false report "Clear MTI." severity note;
mti <= '0';
when others =>
NULL;
end case;
assert false report "Finishing simulation." severity note;
finish;
end if;
end if;
end process sim_triggers;
end process sim_terminate;


-- Signature Dump -------------------------------------------------------------------------
Expand All @@ -287,10 +216,10 @@ begin
dump_rdata <= (others => '0');
dump_ack <= '0';
-- bus access --
if (wb_cpu.cyc = '1') and (wb_cpu.stb = '1') and (wb_cpu.we = '1') and (wb_cpu.addr = x"F0000004") then
if (xbus.cyc = '1') and (xbus.stb = '1') and (xbus.we = '1') and (xbus.addr = x"F0000004") then
dump_ack <= '1';
for i in 7 downto 0 loop -- write 32-bit as 8x lowercase HEX chars
write(line_v, to_hexchar_f(wb_cpu.wdata(3+i*4 downto 0+i*4)));
write(line_v, to_hexchar_f(xbus.wdata(3+i*4 downto 0+i*4)));
end loop;
writeline(dump_file, line_v);
end if;
Expand Down

0 comments on commit fb73c50

Please # to comment.