-- ================================================================================ -- -- NEORV32 - Default Processor Testbench -- -- -------------------------------------------------------------------------------- -- -- 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 ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; use ieee.math_real.all; library neorv32; use neorv32.neorv32_package.all; entity neorv32_tb is generic ( -- processor -- CLOCK_FREQUENCY : natural := 100_000_000; -- clock frequency of clk_i in Hz DUAL_CORE_EN : boolean := true; -- enable dual-core homogeneous SMP BOOT_MODE_SELECT : natural range 0 to 2 := 2; -- boot from pre-initialized IMEM BOOT_ADDR_CUSTOM : std_ulogic_vector(31 downto 0) := x"00000000"; -- custom CPU boot address (if boot_config = 1) RISCV_ISA_C : boolean := false; -- implement compressed extension RISCV_ISA_E : boolean := false; -- implement embedded RF extension RISCV_ISA_M : boolean := true; -- implement mul/div extension RISCV_ISA_U : boolean := true; -- implement user mode extension RISCV_ISA_Zaamo : boolean := true; -- implement atomic read-modify-write operations extension RISCV_ISA_Zalrsc : boolean := true; -- implement atomic reservation-set operations extension RISCV_ISA_Zba : boolean := true; -- implement shifted-add bit-manipulation extension RISCV_ISA_Zbb : boolean := true; -- implement basic bit-manipulation extension RISCV_ISA_Zbkb : boolean := true; -- implement bit-manipulation instructions for cryptography RISCV_ISA_Zbkc : boolean := true; -- implement carry-less multiplication instructions RISCV_ISA_Zbkx : boolean := true; -- implement cryptography crossbar permutation extension RISCV_ISA_Zbs : boolean := true; -- implement single-bit bit-manipulation extension RISCV_ISA_Zfinx : boolean := true; -- implement 32-bit floating-point extension RISCV_ISA_Zicntr : boolean := true; -- implement base counters RISCV_ISA_Zicond : boolean := true; -- implement integer conditional operations RISCV_ISA_Zihpm : boolean := true; -- implement hardware performance monitors RISCV_ISA_Zknd : boolean := true; -- implement cryptography NIST AES decryption extension RISCV_ISA_Zkne : boolean := true; -- implement cryptography NIST AES encryption extension RISCV_ISA_Zknh : boolean := true; -- implement cryptography NIST hash extension RISCV_ISA_Zksed : boolean := true; -- implement ShangMi block cipher extension RISCV_ISA_Zksh : boolean := true; -- implement ShangMi hash extension RISCV_ISA_Zmmul : boolean := true; -- implement multiply-only M sub-extension RISCV_ISA_Zxcfu : boolean := true; -- implement custom (instr.) functions unit CPU_FAST_MUL_EN : boolean := true; -- use DSPs for M extension's multiplier CPU_FAST_SHIFT_EN : boolean := true; -- use barrel shifter for shift operations CPU_RF_HW_RST_EN : boolean := false; -- implement full hardware reset for register file MEM_INT_IMEM_EN : boolean := true; -- implement processor-internal instruction memory MEM_INT_IMEM_SIZE : natural := 32*1024; -- size of processor-internal instruction memory in bytes (use a power of 2) MEM_INT_DMEM_EN : boolean := true; -- implement processor-internal data memory MEM_INT_DMEM_SIZE : natural := 8*1024; -- size of processor-internal data memory in bytes (use a power of 2) ICACHE_EN : boolean := true; -- implement instruction cache ICACHE_NUM_BLOCKS : natural range 1 to 256 := 64; -- i-cache: number of blocks (min 1), has to be a power of 2 ICACHE_BLOCK_SIZE : natural range 4 to 2**16 := 32; -- i-cache: block size in bytes (min 4), has to be a power of 2 DCACHE_EN : boolean := true; -- implement data cache DCACHE_NUM_BLOCKS : natural range 1 to 256 := 32; -- d-cache: number of blocks (min 1), has to be a power of 2 DCACHE_BLOCK_SIZE : natural range 4 to 2**16 := 32; -- d-cache: block size in bytes (min 4), has to be a power of 2 -- external memory A -- EXT_MEM_A_EN : boolean := false; -- enable memory EXT_MEM_A_BASE : std_ulogic_vector(31 downto 0) := x"00000000"; -- base address, has to be word-aligned EXT_MEM_A_SIZE : natural := 64; -- memory size in bytes, min 4 EXT_MEM_A_LATE : natural range 1 to 4096 := 20; -- access latency cycles EXT_MEM_A_FILE : string := ""; -- memory initialization file (plain HEX), no initialization if empty -- external memory B -- EXT_MEM_B_EN : boolean := false; -- enable memory EXT_MEM_B_BASE : std_ulogic_vector(31 downto 0) := x"80000000"; -- base address, has to be word-aligned EXT_MEM_B_SIZE : natural := 64; -- memory size in bytes, min 4 EXT_MEM_B_LATE : natural range 1 to 4096 := 40; -- access latency cycles EXT_MEM_B_FILE : string := "" -- memory initialization file (plain HEX), no initialization if empty ); end neorv32_tb; architecture neorv32_tb_rtl of neorv32_tb is -- generators -- signal clk_en, clk_gen, rst_gen : std_ulogic; constant f_period_c : time := (1 sec) / CLOCK_FREQUENCY; -- IO connection -- signal uart0_txd, uart0_ctsn, uart1_txd, uart1_ctsn : std_ulogic; signal gpio : std_ulogic_vector(31 downto 0); signal i2c_scl, i2c_sda : std_logic; signal twi_scl_i, twi_scl_o, twi_sda_i, twi_sda_o : std_ulogic; signal twd_scl_i, twd_scl_o, twd_sda_i, twd_sda_o : std_ulogic; signal onewire : std_logic; signal onewire_i, onewire_o : std_ulogic; signal spi_csn : std_ulogic_vector(7 downto 0); signal spi_di, spi_do, spi_clk : std_ulogic; signal sdi_di, sdi_do, sdi_clk, sdi_csn : std_ulogic; signal msi, mei, mti : std_ulogic; -- slink -- type slink_t is record data : std_ulogic_vector(31 downto 0); -- data addr : std_ulogic_vector(3 downto 0); -- source/destination ID valid : std_ulogic; -- source valid last : std_ulogic; -- last element of packet ready : std_ulogic; -- sink ready end record; signal slink_tx, slink_rx : slink_t; -- XBUS -- signal xbus_core_req, xbus_ext_mem_a_req, xbus_ext_mem_b_req, xbus_mmio_req, xbus_trig_req : xbus_req_t; signal xbus_core_rsp, xbus_ext_mem_a_rsp, xbus_ext_mem_b_rsp, xbus_mmio_rsp, xbus_trig_rsp : xbus_rsp_t; begin -- Clock & Reset Generators --------------------------------------------------------------- -- ------------------------------------------------------------------------------------------- rst_gen <= '0', '1' after 30*(f_period_c/2); clk_en <= '0', '1' after 60*(f_period_c/2); clk_gen <= (not clk_gen) and clk_en after (f_period_c/2); -- The Core of the Problem ---------------------------------------------------------------- -- ------------------------------------------------------------------------------------------- neorv32_top_inst: neorv32_top generic map ( -- Clocking -- CLOCK_FREQUENCY => CLOCK_FREQUENCY, -- Dual-Core Configuration -- DUAL_CORE_EN => DUAL_CORE_EN, -- Boot Configuration -- BOOT_MODE_SELECT => BOOT_MODE_SELECT, BOOT_ADDR_CUSTOM => BOOT_ADDR_CUSTOM, -- On-Chip Debugger (OCD) -- OCD_EN => true, OCD_HW_BREAKPOINT => true, OCD_AUTHENTICATION => true, -- RISC-V CPU Extensions -- RISCV_ISA_C => RISCV_ISA_C, RISCV_ISA_E => RISCV_ISA_E, RISCV_ISA_M => RISCV_ISA_M, RISCV_ISA_U => RISCV_ISA_U, RISCV_ISA_Zaamo => RISCV_ISA_Zaamo, RISCV_ISA_Zalrsc => RISCV_ISA_Zalrsc, RISCV_ISA_Zba => RISCV_ISA_Zba, RISCV_ISA_Zbb => RISCV_ISA_Zbb, RISCV_ISA_Zbkb => RISCV_ISA_Zbkb, RISCV_ISA_Zbkc => RISCV_ISA_Zbkc, RISCV_ISA_Zbkx => RISCV_ISA_Zbkx, RISCV_ISA_Zbs => RISCV_ISA_Zbs, RISCV_ISA_Zfinx => RISCV_ISA_Zfinx, RISCV_ISA_Zicntr => RISCV_ISA_Zicntr, RISCV_ISA_Zicond => RISCV_ISA_Zicond, RISCV_ISA_Zihpm => RISCV_ISA_Zihpm, RISCV_ISA_Zknd => RISCV_ISA_Zknd, RISCV_ISA_Zkne => RISCV_ISA_Zkne, RISCV_ISA_Zknh => RISCV_ISA_Zknh, RISCV_ISA_Zksed => RISCV_ISA_Zksed, RISCV_ISA_Zksh => RISCV_ISA_Zksh, RISCV_ISA_Zmmul => RISCV_ISA_Zmmul, RISCV_ISA_Zxcfu => RISCV_ISA_Zxcfu, -- Extension Options -- CPU_FAST_MUL_EN => CPU_FAST_MUL_EN, CPU_FAST_SHIFT_EN => CPU_FAST_SHIFT_EN, CPU_RF_HW_RST_EN => CPU_RF_HW_RST_EN, -- Physical Memory Protection (PMP) -- PMP_NUM_REGIONS => 5, PMP_MIN_GRANULARITY => 4, PMP_TOR_MODE_EN => true, PMP_NAP_MODE_EN => true, -- Hardware Performance Monitors (HPM) -- HPM_NUM_CNTS => 12, HPM_CNT_WIDTH => 40, -- Internal Instruction memory -- MEM_INT_IMEM_EN => MEM_INT_IMEM_EN, MEM_INT_IMEM_SIZE => MEM_INT_IMEM_SIZE, -- Internal Data memory -- MEM_INT_DMEM_EN => MEM_INT_DMEM_EN, MEM_INT_DMEM_SIZE => MEM_INT_DMEM_SIZE, -- Internal Cache memory -- ICACHE_EN => ICACHE_EN, ICACHE_NUM_BLOCKS => ICACHE_NUM_BLOCKS, ICACHE_BLOCK_SIZE => ICACHE_BLOCK_SIZE, -- Internal Data Cache (dCACHE) -- DCACHE_EN => DCACHE_EN, DCACHE_NUM_BLOCKS => DCACHE_NUM_BLOCKS, DCACHE_BLOCK_SIZE => DCACHE_BLOCK_SIZE, -- External bus interface -- XBUS_EN => true, XBUS_TIMEOUT => 0, XBUS_REGSTAGE_EN => true, XBUS_CACHE_EN => true, XBUS_CACHE_NUM_BLOCKS => 4, XBUS_CACHE_BLOCK_SIZE => 64, -- Processor peripherals -- IO_GPIO_NUM => 32, IO_CLINT_EN => true, IO_UART0_EN => true, IO_UART0_RX_FIFO => 32, IO_UART0_TX_FIFO => 32, IO_UART1_EN => true, IO_UART1_RX_FIFO => 1, IO_UART1_TX_FIFO => 1, IO_SPI_EN => true, IO_SPI_FIFO => 4, IO_SDI_EN => true, IO_SDI_FIFO => 4, IO_TWI_EN => true, IO_TWI_FIFO => 4, IO_TWD_EN => true, IO_TWD_RX_FIFO => 4, IO_TWD_TX_FIFO => 4, IO_PWM_NUM_CH => 8, IO_WDT_EN => true, IO_TRNG_EN => true, IO_TRNG_FIFO => 4, IO_CFS_EN => true, IO_CFS_CONFIG => (others => '0'), IO_CFS_IN_SIZE => 32, IO_CFS_OUT_SIZE => 32, IO_NEOLED_EN => true, IO_NEOLED_TX_FIFO => 8, IO_GPTMR_EN => true, IO_ONEWIRE_EN => true, IO_ONEWIRE_FIFO => 8, IO_DMA_EN => true, IO_SLINK_EN => true, IO_SLINK_RX_FIFO => 4, IO_SLINK_TX_FIFO => 4, IO_CRC_EN => true, IO_HWSPINLOCK_EN => true ) port map ( -- Global control -- clk_i => clk_gen, rstn_i => rst_gen, rstn_ocd_o => open, rstn_wdt_o => open, -- JTAG on-chip debugger interface -- jtag_tck_i => '0', jtag_tdi_i => '0', jtag_tdo_o => open, jtag_tms_i => '0', -- External bus interface -- xbus_adr_o => xbus_core_req.addr, xbus_dat_o => xbus_core_req.data, xbus_tag_o => xbus_core_req.tag, xbus_we_o => xbus_core_req.we, xbus_sel_o => xbus_core_req.sel, xbus_stb_o => xbus_core_req.stb, xbus_cyc_o => xbus_core_req.cyc, xbus_dat_i => xbus_core_rsp.data, xbus_ack_i => xbus_core_rsp.ack, xbus_err_i => xbus_core_rsp.err, -- Stream Link Interface -- slink_rx_dat_i => slink_rx.data, slink_rx_src_i => slink_rx.addr, slink_rx_val_i => slink_rx.valid, slink_rx_lst_i => slink_rx.last, slink_rx_rdy_o => slink_rx.ready, slink_tx_dat_o => slink_tx.data, slink_tx_dst_o => slink_tx.addr, slink_tx_val_o => slink_tx.valid, slink_tx_lst_o => slink_tx.last, slink_tx_rdy_i => slink_tx.ready, -- GPIO -- gpio_o => gpio, gpio_i => gpio, -- primary UART0 -- uart0_txd_o => uart0_txd, uart0_rxd_i => uart0_txd, uart0_rtsn_o => uart0_ctsn, uart0_ctsn_i => uart0_ctsn, -- secondary UART1 -- uart1_txd_o => uart1_txd, uart1_rxd_i => uart1_txd, uart1_rtsn_o => uart1_ctsn, uart1_ctsn_i => uart1_ctsn, -- SPI -- spi_clk_o => spi_clk, spi_dat_o => spi_do, spi_dat_i => spi_di, spi_csn_o => spi_csn, -- SDI -- sdi_clk_i => sdi_clk, sdi_dat_o => sdi_do, sdi_dat_i => sdi_di, sdi_csn_i => sdi_csn, -- TWI -- twi_sda_i => twi_sda_i, twi_sda_o => twi_sda_o, twi_scl_i => twi_scl_i, twi_scl_o => twi_scl_o, -- TWD -- twd_sda_i => twd_sda_i, twd_sda_o => twd_sda_o, twd_scl_i => twd_scl_i, twd_scl_o => twd_scl_o, -- 1-Wire Interface -- onewire_i => onewire_i, onewire_o => onewire_o, -- PWM -- pwm_o => open, -- Custom Functions Subsystem IO -- cfs_in_i => (others => '0'), cfs_out_o => open, -- NeoPixel-compatible smart LED interface -- neoled_o => open, -- Machine timer system time -- mtime_time_o => open, -- CPU Interrupts -- mtime_irq_i => mti, msw_irq_i => msi, mext_irq_i => mei ); -- Two-Wire Bus - Tri-State Drivers (modules can only actively pull the signals low) ------ -- ------------------------------------------------------------------------------------------- i2c_sda <= '0' when (twi_sda_o = '0') else 'Z'; i2c_scl <= '0' when (twi_scl_o = '0') else 'Z'; twi_sda_i <= std_ulogic(i2c_sda); -- sense input twi_scl_i <= std_ulogic(i2c_scl); -- sense input i2c_sda <= '0' when (twd_sda_o = '0') else 'Z'; i2c_scl <= '0' when (twd_scl_o = '0') else 'Z'; twd_sda_i <= std_ulogic(i2c_sda); -- sense input twd_scl_i <= std_ulogic(i2c_scl); -- sense input -- I2C bus termination with weak pull-ups -- i2c_scl <= 'H'; i2c_sda <= 'H'; -- One-Wire Bus - Tri-State Driver (module can only actively pull the signals low) -------- -- ------------------------------------------------------------------------------------------- onewire <= '0' when (onewire_o = '0') else 'Z'; onewire_i <= std_ulogic(onewire); -- sense input -- 1-Wire bus termination with weak pull-up -- onewire <= 'H'; -- SPI/SDI Loop-Back ---------------------------------------------------------------------- -- ------------------------------------------------------------------------------------------- sdi_clk <= spi_clk; sdi_csn <= spi_csn(7); sdi_di <= spi_do; spi_di <= sdi_do when (spi_csn(7) = '0') else spi_do; -- Stream-Link FIFO Buffer ---------------------------------------------------------------- -- ------------------------------------------------------------------------------------------- slink_buffer: entity neorv32.neorv32_fifo generic map ( FIFO_DEPTH => 4, FIFO_WIDTH => 32+4+1, FIFO_RSYNC => false, FIFO_SAFE => true, FULL_RESET => true ) port map ( -- control -- clk_i => clk_gen, rstn_i => rst_gen, clear_i => '0', half_o => open, level_o => open, -- write port -- wdata_i(31 downto 0) => slink_tx.data, wdata_i(35 downto 32) => slink_tx.addr, wdata_i(36) => slink_tx.last, we_i => slink_tx.valid, free_o => slink_tx.ready, -- read port -- re_i => slink_rx.ready, rdata_o(31 downto 0) => slink_rx.data, rdata_o(35 downto 32) => slink_rx.addr, rdata_o(36) => slink_rx.last, avail_o => slink_rx.valid ); -- UART Simulation Receivers -------------------------------------------------------------- -- ------------------------------------------------------------------------------------------- sim_rx_uart0: entity work.sim_uart_rx generic map ( NAME => "uart0", FCLK => real(CLOCK_FREQUENCY), BAUD => real(19200) ) port map ( clk => clk_gen, rxd => uart0_txd ); sim_rx_uart1: entity work.sim_uart_rx generic map ( NAME => "uart1", FCLK => real(CLOCK_FREQUENCY), BAUD => real(19200) ) port map ( clk => clk_gen, rxd => uart1_txd ); -- XBUS / Wishbone Interconnect ----------------------------------------------------------- -- ------------------------------------------------------------------------------------------- xbus_interconnect: entity work.xbus_gateway generic map ( -- device address size in bytes and base address -- DEV_0_EN => EXT_MEM_A_EN, DEV_0_SIZE => EXT_MEM_A_SIZE, DEV_0_BASE => EXT_MEM_A_BASE, DEV_1_EN => EXT_MEM_B_EN, DEV_1_SIZE => EXT_MEM_B_SIZE, DEV_1_BASE => EXT_MEM_B_BASE, DEV_2_EN => true, DEV_2_SIZE => 64, DEV_2_BASE => x"F0000000", DEV_3_EN => true, DEV_3_SIZE => 4, DEV_3_BASE => x"FF000000" ) port map ( clk_i => clk_gen, rstn_i => rst_gen, -- host port -- host_req_i => xbus_core_req, host_rsp_o => xbus_core_rsp, -- device ports -- dev_0_req_o => xbus_ext_mem_a_req, dev_0_rsp_i => xbus_ext_mem_a_rsp, dev_1_req_o => xbus_ext_mem_b_req, dev_1_rsp_i => xbus_ext_mem_b_rsp, dev_2_req_o => xbus_mmio_req, dev_2_rsp_i => xbus_mmio_rsp, dev_3_req_o => xbus_trig_req, dev_3_rsp_i => xbus_trig_rsp ); -- XBUS: External Memory A ---------------------------------------------------------------- -- ------------------------------------------------------------------------------------------- xbus_external_memory_a_enable: if EXT_MEM_A_EN generate xbus_external_memory_a: entity work.xbus_memory generic map ( MEM_SIZE => EXT_MEM_A_SIZE, MEM_LATE => EXT_MEM_A_LATE, MEM_FILE => EXT_MEM_A_FILE ) port map ( clk_i => clk_gen, rstn_i => rst_gen, xbus_req_i => xbus_ext_mem_a_req, xbus_rsp_o => xbus_ext_mem_a_rsp ); end generate; xbus_external_memory_a_disable: if not EXT_MEM_A_EN generate xbus_ext_mem_a_rsp <= xbus_rsp_terminate_c; end generate; -- XBUS: External Memory B ---------------------------------------------------------------- -- ------------------------------------------------------------------------------------------- xbus_external_memory_b_enable: if EXT_MEM_B_EN generate xbus_external_memory_b: entity work.xbus_memory generic map ( MEM_SIZE => EXT_MEM_B_SIZE, MEM_LATE => EXT_MEM_B_LATE, MEM_FILE => EXT_MEM_B_FILE ) port map ( clk_i => clk_gen, rstn_i => rst_gen, xbus_req_i => xbus_ext_mem_b_req, xbus_rsp_o => xbus_ext_mem_b_rsp ); end generate; xbus_external_memory_b_disable: if not EXT_MEM_B_EN generate xbus_ext_mem_b_rsp <= xbus_rsp_terminate_c; end generate; -- XBUS: External Memory-Mapped IO -------------------------------------------------------- -- ------------------------------------------------------------------------------------------- xbus_mmio: entity work.xbus_memory generic map ( MEM_SIZE => 8, MEM_LATE => 32, MEM_FILE => "" -- no initialization ) port map ( clk_i => clk_gen, rstn_i => rst_gen, xbus_req_i => xbus_mmio_req, xbus_rsp_o => xbus_mmio_rsp ); -- XBUS: External IRQ Trigger ------------------------------------------------------------- -- ------------------------------------------------------------------------------------------- xbus_irq_trigger: process(rst_gen, clk_gen) begin if (rst_gen = '0') then msi <= '0'; mti <= '0'; mei <= '0'; elsif rising_edge(clk_gen) then -- bus response defaults -- xbus_trig_rsp.data <= (others => '0'); xbus_trig_rsp.ack <= '0'; xbus_trig_rsp.err <= '0'; -- trigger RISC-V platform IRQs -- if ((xbus_trig_req.cyc and xbus_trig_req.stb and xbus_trig_req.we and and_reduce_f(xbus_trig_req.sel)) = '1') then xbus_trig_rsp.ack <= '1'; msi <= xbus_trig_req.data(03); -- machine software interrupt mti <= xbus_trig_req.data(07); -- machine timer interrupt mei <= xbus_trig_req.data(11); -- machine external interrupt end if; end if; end process xbus_irq_trigger; end neorv32_tb_rtl;