diff --git a/Makefile b/Makefile index cef2e5c06..de6b9e92c 100644 --- a/Makefile +++ b/Makefile @@ -134,7 +134,7 @@ verible: ## Generates the build folder in sw using CMake to build (compile and linking) ## @param PROJECT= -## @param TARGET=sim(default),pynq-z2,nexys-a7-100t +## @param TARGET=sim(default),systemc,pynq-z2,nexys-a7-100t ## @param LINKER=on_chip(default),flash_load,flash_exec ## @param COMPILER=gcc(default), clang ## @param COMPILER_PREFIX=riscv32-unknown-(default) @@ -153,10 +153,14 @@ app-compile-all: ## @section Simulation -## Verilator simulation +## Verilator simulation with C++ verilator-sim: $(FUSESOC) --cores-root . run --no-export --target=sim --tool=verilator $(FUSESOC_FLAGS) --build openhwgroup.org:systems:core-v-mini-mcu ${FUSESOC_PARAM} 2>&1 | tee buildsim.log +## Verilator simulation with SystemC +verilator-sim-sc: + $(FUSESOC) --cores-root . run --no-export --target=sim_sc --tool=verilator $(FUSESOC_FLAGS) --build openhwgroup.org:systems:core-v-mini-mcu ${FUSESOC_PARAM} 2>&1 | tee buildsim.log + ## Questasim simulation questasim-sim: $(FUSESOC) --cores-root . run --no-export --target=sim --tool=modelsim $(FUSESOC_FLAGS) --build openhwgroup.org:systems:core-v-mini-mcu ${FUSESOC_PARAM} 2>&1 | tee buildsim.log diff --git a/README.md b/README.md index 18c97a10c..e36b76ec8 100644 --- a/README.md +++ b/README.md @@ -231,7 +231,7 @@ make app To run any other application, please use the following command with appropiate parameters: ``` -app PROJECT= TARGET=sim(default),pynq-z2 LINKER=on_chip(default),flash_load,flash_exec COMPILER=gcc(default),clang COMPILER_PREFIX=riscv32-unknown-(default) ARCH=rv32imc(default), +app PROJECT= TARGET=sim(default),systemc,pynq-z2,nexys-a7-100t LINKER=on_chip(default),flash_load,flash_exec COMPILER=gcc(default),clang COMPILER_PREFIX=riscv32-unknown-(default) ARCH=rv32imc(default), Params: - PROJECT (ex: , hello_world(default)) @@ -281,7 +281,7 @@ For example, if you want to set the `FPU` and `COREV_PULP` parameters of the `cv you need to add next to your compilation command `FUSESOC_PARAM="--COREV_PULP=1 --FPU=1"` Below the different EDA examples commands. -### Compiling for Verilator +### Compiling for Verilator (C++ testbench) To simulate your application with Verilator, first compile the HDL: @@ -307,6 +307,39 @@ or to execute all these three steps type: make run-helloworld ``` +### Compiling for Verilator (SystemC testbench) + +To simulate your application with Verilator using `SystemC`, + +make sure you have `SystemC 2.3.3` installed, if not, find it [here](https://www.accellera.org/downloads/standards/systemc). + +Make sure to have the following env variables set: + +``` +export SYSTEMC_INCLUDE=/your_path_to_systemc/systemc/include/ +export SYSTEMC_LIBDIR=/your_path_to_systemc/systemc/lib-linux64/ +``` + +Compile the HDL: + +``` +make verilator-sim-sc +``` + +then, go to your target system built folder + +``` +cd ./build/openhwgroup.org_systems_core-v-mini-mcu_0/sim_sc-verilator +``` + +and type to run your compiled software: + +``` +./Vtestharness +firmware=../../../sw/build/main.hex +``` + +If you want to know what is special about the SystemC testbench, have a look [here](./docs/source/How_to/SystemC.md) + ### Compiling for VCS To simulate your application with VCS, first compile the HDL: diff --git a/core-v-mini-mcu.core b/core-v-mini-mcu.core index e4dc21c8e..34b45f1a9 100644 --- a/core-v-mini-mcu.core +++ b/core-v-mini-mcu.core @@ -193,6 +193,8 @@ filesets: tb-verilator: files: + - tb/XHEEP_CmdLineOptions.hh: { is_include_file: true } + - tb/XHEEP_CmdLineOptions.cpp - tb/tb_top.cpp file_type: cppSource @@ -201,6 +203,14 @@ filesets: - tb/tb_top.sv file_type: systemVerilogSource + tb-sc-verilator: + files: + - tb/XHEEP_CmdLineOptions.hh: { is_include_file: true } + - tb/XHEEP_CmdLineOptions.cpp + - tb/tb_sc_top.cpp + file_type: cppSource + + openroad_base_files: files: - flow/OpenROAD-flow-scripts/flow/Makefile : {file_type: Makefile} @@ -261,6 +271,10 @@ parameters: datatype: bool paramtype: vlogdefine default: false + SIM_SYSTEMC: + datatype: bool + paramtype: vlogdefine + default: false FPGA_NEXYS: datatype: bool paramtype: vlogdefine @@ -301,6 +315,8 @@ targets: - files_rtl_generic - target_sim ? (rtl-simulation) - target_sim ? (tool_verilator? (files_verilator_waiver)) + - target_sim_sc ? (rtl-simulation) + - target_sim_sc ? (tool_verilator? (files_verilator_waiver)) toplevel: [core_v_mini_mcu] sim: @@ -392,6 +408,40 @@ targets: - '-LDFLAGS "-pthread -lutil -lelf"' - "-Wall" + sim_sc: + <<: *default_target + default_tool: modelsim + filesets_append: + - tb-utils + - tool_verilator? (tb-sc-verilator) + - "!integrated_heep? (x_heep_system)" + toplevel: + - tool_verilator? (testharness) + parameters: + - COREV_PULP + - FPU + - JTAG_DPI + - X_EXT + - USE_EXTERNAL_DEVICE_EXAMPLE + - USE_UPF + - REMOVE_OBI_FIFO + - SIM_SYSTEMC=true + tools: + verilator: + mode: sc + verilator_options: + - '--sc' + - '--trace' + - '--trace-structs' + - '--trace-params' + - '--trace-max-array 1024' + - '--x-assign unique' + - '--x-initial unique' + - '--exe tb_sc_top.cpp' + - '-CFLAGS "-std=c++11 -Wall -g -fpermissive"' + - '-LDFLAGS "-pthread -lutil -lelf $(SYSTEMC_LIBDIR)/libsystemc.a"' + - "-Wall" + nexys-a7-100t: <<: *default_target default_tool: vivado diff --git a/docs/source/How_to/Debug.md b/docs/source/How_to/Debug.md index a71034c52..2a5882f66 100644 --- a/docs/source/How_to/Debug.md +++ b/docs/source/How_to/Debug.md @@ -40,7 +40,7 @@ Now we are going to Simulate debugging with core-v-mini-mcu. In this setup, OpenOCD communicates with the remote bitbang server by means of DPIs. The remote bitbang server is simplemented in the folder ./hw/vendor/pulp_platform_pulpissimo/rtl/tb/remote_bitbang and it will be compiled using fusesoc. -### Verilator +### Verilator (C++ only) To simulate your application with Questasim using the remote_bitbang server, you need to compile you system adding the `JTAG DPI` functions: diff --git a/docs/source/How_to/SystemC.md b/docs/source/How_to/SystemC.md new file mode 100644 index 000000000..1a1aad571 --- /dev/null +++ b/docs/source/How_to/SystemC.md @@ -0,0 +1,16 @@ +# SystemC model + +Supporting SystemC model in `X-HEEP` is still a work-in-progress. +However, a simple example is provided in the SystemC testbench available in `tb/tb_sc_top.cpp`. + +When compiling the `X-HEEP` with Verilator using SystemC, the above testbench is used for simulation. +The testbench gets an `X-HEEP` external-memory `obi` master port to communicate with a SystemC memory model. + +Such model is very simple as meant to be an example and is provided in `tb/systemc_tb`. +For those who want to extend the functionality of `X-HEEP` with SystemC, such examples can be used as starting point. + +The SystemC modules leverages `TLM-2.0` as well as baseline SystemC functionalities. + +The `X-HEEP` `obi` port is connected to a `C++` direct-mapped cache who handles `hit` and `miss` with pre-defined latencies. +It uses `TLM-2.0` to communicate with the external SystemC memory on `miss` cache-transactions. +A module in SystemC then communicates with the RTL SystemC model compiled by Verilator to provides read/write data. \ No newline at end of file diff --git a/hw/ip_examples/slow_memory/rtl/slow_memory.sv b/hw/ip_examples/slow_memory/rtl/slow_memory.sv index 785ddd467..6cedd80ae 100644 --- a/hw/ip_examples/slow_memory/rtl/slow_memory.sv +++ b/hw/ip_examples/slow_memory/rtl/slow_memory.sv @@ -79,12 +79,21 @@ module slow_memory #( always_comb begin - gnt_o = 1'b0; - rvalid_o = rvalid_q; - state_n = state_q; - counter_n = counter_q - 1; - rvalid_n = rvalid_q; - + gnt_o = 1'b0; + rvalid_o = rvalid_q; + state_n = state_q; + counter_n = counter_q - 1; + rvalid_n = rvalid_q; + mem_req = '0; + mem_we = '0; + mem_addr = '0; + mem_wdata = '0; + mem_be = '0; + mem_req_n = mem_req_q; + mem_we_n = mem_we_q; + mem_addr_n = mem_addr_q; + mem_wdata_n = mem_wdata_q; + mem_be_n = mem_be_q; unique case (state_q) READY: begin @@ -92,13 +101,13 @@ module slow_memory #( if (req_i) begin gnt_o = random1[0]; if (gnt_o) begin - state_n = WAIT_RVALID; - counter_n = random2[4:0] + 1; - mem_req_n <= req_i; - mem_we_n <= we_i; - mem_addr_n <= addr_i; - mem_wdata_n <= wdata_i; - mem_be_n <= be_i; + state_n = WAIT_RVALID; + counter_n = random2[4:0] + 1; + mem_req_n = req_i; + mem_we_n = we_i; + mem_addr_n = addr_i; + mem_wdata_n = wdata_i; + mem_be_n = be_i; end end end diff --git a/hw/vendor/esl_epfl_cv32e40px.core b/hw/vendor/esl_epfl_cv32e40px.core index 56a485a41..a31337d17 100644 --- a/hw/vendor/esl_epfl_cv32e40px.core +++ b/hw/vendor/esl_epfl_cv32e40px.core @@ -60,3 +60,4 @@ targets: - files_rtl - ff_regfile - target_sim? (files_clk_gate) + - target_sim_sc? (files_clk_gate) diff --git a/hw/vendor/openhwgroup_cv32e20/cve2_top.core b/hw/vendor/openhwgroup_cv32e20/cve2_top.core index cb4e23b4c..0d9d2c5cc 100644 --- a/hw/vendor/openhwgroup_cv32e20/cve2_top.core +++ b/hw/vendor/openhwgroup_cv32e20/cve2_top.core @@ -75,6 +75,7 @@ targets: - tool_veriblelint ? (files_lint_verible) - files_rtl - target_sim ? (files_clk_gate) + - target_sim_sc ? (files_clk_gate) toplevel: cve2_top parameters: - tool_vivado ? (FPGA_XILINX=true) diff --git a/hw/vendor/openhwgroup_cv32e40p.core b/hw/vendor/openhwgroup_cv32e40p.core index f11d35c11..1d8a39f7f 100644 --- a/hw/vendor/openhwgroup_cv32e40p.core +++ b/hw/vendor/openhwgroup_cv32e40p.core @@ -64,3 +64,4 @@ targets: - files_rtl - ff_regfile - target_sim? (files_clk_gate) + - target_sim_sc? (files_clk_gate) diff --git a/hw/vendor/openhwgroup_cv32e40x.core b/hw/vendor/openhwgroup_cv32e40x.core index 70a248de8..7a12b181f 100644 --- a/hw/vendor/openhwgroup_cv32e40x.core +++ b/hw/vendor/openhwgroup_cv32e40x.core @@ -67,3 +67,4 @@ targets: filesets: - files_rtl - target_sim? (files_clk_gate) + - target_sim_sc? (files_clk_gate) diff --git a/hw/vendor/patches/openhwgroup_cv32e20/cv32e20_core.patch b/hw/vendor/patches/openhwgroup_cv32e20/cv32e20_core.patch new file mode 100644 index 000000000..9727dec16 --- /dev/null +++ b/hw/vendor/patches/openhwgroup_cv32e20/cv32e20_core.patch @@ -0,0 +1,12 @@ +diff --git a/cve2_top.core b/cve2_top.core +index cb4e23b4..0d9d2c5c 100644 +--- a/cve2_top.core ++++ b/cve2_top.core +@@ -75,6 +75,7 @@ targets: + - tool_veriblelint ? (files_lint_verible) + - files_rtl + - target_sim ? (files_clk_gate) ++ - target_sim_sc ? (files_clk_gate) + toplevel: cve2_top + parameters: + - tool_vivado ? (FPGA_XILINX=true) diff --git a/hw/vendor/pulp_platform_gpio.core b/hw/vendor/pulp_platform_gpio.core index 630f318a3..773989477 100644 --- a/hw/vendor/pulp_platform_gpio.core +++ b/hw/vendor/pulp_platform_gpio.core @@ -39,6 +39,7 @@ targets: - rtl - "gpio-test? (testbench)" - target_sim? (clock-gate) + - target_sim_sc? (clock-gate) - target_asic_synthesis? (clock-gate) - target_asic_yosys_synthesis? (clock-gate) - target_nexys-a7-100t? (no-clock-gate) diff --git a/hw/vendor/pulp_platform_tech_cells_generic.core b/hw/vendor/pulp_platform_tech_cells_generic.core index 6a70b8e22..02d8b12c0 100644 --- a/hw/vendor/pulp_platform_tech_cells_generic.core +++ b/hw/vendor/pulp_platform_tech_cells_generic.core @@ -20,3 +20,4 @@ targets: default: filesets: - target_sim? (rtl_sim) + - target_sim_sc? (rtl_sim) diff --git a/sw/applications/example_dma/main.c b/sw/applications/example_dma/main.c index bbab36980..ea842d8a4 100644 --- a/sw/applications/example_dma/main.c +++ b/sw/applications/example_dma/main.c @@ -208,7 +208,7 @@ int main(int argc, char *argv[]) #endif // TEST_ADDRESS_MODE -#ifndef TARGET_PYNQ_Z2 +#if defined(TARGET_SIM) || defined(TARGET_SYSTEMC) #ifdef TEST_ADDRESS_MODE_EXTERNAL_DEVICE @@ -275,7 +275,7 @@ int main(int argc, char *argv[]) #endif //TEST_ADDRESS_MODE_EXTERNAL_DEVICE #else - #pragma message( "TEST_ADDRESS_MODE_EXTERNAL_DEVICE is not executed on PYNQ Z2" ) + #pragma message( "TEST_ADDRESS_MODE_EXTERNAL_DEVICE is not executed on target different than TARGET_SIM" ) #endif #ifdef TEST_PENDING_TRANSACTION diff --git a/sw/applications/example_ext_memory/main.c b/sw/applications/example_ext_memory/main.c new file mode 100644 index 000000000..8af03ad46 --- /dev/null +++ b/sw/applications/example_ext_memory/main.c @@ -0,0 +1,128 @@ +// Copyright EPFL contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include +#include +#include +#include "core_v_mini_mcu.h" +#include "x-heep.h" + +#define BUFF_LEN 100 + +uint32_t buffer_rnd_index[BUFF_LEN]; + +#ifdef TARGET_SYSTEMC +//make app PROJECT=example_ext_memory TARGET=systemc +#define CACHE_FLUSH 1 +#define CACHE_BYPASS 2 +#define CACHE_SIZE 4*1024 +#endif + +#define MEMORY_SIZE 32*1024 +#define MEMORY_ADDR_MASK 0x7FFF +#define MEMORY_MAX_WORD_INDEX (MEMORY_SIZE/4) + +int is_in_array(uint32_t number, uint32_t* array, int N ) { + + for (int i=0;i -#include -#include "csr.h" -#include "hart.h" -#include "handler.h" -#include "core_v_mini_mcu.h" -#include "rv_timer.h" -#include "rv_timer_regs.h" -#include "soc_ctrl.h" -#include "rv_plic.h" -#include "rv_plic_regs.h" -#include "spi_host.h" -#include "spi_host_regs.h" -#include "dma.h" -#include "fast_intr_ctrl.h" -#include "gpio.h" -#include "fast_intr_ctrl_regs.h" -#include "x-heep.h" - -#define REVERT_24b_ADDR(addr) ((((uint32_t)addr & 0xff0000) >> 16) | ((uint32_t)addr & 0xff00) | (((uint32_t)addr & 0xff) << 16)) -#define FLASH_ADDR 0x00000000 -#define FLASH_SIZE 64 * 1024 * 1024 -#define FLASH_CLK_MAX_HZ (133 * 1000 * 1000) - - -/* By default, printfs are activated for FPGA and disabled for simulation. */ -#define PRINTF_IN_FPGA 1 -#define PRINTF_IN_SIM 0 - -#if TARGET_SIM && PRINTF_IN_SIM - #define PRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) -#elif TARGET_PYNQ_Z2 && PRINTF_IN_FPGA - #define PRINTF(fmt, ...) printf(fmt, ## __VA_ARGS__) -#else - #define PRINTF(...) -#endif - -// Interrupt controller variables -plic_result_t plic_res; -uint32_t intr_num; - -//volatile int8_t timer_flag; -volatile int8_t spi_intr_flag; - -spi_host_t spi_host_flash; - -void dma_intr_handler_trans_done(){ - PRINTF("#\n\r"); -} - -void fic_irq_spi_flash(){ - // Disable SPI interrupts - spi_enable_evt_intr(&spi_host_flash, false); - spi_enable_rxwm_intr(&spi_host_flash, false); - spi_intr_flag = 1; - PRINTF("@\n\r"); -} - - -void write_to_flash(spi_host_t *SPI, uint16_t *data, uint32_t byte_count, uint32_t addr) -{ - uint32_t write_to_mem = 0x02; - spi_write_word(SPI, write_to_mem); - uint32_t cmd_write_to_mem = spi_create_command((spi_command_t){ - .len = 0, - .csaat = true, - .speed = kSpiSpeedStandard, - .direction = kSpiDirTxOnly - }); - spi_set_command(SPI, cmd_write_to_mem); - spi_wait_for_ready(SPI); - - uint32_t addr_cmd = __builtin_bswap32(addr); - spi_write_word(SPI, addr_cmd); - uint32_t cmd_address = spi_create_command((spi_command_t){ - .len = 3, - .csaat = true, - .speed = kSpiSpeedStandard, - .direction = kSpiDirTxOnly - }); - spi_set_command(SPI, cmd_address); - spi_wait_for_ready(SPI); - - uint32_t *fifo_ptr_tx = SPI->base_addr.base + SPI_HOST_TXDATA_REG_OFFSET; - - // -- DMA CONFIGURATION -- - dma_init(NULL); - - dma_target_t tgt_src = { - .ptr = data, - .inc_du = 1, - .size_du = 64, - .type = DMA_DATA_TYPE_HALF_WORD, - .trig = DMA_TRIG_MEMORY, - }; - dma_target_t tgt_dst = { - .ptr = fifo_ptr_tx, - .inc_du = 0, - .size_du = 0, - .type = DMA_DATA_TYPE_HALF_WORD, - .trig = DMA_TRIG_SLOT_SPI_FLASH_TX, - }; - dma_trans_t trans = { - .src = &tgt_src, - .dst = &tgt_dst, - .end = DMA_TRANS_END_INTR, - }; - - dma_config_flags_t res; - - spi_intr_flag = 0; - - res = dma_validate_transaction( &trans, DMA_ENABLE_REALIGN, DMA_PERFORM_CHECKS_INTEGRITY ); - PRINTF("trans: %u\n\r", res ); - res = dma_load_transaction(&trans); - PRINTF("load: %u\n\r", res ); - res = dma_launch(&trans); - - // Wait for the first data to arrive to the TX FIFO before enabling interrupt - spi_wait_for_tx_not_empty(SPI); - // Enable event interrupt - spi_enable_evt_intr(SPI, true); - // Enable TX empty interrupt - spi_enable_txempty_intr(SPI, true); - - const uint32_t cmd_write_tx = spi_create_command((spi_command_t){ - .len = byte_count - 1, - .csaat = false, - .speed = kSpiSpeedStandard, - .direction = kSpiDirTxOnly - }); - spi_set_command(SPI, cmd_write_tx); - spi_wait_for_ready(SPI); - - // Wait for SPI interrupt - while(spi_intr_flag == 0) { - wait_for_interrupt(); - } - - PRINTF("%d words written to flash.\n\n\r", byte_count/4); -} - -int main(int argc, char *argv[]) -{ - // Get current Frequency - soc_ctrl_t soc_ctrl; - soc_ctrl.base_addr = mmio_region_from_addr((uintptr_t)SOC_CTRL_START_ADDRESS); - soc_ctrl_select_spi_host(&soc_ctrl); - - uint32_t core_clk = soc_ctrl_get_frequency(&soc_ctrl); - - // Enable interrupt on processor side - // Enable global interrupt for machine-level interrupts - CSR_SET_BITS(CSR_REG_MSTATUS, 0x8); - uint32_t mask = 1 << 21; - CSR_SET_BITS(CSR_REG_MIE, mask); - spi_intr_flag = 0; - // Set mie.MEIE bit to one to enable timer interrupt - mask = 1 << 7; - CSR_SET_BITS(CSR_REG_MIE, mask); - - spi_host_flash.base_addr = mmio_region_from_addr((uintptr_t)SPI_HOST_START_ADDRESS); - spi_set_enable(&spi_host_flash, true); - spi_output_enable(&spi_host_flash, true); - - uint16_t clk_div = 0; - if (FLASH_CLK_MAX_HZ < core_clk / 2) - { - clk_div = (core_clk / (FLASH_CLK_MAX_HZ)-2) / 2; // The value is truncated - if (core_clk / (2 + 2 * clk_div) > FLASH_CLK_MAX_HZ) - clk_div += 1; // Adjust if the truncation was not 0 - } - - // SPI Configuration - // Configure chip 0 (flash memory) - const uint32_t chip_cfg_flash = spi_create_configopts((spi_configopts_t){ - .clkdiv = clk_div, - .csnidle = 0xF, - .csntrail = 0xF, - .csnlead = 0xF, - .fullcyc = false, - .cpha = 0, - .cpol = 0}); - spi_set_configopts(&spi_host_flash, 0, chip_cfg_flash); - spi_set_csid(&spi_host_flash, 0); - - // To set the number of dummy cycles we have to send command 0x11 and then a 1B value - const uint32_t reset_cmd = 0x11; - spi_write_word(&spi_host_flash, reset_cmd); - const uint32_t cmd_reset = spi_create_command((spi_command_t){ - .len = 0, - .csaat = true, - .speed = kSpiSpeedStandard, - .direction = kSpiDirTxOnly - }); - spi_set_command(&spi_host_flash, cmd_reset); - spi_wait_for_ready(&spi_host_flash); - - const uint32_t set_dummy_cycle = 0x07; - spi_write_word(&spi_host_flash, set_dummy_cycle); - const uint32_t cmd_set_dummy = spi_create_command((spi_command_t){ - .len = 0, - .csaat = false, - .speed = kSpiSpeedStandard, - .direction = kSpiDirTxOnly - }); - - spi_set_command(&spi_host_flash, cmd_set_dummy); - spi_wait_for_ready(&spi_host_flash); - - uint32_t results[32]; - for(uint32_t i = 0; i < 32; i++){ - results[i] = i; - } - - write_to_flash(&spi_host_flash, results, sizeof(*results) * 32, FLASH_ADDR); - - PRINTF("Success.\n\r"); - - return EXIT_SUCCESS; -} diff --git a/sw/device/lib/runtime/core_v_mini_mcu.c b/sw/device/lib/runtime/core_v_mini_mcu.c index 2d0af1e38..326fd963e 100644 --- a/sw/device/lib/runtime/core_v_mini_mcu.c +++ b/sw/device/lib/runtime/core_v_mini_mcu.c @@ -5,8 +5,10 @@ #include "core_v_mini_mcu.h" #include -// functions +//heep functions prototypes uint32_t * heep_get_flash_address_offset(uint32_t* data_address_lma); +void heep_init_lfsr(); +uint32_t heep_rand_lfsr(); // this translates the logical address of the FLASH relative to 0 instead of FLASH_MEM_START_ADDRESS, as used by the BSP uint32_t * heep_get_flash_address_offset(uint32_t* data_address_lma){ @@ -20,3 +22,16 @@ uint32_t * heep_get_flash_address_offset(uint32_t* data_address_lma){ #endif } + +//get random values +uint32_t lfsr; + +void heep_init_lfsr() { + lfsr = (uint32_t)0xAABBCCDD; +} + +uint32_t heep_rand_lfsr() { + uint32_t bit = (lfsr ^ (lfsr >> 10) ^ (lfsr >> 11) ^ (lfsr >> 12)) & 1; + lfsr = (lfsr >> 1) | (bit << 31); + return lfsr; +} diff --git a/sw/device/lib/runtime/syscalls.c b/sw/device/lib/runtime/syscalls.c index b3df658e8..a9a7fcc20 100644 --- a/sw/device/lib/runtime/syscalls.c +++ b/sw/device/lib/runtime/syscalls.c @@ -49,6 +49,7 @@ void * _sbrk (ptrdiff_t __incr); int _unlink (const char *__path); ssize_t _write (int __fd, const void *__buf, size_t __nbyte); int _execve (const char *__path, char * const __argv[], char * const __envp[]); +int _kill (pid_t pid, int sig); #endif @@ -163,7 +164,7 @@ int _isatty(int file) return (file == STDOUT_FILENO); } -int _kill(int pid, int sig) +int _kill(pid_t pid, int sig) { errno = EINVAL; return -1; diff --git a/sw/device/target/systemc/x-heep.h b/sw/device/target/systemc/x-heep.h new file mode 100644 index 000000000..1c1390cb1 --- /dev/null +++ b/sw/device/target/systemc/x-heep.h @@ -0,0 +1,30 @@ +// Copyright EPFL contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef X_HEEP +#define X_HEEP + +#pragma message ( "the x-heep.h for SIMULATION in SYSTEMC is used" ) + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + +#define REFERENCE_CLOCK_Hz 100*1000*1000 +#define UART_BAUDRATE 256000 +#define TARGET_SYSTEMC 1 + +/** + * As the hw is configurable, we can have setups with different number of + * Gpio pins + */ +#define MAX_PIN 32 + + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +#endif // X_HEEP diff --git a/tb/XHEEP_CmdLineOptions.cpp b/tb/XHEEP_CmdLineOptions.cpp new file mode 100644 index 000000000..947d31ef3 --- /dev/null +++ b/tb/XHEEP_CmdLineOptions.cpp @@ -0,0 +1,100 @@ +#include "XHEEP_CmdLineOptions.hh" +#include +#include + +XHEEP_CmdLineOptions::XHEEP_CmdLineOptions(int argc, char* argv[]) // define default constructor +{ + this->argc = argc; + this->argv = argv; +} + +std::string XHEEP_CmdLineOptions::getCmdOption(int argc, char* argv[], const std::string& option) +{ + std::string cmd; + for( int i = 0; i < argc; ++i) + { + std::string arg = argv[i]; + size_t arg_size = arg.length(); + size_t option_size = option.length(); + + if(arg.find(option)==0){ + cmd = arg.substr(option_size,arg_size-option_size); + } + } + return cmd; +} + +bool XHEEP_CmdLineOptions::get_use_openocd() +{ + + std::string arg_openocd = this->getCmdOption(this->argc, this->argv, "+openOCD=");; + + bool use_openocd = false; + + if(arg_openocd.empty()){ + std::cout<<"[TESTBENCH]: No OpenOCD is used"<getCmdOption(this->argc, this->argv, "+firmware="); + + if(firmware.empty()){ + std::cout<<"[TESTBENCH]: No firmware specified"<getCmdOption(this->argc, this->argv, "+max_sim_time="); + unsigned int max_sim_time; + + max_sim_time = 0; + if(arg_max_sim_time.empty()){ + std::cout<<"[TESTBENCH]: No Max time specified"<getCmdOption(this->argc, this->argv, "+boot_sel="); + unsigned int boot_sel = 0; + + if(arg_boot_sel.empty()){ + std::cout<<"[TESTBENCH]: No Boot Option specified, using jtag (boot_sel=0)"< + +class XHEEP_CmdLineOptions // declare Calculator class +{ + + public: // public members + XHEEP_CmdLineOptions(int argc, char* argv[]); // default constructor + + std::string getCmdOption(int argc, char* argv[], const std::string& option); // get options from cmd lines + bool get_use_openocd(); + std::string get_firmware(); + unsigned int get_max_sim_time(bool& run_all); + unsigned int get_boot_sel(); + int argc; + char** argv; + +}; + + + +#endif diff --git a/tb/systemc_tb/Cache.h b/tb/systemc_tb/Cache.h new file mode 100644 index 000000000..d9c94b5d0 --- /dev/null +++ b/tb/systemc_tb/Cache.h @@ -0,0 +1,221 @@ +#ifndef CACHE_H +#define CACHE_H + +#include +#include +#include +#include + + +// Target module representing a simple direct mapped cache +class CacheMemory +{ + +public: + uint32_t cache_size_byte = 4*1024; + uint32_t number_of_blocks = 256; + + uint32_t nbits_blocks = 0; + uint32_t nbits_tags = 0; + uint32_t nbits_index = 0; + uint32_t block_size_byte = 0; + + enum { ARCHITECTURE_bits = 32 }; + + std::ofstream cacheFile; + + typedef struct cache_line { + uint32_t tag; + bool valid; + uint8_t* data; + } cache_line_t; + + cache_line_t* cache_array; + + + CacheMemory(): cacheFile("cache_status.log") + { + cache_array = NULL; + } + + void create_cache() { + cache_array = new cache_line_t[number_of_blocks]; + this->block_size_byte = get_block_size(); + this->nbits_blocks = log2(block_size_byte); + this->nbits_index = log2(number_of_blocks); + this->nbits_tags = ARCHITECTURE_bits - nbits_index - nbits_blocks; + printf("bits block %d, index %d, tags %d\n",nbits_blocks, nbits_index, nbits_tags ); + } + + void create_cache(uint32_t cache_size_byte, uint32_t number_of_blocks) { + this->cache_size_byte = cache_size_byte; + this->number_of_blocks = number_of_blocks; + cache_array = new cache_line_t[number_of_blocks]; + this->block_size_byte = get_block_size(); + this->nbits_blocks = log2(block_size_byte); + this->nbits_index = log2(number_of_blocks); + this->nbits_tags = ARCHITECTURE_bits - nbits_index - nbits_blocks; + } + + uint32_t initialize_cache() { + if(cache_array == NULL) { + return -1; + } + // Initialize memory with random data + for (int i = 0; i < number_of_blocks; i++) { + cache_array[i].valid = false; + cache_array[i].tag = 0; + cache_array[i].data = new uint8_t[block_size_byte]; + for(int j = 0; j> nbits_blocks) & mask_index ); + } + + uint32_t get_block_offset(uint32_t address) { + uint32_t mask_block = (1 << nbits_blocks) - 1; + return (uint32_t)(address & mask_block); + } + + uint32_t get_base_address(uint32_t address) { + return (uint32_t)((address >> nbits_blocks) << nbits_blocks); + } + + uint32_t get_tag(uint32_t address) { + return (uint32_t)(address >> (nbits_index+nbits_blocks)); + } + + uint32_t get_tag_from_index(uint32_t index) { + return cache_array[index].tag; + } + + + bool cache_hit(uint32_t address) { + uint32_t index = get_index(address); + uint32_t tag = get_tag(address); + return ( cache_array[index].valid && tag == cache_array[index].tag); + + } + + void add_entry(uint32_t address, uint8_t* new_data) { + uint32_t index = get_index(address); + uint32_t tag = get_tag(address); + cache_array[index].valid = true; + cache_array[index].tag = tag; + memcpy(cache_array[index].data, new_data, block_size_byte); + } + + void get_data(uint32_t address, uint8_t* new_data) { + uint32_t index = get_index(address); + memcpy(new_data, cache_array[index].data, block_size_byte); + } + + void get_data_at_index(uint32_t index, uint8_t* new_data) { + memcpy(new_data, cache_array[index].data, block_size_byte); + } + + uint32_t get_address(uint32_t address){ + uint32_t index = get_index(address); + uint32_t tag = cache_array[index].tag; + uint32_t new_address = (tag << (nbits_index+nbits_blocks)) | (index<get_block_offset(address); + uint8_t* new_data = new uint8_t[block_size_byte]; + this->get_data(address, new_data); + data_word = *((int32_t *)&new_data[block_offset]); + delete new_data; + return data_word; + } + + void set_word(uint32_t address, int32_t data_word) { + uint32_t block_offset = this->get_block_offset(address); + uint8_t* new_data = new uint8_t[block_size_byte]; + this->get_data(address, new_data); + *((int32_t *)&new_data[block_offset]) = data_word; + for(int i=0;iadd_entry(address, new_data); + delete new_data; + } + + bool is_entry_valid(uint32_t address) { + uint32_t index = get_index(address); + return cache_array[index].valid; + } + + bool is_entry_valid_at_index(uint32_t index) { + return cache_array[index].valid; + } + + void print_cache_status(uint32_t operation_id, std::string time_str) { + if (cacheFile.is_open()) { + std::string log_cache = ""; + std::ostringstream ss; + + log_cache+= std::to_string(operation_id) + "): " + time_str + "\n"; + log_cache+= "INDEX | TAG | DATA BLOCK | VALID\n"; + + for(int i=0;inbits_index/4) << std::setfill('0') << std::hex << static_cast(i); + log_cache+= ss.str() + " | "; + ss.str(""); + ss.clear(); + ss << "0x" << std::setw(this->nbits_tags/4) << std::setfill('0') << std::hex << cache_array[i].tag; + log_cache+= ss.str() + " | 0x"; + ss.str(""); + ss.clear(); + for(int j = 0; j(cache_array[i].data[j]); + log_cache+= ss.str() + " | "; + log_cache+= std::string( cache_array[i].valid ? "1" : "0" ) + "\n"; + + cacheFile << log_cache; + ss.str(""); + ss.clear(); + log_cache = std::string(""); + } + } else { + std::cout << "Failed to create the Cache file." << std::endl; + } + } + + /* main memory address + 0x7052 = 'b111_0000_0101_0010' + + cache size = 4KB, + number_of_blocks = 256, thus index is on 8bit + block_size_in_byte = 4KB/256 = 16bytes, i.e. 4 words + + 111: tag + 0000_0101: used as index + 0010: used for block offset , 4bits as 16 bytes + + + get_tag(0x7052) --> 0x7 + get_index(0x7052) --> 0x5 + get_block_offset(0x7052) --> 0x2 + + + */ +}; + +#endif diff --git a/tb/systemc_tb/MainMemory.h b/tb/systemc_tb/MainMemory.h new file mode 100644 index 000000000..984f81cf3 --- /dev/null +++ b/tb/systemc_tb/MainMemory.h @@ -0,0 +1,68 @@ +#ifndef MEMORY_H +#define MEMORY_H + +// Needed for the simple_target_socket +#define SC_INCLUDE_DYNAMIC_PROCESSES + +#include "systemc" +using namespace sc_core; +using namespace sc_dt; +using namespace std; + +#include "tlm.h" +#include "tlm_utils/simple_target_socket.h" + + +// Target module representing a simple direct mapped cache +SC_MODULE(MainMemory) +{ + // TLM-2 socket, defaults to 32-bits wide, base protocol + tlm_utils::simple_target_socket socket; + + enum { SIZE = 32*1024/4 }; //32KB word addressable + + int32_t mem[SIZE]; + + + SC_CTOR(MainMemory) + : socket("socket") + { + // Register callback for incoming b_transport interface method call + socket.register_b_transport(this, &MainMemory::b_transport); + + // Initialize memory with random data + for (int i = 0; i < SIZE; i++) + mem[i] = 0xAA000000 | (rand() % 256); + } + + // TLM-2 blocking transport method + virtual void b_transport( tlm::tlm_generic_payload& trans, sc_time& delay ) + { + tlm::tlm_command cmd = trans.get_command(); + sc_dt::uint64 adr = trans.get_address() / 4; + unsigned char* ptr = trans.get_data_ptr(); + unsigned int len = trans.get_data_length(); + unsigned char* byt = trans.get_byte_enable_ptr(); + unsigned int wid = trans.get_streaming_width(); + + // Obliged to check address range and check for unsupported features, + // i.e. byte enables, streaming, and bursts + // Can ignore DMI hint and extensions + // Using the SystemC report handler is an acceptable way of signalling an error + + if (adr >= sc_dt::uint64(SIZE) || byt != 0 || len > 4 || wid < len) + SC_REPORT_ERROR("TLM-2", "Target does not support given generic payload transaction"); + + // Obliged to implement read and write commands + if ( cmd == tlm::TLM_READ_COMMAND ) + memcpy(ptr, &mem[adr], len); + else if ( cmd == tlm::TLM_WRITE_COMMAND ) + memcpy(&mem[adr], ptr, len); + + // Obliged to set response status to indicate successful completion + trans.set_response_status( tlm::TLM_OK_RESPONSE ); + } + +}; + +#endif diff --git a/tb/systemc_tb/MemoryRequest.h b/tb/systemc_tb/MemoryRequest.h new file mode 100644 index 000000000..c0a7d1c7f --- /dev/null +++ b/tb/systemc_tb/MemoryRequest.h @@ -0,0 +1,241 @@ +#ifndef MEMORYREQUEST_H +#define MEMORYREQUEST_H + +#include "systemc" +using namespace sc_core; +using namespace sc_dt; +using namespace std; + +#include "tlm.h" +#include "tlm_utils/simple_initiator_socket.h" + +#include "Cache.h" + +#include +#include +#include +#include + +// MemoryRequest module generating generic payload transactions + +SC_MODULE(MemoryRequest) +{ + // TLM-2 socket, defaults to 32-bits wide, base protocol + tlm_utils::simple_initiator_socket socket; + bool we_i; + uint32_t be_i; + uint32_t addr_i; + uint32_t rwdata_io; + CacheMemory* cache; + std::ofstream heep_mem_transactions; + bool bypass_state = false; + + typedef struct cache_statistics + { + uint32_t number_of_transactions; + uint32_t number_of_hit; + uint32_t number_of_miss; + } cache_statistics_t; + + cache_statistics_t cache_stat; + + SC_CTOR(MemoryRequest) + : socket("socket"), // Construct and name socket + heep_mem_transactions("heep_mem_transactions.log") + { + + cache = new CacheMemory; + cache->create_cache(); + cache->initialize_cache(); + cache_stat.number_of_transactions = 0; + cache_stat.number_of_hit = 0; + cache_stat.number_of_miss = 0; + cache->print_cache_status(cache_stat.number_of_transactions++, sc_time_stamp().to_string()); + + SC_THREAD(thread_process); + } + + + uint32_t memory_copy(uint32_t addr, int32_t* buffer_data, int N, bool write_enable, tlm::tlm_generic_payload* trans, sc_time delay) { + + tlm::tlm_command cmd = write_enable ? tlm::TLM_WRITE_COMMAND : tlm::TLM_READ_COMMAND; + + //first read block_size bytes from memory to place them in cache regardless of the cmd + for(int i=0; i < N; i++){ + trans->set_command( cmd ); + trans->set_address( (addr + i*4) & 0x00007FFF ); //15bits + trans->set_data_ptr( reinterpret_cast(&buffer_data[i]) ); + trans->set_data_length( 4 ); + trans->set_streaming_width( 4 ); // = data_length to indicate no streaming + trans->set_byte_enable_ptr( 0 ); // 0 indicates unused + trans->set_dmi_allowed( false ); // Mandatory initial value + trans->set_response_status( tlm::TLM_INCOMPLETE_RESPONSE ); // Mandatory initial value + socket->b_transport( *trans, delay ); // Blocking transport call + + if(bypass_state){ + if(write_enable) + heep_mem_transactions << "Writing to Mem[" << hex << ((addr + i*4) & 0x00007FFF) << "]: " << buffer_data[i] << " at time " << sc_time_stamp() <is_response_error() ) + SC_REPORT_ERROR("TLM-2", "Response error from b_transport"); + } + return N; + } + + + void thread_process() + { + // TLM-2 generic payload transaction, reused across calls to b_transport + tlm::tlm_generic_payload* trans = new tlm::tlm_generic_payload; + + sc_time delay_gnt_miss = sc_time(100, SC_NS); + sc_time delay_rvalid_miss = sc_time(100, SC_NS); + + sc_time delay_rvalid_hit = sc_time(20, SC_NS); //as of today, it must be >=20 + + sc_time delay = sc_time(1, SC_NS); + + uint32_t cache_block_size_byte = cache->get_block_size(); + uint32_t cache_block_size_word = cache->get_block_size()/4; + uint8_t* cache_data = new uint8_t[cache_block_size_byte]; + int32_t* main_mem_data = new int32_t[cache_block_size_word]; + uint32_t address_to_replace; + uint32_t cache_flushed; + + while(true) { + + wait(obi_new_req); + + heep_mem_transactions << "X-HEEP tlm_generic_payload REQ: { " << (we_i ? 'W' : 'R') << ", @0x" << hex << addr_i + << " , DATA = 0x" << hex << rwdata_io << " BE = " << hex << be_i <<", at time " << sc_time_stamp() << " }" << std::endl; + + if(be_i!=0xF) { + SC_REPORT_ERROR("OBI External Memory SystemC", "ByteEnable different than 0xF is not supported"); + } + + //if we are writing 1 or 2 to last address, flush cache or bypass + if(we_i && ((addr_i & 0x00007FFF) == 0x7FFC)){ + + if(rwdata_io == 1){ + //FLUSH Cache + heep_mem_transactions << "X-HEEP Flush Cache, at time " << sc_time_stamp() << " }" << std::endl; + uint32_t cache_number_of_blocks = cache->number_of_blocks; + heep_mem_transactions<<"Cache Flushing at time "<is_entry_valid_at_index(i)) { + cache_flushed++; + //if we are going to replace a valid entry + cache->get_data_at_index(i, cache_data); + address_to_replace = cache->get_address_at_index(i); + //write back + memory_copy(address_to_replace, (uint32_t *)cache_data, cache_block_size_word, true, trans, delay); + } + } + heep_mem_transactions<<"Cache Flushed "<< dec << cache_flushed << " entries"<cache_hit(addr_i)){ + + heep_mem_transactions << "Cache HIT on address " << hex << addr_i << " at time " << sc_time_stamp() <get_word(addr_i); + //if Write, writes to cache + if(we_i) + cache->set_word(addr_i, rwdata_io); + else + rwdata_io = main_mem_data[0]; + wait(delay_rvalid_hit); + } + + else { //miss case + + cache_stat.number_of_miss++; + + heep_mem_transactions << "Cache MISS on address " << hex << addr_i << " at time " << sc_time_stamp() <get_base_address(addr_i); + uint32_t addr_offset = cache->get_block_offset(addr_i); + + //first read block_size bytes from memory to place them in cache regardless of the cmd + memory_copy(addr_to_read, main_mem_data, cache_block_size_word, false, trans, delay); + uint32_t index_to_add = cache->get_index(addr_i); + uint32_t tag_to_add = cache->get_tag(addr_i); + + heep_mem_transactions << "Adding to Cache TAG " << hex << tag_to_add << " and index " << hex << index_to_add <is_entry_valid(addr_i)) { + //if we are going to replace a valid entry + cache->get_data(addr_i, cache_data); + address_to_replace = cache->get_address(addr_i); + uint32_t index_to_replace = cache->get_index(addr_i); + uint32_t tag_to_replace = cache->get_tag_from_index(index_to_replace); + + heep_mem_transactions << "Cache Replace address " << hex << addr_i << " with address " << hex << address_to_replace << " due to the MISS at time " << sc_time_stamp() <add_entry(addr_i, (uint8_t*)main_mem_data); + + //if Write, writes to cache + if(we_i) + cache->set_word(addr_i, rwdata_io); + + //now give back the rdata + rwdata_io = main_mem_data[addr_offset>>2]; //>>2 as addr_offset is for byte address, not words + + //wait some time before giving the rvalid + wait(delay_rvalid_miss); + + } + } + } + + heep_mem_transactions << "X-HEEP tlm_generic_payload RESP: { DATA = 0x" << hex << rwdata_io <<", at time " << sc_time_stamp() << " }" << std::endl; + cache->print_cache_status(cache_stat.number_of_transactions++, sc_time_stamp().to_string()); + + obi_new_rvalid.notify(); + + } + } +}; + +#endif diff --git a/tb/tb_sc_top.cpp b/tb/tb_sc_top.cpp new file mode 100644 index 000000000..b619d9500 --- /dev/null +++ b/tb/tb_sc_top.cpp @@ -0,0 +1,342 @@ +// Copyright 2024 EPFL +// Solderpad Hardware License, Version 2.1, see LICENSE.md for details. +// SPDX-License-Identifier: Apache-2.0 WITH SHL-2.1 + +#include "verilated.h" +#include +#include "Vtestharness.h" +#include "Vtestharness__Syms.h" +#include "systemc.h" +#include +#include +#include "XHEEP_CmdLineOptions.hh" + +sc_event reset_done_event; +sc_event obi_new_gnt; +sc_event obi_new_rvalid; +sc_event obi_new_req; + + +#include "systemc_tb/MemoryRequest.h" +#include "systemc_tb/MainMemory.h" + + +#define CLK_PERIOD 10 + +SC_MODULE(external_memory) +{ + MemoryRequest *memory_request; + MainMemory *memory; + + sc_in clk_i; + sc_in ext_systemc_req_req_i; + sc_in ext_systemc_req_we_i; + sc_in ext_systemc_req_be_i; + sc_in ext_systemc_req_addr_i; + sc_in ext_systemc_req_wdata_i; + sc_out ext_systemc_resp_gnt_o; + sc_out ext_systemc_resp_rvalid_o; + sc_out ext_systemc_resp_rdata_o; + + void notify_obi_transaction () { + if(ext_systemc_req_req_i) { + obi_new_req.notify(); + memory_request->we_i = ext_systemc_req_we_i; + memory_request->be_i = ext_systemc_req_be_i; + memory_request->addr_i = ext_systemc_req_addr_i; + memory_request->rwdata_io = ext_systemc_req_wdata_i; + } + } + + void give_gnt_back () { + while (true) { + ext_systemc_resp_gnt_o.write(false); + wait(obi_new_gnt); + ext_systemc_resp_gnt_o.write(true); + wait(); + } + } + + void give_rvalid_rdata_back () { + while (true) { + ext_systemc_resp_rvalid_o.write(false); + wait(obi_new_rvalid); + ext_systemc_resp_rvalid_o.write(true); + ext_systemc_resp_rdata_o.write(memory_request->rwdata_io); + wait(); + } + } + + SC_CTOR(external_memory) + { + // Instantiate components + memory_request = new MemoryRequest("memory_request"); + memory = new MainMemory ("main_memory"); + + SC_METHOD(notify_obi_transaction); + sensitive << ext_systemc_req_req_i; + + SC_CTHREAD(give_gnt_back, clk_i.pos()); + SC_CTHREAD(give_rvalid_rdata_back, clk_i.pos()); + + // Bind memory_request socket to target socket + memory_request->socket.bind( memory->socket ); + } +}; + + +SC_MODULE(testbench) +{ + + sc_in clk_i; + sc_out clk_o; + sc_out rst_no; + sc_out boot_select_o; + sc_out execute_from_flash_o; + sc_out jtag_tck_o; + sc_out jtag_tms_o; + sc_out jtag_trst_n_o; + sc_out jtag_tdi_o; + + Vtestharness* dut; + std::string* firmware; + + bool boot_select_option; + unsigned int reset_cycles = 30; + + void make_clock () { + while(1) { + clk_o.write(false); + wait(); + clk_o.write(true); + wait(); + } + } + + void do_reset_cycle () { + //active low + //----- + rst_no.write(false); + + for(int i=0;itb_loadHEX(firmware->c_str()); + } + + void set_exit_loop () { + wait(); + dut->tb_set_exit_loop(); + } + + void make_stimuli () { + + boot_select_o.write(boot_select_option); + execute_from_flash_o.write(true); + jtag_tck_o.write(false); + jtag_tms_o.write(false); + jtag_trst_n_o.write(false); + jtag_tdi_o.write(false); + + std::cout<<"Start Reset Cycle: "<c_str()<get_use_openocd(); + firmware = cmd_lines_options->get_firmware(); + + if(firmware.empty() && use_openocd==false) { + std::cout<<"You must specify the firmware if you are not using OpenOCD"<get_max_sim_time(run_all); + + boot_sel = cmd_lines_options->get_boot_sel(); + + if(use_openocd) { + std::cout<<"[TESTBENCH]: ERROR: Executing from OpenOCD in SystemC is not supported (yet) in X-HEEP"<>1, SC_NS, 0.5); + + Vtestharness dut("TOP"); + testbench tb("testbench"); + external_memory ext_mem("external_memory"); + + svSetScope(svGetScopeFromName("TOP.testharness")); + svScope scope = svGetScope(); + if (!scope) { + std::cout<<"Warning: svGetScope failed"<< std::endl; + exit(EXIT_FAILURE); + } + + + // static values + tb.boot_select_option = boot_sel == 1; + + + // Vtestharness interface + sc_signal clk; + sc_signal rst_n; + sc_signal boot_select; + sc_signal execute_from_flash; + sc_signal jtag_tck; + sc_signal jtag_tms; + sc_signal jtag_trst_n; + sc_signal jtag_tdi; + sc_signal jtag_tdo; + sc_signal exit_value; + sc_signal exit_valid; + sc_signal ext_systemc_req_req; + sc_signal ext_systemc_req_we; + sc_signal ext_systemc_req_be; + sc_signal ext_systemc_req_addr; + sc_signal ext_systemc_req_wdata; + sc_signal ext_systemc_resp_gnt; + sc_signal ext_systemc_resp_rvalid; + sc_signal ext_systemc_resp_rdata; + + + + tb.clk_i(clock_sig); + tb.clk_o(clk); + tb.rst_no(rst_n); + tb.boot_select_o(boot_select); + tb.execute_from_flash_o(execute_from_flash); + tb.jtag_tck_o(jtag_tck); + tb.jtag_tms_o(jtag_tms); + tb.jtag_trst_n_o(jtag_trst_n); + tb.jtag_tdi_o(jtag_tdi); + + tb.dut = &dut; + tb.firmware = &firmware; + + dut.clk_i(clk); + dut.rst_ni(rst_n); + dut.boot_select_i(boot_select); + dut.execute_from_flash_i(execute_from_flash); + dut.jtag_tck_i(jtag_tck); + dut.jtag_tms_i(jtag_tms); + dut.jtag_trst_ni(jtag_trst_n); + dut.jtag_tdi_i(jtag_tdi); + dut.jtag_tdo_o(jtag_tdo); + dut.exit_value_o(exit_value); + dut.exit_valid_o(exit_valid); + dut.ext_systemc_req_req_o(ext_systemc_req_req); + dut.ext_systemc_req_we_o(ext_systemc_req_we); + dut.ext_systemc_req_be_o(ext_systemc_req_be); + dut.ext_systemc_req_addr_o(ext_systemc_req_addr); + dut.ext_systemc_req_wdata_o(ext_systemc_req_wdata); + dut.ext_systemc_resp_gnt_i(ext_systemc_resp_gnt); + dut.ext_systemc_resp_rvalid_i(ext_systemc_resp_rvalid); + dut.ext_systemc_resp_rdata_i(ext_systemc_resp_rdata); + + ext_mem.clk_i(clk); + ext_mem.ext_systemc_req_req_i(ext_systemc_req_req); + ext_mem.ext_systemc_req_we_i(ext_systemc_req_we); + ext_mem.ext_systemc_req_be_i(ext_systemc_req_be); + ext_mem.ext_systemc_req_addr_i(ext_systemc_req_addr); + ext_mem.ext_systemc_req_wdata_i(ext_systemc_req_wdata); + ext_mem.ext_systemc_resp_gnt_o(ext_systemc_resp_gnt); + ext_mem.ext_systemc_resp_rdata_o(ext_systemc_resp_rdata); + ext_mem.ext_systemc_resp_rvalid_o(ext_systemc_resp_rvalid); + + + + // You must do one evaluation before enabling waves, in order to allow + // SystemC to interconnect everything for testing. + sc_start(1, SC_NS); + + + VerilatedVcdSc* tfp = nullptr; + tfp = new VerilatedVcdSc; + dut.trace(tfp, 99); // Trace 99 levels of hierarchy + tfp->open("waveform.vcd"); + + // Simulate until $finish + while (!Verilated::gotFinish() && exit_valid !=1 ) { + // Flush the wave files each cycle so we can immediately see the output + // Don't do this in "real" programs, do it in an abort() handler instead + if (tfp) tfp->flush(); + // Simulate 1ns + sc_start(1, SC_NS); + } + + if(exit_valid == 1) { + std::cout<<"Program Finished with value "<< exit_value <close(); + tfp = nullptr; + } + + + exit(exit_val); + +} diff --git a/tb/tb_top.cpp b/tb/tb_top.cpp index 956b77d69..7dd4757e6 100644 --- a/tb/tb_top.cpp +++ b/tb/tb_top.cpp @@ -10,26 +10,10 @@ #include #include +#include "XHEEP_CmdLineOptions.hh" vluint64_t sim_time = 0; - -std::string getCmdOption(int argc, char* argv[], const std::string& option) -{ - std::string cmd; - for( int i = 0; i < argc; ++i) - { - std::string arg = argv[i]; - size_t arg_size = arg.length(); - size_t option_size = option.length(); - - if(arg.find(option)==0){ - cmd = arg.substr(option_size,arg_size-option_size); - } - } - return cmd; -} - void runCycles(unsigned int ncycles, Vtestharness *dut, VerilatedFstC *m_trace){ for(unsigned int i = 0; i < ncycles; i++) { dut->clk_i ^= 1; @@ -42,12 +26,11 @@ void runCycles(unsigned int ncycles, Vtestharness *dut, VerilatedFstC *m_trace){ int main (int argc, char * argv[]) { - unsigned int SRAM_SIZE; - std::string firmware, arg_max_sim_time, arg_openocd, arg_boot_sel, arg_execute_from_flash; - unsigned int max_sim_time; + std::string firmware; + unsigned int max_sim_time, boot_sel, exit_val; bool use_openocd; bool run_all = false; - int i,j, exit_val, boot_sel, execute_from_flash; + Verilated::commandArgs(argc, argv); // Instantiate the model @@ -59,59 +42,24 @@ int main (int argc, char * argv[]) dut->trace (m_trace, 99); m_trace->open ("waveform.vcd"); - arg_openocd = getCmdOption(argc, argv, "+openOCD="); - use_openocd = false; - if(arg_openocd.empty()){ - std::cout<<"[TESTBENCH]: No OpenOCD is used"<get_use_openocd(); + firmware = cmd_lines_options->get_firmware(); - arg_max_sim_time = getCmdOption(argc, argv, "+max_sim_time="); - max_sim_time = 0; - if(arg_max_sim_time.empty()){ - std::cout<<"[TESTBENCH]: No Max time specified"<get_max_sim_time(run_all); - arg_boot_sel = getCmdOption(argc, argv, "+execute_from_flash="); - execute_from_flash = 1; + boot_sel = cmd_lines_options->get_boot_sel(); if(boot_sel == 1) { std::cout<<"[TESTBENCH]: ERROR: Executing from SPI is not supported (yet) in Verilator"<jtag_tms_i = 0; dut->jtag_trst_ni = 0; dut->jtag_tdi_i = 0; - dut->execute_from_flash_i = execute_from_flash; + dut->execute_from_flash_i = 1; //this cause boot_sel cannot be 1 anyway dut->boot_select_i = boot_sel; dut->eval(); @@ -172,6 +120,7 @@ int main (int argc, char * argv[]) m_trace->close(); delete dut; + delete cmd_lines_options; exit(exit_val); diff --git a/tb/testharness.sv b/tb/testharness.sv index 6b1b36519..2a4c70403 100644 --- a/tb/testharness.sv +++ b/tb/testharness.sv @@ -21,6 +21,17 @@ module testharness #( inout wire boot_select_i, inout wire execute_from_flash_i, +`ifdef SIM_SYSTEMC + output logic ext_systemc_req_req_o, + output logic ext_systemc_req_we_o, + output logic [ 3:0] ext_systemc_req_be_o, + output logic [31:0] ext_systemc_req_addr_o, + output logic [31:0] ext_systemc_req_wdata_o, + + input logic ext_systemc_resp_gnt_i, + input logic ext_systemc_resp_rvalid_i, + input logic [31:0] ext_systemc_resp_rdata_i, +`endif input wire jtag_tck_i, input wire jtag_tms_i, input wire jtag_trst_ni, @@ -102,10 +113,6 @@ module testharness #( reg_pkg::reg_req_t [testharness_pkg::EXT_NPERIPHERALS-1:0] ext_periph_slv_req; reg_pkg::reg_rsp_t [testharness_pkg::EXT_NPERIPHERALS-1:0] ext_periph_slv_rsp; - // External xbar slave example port - obi_req_t slow_ram_slave_req; - obi_resp_t slow_ram_slave_resp; - // External interrupts logic [NEXT_INT_RND-1:0] intr_vector_ext; logic memcopy_intr; @@ -366,20 +373,45 @@ module testharness #( .exit() ); - assign mux_jtag_tck = JTAG_DPI ? sim_jtag_tck : jtag_tck_i; - assign mux_jtag_tms = JTAG_DPI ? sim_jtag_tms : jtag_tms_i; - assign mux_jtag_tdi = JTAG_DPI ? sim_jtag_tdi : jtag_tdi_i; - assign mux_jtag_trstn = JTAG_DPI ? sim_jtag_trstn : jtag_trst_ni; + assign mux_jtag_tck = JTAG_DPI ? sim_jtag_tck : jtag_tck_i; + assign mux_jtag_tms = JTAG_DPI ? sim_jtag_tms : jtag_tms_i; + assign mux_jtag_tdi = JTAG_DPI ? sim_jtag_tdi : jtag_tdi_i; + assign mux_jtag_trstn = JTAG_DPI ? sim_jtag_trstn : jtag_trst_ni; + + assign sim_jtag_tdo = JTAG_DPI ? mux_jtag_tdo : '0; + assign jtag_tdo_o = !JTAG_DPI ? mux_jtag_tdo : '0; + + // External xbar slave example port + obi_req_t slow_ram_slave_req; + obi_resp_t slow_ram_slave_resp; - assign sim_jtag_tdo = JTAG_DPI ? mux_jtag_tdo : '0; - assign jtag_tdo_o = !JTAG_DPI ? mux_jtag_tdo : '0; +`ifndef SIM_SYSTEMC assign slow_ram_slave_req = ext_slave_req[SLOW_MEMORY_IDX]; assign ext_slave_resp[SLOW_MEMORY_IDX] = slow_ram_slave_resp; +`else + + obi_req_t ext_systemc_req; + obi_resp_t ext_systemc_resp; + + assign ext_systemc_req_req_o = ext_systemc_req.req; + assign ext_systemc_req_we_o = ext_systemc_req.we; + assign ext_systemc_req_be_o = ext_systemc_req.be; + assign ext_systemc_req_addr_o = ext_systemc_req.addr; + assign ext_systemc_req_wdata_o = ext_systemc_req.wdata; + + assign ext_systemc_resp.gnt = ext_systemc_resp_gnt_i; + assign ext_systemc_resp.rvalid = ext_systemc_resp_rvalid_i; + assign ext_systemc_resp.rdata = ext_systemc_resp_rdata_i; + + assign ext_systemc_req = ext_slave_req[SLOW_MEMORY_IDX]; + assign ext_slave_resp[SLOW_MEMORY_IDX] = ext_systemc_resp; +`endif generate if (USE_EXTERNAL_DEVICE_EXAMPLE) begin : gen_USE_EXTERNAL_DEVICE_EXAMPLE +`ifndef SIM_SYSTEMC obi_pkg::obi_req_t slave_fifoout_req; obi_pkg::obi_resp_t slave_fifoout_resp; @@ -395,14 +427,14 @@ module testharness #( // External xbar slave memory example slow_memory #( - .NumWords (128), + .NumWords (8192), .DataWidth(32'd32) ) slow_ram_i ( .clk_i, .rst_ni, .req_i(slave_fifoout_req.req), .we_i(slave_fifoout_req.we), - .addr_i(slave_fifoout_req.addr[8:2]), + .addr_i(slave_fifoout_req.addr[15:2]), .wdata_i(slave_fifoout_req.wdata), .be_i(slave_fifoout_req.be), // output ports @@ -410,6 +442,7 @@ module testharness #( .rdata_o(slave_fifoout_resp.rdata), .rvalid_o(slave_fifoout_resp.rvalid) ); +`endif parameter DMA_TRIGGER_SLOT_NUM = 4; diff --git a/tb/testharness_pkg.sv b/tb/testharness_pkg.sv index d014e0b24..d74011f44 100644 --- a/tb/testharness_pkg.sv +++ b/tb/testharness_pkg.sv @@ -40,19 +40,19 @@ package testharness_pkg; localparam logic [31:0] MEMCOPY_CTRL_IDX = 32'd0; // External AMS Peripheral - localparam logic [31:0] AMS_START_ADDRESS = core_v_mini_mcu_pkg::EXT_PERIPHERAL_START_ADDRESS + 32'h001000; + localparam logic [31:0] AMS_START_ADDRESS = core_v_mini_mcu_pkg::EXT_PERIPHERAL_START_ADDRESS + 32'h01000; localparam logic [31:0] AMS_SIZE = 32'h100; localparam logic [31:0] AMS_END_ADDRESS = AMS_START_ADDRESS + AMS_SIZE; localparam logic [31:0] AMS_IDX = 32'd1; // External InterFaced FIFO (IFFIFO) Peripheral - localparam logic [31:0] IFFIFO_START_ADDRESS = core_v_mini_mcu_pkg::EXT_PERIPHERAL_START_ADDRESS + 32'h002000; + localparam logic [31:0] IFFIFO_START_ADDRESS = core_v_mini_mcu_pkg::EXT_PERIPHERAL_START_ADDRESS + 32'h02000; localparam logic [31:0] IFFIFO_SIZE = 32'h100; localparam logic [31:0] IFFIFO_END_ADDRESS = IFFIFO_START_ADDRESS + IFFIFO_SIZE; localparam logic [31:0] IFFIFO_IDX = 32'd2; // External Simple Accelerator Peripheral - localparam logic [31:0] SIMPLE_ACC_START_ADDRESS = core_v_mini_mcu_pkg::EXT_PERIPHERAL_START_ADDRESS + 32'h003000; + localparam logic [31:0] SIMPLE_ACC_START_ADDRESS = core_v_mini_mcu_pkg::EXT_PERIPHERAL_START_ADDRESS + 32'h03000; localparam logic [31:0] SIMPLE_ACC_SIZE = 32'h100; localparam logic [31:0] SIMPLE_ACC_END_ADDRESS = SIMPLE_ACC_START_ADDRESS + SIMPLE_ACC_SIZE; localparam logic [31:0] SIMPLE_ACC_IDX = 32'd3; diff --git a/x-heep-tb-utils.core b/x-heep-tb-utils.core index 82055fb6e..98252573e 100644 --- a/x-heep-tb-utils.core +++ b/x-heep-tb-utils.core @@ -71,6 +71,11 @@ filesets: - tb/tb_top.cpp file_type: cppSource + tb-sc-verilator: + files: + - tb/tb_sc_top.cpp + file_type: cppSource + targets: default: &default_target filesets: