diff --git a/Makefile b/Makefile index d044f0ccd1..823b43ebb4 100644 --- a/Makefile +++ b/Makefile @@ -99,10 +99,10 @@ ifneq ($(spike-tandem),) endif # target takes one of the following cva6 hardware configuration: -# cv64a6_imafdc_sv39, cv32a6_imac_sv0, cv32a6_imac_sv32, cv32a6_imafc_sv32, cv32a6_ima_sv32_fpga +# cv64a6_imafdc_sv39, cv32a6_imac_sv0, cv32a6_imac_sv32, cv32a6_imafc_sv32, cv32a6_ima_sv32_fpga, cv64a6_imafdch_sv39 # Changing the default target to cv32a60x for Step1 verification target ?= cv64a6_imafdc_sv39 -ifeq ($(target), cv64a6_imafdc_sv39) +ifeq ($(target), $(filter $(target),cv64a6_imafdc_sv39 cv64a6_imafdch_sv39)) XLEN ?= 64 else XLEN ?= 32 @@ -295,12 +295,12 @@ altera_filter := corev_apu/tb/ariane_testharness.sv \ corev_apu/riscv-dbg/src/dmi_jtag_tap.sv \ corev_apu/riscv-dbg/src/dmi_jtag.sv \ corev_apu/fpga/src/apb_uart/src/reg_uart_wrap.sv - + altera_filter := $(addprefix $(root-dir), $(altera_filter)) xil_debug_filter = $(addprefix $(root-dir), corev_apu/riscv-dbg/src/dm_obi_top.sv) xil_debug_filter += $(addprefix $(root-dir), corev_apu/riscv-dbg/src/dm_pkg.sv) xil_debug_filter += $(addprefix $(root-dir), corev_apu/riscv-dbg/src/dmi_vjtag_tap.sv) -xil_debug_filter += $(addprefix $(root-dir), corev_apu/riscv-dbg/src/dmi_vjtag.sv) +xil_debug_filter += $(addprefix $(root-dir), corev_apu/riscv-dbg/src/dmi_vjtag.sv) src := $(filter-out $(xil_debug_filter), $(src)) fpga_src += corev_apu/fpga/src/bootrom/bootrom_$(XLEN).sv @@ -350,6 +350,7 @@ endif uvm-flags += +UVM_NO_RELNOTES +UVM_VERBOSITY=UVM_LOW questa-flags += -t 1ns -64 $(gui-sim) $(QUESTASIM_FLAGS) \ +tohost_addr=$(shell ${RISCV}/bin/${CV_SW_PREFIX}nm -B $(elf) | grep -w tohost | cut -d' ' -f1) \ + +fromhost_addr=$(shell ${RISCV}/bin/${CV_SW_PREFIX}nm -B $(elf) | grep -w fromhost | cut -d' ' -f1) \ +core_name=$(target) +define+QUESTA -suppress 3356 -suppress 3579 +report_file=$(report_file) \ $(spike-yaml-plusarg) compile_flag_vhd += -64 -nologo -quiet -2008 @@ -591,6 +592,7 @@ xrun_sim: xrun_comp +UVM_TESTNAME=$(test_case) \ +time_out=200000000000 \ +tohost_addr=$(shell ${RISCV}/bin/${CV_SW_PREFIX}nm -B $(elf) | grep -w tohost | cut -d' ' -f1) \ + +fromhost_addr=$(shell ${RISCV}/bin/${CV_SW_PREFIX}nm -B $(elf) | grep -w fromhost | cut -d' ' -f1) \ -log $(XRUN_RUN_LOG) \ +gui \ +permissive-off \ @@ -700,7 +702,8 @@ verilate_command := $(verilator) --no-timing verilator_config.vlt --threads-dpi none \ --Mdir $(ver-library) -O3 \ --exe corev_apu/tb/ariane_tb.cpp corev_apu/tb/dpi/SimDTM.cc corev_apu/tb/dpi/SimJTAG.cc \ - corev_apu/tb/dpi/remote_bitbang.cc corev_apu/tb/dpi/msim_helper.cc + corev_apu/tb/dpi/remote_bitbang.cc corev_apu/tb/dpi/msim_helper.cc \ + corev_apu/tb/dpi/syscalls.cc # User Verilator, at some point in the future this will be auto-generated verilate: diff --git a/core/cva6_mmu/cva6_mmu.sv b/core/cva6_mmu/cva6_mmu.sv index b96928b878..35d12e39f9 100644 --- a/core/cva6_mmu/cva6_mmu.sv +++ b/core/cva6_mmu/cva6_mmu.sv @@ -365,7 +365,7 @@ module cva6_mmu always_comb begin : instr_interface // MMU disabled: just pass through icache_areq_o.fetch_valid = icache_areq_i.fetch_req; - icache_areq_o.fetch_paddr = CVA6Cfg.PLEN'(icache_areq_i.fetch_vaddr[((CVA6Cfg.PLEN > CVA6Cfg.VLEN) ? CVA6Cfg.VLEN -1: CVA6Cfg.PLEN -1 ):0]); + icache_areq_o.fetch_paddr = CVA6Cfg.PLEN'(icache_areq_i.fetch_vaddr[((CVA6Cfg.PLEN > CVA6Cfg.VLEN) ? CVA6Cfg.VLEN -1: CVA6Cfg.PLEN -1 ):0]); // two potential exception sources: // 1. HPTW threw an exception -> signal with a page fault exception // 2. We got an access error because of insufficient permissions -> throw an access exception @@ -382,9 +382,12 @@ module cva6_mmu // AXI decode error), or when PTW performs walk due to ITLB miss and raises // an error. if ((enable_translation_i || enable_g_translation_i)) begin - // we work with SV39 or SV32, so if VM is enabled, check that all bits [CVA6Cfg.VLEN-1:CVA6Cfg.SV-1] are equal - if (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[CVA6Cfg.VLEN-1:CVA6Cfg.SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[CVA6Cfg.VLEN-1:CVA6Cfg.SV-1]) == 1'b0)) begin - + // If second-level address translation is enabled: + // - in VS stage, check that all bits [CVA6Cfg.VLEN-1:CVA6Cfg.SV-1] are equal + // - in pure G stage (SV39x4 mode), [CVA6Cfg.VLEN-1:CVA6Cfg.GPLEN] must be zero. + // - in pure G stage (SV32x4 mode), no check is needed. + if ((enable_translation_i && (icache_areq_i.fetch_req && !((&icache_areq_i.fetch_vaddr[CVA6Cfg.VLEN-1:CVA6Cfg.SV-1]) == 1'b1 || (|icache_areq_i.fetch_vaddr[CVA6Cfg.VLEN-1:CVA6Cfg.SV-1]) == 1'b0))) + ||((enable_g_translation_i && !enable_translation_i) && !(|icache_areq_i.fetch_vaddr[((CVA6Cfg.XLEN == 32)?(CVA6Cfg.GPLEN):(CVA6Cfg.VLEN-1)):CVA6Cfg.GPLEN]) == 1'b0)) begin icache_areq_o.fetch_exception.cause = riscv::INSTR_PAGE_FAULT; icache_areq_o.fetch_exception.valid = 1'b1; if (CVA6Cfg.TvalEn) @@ -473,7 +476,7 @@ module cva6_mmu end else begin icache_areq_o.fetch_exception.cause = riscv::INSTR_ACCESS_FAULT; icache_areq_o.fetch_exception.valid = 1'b1; - if (CVA6Cfg.TvalEn) //To confirm this is the right TVAL + if (CVA6Cfg.TvalEn) // To confirm this is the right TVAL icache_areq_o.fetch_exception.tval = CVA6Cfg.XLEN'(update_vaddr); if (CVA6Cfg.RVH) begin icache_areq_o.fetch_exception.tval2 = '0; diff --git a/core/cva6_mmu/cva6_tlb.sv b/core/cva6_mmu/cva6_tlb.sv index a43596f322..7fcc348834 100644 --- a/core/cva6_mmu/cva6_tlb.sv +++ b/core/cva6_mmu/cva6_tlb.sv @@ -55,7 +55,6 @@ module cva6_tlb output logic [CVA6Cfg.PtLevels-2:0] lu_is_page_o, output logic lu_hit_o ); - localparam GPPN2 = (CVA6Cfg.XLEN == 32) ? CVA6Cfg.VLEN - 33 : 10; // SV39 defines three levels of page tables struct packed { logic [CVA6Cfg.ASID_WIDTH-1:0] asid; @@ -229,10 +228,10 @@ module cva6_tlb lu_gpaddr_o = {content_q[i].pte.ppn[(CVA6Cfg.GPPNW-1):0], lu_vaddr_i[11:0]}; // S-stage Mega page if (tags_q[i].is_page[1][0]) - lu_gpaddr_o[12+CVA6Cfg.VpnLen/CVA6Cfg.PtLevels-1:12] = lu_vaddr_i[12+CVA6Cfg.VpnLen/CVA6Cfg.PtLevels-1:12]; + lu_gpaddr_o[12+(CVA6Cfg.VpnLen/CVA6Cfg.PtLevels)-1:12] = lu_vaddr_i[12+(CVA6Cfg.VpnLen/CVA6Cfg.PtLevels)-1:12]; // S-stage Giga page if (tags_q[i].is_page[0][0]) - lu_gpaddr_o[12+2*CVA6Cfg.VpnLen/CVA6Cfg.PtLevels-1:12] = lu_vaddr_i[12+2*(CVA6Cfg.VpnLen/CVA6Cfg.PtLevels)-1:12]; + lu_gpaddr_o[12+2*(CVA6Cfg.VpnLen/CVA6Cfg.PtLevels)-1:12] = lu_vaddr_i[12+2*(CVA6Cfg.VpnLen/CVA6Cfg.PtLevels)-1:12]; end else begin lu_gpaddr_o = CVA6Cfg.GPLEN'(lu_vaddr_i[(CVA6Cfg.XLEN == 32?CVA6Cfg.VLEN:CVA6Cfg.GPLEN)-1:0]); end diff --git a/core/include/cv64a6_imafdch_sv39_config_pkg.sv b/core/include/cv64a6_imafdch_sv39_config_pkg.sv index 9442b1133c..ca25b339c2 100644 --- a/core/include/cv64a6_imafdch_sv39_config_pkg.sv +++ b/core/include/cv64a6_imafdch_sv39_config_pkg.sv @@ -49,7 +49,7 @@ package cva6_config_pkg; localparam CVA6ConfigDcacheInvalidateOnFlush = 1'b0; localparam CVA6ConfigDcacheIdWidth = 1; - localparam CVA6ConfigMemTidWidth = 2; + localparam CVA6ConfigMemTidWidth = CVA6ConfigAxiIdWidth; localparam CVA6ConfigWtDcacheWbufDepth = 8; @@ -69,7 +69,7 @@ package cva6_config_pkg; localparam CVA6ConfigPerfCounterEn = 1; - localparam config_pkg::cache_type_t CVA6ConfigDcacheType = config_pkg::WT; + localparam config_pkg::cache_type_t CVA6ConfigDcacheType = config_pkg::HPDCACHE_WT; localparam CVA6ConfigMmuPresent = 1; @@ -108,7 +108,7 @@ package cva6_config_pkg; CvxifEn: bit'(CVA6ConfigCvxifEn), CoproType: config_pkg::COPRO_NONE, RVZiCond: bit'(CVA6ConfigRVZiCond), - RVZiCbom: bit'(0), + RVZiCbom: bit'(1), RVZicntr: bit'(1), RVZihpm: bit'(1), NrScoreboardEntries: unsigned'(CVA6ConfigNrScoreboardEntries), diff --git a/core/include/cv64a6_imafdch_sv39_wb_config_pkg.sv b/core/include/cv64a6_imafdch_sv39_wb_config_pkg.sv index 485c787b1c..dea846594d 100644 --- a/core/include/cv64a6_imafdch_sv39_wb_config_pkg.sv +++ b/core/include/cv64a6_imafdch_sv39_wb_config_pkg.sv @@ -69,7 +69,7 @@ package cva6_config_pkg; localparam CVA6ConfigPerfCounterEn = 1; - localparam config_pkg::cache_type_t CVA6ConfigDcacheType = config_pkg::WB; + localparam config_pkg::cache_type_t CVA6ConfigDcacheType = config_pkg::HPDCACHE_WB; localparam CVA6ConfigMmuPresent = 1; @@ -108,7 +108,7 @@ package cva6_config_pkg; CvxifEn: bit'(CVA6ConfigCvxifEn), CoproType: config_pkg::COPRO_NONE, RVZiCond: bit'(CVA6ConfigRVZiCond), - RVZiCbom: bit'(0), + RVZiCbom: bit'(1), RVZicntr: bit'(1), RVZihpm: bit'(1), NrScoreboardEntries: unsigned'(CVA6ConfigNrScoreboardEntries), diff --git a/corev_apu/tb/ariane_tb.cpp b/corev_apu/tb/ariane_tb.cpp index 3653eaa516..32676c4876 100644 --- a/corev_apu/tb/ariane_tb.cpp +++ b/corev_apu/tb/ariane_tb.cpp @@ -43,6 +43,7 @@ #include #include #include "remote_bitbang.h" +#include "dpi/syscalls.h" // This software is heavily based on Rocket Chip // Checkout this awesome project: @@ -355,6 +356,8 @@ int main(int argc, char **argv) { } } + SyscallHandler::init(MEM); + while (!dtm->done() && !jtag->done() && !(top->exit_o & 0x1)) { top->clk_i = 0; top->eval(); diff --git a/corev_apu/tb/ariane_testharness.sv b/corev_apu/tb/ariane_testharness.sv index e4e3236392..b1eb60e0e2 100644 --- a/corev_apu/tb/ariane_testharness.sv +++ b/corev_apu/tb/ariane_testharness.sv @@ -24,25 +24,26 @@ `endif module ariane_testharness #( - parameter config_pkg::cva6_cfg_t CVA6Cfg = build_config_pkg::build_config(cva6_config_pkg::cva6_cfg), - // - parameter int unsigned AXI_USER_WIDTH = CVA6Cfg.AxiUserWidth, - parameter int unsigned AXI_USER_EN = CVA6Cfg.AXI_USER_EN, - parameter int unsigned AXI_ADDRESS_WIDTH = 64, - parameter int unsigned AXI_DATA_WIDTH = 64, - parameter bit InclSimDTM = 1'b1, - parameter int unsigned NUM_WORDS = 2**25, // memory size - parameter bit StallRandomOutput = 1'b0, - parameter bit StallRandomInput = 1'b0 + parameter config_pkg::cva6_cfg_t CVA6Cfg = build_config_pkg::build_config( + cva6_config_pkg::cva6_cfg + ), + // + parameter int unsigned AXI_USER_WIDTH = CVA6Cfg.AxiUserWidth, + parameter int unsigned AXI_USER_EN = CVA6Cfg.AXI_USER_EN, + parameter int unsigned AXI_ADDRESS_WIDTH = 64, + parameter int unsigned AXI_DATA_WIDTH = 64, + parameter bit InclSimDTM = 1'b1, + parameter int unsigned NUM_WORDS = 2 ** 25, // memory size + parameter bit StallRandomOutput = 1'b0, + parameter bit StallRandomInput = 1'b0, + parameter int NR_CORES = 1 ) ( - input logic clk_i, - input logic rtc_i, - input logic rst_ni, - output logic [31:0] exit_o + input logic clk_i, + input logic rtc_i, + input logic rst_ni, + output logic [31:0] exit_o ); - localparam [7:0] hart_id = '0; - // RVFI localparam type rvfi_instr_t = `RVFI_INSTR_T(CVA6Cfg); localparam type rvfi_csr_elmt_t = `RVFI_CSR_ELMT_T(CVA6Cfg); @@ -54,74 +55,74 @@ module ariane_testharness #( localparam type rvfi_probes_instr_t = `RVFI_PROBES_INSTR_T(CVA6Cfg); localparam type rvfi_probes_csr_t = `RVFI_PROBES_CSR_T(CVA6Cfg); localparam type rvfi_probes_t = struct packed { - rvfi_probes_csr_t csr; + rvfi_probes_csr_t csr; rvfi_probes_instr_t instr; }; // disable test-enable - logic test_en; - logic ndmreset; - logic ndmreset_n; - logic debug_req_core; + logic test_en; + logic ndmreset; + logic ndmreset_n; + logic debug_req_core; - int jtag_enable; - logic init_done; + int jtag_enable; + logic init_done; logic [31:0] jtag_exit, dmi_exit; - logic [31:0] rvfi_exit; - logic [31:0] tracer_exit; - logic [31:0] tandem_exit; - - logic jtag_TCK; - logic jtag_TMS; - logic jtag_TDI; - logic jtag_TRSTn; - logic jtag_TDO_data; - logic jtag_TDO_driven; - - logic debug_req_valid; - logic debug_req_ready; - logic debug_resp_valid; - logic debug_resp_ready; - - logic jtag_req_valid; - logic [6:0] jtag_req_bits_addr; - logic [1:0] jtag_req_bits_op; - logic [31:0] jtag_req_bits_data; - logic jtag_resp_ready; - logic jtag_resp_valid; - - logic dmi_req_valid; - logic dmi_resp_ready; - logic dmi_resp_valid; - - dm::dmi_req_t jtag_dmi_req; - dm::dmi_req_t dmi_req; - - dm::dmi_req_t debug_req; - dm::dmi_resp_t debug_resp; + logic [31:0] rvfi_exit; + logic [31:0] tracer_exit; + logic [31:0] tandem_exit; + + logic jtag_TCK; + logic jtag_TMS; + logic jtag_TDI; + logic jtag_TRSTn; + logic jtag_TDO_data; + logic jtag_TDO_driven; + + logic debug_req_valid; + logic debug_req_ready; + logic debug_resp_valid; + logic debug_resp_ready; + + logic jtag_req_valid; + logic [ 6:0] jtag_req_bits_addr; + logic [ 1:0] jtag_req_bits_op; + logic [31:0] jtag_req_bits_data; + logic jtag_resp_ready; + logic jtag_resp_valid; + + logic dmi_req_valid; + logic dmi_resp_ready; + logic dmi_resp_valid; + + dm::dmi_req_t jtag_dmi_req; + dm::dmi_req_t dmi_req; + + dm::dmi_req_t debug_req; + dm::dmi_resp_t debug_resp; assign test_en = 1'b0; AXI_BUS #( - .AXI_ADDR_WIDTH ( AXI_ADDRESS_WIDTH ), - .AXI_DATA_WIDTH ( AXI_DATA_WIDTH ), - .AXI_ID_WIDTH ( ariane_axi_soc::IdWidth ), - .AXI_USER_WIDTH ( AXI_USER_WIDTH ) - ) slave[ariane_soc::NrSlaves-1:0](); + .AXI_ADDR_WIDTH(AXI_ADDRESS_WIDTH), + .AXI_DATA_WIDTH(AXI_DATA_WIDTH), + .AXI_ID_WIDTH (ariane_axi_soc::IdWidth), + .AXI_USER_WIDTH(AXI_USER_WIDTH) + ) slave[ariane_soc::NrSlaves-1:0] (); AXI_BUS #( - .AXI_ADDR_WIDTH ( AXI_ADDRESS_WIDTH ), - .AXI_DATA_WIDTH ( AXI_DATA_WIDTH ), - .AXI_ID_WIDTH ( ariane_axi_soc::IdWidthSlave ), - .AXI_USER_WIDTH ( AXI_USER_WIDTH ) - ) master[ariane_soc::NB_PERIPHERALS-1:0](); + .AXI_ADDR_WIDTH(AXI_ADDRESS_WIDTH), + .AXI_DATA_WIDTH(AXI_DATA_WIDTH), + .AXI_ID_WIDTH (ariane_axi_soc::IdWidthSlave), + .AXI_USER_WIDTH(AXI_USER_WIDTH) + ) master[ariane_soc::NB_PERIPHERALS-1:0] (); rstgen i_rstgen_main ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni & (~ndmreset) ), - .test_mode_i ( test_en ), - .rst_no ( ndmreset_n ), - .init_no ( ) // keep open + .clk_i (clk_i), + .rst_ni (rst_ni & (~ndmreset)), + .test_mode_i(test_en), + .rst_no (ndmreset_n), + .init_no () // keep open ); // --------------- @@ -132,55 +133,58 @@ module ariane_testharness #( logic debug_enable; initial begin if (!$value$plusargs("jtag_rbb_enable=%b", jtag_enable)) jtag_enable = 'h0; - if ($test$plusargs("debug_disable")) debug_enable = 'h0; else debug_enable = 'h1; + if ($test$plusargs("debug_disable")) debug_enable = 'h0; + else debug_enable = 'h1; if (CVA6Cfg.XLEN != 32 & CVA6Cfg.XLEN != 64) $error("CVA6Cfg.XLEN different from 32 and 64"); end // debug if MUX - assign debug_req_valid = (jtag_enable[0]) ? jtag_req_valid : dmi_req_valid; - assign debug_resp_ready = (jtag_enable[0]) ? jtag_resp_ready : dmi_resp_ready; - assign debug_req = (jtag_enable[0]) ? jtag_dmi_req : dmi_req; + assign debug_req_valid = (jtag_enable[0]) ? jtag_req_valid : dmi_req_valid; + assign debug_resp_ready = (jtag_enable[0]) ? jtag_resp_ready : dmi_resp_ready; + assign debug_req = (jtag_enable[0]) ? jtag_dmi_req : dmi_req; if (ariane_pkg::RVFI) begin - assign exit_o = (jtag_enable[0]) ? jtag_exit : rvfi_exit; + for (genvar i = 0; i < 32; i++) begin + assign exit_o[i] = (jtag_enable[0]) ? jtag_exit[i] : rvfi_exit[i]; + end end else begin - assign exit_o = (jtag_enable[0]) ? jtag_exit : dmi_exit; + assign exit_o = (jtag_enable[0]) ? jtag_exit : dmi_exit; end - assign jtag_resp_valid = (jtag_enable[0]) ? debug_resp_valid : 1'b0; - assign dmi_resp_valid = (jtag_enable[0]) ? 1'b0 : debug_resp_valid; + assign jtag_resp_valid = (jtag_enable[0]) ? debug_resp_valid : 1'b0; + assign dmi_resp_valid = (jtag_enable[0]) ? 1'b0 : debug_resp_valid; // SiFive's SimJTAG Module // Converts to DPI calls SimJTAG i_SimJTAG ( - .clock ( clk_i ), - .reset ( ~rst_ni ), - .enable ( jtag_enable[0] ), - .init_done ( init_done ), - .jtag_TCK ( jtag_TCK ), - .jtag_TMS ( jtag_TMS ), - .jtag_TDI ( jtag_TDI ), - .jtag_TRSTn ( jtag_TRSTn ), - .jtag_TDO_data ( jtag_TDO_data ), - .jtag_TDO_driven ( jtag_TDO_driven ), - .exit ( jtag_exit ) + .clock (clk_i), + .reset (~rst_ni), + .enable (jtag_enable[0]), + .init_done (init_done), + .jtag_TCK (jtag_TCK), + .jtag_TMS (jtag_TMS), + .jtag_TDI (jtag_TDI), + .jtag_TRSTn (jtag_TRSTn), + .jtag_TDO_data (jtag_TDO_data), + .jtag_TDO_driven(jtag_TDO_driven), + .exit (jtag_exit) ); dmi_jtag i_dmi_jtag ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .testmode_i ( test_en ), - .dmi_req_o ( jtag_dmi_req ), - .dmi_req_valid_o ( jtag_req_valid ), - .dmi_req_ready_i ( debug_req_ready ), - .dmi_resp_i ( debug_resp ), - .dmi_resp_ready_o ( jtag_resp_ready ), - .dmi_resp_valid_i ( jtag_resp_valid ), - .dmi_rst_no ( ), // not connected - .tck_i ( jtag_TCK ), - .tms_i ( jtag_TMS ), - .trst_ni ( jtag_TRSTn ), - .td_i ( jtag_TDI ), - .td_o ( jtag_TDO_data ), - .tdo_oe_o ( jtag_TDO_driven ) + .clk_i (clk_i), + .rst_ni (rst_ni), + .testmode_i (test_en), + .dmi_req_o (jtag_dmi_req), + .dmi_req_valid_o (jtag_req_valid), + .dmi_req_ready_i (debug_req_ready), + .dmi_resp_i (debug_resp), + .dmi_resp_ready_o(jtag_resp_ready), + .dmi_resp_valid_i(jtag_resp_valid), + .dmi_rst_no (), // not connected + .tck_i (jtag_TCK), + .tms_i (jtag_TMS), + .trst_ni (jtag_TRSTn), + .td_i (jtag_TDI), + .td_o (jtag_TDO_data), + .tdo_oe_o (jtag_TDO_driven) ); // SiFive's SimDTM Module @@ -190,18 +194,18 @@ module ariane_testharness #( if (InclSimDTM) begin SimDTM i_SimDTM ( - .clk ( clk_i ), - .reset ( ~rst_ni ), - .debug_req_valid ( dmi_req_valid ), - .debug_req_ready ( debug_req_ready ), - .debug_req_bits_addr ( dmi_req.addr ), - .debug_req_bits_op ( debug_req_bits_op ), - .debug_req_bits_data ( dmi_req.data ), - .debug_resp_valid ( dmi_resp_valid ), - .debug_resp_ready ( dmi_resp_ready ), - .debug_resp_bits_resp ( debug_resp.resp ), - .debug_resp_bits_data ( debug_resp.data ), - .exit ( dmi_exit ) + .clk (clk_i), + .reset (~rst_ni), + .debug_req_valid (dmi_req_valid), + .debug_req_ready (debug_req_ready), + .debug_req_bits_addr (dmi_req.addr), + .debug_req_bits_op (debug_req_bits_op), + .debug_req_bits_data (dmi_req.data), + .debug_resp_valid (dmi_resp_valid), + .debug_resp_ready (dmi_resp_ready), + .debug_resp_bits_resp(debug_resp.resp), + .debug_resp_bits_data(debug_resp.data), + .exit (dmi_exit) ); end else begin assign dmi_req_valid = '0; @@ -220,121 +224,120 @@ module ariane_testharness #( int dmi_del_cnt_d, dmi_del_cnt_q; assign dmi_del_cnt_d = (dmi_del_cnt_q) ? dmi_del_cnt_q - 1 : 0; - assign debug_req_core = (dmi_del_cnt_q) ? 1'b0 : - (!debug_enable) ? 1'b0 : debug_req_core_ungtd; + assign debug_req_core = (dmi_del_cnt_q) ? 1'b0 : (!debug_enable) ? 1'b0 : debug_req_core_ungtd; always_ff @(posedge clk_i or negedge rst_ni) begin : p_dmi_del_cnt - if(!rst_ni) begin + if (!rst_ni) begin dmi_del_cnt_q <= DmiDelCycles; end else begin dmi_del_cnt_q <= dmi_del_cnt_d; end end - ariane_axi::req_t dm_axi_m_req; - ariane_axi::resp_t dm_axi_m_resp; - - logic dm_slave_req; - logic dm_slave_we; - logic [64-1:0] dm_slave_addr; - logic [64/8-1:0] dm_slave_be; - logic [64-1:0] dm_slave_wdata; - logic [64-1:0] dm_slave_rdata; - - logic dm_master_req; - logic [64-1:0] dm_master_add; - logic dm_master_we; - logic [64-1:0] dm_master_wdata; - logic [64/8-1:0] dm_master_be; - logic dm_master_gnt; - logic dm_master_r_valid; - logic [64-1:0] dm_master_r_rdata; + ariane_axi::req_t dm_axi_m_req; + ariane_axi::resp_t dm_axi_m_resp; + + logic dm_slave_req; + logic dm_slave_we; + logic [ 64-1:0] dm_slave_addr; + logic [64/8-1:0] dm_slave_be; + logic [ 64-1:0] dm_slave_wdata; + logic [ 64-1:0] dm_slave_rdata; + + logic dm_master_req; + logic [ 64-1:0] dm_master_add; + logic dm_master_we; + logic [ 64-1:0] dm_master_wdata; + logic [64/8-1:0] dm_master_be; + logic dm_master_gnt; + logic dm_master_r_valid; + logic [ 64-1:0] dm_master_r_rdata; // debug module dm_top #( - .NrHarts ( 1 ), - .BusWidth ( AXI_DATA_WIDTH ), - .SelectableHarts ( 1'b1 ) + .NrHarts (NR_CORES), + .BusWidth (AXI_DATA_WIDTH), + .SelectableHarts(1'b1) ) i_dm_top ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), // PoR - .testmode_i ( test_en ), - .ndmreset_o ( ndmreset ), - .dmactive_o ( ), // active debug session - .debug_req_o ( debug_req_core_ungtd ), - .unavailable_i ( '0 ), - .hartinfo_i ( {ariane_pkg::DebugHartInfo} ), - .slave_req_i ( dm_slave_req ), - .slave_we_i ( dm_slave_we ), - .slave_addr_i ( dm_slave_addr ), - .slave_be_i ( dm_slave_be ), - .slave_wdata_i ( dm_slave_wdata ), - .slave_rdata_o ( dm_slave_rdata ), - .master_req_o ( dm_master_req ), - .master_add_o ( dm_master_add ), - .master_we_o ( dm_master_we ), - .master_wdata_o ( dm_master_wdata ), - .master_be_o ( dm_master_be ), - .master_gnt_i ( dm_master_gnt ), - .master_r_valid_i ( dm_master_r_valid ), - .master_r_rdata_i ( dm_master_r_rdata ), - .dmi_rst_ni ( rst_ni ), - .dmi_req_valid_i ( debug_req_valid ), - .dmi_req_ready_o ( debug_req_ready ), - .dmi_req_i ( debug_req ), - .dmi_resp_valid_o ( debug_resp_valid ), - .dmi_resp_ready_i ( debug_resp_ready ), - .dmi_resp_o ( debug_resp ) + .clk_i (clk_i), + .rst_ni (rst_ni), // PoR + .testmode_i (test_en), + .ndmreset_o (ndmreset), + .dmactive_o (), // active debug session + .debug_req_o (debug_req_core_ungtd), + .unavailable_i ('0), + .hartinfo_i ({ariane_pkg::DebugHartInfo}), + .slave_req_i (dm_slave_req), + .slave_we_i (dm_slave_we), + .slave_addr_i (dm_slave_addr), + .slave_be_i (dm_slave_be), + .slave_wdata_i (dm_slave_wdata), + .slave_rdata_o (dm_slave_rdata), + .master_req_o (dm_master_req), + .master_add_o (dm_master_add), + .master_we_o (dm_master_we), + .master_wdata_o (dm_master_wdata), + .master_be_o (dm_master_be), + .master_gnt_i (dm_master_gnt), + .master_r_valid_i(dm_master_r_valid), + .master_r_rdata_i(dm_master_r_rdata), + .dmi_rst_ni (rst_ni), + .dmi_req_valid_i (debug_req_valid), + .dmi_req_ready_o (debug_req_ready), + .dmi_req_i (debug_req), + .dmi_resp_valid_o(debug_resp_valid), + .dmi_resp_ready_i(debug_resp_ready), + .dmi_resp_o (debug_resp) ); axi2mem #( - .AXI_ID_WIDTH ( ariane_axi_soc::IdWidthSlave ), - .AXI_ADDR_WIDTH ( AXI_ADDRESS_WIDTH ), - .AXI_DATA_WIDTH ( AXI_DATA_WIDTH ), - .AXI_USER_WIDTH ( AXI_USER_WIDTH ) + .AXI_ID_WIDTH (ariane_axi_soc::IdWidthSlave), + .AXI_ADDR_WIDTH(AXI_ADDRESS_WIDTH), + .AXI_DATA_WIDTH(AXI_DATA_WIDTH), + .AXI_USER_WIDTH(AXI_USER_WIDTH) ) i_dm_axi2mem ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .slave ( master[ariane_soc::Debug] ), - .req_o ( dm_slave_req ), - .we_o ( dm_slave_we ), - .addr_o ( dm_slave_addr ), - .be_o ( dm_slave_be ), - .user_o ( ), - .data_o ( dm_slave_wdata ), - .user_i ( '0 ), - .data_i ( dm_slave_rdata ) + .clk_i (clk_i), + .rst_ni(rst_ni), + .slave (master[ariane_soc::Debug]), + .req_o (dm_slave_req), + .we_o (dm_slave_we), + .addr_o(dm_slave_addr), + .be_o (dm_slave_be), + .user_o(), + .data_o(dm_slave_wdata), + .user_i('0), + .data_i(dm_slave_rdata) ); `AXI_ASSIGN_FROM_REQ(slave[1], dm_axi_m_req) `AXI_ASSIGN_TO_RESP(dm_axi_m_resp, slave[1]) axi_adapter #( - .CVA6Cfg ( CVA6Cfg ), - .DATA_WIDTH ( AXI_DATA_WIDTH ), - .axi_req_t ( ariane_axi::req_t ), - .axi_rsp_t ( ariane_axi::resp_t ) + .CVA6Cfg (CVA6Cfg), + .DATA_WIDTH(AXI_DATA_WIDTH), + .axi_req_t (ariane_axi::req_t), + .axi_rsp_t (ariane_axi::resp_t) ) i_dm_axi_master ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .req_i ( dm_master_req ), - .type_i ( ariane_pkg::SINGLE_REQ ), - .amo_i ( ariane_pkg::AMO_NONE ), - .gnt_o ( dm_master_gnt ), - .addr_i ( dm_master_add ), - .we_i ( dm_master_we ), - .wdata_i ( dm_master_wdata ), - .be_i ( dm_master_be ), - .size_i ( 2'b11 ), // always do 64bit here and use byte enables to gate - .id_i ( '0 ), - .valid_o ( dm_master_r_valid ), - .rdata_o ( dm_master_r_rdata ), - .id_o ( ), - .critical_word_o ( ), - .critical_word_valid_o ( ), - .axi_req_o ( dm_axi_m_req ), - .axi_resp_i ( dm_axi_m_resp ) + .clk_i(clk_i), + .rst_ni(rst_ni), + .req_i(dm_master_req), + .type_i(ariane_pkg::SINGLE_REQ), + .amo_i(ariane_pkg::AMO_NONE), + .gnt_o(dm_master_gnt), + .addr_i(dm_master_add), + .we_i(dm_master_we), + .wdata_i(dm_master_wdata), + .be_i(dm_master_be), + .size_i(2'b11), // always do 64bit here and use byte enables to gate + .id_i('0), + .valid_o(dm_master_r_valid), + .rdata_o(dm_master_r_rdata), + .id_o(), + .critical_word_o(), + .critical_word_valid_o(), + .axi_req_o(dm_axi_m_req), + .axi_resp_i(dm_axi_m_resp) ); @@ -343,32 +346,32 @@ module ariane_testharness #( // --------------- logic rom_req; logic [AXI_ADDRESS_WIDTH-1:0] rom_addr; - logic [AXI_DATA_WIDTH-1:0] rom_rdata; + logic [ AXI_DATA_WIDTH-1:0] rom_rdata; axi2mem #( - .AXI_ID_WIDTH ( ariane_axi_soc::IdWidthSlave ), - .AXI_ADDR_WIDTH ( AXI_ADDRESS_WIDTH ), - .AXI_DATA_WIDTH ( AXI_DATA_WIDTH ), - .AXI_USER_WIDTH ( AXI_USER_WIDTH ) + .AXI_ID_WIDTH (ariane_axi_soc::IdWidthSlave), + .AXI_ADDR_WIDTH(AXI_ADDRESS_WIDTH), + .AXI_DATA_WIDTH(AXI_DATA_WIDTH), + .AXI_USER_WIDTH(AXI_USER_WIDTH) ) i_axi2rom ( - .clk_i ( clk_i ), - .rst_ni ( ndmreset_n ), - .slave ( master[ariane_soc::ROM] ), - .req_o ( rom_req ), - .we_o ( ), - .addr_o ( rom_addr ), - .be_o ( ), - .user_o ( ), - .data_o ( ), - .user_i ( '0 ), - .data_i ( rom_rdata ) + .clk_i (clk_i), + .rst_ni(ndmreset_n), + .slave (master[ariane_soc::ROM]), + .req_o (rom_req), + .we_o (), + .addr_o(rom_addr), + .be_o (), + .user_o(), + .data_o(), + .user_i('0), + .data_i(rom_rdata) ); bootrom i_bootrom ( - .clk_i ( clk_i ), - .req_i ( rom_req ), - .addr_i ( rom_addr ), - .rdata_o ( rom_rdata ) + .clk_i (clk_i), + .req_i (rom_req), + .addr_i (rom_addr), + .rdata_o(rom_rdata) ); // ------------------------------ @@ -382,15 +385,15 @@ module ariane_testharness #( `AXI_ASSIGN_TO_REQ(gpio_req, master[ariane_soc::GPIO]) `AXI_ASSIGN_FROM_RESP(master[ariane_soc::GPIO], gpio_resp) axi_err_slv #( - .AxiIdWidth ( ariane_axi_soc::IdWidthSlave ), - .req_t ( ariane_axi_soc::req_slv_t ), - .resp_t ( ariane_axi_soc::resp_slv_t ) + .AxiIdWidth(ariane_axi_soc::IdWidthSlave), + .req_t (ariane_axi_soc::req_slv_t), + .resp_t (ariane_axi_soc::resp_slv_t) ) i_gpio_err_slv ( - .clk_i ( clk_i ), - .rst_ni ( ndmreset_n ), - .test_i ( test_en ), - .slv_req_i ( gpio_req ), - .slv_resp_o ( gpio_resp ) + .clk_i (clk_i), + .rst_ni (ndmreset_n), + .test_i (test_en), + .slv_req_i (gpio_req), + .slv_resp_o(gpio_resp) ); @@ -398,98 +401,98 @@ module ariane_testharness #( // Memory + Exclusive Access // ------------------------------ AXI_BUS #( - .AXI_ADDR_WIDTH ( AXI_ADDRESS_WIDTH ), - .AXI_DATA_WIDTH ( AXI_DATA_WIDTH ), - .AXI_ID_WIDTH ( ariane_axi_soc::IdWidthSlave ), - .AXI_USER_WIDTH ( AXI_USER_WIDTH ) - ) dram(); + .AXI_ADDR_WIDTH(AXI_ADDRESS_WIDTH), + .AXI_DATA_WIDTH(AXI_DATA_WIDTH), + .AXI_ID_WIDTH (ariane_axi_soc::IdWidthSlave), + .AXI_USER_WIDTH(AXI_USER_WIDTH) + ) dram (); logic req; logic we; logic [AXI_ADDRESS_WIDTH-1:0] addr; - logic [AXI_DATA_WIDTH/8-1:0] be; - logic [AXI_DATA_WIDTH-1:0] wdata; - logic [AXI_DATA_WIDTH-1:0] rdata; - logic [AXI_USER_WIDTH-1:0] wuser; - logic [AXI_USER_WIDTH-1:0] ruser; + logic [ AXI_DATA_WIDTH/8-1:0] be; + logic [ AXI_DATA_WIDTH-1:0] wdata; + logic [ AXI_DATA_WIDTH-1:0] rdata; + logic [ AXI_USER_WIDTH-1:0] wuser; + logic [ AXI_USER_WIDTH-1:0] ruser; axi_riscv_atomics_wrap #( - .AXI_ADDR_WIDTH ( AXI_ADDRESS_WIDTH ), - .AXI_DATA_WIDTH ( AXI_DATA_WIDTH ), - .AXI_ID_WIDTH ( ariane_axi_soc::IdWidthSlave ), - .AXI_USER_WIDTH ( AXI_USER_WIDTH ), - .AXI_MAX_WRITE_TXNS ( 1 ), - .RISCV_WORD_WIDTH ( 64 ) + .AXI_ADDR_WIDTH (AXI_ADDRESS_WIDTH), + .AXI_DATA_WIDTH (AXI_DATA_WIDTH), + .AXI_ID_WIDTH (ariane_axi_soc::IdWidthSlave), + .AXI_USER_WIDTH (AXI_USER_WIDTH), + .AXI_MAX_WRITE_TXNS(1), + .RISCV_WORD_WIDTH (64) ) i_axi_riscv_atomics ( - .clk_i, - .rst_ni ( ndmreset_n ), - .slv ( master[ariane_soc::DRAM] ), - .mst ( dram ) + .clk_i, + .rst_ni(ndmreset_n), + .slv (master[ariane_soc::DRAM]), + .mst (dram) ); AXI_BUS #( - .AXI_ADDR_WIDTH ( AXI_ADDRESS_WIDTH ), - .AXI_DATA_WIDTH ( AXI_DATA_WIDTH ), - .AXI_ID_WIDTH ( ariane_axi_soc::IdWidthSlave ), - .AXI_USER_WIDTH ( AXI_USER_WIDTH ) - ) dram_delayed(); + .AXI_ADDR_WIDTH(AXI_ADDRESS_WIDTH), + .AXI_DATA_WIDTH(AXI_DATA_WIDTH), + .AXI_ID_WIDTH (ariane_axi_soc::IdWidthSlave), + .AXI_USER_WIDTH(AXI_USER_WIDTH) + ) dram_delayed (); axi_delayer_intf #( - .AXI_ID_WIDTH ( ariane_axi_soc::IdWidthSlave ), - .AXI_ADDR_WIDTH ( AXI_ADDRESS_WIDTH ), - .AXI_DATA_WIDTH ( AXI_DATA_WIDTH ), - .AXI_USER_WIDTH ( AXI_USER_WIDTH ), - .STALL_RANDOM_INPUT ( StallRandomInput ), - .STALL_RANDOM_OUTPUT ( StallRandomOutput ), - .FIXED_DELAY_INPUT ( 0 ), - .FIXED_DELAY_OUTPUT ( 0 ) + .AXI_ID_WIDTH (ariane_axi_soc::IdWidthSlave), + .AXI_ADDR_WIDTH (AXI_ADDRESS_WIDTH), + .AXI_DATA_WIDTH (AXI_DATA_WIDTH), + .AXI_USER_WIDTH (AXI_USER_WIDTH), + .STALL_RANDOM_INPUT (StallRandomInput), + .STALL_RANDOM_OUTPUT(StallRandomOutput), + .FIXED_DELAY_INPUT (0), + .FIXED_DELAY_OUTPUT (0) ) i_axi_delayer ( - .clk_i ( clk_i ), - .rst_ni ( ndmreset_n ), - .slv ( dram ), - .mst ( dram_delayed ) + .clk_i (clk_i), + .rst_ni(ndmreset_n), + .slv (dram), + .mst (dram_delayed) ); axi2mem #( - .AXI_ID_WIDTH ( ariane_axi_soc::IdWidthSlave ), - .AXI_ADDR_WIDTH ( AXI_ADDRESS_WIDTH ), - .AXI_DATA_WIDTH ( AXI_DATA_WIDTH ), - .AXI_USER_WIDTH ( AXI_USER_WIDTH ) + .AXI_ID_WIDTH (ariane_axi_soc::IdWidthSlave), + .AXI_ADDR_WIDTH(AXI_ADDRESS_WIDTH), + .AXI_DATA_WIDTH(AXI_DATA_WIDTH), + .AXI_USER_WIDTH(AXI_USER_WIDTH) ) i_axi2mem ( - .clk_i ( clk_i ), - .rst_ni ( ndmreset_n ), - .slave ( dram_delayed ), - .req_o ( req ), - .we_o ( we ), - .addr_o ( addr ), - .be_o ( be ), - .user_o ( wuser ), - .data_o ( wdata ), - .user_i ( ruser ), - .data_i ( rdata ) + .clk_i (clk_i), + .rst_ni(ndmreset_n), + .slave (dram_delayed), + .req_o (req), + .we_o (we), + .addr_o(addr), + .be_o (be), + .user_o(wuser), + .data_o(wdata), + .user_i(ruser), + .data_i(rdata) ); sram #( - .DATA_WIDTH ( AXI_DATA_WIDTH ), - .USER_WIDTH ( AXI_USER_WIDTH ), - .USER_EN ( AXI_USER_EN ), + .DATA_WIDTH(AXI_DATA_WIDTH), + .USER_WIDTH(AXI_USER_WIDTH), + .USER_EN (AXI_USER_EN), `ifdef VERILATOR - .SIM_INIT ( "none" ), + .SIM_INIT ("none"), `else - .SIM_INIT ( "zeros" ), + .SIM_INIT ("zeros"), `endif - .NUM_WORDS ( NUM_WORDS ) + .NUM_WORDS (NUM_WORDS) ) i_sram ( - .clk_i ( clk_i ), - .rst_ni ( rst_ni ), - .req_i ( req ), - .we_i ( we ), - .addr_i ( addr[$clog2(NUM_WORDS)-1+$clog2(AXI_DATA_WIDTH/8):$clog2(AXI_DATA_WIDTH/8)] ), - .wuser_i ( wuser ), - .wdata_i ( wdata ), - .be_i ( be ), - .ruser_o ( ruser ), - .rdata_o ( rdata ) + .clk_i (clk_i), + .rst_ni (rst_ni), + .req_i (req), + .we_i (we), + .addr_i (addr[$clog2(NUM_WORDS)-1+$clog2(AXI_DATA_WIDTH/8):$clog2(AXI_DATA_WIDTH/8)]), + .wuser_i(wuser), + .wdata_i(wdata), + .be_i (be), + .ruser_o(ruser), + .rdata_o(rdata) ); // --------------- @@ -499,74 +502,114 @@ module ariane_testharness #( axi_pkg::xbar_rule_64_t [ariane_soc::NB_PERIPHERALS-1:0] addr_map; assign addr_map = '{ - '{ idx: ariane_soc::Debug, start_addr: ariane_soc::DebugBase, end_addr: ariane_soc::DebugBase + ariane_soc::DebugLength }, - '{ idx: ariane_soc::ROM, start_addr: ariane_soc::ROMBase, end_addr: ariane_soc::ROMBase + ariane_soc::ROMLength }, - '{ idx: ariane_soc::CLINT, start_addr: ariane_soc::CLINTBase, end_addr: ariane_soc::CLINTBase + ariane_soc::CLINTLength }, - '{ idx: ariane_soc::PLIC, start_addr: ariane_soc::PLICBase, end_addr: ariane_soc::PLICBase + ariane_soc::PLICLength }, - '{ idx: ariane_soc::UART, start_addr: ariane_soc::UARTBase, end_addr: ariane_soc::UARTBase + ariane_soc::UARTLength }, - '{ idx: ariane_soc::Timer, start_addr: ariane_soc::TimerBase, end_addr: ariane_soc::TimerBase + ariane_soc::TimerLength }, - '{ idx: ariane_soc::SPI, start_addr: ariane_soc::SPIBase, end_addr: ariane_soc::SPIBase + ariane_soc::SPILength }, - '{ idx: ariane_soc::Ethernet, start_addr: ariane_soc::EthernetBase, end_addr: ariane_soc::EthernetBase + ariane_soc::EthernetLength }, - '{ idx: ariane_soc::GPIO, start_addr: ariane_soc::GPIOBase, end_addr: ariane_soc::GPIOBase + ariane_soc::GPIOLength }, - '{ idx: ariane_soc::DRAM, start_addr: ariane_soc::DRAMBase, end_addr: ariane_soc::DRAMBase + ariane_soc::DRAMLength } - }; + '{ + idx: ariane_soc::Debug, + start_addr: ariane_soc::DebugBase, + end_addr: ariane_soc::DebugBase + ariane_soc::DebugLength + }, + '{ + idx: ariane_soc::ROM, + start_addr: ariane_soc::ROMBase, + end_addr: ariane_soc::ROMBase + ariane_soc::ROMLength + }, + '{ + idx: ariane_soc::CLINT, + start_addr: ariane_soc::CLINTBase, + end_addr: ariane_soc::CLINTBase + ariane_soc::CLINTLength + }, + '{ + idx: ariane_soc::PLIC, + start_addr: ariane_soc::PLICBase, + end_addr: ariane_soc::PLICBase + ariane_soc::PLICLength + }, + '{ + idx: ariane_soc::UART, + start_addr: ariane_soc::UARTBase, + end_addr: ariane_soc::UARTBase + ariane_soc::UARTLength + }, + '{ + idx: ariane_soc::Timer, + start_addr: ariane_soc::TimerBase, + end_addr: ariane_soc::TimerBase + ariane_soc::TimerLength + }, + '{ + idx: ariane_soc::SPI, + start_addr: ariane_soc::SPIBase, + end_addr: ariane_soc::SPIBase + ariane_soc::SPILength + }, + '{ + idx: ariane_soc::Ethernet, + start_addr: ariane_soc::EthernetBase, + end_addr: ariane_soc::EthernetBase + ariane_soc::EthernetLength + }, + '{ + idx: ariane_soc::GPIO, + start_addr: ariane_soc::GPIOBase, + end_addr: ariane_soc::GPIOBase + ariane_soc::GPIOLength + }, + '{ + idx: ariane_soc::DRAM, + start_addr: ariane_soc::DRAMBase, + end_addr: ariane_soc::DRAMBase + ariane_soc::DRAMLength + } + }; localparam axi_pkg::xbar_cfg_t AXI_XBAR_CFG = '{ - NoSlvPorts: unsigned'(ariane_soc::NrSlaves), - NoMstPorts: unsigned'(ariane_soc::NB_PERIPHERALS), - MaxMstTrans: unsigned'(1), // Probably requires update - MaxSlvTrans: unsigned'(1), // Probably requires update - FallThrough: 1'b0, - LatencyMode: axi_pkg::NO_LATENCY, - AxiIdWidthSlvPorts: unsigned'(ariane_axi_soc::IdWidth), - AxiIdUsedSlvPorts: unsigned'(ariane_axi_soc::IdWidth), - UniqueIds: 1'b0, - AxiAddrWidth: unsigned'(AXI_ADDRESS_WIDTH), - AxiDataWidth: unsigned'(AXI_DATA_WIDTH), - NoAddrRules: unsigned'(ariane_soc::NB_PERIPHERALS) + NoSlvPorts: unsigned'(ariane_soc::NrSlaves), + NoMstPorts: unsigned'(ariane_soc::NB_PERIPHERALS), + MaxMstTrans: unsigned'(1), // Probably requires update + MaxSlvTrans: unsigned'(1), // Probably requires update + FallThrough: 1'b0, + LatencyMode: axi_pkg::NO_LATENCY, + AxiIdWidthSlvPorts: unsigned'(ariane_axi_soc::IdWidth), + AxiIdUsedSlvPorts: unsigned'(ariane_axi_soc::IdWidth), + UniqueIds: 1'b0, + AxiAddrWidth: unsigned'(AXI_ADDRESS_WIDTH), + AxiDataWidth: unsigned'(AXI_DATA_WIDTH), + NoAddrRules: unsigned'(ariane_soc::NB_PERIPHERALS) }; axi_xbar_intf #( - .AXI_USER_WIDTH ( AXI_USER_WIDTH ), - .Cfg ( AXI_XBAR_CFG ), - .rule_t ( axi_pkg::xbar_rule_64_t ) + .AXI_USER_WIDTH(AXI_USER_WIDTH), + .Cfg (AXI_XBAR_CFG), + .rule_t (axi_pkg::xbar_rule_64_t) ) i_axi_xbar ( - .clk_i ( clk_i ), - .rst_ni ( ndmreset_n ), - .test_i ( test_en ), - .slv_ports ( slave ), - .mst_ports ( master ), - .addr_map_i ( addr_map ), - .en_default_mst_port_i ( '0 ), - .default_mst_port_i ( '0 ) + .clk_i (clk_i), + .rst_ni (ndmreset_n), + .test_i (test_en), + .slv_ports (slave), + .mst_ports (master), + .addr_map_i (addr_map), + .en_default_mst_port_i('0), + .default_mst_port_i ('0) ); // --------------- // CLINT // --------------- - logic ipi; - logic timer_irq; + logic [NR_CORES-1:0] ipi; + logic [NR_CORES-1:0] timer_irq; - ariane_axi_soc::req_slv_t axi_clint_req; + ariane_axi_soc::req_slv_t axi_clint_req; ariane_axi_soc::resp_slv_t axi_clint_resp; clint #( - .CVA6Cfg ( CVA6Cfg ), - .AXI_ADDR_WIDTH ( AXI_ADDRESS_WIDTH ), - .AXI_DATA_WIDTH ( AXI_DATA_WIDTH ), - .AXI_ID_WIDTH ( ariane_axi_soc::IdWidthSlave ), - .NR_CORES ( 1 ), - .axi_req_t ( ariane_axi_soc::req_slv_t ), - .axi_resp_t ( ariane_axi_soc::resp_slv_t ) + .CVA6Cfg (CVA6Cfg), + .AXI_ADDR_WIDTH(AXI_ADDRESS_WIDTH), + .AXI_DATA_WIDTH(AXI_DATA_WIDTH), + .AXI_ID_WIDTH (ariane_axi_soc::IdWidthSlave), + .NR_CORES (NR_CORES), + .axi_req_t (ariane_axi_soc::req_slv_t), + .axi_resp_t (ariane_axi_soc::resp_slv_t) ) i_clint ( - .clk_i ( clk_i ), - .rst_ni ( ndmreset_n ), - .testmode_i ( test_en ), - .axi_req_i ( axi_clint_req ), - .axi_resp_o ( axi_clint_resp ), - .rtc_i ( rtc_i ), - .timer_irq_o ( timer_irq ), - .ipi_o ( ipi ) + .clk_i (clk_i), + .rst_ni (ndmreset_n), + .testmode_i (test_en), + .axi_req_i (axi_clint_req), + .axi_resp_o (axi_clint_resp), + .rtc_i (rtc_i), + .timer_irq_o(timer_irq), + .ipi_o (ipi) ); `AXI_ASSIGN_TO_REQ(axi_clint_req, master[ariane_soc::CLINT]) @@ -579,46 +622,53 @@ module ariane_testharness #( logic [1:0] irqs; ariane_peripherals #( - .AxiAddrWidth ( AXI_ADDRESS_WIDTH ), - .AxiDataWidth ( AXI_DATA_WIDTH ), - .AxiIdWidth ( ariane_axi_soc::IdWidthSlave ), - .AxiUserWidth ( AXI_USER_WIDTH ), + .AxiAddrWidth(AXI_ADDRESS_WIDTH), + .AxiDataWidth(AXI_DATA_WIDTH), + .AxiIdWidth (ariane_axi_soc::IdWidthSlave), + .AxiUserWidth(AXI_USER_WIDTH), `ifndef VERILATOR - .InclUART ( 1'b1 ), + .InclUART (1'b1), `else - .InclUART ( 1'b0 ), + .InclUART (1'b0), `endif - .InclSPI ( 1'b0 ), - .InclEthernet ( 1'b0 ) + .InclSPI (1'b0), + .InclEthernet(1'b0) ) i_ariane_peripherals ( - .clk_i ( clk_i ), - .rst_ni ( ndmreset_n ), - .plic ( master[ariane_soc::PLIC] ), - .uart ( master[ariane_soc::UART] ), - .spi ( master[ariane_soc::SPI] ), - .ethernet ( master[ariane_soc::Ethernet] ), - .timer ( master[ariane_soc::Timer] ), - .irq_o ( irqs ), - .rx_i ( rx ), - .tx_o ( tx ), - .eth_txck ( ), - .eth_rxck ( ), - .eth_rxctl ( ), - .eth_rxd ( ), - .eth_rst_n ( ), - .eth_tx_en ( ), - .eth_txd ( ), - .phy_mdio ( ), - .eth_mdc ( ), - .mdio ( ), - .mdc ( ), - .spi_clk_o ( ), - .spi_mosi ( ), - .spi_miso ( ), - .spi_ss ( ) + .clk_i (clk_i), + .rst_ni (ndmreset_n), + .plic (master[ariane_soc::PLIC]), + .uart (master[ariane_soc::UART]), + .spi (master[ariane_soc::SPI]), + .ethernet (master[ariane_soc::Ethernet]), + .timer (master[ariane_soc::Timer]), + .irq_o (irqs), + .rx_i (rx), + .tx_o (tx), + .eth_txck (), + .eth_rxck (), + .eth_rxctl(), + .eth_rxd (), + .eth_rst_n(), + .eth_tx_en(), + .eth_txd (), + .phy_mdio (), + .eth_mdc (), + .mdio (), + .mdc (), + .spi_clk_o(), + .spi_mosi (), + .spi_miso (), + .spi_ss () ); - uart_bus #(.BAUD_RATE(115200), .PARITY_EN(0)) i_uart_bus (.rx(tx), .tx(rx), .rx_en(1'b1)); + uart_bus #( + .BAUD_RATE(115200), + .PARITY_EN(0) + ) i_uart_bus ( + .rx(tx), + .tx(rx), + .rx_en(1'b1) + ); // --------------- // Core @@ -632,29 +682,29 @@ module ariane_testharness #( iti_to_encoder_t iti_to_encoder; ariane #( - .CVA6Cfg ( CVA6Cfg ), - .rvfi_probes_instr_t ( rvfi_probes_instr_t ), - .rvfi_probes_csr_t ( rvfi_probes_csr_t ), - .rvfi_probes_t ( rvfi_probes_t ), - .noc_req_t ( ariane_axi::req_t ), - .noc_resp_t ( ariane_axi::resp_t ) + .CVA6Cfg (CVA6Cfg), + .rvfi_probes_instr_t(rvfi_probes_instr_t), + .rvfi_probes_csr_t (rvfi_probes_csr_t), + .rvfi_probes_t (rvfi_probes_t), + .noc_req_t (ariane_axi::req_t), + .noc_resp_t (ariane_axi::resp_t), ) i_ariane ( - .clk_i ( clk_i ), - .rst_ni ( ndmreset_n ), - .boot_addr_i ( ariane_soc::ROMBase ), // start fetching from ROM - .hart_id_i ( {56'h0, hart_id} ), - .irq_i ( irqs ), - .ipi_i ( ipi ), - .time_irq_i ( timer_irq ), - .rvfi_probes_o ( rvfi_probes ), -// Disable Debug when simulating with Spike + .clk_i (clk_i), + .rst_ni (ndmreset_n), + .boot_addr_i (ariane_soc::ROMBase), // start fetching from ROM + .hart_id_i ({56'h0, hart_id}), + .irq_i (irqs), + .ipi_i (ipi), + .time_irq_i (timer_irq), + .rvfi_probes_o(rvfi_probes), + // Disable Debug when simulating with Spike `ifdef SPIKE_TANDEM - .debug_req_i ( 1'b0 ), + .debug_req_i (1'b0), `else - .debug_req_i ( debug_req_core ), + .debug_req_i (debug_req_core), `endif - .noc_req_o ( axi_ariane_req ), - .noc_resp_i ( axi_ariane_resp ) + .noc_req_o (axi_ariane_req), + .noc_resp_i (axi_ariane_resp) ); `AXI_ASSIGN_FROM_REQ(slave[0], axi_ariane_req) @@ -677,117 +727,118 @@ module ariane_testharness #( end end - cva6_iti #( - .CVA6Cfg (CVA6Cfg), - .CAUSE_LEN (iti_pkg::CAUSE_LEN), - .ITYPE_LEN (iti_pkg::ITYPE_LEN), - .IRETIRE_LEN (iti_pkg::IRETIRE_LEN), - .block_mode(0), - .rvfi_to_iti_t(rvfi_to_iti_t), - .iti_to_encoder_t(iti_to_encoder_t) - ) i_cva6_iti ( - .clk_i (clk_i), - .rst_ni (ndmreset_n), - // inputs from rvfi - .valid_i(rvfi_to_iti.valid), - .rvfi_to_iti_i(rvfi_to_iti), - .valid_o(), - .iti_to_encoder_o(iti_to_encoder) - ); + cva6_iti #( + .CVA6Cfg (CVA6Cfg), + .CAUSE_LEN (iti_pkg::CAUSE_LEN), + .ITYPE_LEN (iti_pkg::ITYPE_LEN), + .IRETIRE_LEN (iti_pkg::IRETIRE_LEN), + .block_mode(0), + .rvfi_to_iti_t(rvfi_to_iti_t), + .iti_to_encoder_t(iti_to_encoder_t) + ) i_iti ( + .clk_i(clk_i), + .rst_ni(ndmreset_n), + // inputs from rvfi + .valid_i(rvfi_to_iti.valid), + .rvfi_to_iti_i(rvfi_to_iti), + // outputs for the encoder module TODO + .valid_o(), + .iti_to_encoder_o(iti_to_encoder) + ); - logic packet_valid; - te_pkg::it_packet_type_e [0:0] packet_type; - logic [te_pkg::P_LEN-1:0] packet_length; - logic [te_pkg::PAYLOAD_LEN-1:0] packet_payload; - - rv_tracer #( - .N(1), - .ONLY_BRANCHES(1) - ) i_encoder( - .clk_i (clk_i), - .rst_ni (rst_ni), - .valid_i (iti_to_encoder.valid), - .itype_i (iti_to_encoder.itype), - .cause_i (iti_to_encoder.cause), - .tval_i (iti_to_encoder.tval), - .priv_i (iti_to_encoder.priv), - .iaddr_i (iti_to_encoder.iaddr), - .iretire_i (iti_to_encoder.iretire), - .ilastsize_i (iti_to_encoder.ilastsize), - .time_i (iti_to_encoder.cycles), - .tvec_i ('0), - .epc_i ('0), - .encapsulator_ready_i('1), - .paddr_i ('0), - .pwrite_i ('0), - .psel_i ('0), - .penable_i ('0), - .pwdata_i ('0), - .packet_valid_o (packet_valid), - .packet_type_o (packet_type), - .packet_length_o (packet_length), - .packet_payload_o (packet_payload), - .stall_o (), - .pready_o (), - .prdata_o () - ); + logic packet_valid; + te_pkg::it_packet_type_e [ 0:0] packet_type; + logic [ te_pkg::P_LEN-1:0] packet_length; + logic [te_pkg::PAYLOAD_LEN-1:0] packet_payload; + + rv_tracer #( + .N(1), + .ONLY_BRANCHES(1) + ) i_encoder ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .valid_i (iti_to_encoder.valid), + .itype_i (iti_to_encoder.itype), + .cause_i (iti_to_encoder.cause), + .tval_i (iti_to_encoder.tval), + .priv_i (iti_to_encoder.priv), + .iaddr_i (iti_to_encoder.iaddr), + .iretire_i (iti_to_encoder.iretire), + .ilastsize_i (iti_to_encoder.ilastsize), + .time_i (iti_to_encoder.cycles), + .tvec_i ('0), + .epc_i ('0), + .encapsulator_ready_i('1), + .paddr_i ('0), + .pwrite_i ('0), + .psel_i ('0), + .penable_i ('0), + .pwdata_i ('0), + .packet_valid_o (packet_valid), + .packet_type_o (packet_type), + .packet_length_o (packet_length), + .packet_payload_o (packet_payload), + .stall_o (), + .pready_o (), + .prdata_o () + ); - logic encap_valid; - encap_pkg::encap_fifo_entry_s encap_fifo_entry_i; - encap_pkg::encap_fifo_entry_s encap_fifo_entry_o; - logic encap_fifo_full; - logic encap_fifo_empty; - logic encap_fifo_pop; - - encapsulator i_encapsulator ( - .clk_i (clk_i), - .valid_i (packet_valid), - .packet_length_i (packet_length), - .flow_i ('0), - .timestamp_present_i('1), - //.srcid_i(), - .timestamp_i (rvfi_to_iti.cycles), - //.type_i(), - .trace_payload_i (packet_payload), - .valid_o (encap_valid), - .encap_fifo_entry_o (encap_fifo_entry_i) - ); + logic encap_valid; + encap_pkg::encap_fifo_entry_s encap_fifo_entry_i; + encap_pkg::encap_fifo_entry_s encap_fifo_entry_o; + logic encap_fifo_full; + logic encap_fifo_empty; + logic encap_fifo_pop; + + encapsulator i_encapsulator ( + .clk_i (clk_i), + .valid_i (packet_valid), + .packet_length_i (packet_length), + .flow_i ('0), + .timestamp_present_i('1), + //.srcid_i(), + .timestamp_i (rvfi_to_iti.cycles), + //.type_i(), + .trace_payload_i (packet_payload), + .valid_o (encap_valid), + .encap_fifo_entry_o (encap_fifo_entry_i) + ); - fifo_v3 # ( - .DEPTH(16), - .dtype(encap_pkg::encap_fifo_entry_s) - ) i_fifo_encap ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .flush_i ('0), - .testmode_i('0), - .full_o (encap_fifo_full), - .empty_o (encap_fifo_empty), - .usage_o (), - .data_i (encap_fifo_entry_i), - .push_i (encap_valid), - .data_o (encap_fifo_entry_o), - .pop_i (encap_fifo_pop) - ); - localparam DATA_LEN = 8; - - logic slicer_valid; - logic [DATA_LEN-1:0] slice; - logic [$clog2(DATA_LEN)-4:0] valid_bytes; - - slicer_DPTI #( - .SLICE_LEN(DATA_LEN), - .NO_TIME ('0) - ) i_slicer ( - .clk_i (clk_i), - .rst_ni (rst_ni), - .valid_i (!encap_fifo_empty), - .encap_fifo_entry_i(encap_fifo_entry_o), - .fifo_full_i ('0), // usrFull DPTI in ariane_xilinx - .valid_o (slicer_valid), - .slice_o (slice), - .done_o (encap_fifo_pop) - ); + fifo_v3 #( + .DEPTH(16), + .dtype(encap_pkg::encap_fifo_entry_s) + ) i_fifo_encap ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i ('0), + .testmode_i('0), + .full_o (encap_fifo_full), + .empty_o (encap_fifo_empty), + .usage_o (), + .data_i (encap_fifo_entry_i), + .push_i (encap_valid), + .data_o (encap_fifo_entry_o), + .pop_i (encap_fifo_pop) + ); + localparam DATA_LEN = 8; + + logic slicer_valid; + logic [ DATA_LEN-1:0] slice; + logic [$clog2(DATA_LEN)-4:0] valid_bytes; + + slicer_DPTI #( + .SLICE_LEN(DATA_LEN), + .NO_TIME ('0) + ) i_slicer ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .valid_i (!encap_fifo_empty), + .encap_fifo_entry_i(encap_fifo_entry_o), + .fifo_full_i ('0), // usrFull DPTI in ariane_xilinx + .valid_o (slicer_valid), + .slice_o (slice), + .done_o (encap_fifo_pop) + ); cva6_rvfi #( .CVA6Cfg (CVA6Cfg), @@ -802,76 +853,73 @@ module ariane_testharness #( .rst_ni (rst_ni), .rvfi_probes_i(rvfi_probes), .rvfi_instr_o (rvfi_instr), - .rvfi_to_iti_o (rvfi_to_iti), + .rvfi_to_iti_o(rvfi_to_iti), .rvfi_csr_o (rvfi_csr) ); - rvfi_tracer #( - .CVA6Cfg(CVA6Cfg), - .rvfi_instr_t(rvfi_instr_t), - .rvfi_csr_t(rvfi_csr_t), - // - .HART_ID(hart_id), - .DEBUG_START(0), - .DEBUG_STOP(0) + rvfi_tracer #( + .CVA6Cfg(CVA6Cfg), + .rvfi_instr_t(rvfi_instr_t), + .rvfi_csr_t(rvfi_csr_t), + // + .HART_ID(0), + .DEBUG_START(0), + .DEBUG_STOP(0) ) i_rvfi_tracer ( - .clk_i(clk_i), - .rst_ni(rst_ni), - .rvfi_i(rvfi_instr), - .rvfi_csr_i(rvfi_csr), - .end_of_test_o(tracer_exit) + .clk_i(clk_i), + .rst_ni(rst_ni), + .rvfi_i(rvfi_instr), + .rvfi_csr_i(rvfi_csr), + .end_of_test_o(tracer_exit) ); `ifdef SPIKE_TANDEM - spike #( - .CVA6Cfg ( CVA6Cfg ), - .rvfi_instr_t(rvfi_instr_t), - .rvfi_csr_t(rvfi_csr_t) - ) i_spike ( - .clk_i, - .rst_ni, - .clint_tick_i ( rtc_i ), - .rvfi_i ( rvfi_instr ), - .rvfi_csr_i ( rvfi_csr ), - .end_of_test_o ( tandem_exit ) - ); - initial begin - $display("Running binary in tandem mode"); - end - - bit tandem_timeout_enable; - bit [31:0] tandem_timeout; - localparam TANDEM_TIMEOUT_THRESHOLD = 60; + spike #( + .CVA6Cfg(CVA6Cfg), + .rvfi_instr_t(rvfi_instr_t), + .rvfi_csr_t(rvfi_csr_t) + ) i_spike ( + .clk_i, + .rst_ni, + .clint_tick_i (rtc_i), + .rvfi_i (rvfi_instr), + .rvfi_csr_i (rvfi_csr), + .end_of_test_o(tandem_exit) + ); + initial begin + $display("Running binary in tandem mode"); + end - // Tandem timeout logic - always_ff @(posedge clk_i) begin - if(tandem_timeout > TANDEM_TIMEOUT_THRESHOLD) - tandem_timeout_enable <= 0; - else if (tracer_exit) - tandem_timeout_enable <= 1; + bit tandem_timeout_enable; + bit [31:0] tandem_timeout; + localparam TANDEM_TIMEOUT_THRESHOLD = 60; - if (tandem_timeout_enable) - tandem_timeout <= tandem_timeout + 1; - end + // Tandem timeout logic + always_ff @(posedge clk_i) begin + if (tandem_timeout > TANDEM_TIMEOUT_THRESHOLD) tandem_timeout_enable <= 0; + else if (tracer_exit) tandem_timeout_enable <= 1; - always_ff @(posedge clk_i) begin - if (tandem_exit || (tandem_timeout > TANDEM_TIMEOUT_THRESHOLD)) begin - rvfi_exit <= tracer_exit; - end + if (tandem_timeout_enable) tandem_timeout <= tandem_timeout + 1; + end + always_ff @(posedge clk_i) begin + if (&tandem_exit || (tandem_timeout > TANDEM_TIMEOUT_THRESHOLD)) begin + rvfi_exit <= tracer_exit; end + + end `else - assign rvfi_exit = tracer_exit; + assign rvfi_exit = tracer_exit; `endif `ifdef VERILATOR - initial begin - string verbosity; - if ($value$plusargs("UVM_VERBOSITY=%s",verbosity)) begin - uvm_set_verbosity_level(verbosity); - `uvm_info("ariane_testharness", $sformatf("Set UVM_VERBOSITY to %s", verbosity), UVM_NONE) - end + initial begin + string verbosity; + if ($value$plusargs("UVM_VERBOSITY=%s", verbosity)) begin + uvm_set_verbosity_level(verbosity); + `uvm_info("ariane_testharness", $sformatf("Set UVM_VERBOSITY to %s", verbosity), UVM_NONE) end + end `endif @@ -879,65 +927,65 @@ module ariane_testharness #( // AXI 4 Assertion IP integration - You will need to get your own copy of this IP if you want // to use it Axi4PC #( - .DATA_WIDTH(ariane_axi_soc::DataWidth), - .WID_WIDTH(ariane_axi_soc::IdWidthSlave), - .RID_WIDTH(ariane_axi_soc::IdWidthSlave), - .AWUSER_WIDTH(ariane_axi_soc::UserWidth), - .WUSER_WIDTH(ariane_axi_soc::UserWidth), - .BUSER_WIDTH(ariane_axi_soc::UserWidth), - .ARUSER_WIDTH(ariane_axi_soc::UserWidth), - .RUSER_WIDTH(ariane_axi_soc::UserWidth), - .ADDR_WIDTH(ariane_axi_soc::AddrWidth) + .DATA_WIDTH(ariane_axi_soc::DataWidth), + .WID_WIDTH(ariane_axi_soc::IdWidthSlave), + .RID_WIDTH(ariane_axi_soc::IdWidthSlave), + .AWUSER_WIDTH(ariane_axi_soc::UserWidth), + .WUSER_WIDTH(ariane_axi_soc::UserWidth), + .BUSER_WIDTH(ariane_axi_soc::UserWidth), + .ARUSER_WIDTH(ariane_axi_soc::UserWidth), + .RUSER_WIDTH(ariane_axi_soc::UserWidth), + .ADDR_WIDTH(ariane_axi_soc::AddrWidth) ) i_Axi4PC ( - .ACLK(clk_i), - .ARESETn(ndmreset_n), - .AWID(dram.aw_id), - .AWADDR(dram.aw_addr), - .AWLEN(dram.aw_len), - .AWSIZE(dram.aw_size), - .AWBURST(dram.aw_burst), - .AWLOCK(dram.aw_lock), - .AWCACHE(dram.aw_cache), - .AWPROT(dram.aw_prot), - .AWQOS(dram.aw_qos), - .AWREGION(dram.aw_region), - .AWUSER(dram.aw_user), - .AWVALID(dram.aw_valid), - .AWREADY(dram.aw_ready), - .WLAST(dram.w_last), - .WDATA(dram.w_data), - .WSTRB(dram.w_strb), - .WUSER(dram.w_user), - .WVALID(dram.w_valid), - .WREADY(dram.w_ready), - .BID(dram.b_id), - .BRESP(dram.b_resp), - .BUSER(dram.b_user), - .BVALID(dram.b_valid), - .BREADY(dram.b_ready), - .ARID(dram.ar_id), - .ARADDR(dram.ar_addr), - .ARLEN(dram.ar_len), - .ARSIZE(dram.ar_size), - .ARBURST(dram.ar_burst), - .ARLOCK(dram.ar_lock), - .ARCACHE(dram.ar_cache), - .ARPROT(dram.ar_prot), - .ARQOS(dram.ar_qos), - .ARREGION(dram.ar_region), - .ARUSER(dram.ar_user), - .ARVALID(dram.ar_valid), - .ARREADY(dram.ar_ready), - .RID(dram.r_id), - .RLAST(dram.r_last), - .RDATA(dram.r_data), - .RRESP(dram.r_resp), - .RUSER(dram.r_user), - .RVALID(dram.r_valid), - .RREADY(dram.r_ready), - .CACTIVE('0), - .CSYSREQ('0), - .CSYSACK('0) + .ACLK(clk_i), + .ARESETn(ndmreset_n), + .AWID(dram.aw_id), + .AWADDR(dram.aw_addr), + .AWLEN(dram.aw_len), + .AWSIZE(dram.aw_size), + .AWBURST(dram.aw_burst), + .AWLOCK(dram.aw_lock), + .AWCACHE(dram.aw_cache), + .AWPROT(dram.aw_prot), + .AWQOS(dram.aw_qos), + .AWREGION(dram.aw_region), + .AWUSER(dram.aw_user), + .AWVALID(dram.aw_valid), + .AWREADY(dram.aw_ready), + .WLAST(dram.w_last), + .WDATA(dram.w_data), + .WSTRB(dram.w_strb), + .WUSER(dram.w_user), + .WVALID(dram.w_valid), + .WREADY(dram.w_ready), + .BID(dram.b_id), + .BRESP(dram.b_resp), + .BUSER(dram.b_user), + .BVALID(dram.b_valid), + .BREADY(dram.b_ready), + .ARID(dram.ar_id), + .ARADDR(dram.ar_addr), + .ARLEN(dram.ar_len), + .ARSIZE(dram.ar_size), + .ARBURST(dram.ar_burst), + .ARLOCK(dram.ar_lock), + .ARCACHE(dram.ar_cache), + .ARPROT(dram.ar_prot), + .ARQOS(dram.ar_qos), + .ARREGION(dram.ar_region), + .ARUSER(dram.ar_user), + .ARVALID(dram.ar_valid), + .ARREADY(dram.ar_ready), + .RID(dram.r_id), + .RLAST(dram.r_last), + .RDATA(dram.r_data), + .RRESP(dram.r_resp), + .RUSER(dram.r_user), + .RVALID(dram.r_valid), + .RREADY(dram.r_ready), + .CACTIVE('0), + .CSYSREQ('0), + .CSYSACK('0) ); `endif endmodule diff --git a/corev_apu/tb/dpi/syscalls.cc b/corev_apu/tb/dpi/syscalls.cc new file mode 100644 index 0000000000..3cf7167deb --- /dev/null +++ b/corev_apu/tb/dpi/syscalls.cc @@ -0,0 +1,41 @@ + +#include +#include +#include +#include + +#include "syscalls.h" + +uintptr_t SyscallHandler::_mem; + +constexpr uint64_t MEM_OFFSET=0x80000000; +constexpr uint64_t WORDLEN_BYTE=8; + +void emulate_syscall (long long tohost, long long fromhost) { + SyscallHandler::emulate_syscall(tohost, fromhost); +} + +void SyscallHandler::init(void* mem) { + _mem = (uintptr_t) mem - MEM_OFFSET; +} + +void SyscallHandler::emulate_syscall( + long long tohost, long long fromhost) { + long long which, arg0, arg1, arg2; + which = *(long long*) (_mem + tohost); + arg0 = *(long long*) (_mem + tohost + WORDLEN_BYTE); + arg1 = *(long long*) (_mem + tohost + 2*WORDLEN_BYTE); + arg2 = *(long long*) (_mem + tohost + 3*WORDLEN_BYTE); + switch (which) { + case 64: { + long ret = write((int) arg0, (void*)(_mem + arg1), (size_t) arg2); + *((long*) (_mem + tohost)) = ret; + break; + } + default: { + fprintf(stderr, "[ERROR]: Unknown syscall %lld\n", which); + break; + } + } + *((uint64_t*) (_mem + fromhost)) = 1; +} diff --git a/corev_apu/tb/dpi/syscalls.h b/corev_apu/tb/dpi/syscalls.h new file mode 100644 index 0000000000..5dec89003a --- /dev/null +++ b/corev_apu/tb/dpi/syscalls.h @@ -0,0 +1,12 @@ +#include +#include + +extern "C" void emulate_syscall (long long tohost, long long fromhost); + +class SyscallHandler { + static uintptr_t _mem; + +public: + static void init(void* mem); + static void emulate_syscall(long long tohost, long long fromhost); +}; diff --git a/corev_apu/tb/rvfi_tracer.sv b/corev_apu/tb/rvfi_tracer.sv index ae158d118d..2e8311b476 100644 --- a/corev_apu/tb/rvfi_tracer.sv +++ b/corev_apu/tb/rvfi_tracer.sv @@ -9,34 +9,51 @@ // `ifndef READ_SYMBOL_T `define READ_SYMBOL_T -import "DPI-C" function byte read_symbol (input string symbol_name, inout longint unsigned address); +import "DPI-C" function byte read_symbol( + input string symbol_name, + inout longint unsigned address +); `endif `ifndef READ_ELF_T `define READ_ELF_T import "DPI-C" function void read_elf(input string filename); -import "DPI-C" function byte get_section(output longint address, output longint len); -import "DPI-C" context function void read_section_sv(input longint address, inout byte buffer[]); +import "DPI-C" function byte get_section( + output longint address, + output longint len +); +import "DPI-C" context function void read_section_sv( + input longint address, + inout byte buffer[] +); +`endif + +`ifndef SYSCALL_T +import "DPI-C" function void emulate_syscall( + input longint tohost, + input longint fromhost +); `endif module rvfi_tracer #( - parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, - parameter type rvfi_instr_t = logic, - parameter type rvfi_csr_t = logic, - // - parameter logic [7:0] HART_ID = '0, - parameter int unsigned DEBUG_START = 0, - parameter int unsigned DEBUG_STOP = 0 -)( - input logic clk_i, - input logic rst_ni, - input rvfi_instr_t[CVA6Cfg.NrCommitPorts-1:0] rvfi_i, - input rvfi_csr_t rvfi_csr_i, - output logic[31:0] end_of_test_o + parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty, + parameter type rvfi_instr_t = logic, + parameter type rvfi_csr_t = logic, + // + parameter logic [7:0] HART_ID = '0, + parameter int unsigned DEBUG_START = 0, + parameter int unsigned DEBUG_STOP = 0 +) ( + input logic clk_i, + input logic rst_ni, + input rvfi_instr_t [CVA6Cfg.NrCommitPorts-1:0] rvfi_i, + input rvfi_csr_t rvfi_csr_i, + output logic [ 31:0] end_of_test_o ); longint unsigned TOHOST_ADDR; + longint unsigned FROMHOST_ADDR; string binary; int f; int unsigned SIM_FINISH; @@ -45,29 +62,50 @@ module rvfi_tracer #( f = $fopen($sformatf("trace_rvfi_hart_%h.dasm", HART_ID), "w"); if (!$value$plusargs("time_out=%d", SIM_FINISH)) SIM_FINISH = 2000000; if (!$value$plusargs("tohost_addr=%h", TOHOST_ADDR)) TOHOST_ADDR = '0; + if (!$value$plusargs("fromhost_addr=%h", FROMHOST_ADDR)) FROMHOST_ADDR = '0; if (TOHOST_ADDR == '0) begin - if (!$value$plusargs("elf_file=%s", binary)) binary = ""; - if (binary != "") begin - read_elf(binary); - read_symbol("tohost", TOHOST_ADDR); - end - $display("*** [rvf_tracer] INFO: Loading binary : %s", binary); - $display("*** [rvf_tracer] INFO: tohost_addr: %h", TOHOST_ADDR); - if (TOHOST_ADDR == '0) begin - $display("*** [rvf_tracer] WARNING: No valid address of 'tohost' (tohost == 0x%h), termination possible only by timeout or Ctrl-C!\n", TOHOST_ADDR); - $fwrite(f, "*** [rvfi_tracer] WARNING No valid address of 'tohost' (tohost == 0x%h), termination possible only by timeout or Ctrl-C!\n", TOHOST_ADDR); - end + if (!$value$plusargs("elf_file=%s", binary)) binary = ""; + if (binary != "") begin + read_elf(binary); + $display("*** [rvf_tracer %d] INFO: Loading binary : %s", HART_ID, binary); + end + read_symbol("tohost", TOHOST_ADDR); + $display("*** [rvf_tracer %d] INFO: tohost_addr: %h", HART_ID, TOHOST_ADDR); + end + if (FROMHOST_ADDR == '0) begin + if (!$value$plusargs("elf_file=%s", binary)) binary = ""; + if (binary != "") begin + read_elf(binary); + $display("*** [rvf_tracer %d] INFO: Loading binary : %s", HART_ID, binary); + end + $display("*** [rvf_tracer %d] INFO: fromhost_addr: %h", HART_ID, FROMHOST_ADDR); + read_symbol("fromhost", FROMHOST_ADDR); + end + if (TOHOST_ADDR == '0) begin + $display( + "*** [rvf_tracer %d] WARNING: No valid address of 'tohost' (tohost == 0x%h), termination possible only by timeout or Ctrl-C!\n", + HART_ID, TOHOST_ADDR); + $fwrite( + f, + "*** [rvfi_tracer %d] WARNING No valid address of 'tohost' (tohost == 0x%h), termination possible only by timeout or Ctrl-C!\n", + HART_ID, TOHOST_ADDR); + end + if (FROMHOST_ADDR == '0) begin + $display( + "*** [rvf_tracer %d] WARNING: No valid address of 'fromhost_addr' (fromhost_addr == 0x%h), syscalls will not work\n", + HART_ID, FROMHOST_ADDR); + $fwrite( + f, + "*** [rvfi_tracer %d] WARNING No valid address of 'fromhost_addr' (fromhost_addr == 0x%h), syscalls will not work\n", + HART_ID, FROMHOST_ADDR); end end final $fclose(f); logic [31:0] cycles; - // Generate the trace based on RVFI - logic [63:0] pc64; string cause; - logic[31:0] end_of_test_q; - logic[31:0] end_of_test_d; + logic [31:0] end_of_test_q; function automatic logic fp_instr_writes_gpr(logic [31:0] insn); logic [6:0] opcode; @@ -102,31 +140,34 @@ module rvfi_tracer #( return 1'b0; endfunction - assign end_of_test_o = end_of_test_d; + assign end_of_test_o = end_of_test_q; always_ff @(posedge clk_i) begin - end_of_test_q <= (rst_ni && (end_of_test_d[0] == 1'b1)) ? end_of_test_d : 0; + logic [31:0] end_of_test_d; + // Generate the trace based on RVFI + logic [63:0] pc64; + + end_of_test_d = (rst_ni && (end_of_test_q[0] == 1'b1)) ? end_of_test_q : 0; for (int i = 0; i < CVA6Cfg.NrCommitPorts; i++) begin - pc64 = {{CVA6Cfg.XLEN-CVA6Cfg.VLEN{rvfi_i[i].pc_rdata[CVA6Cfg.VLEN-1]}}, rvfi_i[i].pc_rdata}; + pc64 = { + {CVA6Cfg.XLEN - CVA6Cfg.VLEN{rvfi_i[i].pc_rdata[CVA6Cfg.VLEN-1]}}, rvfi_i[i].pc_rdata + }; // print the instruction information if the instruction is valid or a trap is taken if (rvfi_i[i].valid) begin logic dest_is_fp; // Instruction information if (rvfi_i[i].intr[2]) begin - $fwrite(f, "core INTERRUPT 0: 0x%h (0x%h) DASM(%h)\n", - pc64, rvfi_i[i].insn, rvfi_i[i].insn); - end - else begin - $fwrite(f, "core 0: 0x%h (0x%h) DASM(%h)\n", - pc64, rvfi_i[i].insn, rvfi_i[i].insn); + $fwrite(f, "core INTERRUPT %d: 0x%h (0x%h) DASM(%h)\n", HART_ID, pc64, rvfi_i[i].insn, + rvfi_i[i].insn); + end else begin + $fwrite(f, "core %d: 0x%h (0x%h) DASM(%h)\n", HART_ID, pc64, rvfi_i[i].insn, + rvfi_i[i].insn); end // Destination register information if (rvfi_i[i].insn[1:0] != 2'b11) begin - $fwrite(f, "%h 0x%h (0x%h)", - rvfi_i[i].mode, pc64, rvfi_i[i].insn[15:0]); + $fwrite(f, "%h 0x%h (0x%h)", rvfi_i[i].mode, pc64, rvfi_i[i].insn[15:0]); end else begin - $fwrite(f, "%h 0x%h (0x%h)", - rvfi_i[i].mode, pc64, rvfi_i[i].insn); + $fwrite(f, "%h 0x%h (0x%h)", rvfi_i[i].mode, pc64, rvfi_i[i].insn); end // Decode instruction to know if destination register is FP register. // Handle both uncompressed and compressed instructions. @@ -159,11 +200,16 @@ module rvfi_tracer #( // Handle memory writes (including for AMO instructions which have both rd and mem_wmask) if (rvfi_i[i].mem_wmask != 0) begin $fwrite(f, " mem 0x%h 0x%h", rvfi_i[i].mem_addr, rvfi_i[i].mem_wdata); - if (TOHOST_ADDR != '0 && - rvfi_i[i].mem_paddr == TOHOST_ADDR && - rvfi_i[i].mem_wdata[0] == 1'b1) begin - end_of_test_q <= rvfi_i[i].mem_wdata[31:0]; - $display("*** [rvfi_tracer] INFO: Simulation terminated after %d cycles!\n", cycles); + if (TOHOST_ADDR != '0 && rvfi_i[i].mem_paddr == TOHOST_ADDR) begin + case (rvfi_i[i].mem_wdata[0]) + 'b1: begin + end_of_test_d = rvfi_i[i].mem_wdata[31:0]; + $display( + "*** [rvfi_tracer %d] INFO: Simulation terminated after %d cycles with value 0x%h!", + HART_ID, cycles, rvfi_i[i].mem_wdata[31:0]); + end + 'b0: emulate_syscall(rvfi_i[i].mem_wdata, FROMHOST_ADDR); + endcase end end $fwrite(f, "\n"); @@ -184,7 +230,8 @@ module rvfi_tracer #( 32'hc: cause = "INSTR_PAGE_FAULT"; 32'hd: cause = "LOAD_PAGE_FAULT"; 32'hf: cause = "STORE_PAGE_FAULT"; - endcase; + endcase + ; if (rvfi_i[i].insn[1:0] != 2'b11) begin $fwrite(f, "%s exception @ 0x%h (0x%h)\n", cause, pc64, rvfi_i[i].insn[15:0]); end else begin @@ -194,28 +241,25 @@ module rvfi_tracer #( end end - if (~rst_ni) - cycles <= 0; - else - cycles <= cycles+1; - if (cycles > SIM_FINISH) - end_of_test_q <= 32'hffff_ffff; + if (~rst_ni) cycles <= 0; + else cycles <= cycles + 1; + if (cycles > SIM_FINISH) end_of_test_d = 32'hffff_ffff; - end_of_test_d <= end_of_test_q; + end_of_test_q <= end_of_test_d; end // Trace any custom signals // Define signals to be traced by adding them into debug and name arrays string name[0:10]; - logic[63:0] debug[0:10], debug_previous[0:10]; + logic [63:0] debug[0:10], debug_previous[0:10]; always_ff @(posedge clk_i) begin if (cycles > DEBUG_START && cycles < DEBUG_STOP) for (int index = 0; index < 100; index++) - if (debug_previous[index] != debug[index]) - $fwrite(f, "%d %s %x\n", cycles, name[index], debug[index]); + if (debug_previous[index] != debug[index]) + $fwrite(f, "%d %s %x\n", cycles, name[index], debug[index]); debug_previous <= debug; end -endmodule // rvfi_tracer +endmodule // rvfi_tracer diff --git a/verif/regress/hypervisor-tests-cv64a6_imafdch_sv39.sh b/verif/regress/hypervisor-tests-cv64a6_imafdch_sv39.sh new file mode 100755 index 0000000000..5d5620e43e --- /dev/null +++ b/verif/regress/hypervisor-tests-cv64a6_imafdch_sv39.sh @@ -0,0 +1,71 @@ +# Copyright 2021 Thales DIS design services SAS +# Inria 2026 +# +# Licensed under the Solderpad Hardware Licence, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# SPDX-License-Identifier: Apache-2.0 WITH SHL-2.0 +# You may obtain a copy of the License at https://solderpad.org/licenses/ +# +# Original Author: Jean-Roch COULON - Thales +# Modified by Nicolas Derumigny - Inria + +# where are the tools +if ! [ -n "$RISCV" ]; then + echo "Error: RISCV variable undefined" + return +fi + +if ! [ -n "$DV_SIMULATORS" ]; then + DV_SIMULATORS=vcs-testharness,spike +fi + +# install the required tools +if [[ "$DV_SIMULATORS" == *"veri-testharness"* ]]; then + source ./verif/regress/install-verilator.sh +fi +source ./verif/regress/install-spike.sh + +# install the required test suites +source ./verif/regress/install-riscv-compliance.sh +source ./verif/regress/install-riscv-tests.sh +source ./verif/regress/install-riscv-arch-test.sh + +# setup sim env +source ./verif/sim/setup-env.sh + +echo "$SPIKE_INSTALL_DIR$" + +if ! [ -n "$UVM_VERBOSITY" ]; then + export UVM_VERBOSITY=UVM_NONE +fi + +export DV_OPTS="$DV_OPTS --issrun_opts=+debug_disable=1+UVM_VERBOSITY=$UVM_VERBOSITY" + +CC_OPTS="-static -mcmodel=medany -fvisibility=hidden -nostdlib -nostartfiles -g ../tests/custom/common/syscalls.c ../tests/custom/common/crt.S -I../tests/custom/env -I../tests/custom/common -lgcc -D__riscv_cbo" + + +cd verif/sim/ + +srcs=( + ../tests/custom/rvh/main_timer.c + ../tests/custom/rvh/main_u.c + ../tests/custom/rvh/main_vs.c + ../tests/custom/rvh/main_satp.c + ../tests/custom/rvh/main_vsatp.c + ../tests/custom/rvh/main_hgatp.c +) +srcA=( + ../tests/custom/rvh/call.c + ../tests/custom/rvh/mmode.c + ../tests/custom/rvh/page_table.s + ../tests/custom/rvh/tests.c + ../tests/custom/rvh/trampoline.S + ../tests/custom/rvh/utils.c + ../tests/custom/rvh/vm.c +) + +for src in "${srcs[@]}"; do + python3 cva6.py --c_tests $src --iss_yaml cva6.yaml --compare_outputs --no_ecall_exit --target cv64a6_imafdch_sv39 --iss=$DV_SIMULATORS --gcc_opts="${srcA[*]} $CC_OPTS" $DV_OPTS --linker=../tests/custom/rvh/main.ld +done + +cd - diff --git a/verif/sim/Makefile b/verif/sim/Makefile index 05efa32b57..00ef3cc5d4 100644 --- a/verif/sim/Makefile +++ b/verif/sim/Makefile @@ -20,6 +20,10 @@ ifndef TARGET_CFG export TARGET_CFG = $(target) endif +CVA6_NR_HARTS ?= 1 +STDOUT ?= &2 +HART_IDS := $(shell seq -f "%02g" 0 $$(($(CVA6_NR_HARTS)-1))) + HPDCACHE_DIR ?= $(CVA6_REPO_DIR)/core/cache_subsystem/hpdcache export HPDCACHE_DIR HPDCACHE_TARGET_CFG ?= ${CVA6_REPO_DIR}/core/include/cva6_hpdcache_default_config_pkg.sv @@ -139,8 +143,8 @@ endif ############################################################################### spike: LD_LIBRARY_PATH="$$(realpath ../../tools/spike/lib):$$LD_LIBRARY_PATH" \ - $(tool_path)/spike $(spike_stepout) $(spike_extension) --log-commits --isa=$(variant) --priv=$(priv) $(spike_params_final) -l $(elf) - grep -v '^\([[]\|/top/\)' $(log).iss > $(log) + $(tool_path)/spike $(spike_stepout) $(spike_extension) --log-commits --isa=$(variant) --priv=$(priv) $(spike_params_final) -l $(elf) 1>$(STDOUT) + grep -v '^\([[]\|/top/\)' $(log).iss > $(log)_hart_00.log ############################################################################### # UVM specific commands, variables @@ -194,6 +198,7 @@ COMMON_PLUS_ARGS = \ +core_name=$(target) \ $(spike-yaml-plusarg) \ +tohost_addr=$(shell $$RISCV/bin/$(CV_SW_PREFIX)nm -B $(elf) | grep -w tohost | cut -d' ' -f1) \ + +fromhost_addr=$(shell $$RISCV/bin/$(CV_SW_PREFIX)nm -B $(elf) | grep -w fromhost | cut -d' ' -f1) \ +signature=$(elf).signature_output +UVM_TESTNAME=uvmt_cva6_firmware_test_c \ +report_file=$(log).yaml +core_name=$(target) @@ -295,7 +300,7 @@ vcs-uvm: vcs_uvm_comp vcs_uvm_run [ ! -f $(VCS_WORK_DIR)/novas.fsdb ] || \ mv $(VCS_WORK_DIR)/novas.fsdb `dirname $(log)`/`basename $(log) .log`.fsdb # Generate disassembled log. - $(tool_path)/spike-dasm --isa=$(variant) < ./vcs_results/default/vcs.d/trace_rvfi_hart_00.dasm > $(log) + $(foreach h,$(HART_IDS),$(tool_path)/spike-dasm --isa=$(variant) < ./vcs_results/default/vcs.d/trace_rvfi_hart_$(h).dasm > $(log)_hart$(h).log &&) true ### XRUN UVM rules @@ -325,6 +330,7 @@ xrun_uvm_run: +define+UVM_NO_DEPRECATED \ +UVM_TESTNAME=uvmt_cva6_firmware_test_c \ +tohost_addr=$(shell ${RISCV}/bin/${CV_SW_PREFIX}nm -B $(elf) | grep -w tohost | cut -d' ' -f1) \ + +fromhost_addr=$(shell ${RISCV}/bin/${CV_SW_PREFIX}nm -B $(elf) | grep -w fromhost | cut -d' ' -f1) \ $(cov-comp-opt) $(issrun_opts) @@ -370,8 +376,8 @@ questa_uvm_run: +define+UNSUPPORTED_WITH+ \ $(issrun_opts) \ uvmt_cva6_tb_opt - $(tool_path)/spike-dasm --isa=$(variant) < ./trace_rvfi_hart_00.dasm > $(log) - grep $(isspostrun_opts) ./trace_rvfi_hart_00.dasm + $(foreach h,$(HART_IDS),$(tool_path)/spike-dasm --isa=$(variant) < ./trace_rvfi_hart_$(h).dasm > $(log)_hart$(h).log &&) true + $(foreach h,$(HART_IDS),grep $(isspostrun_opts) ./trace_rvfi_hart_$(h).dasm &&) true # TODO: Add support for waveform collection. @@ -409,36 +415,37 @@ vcs-testharness: [ ! -f novas.fsdb ] || \ mv novas.fsdb `dirname $(log)`/`basename $(log) .log`.fsdb # Generate disassembled log. - $(tool_path)/spike-dasm --isa=$(variant) < ./trace_rvfi_hart_00.dasm > $(log) - grep $(isspostrun_opts) ./trace_rvfi_hart_00.dasm + $(foreach h,$(HART_IDS),$(tool_path)/spike-dasm --isa=$(variant) < ./trace_rvfi_hart_$(h).dasm > $(log)_hart$(h).log &&) true + $(foreach h,$(HART_IDS),grep $(isspostrun_opts) ./trace_rvfi_hart_$(h).dasm &&) true veri-testharness-pk: rm -rf $(path_var)/work-ver @echo "Trace compact value $(TRACE_COMPACT)" - make -C $(path_var) verilate verilator="verilator --no-timing" target=$(target) defines=$(subst +define+,,$(isscomp_opts)) + make -C $(path_var) verilate verilator="verilator --no-timing -GNR_CORES=$(CVA6_NR_HARTS)" target=$(target) defines=$(subst +define+,,$(isscomp_opts)) $(path_var)/work-ver/Variane_testharness $(if $(TRACE_COMPACT), -f verilator.fst) $(if $(TRACE_FAST), -v verilator.vcd) \ $(PK_INSTALL_DIR)/riscv-none-elf/bin/pk $(elf) \ +tohost_addr=$(shell $$RISCV/bin/$(CV_SW_PREFIX)nm -B $(PK_INSTALL_DIR)/riscv-none-elf/bin/pk | grep -w tohost | cut -d' ' -f1) \ + +fromhost_addr=$(shell $$RISCV/bin/$(CV_SW_PREFIX)nm -B $(PK_INSTALL_DIR)/riscv-none-elf/bin/pk | grep -w fromhost | cut -d' ' -f1) \ $(issrun_opts) # If present, move default waveform files to log directory. # Keep track of target in waveform file name. [ ! -f verilator.fst ] || mv verilator.fst `dirname $(log)`/`basename $(log) .log`.fst [ ! -f verilator.vcd ] || mv verilator.vcd `dirname $(log)`/`basename $(log) .log`.vcd # Generate disassembled log. - $(tool_path)/spike-dasm --isa=$(variant) < ./trace_rvfi_hart_00.dasm > $(log) - grep $(isspostrun_opts) ./trace_rvfi_hart_00.dasm + $(foreach h,$(HART_IDS),$(tool_path)/spike-dasm --isa=$(variant) < ./trace_rvfi_hart_$(h).dasm > $(log)_hart$(h).log &&) true + $(foreach h,$(HART_IDS),grep $(isspostrun_opts) ./trace_rvfi_hart_$(h).dasm &&) true veri-testharness: - make -C $(path_var) verilate verilator="verilator --no-timing" target=$(target) defines=$(subst +define+,,$(isscomp_opts)) + make -C $(path_var) verilate verilator="verilator --no-timing -GNR_CORES=$(CVA6_NR_HARTS)" target=$(target) defines=$(subst +define+,,$(isscomp_opts)) $(path_var)/work-ver/Variane_testharness $(if $(TRACE_COMPACT), -f verilator.fst) $(if $(TRACE_FAST), -v verilator.vcd) $(elf) $(issrun_opts) \ - $(COMMON_PLUS_ARGS) + $(COMMON_PLUS_ARGS) 1>$(STDOUT) # If present, move default waveform files to log directory. # Keep track of target in waveform file name. [ ! -f verilator.fst ] || mv verilator.fst `dirname $(log)`/`basename $(log) .log`.fst [ ! -f verilator.vcd ] || mv verilator.vcd `dirname $(log)`/`basename $(log) .log`.vcd # Generate disassembled log. - $(tool_path)/spike-dasm --isa=$(variant) < ./trace_rvfi_hart_00.dasm > $(log) - grep $(isspostrun_opts) ./trace_rvfi_hart_00.dasm + $(foreach h,$(HART_IDS),$(tool_path)/spike-dasm --isa=$(variant) < ./trace_rvfi_hart_$(h).dasm > $(log)_hart_$(h).log &&) true + $(foreach h,$(HART_IDS),grep $(isspostrun_opts) ./trace_rvfi_hart_$(h).dasm &&) true xrun-testharness: @echo "[XRUN-TESTHARNESS] Running" @@ -450,15 +457,15 @@ xrun-testharness: @echo "[XRUN-TESTHARNESS] $(elf)" make -C $(path_var) xrun_sim target=$(target) defines=$(subst +define+,,$(isscomp_opts))$(if $(spike-tandem),SPIKE_TANDEM=1) @echo "[XRUN-TESTHARNESS1] $(elf)" - $(CVA6_REPO_DIR)/tools/spike/spike-dasm --isa=$(variant) < ./xrun_results/xcelium.d/trace_rvfi_hart_00.dasm > $(log) + $(foreach h,$(HART_IDS),$(tool_path)/spike-dasm --isa=$(variant) < ./xrun_results/xcelium.d/trace_rvfi_hart_$(h).dasm > $(log)_hart_$(h).log &&) true questa-testharness: mkdir -p $(path_var)/tmp make -C $(path_var) sim target=$(target) defines=$(subst +define+,,$(isscomp_opts)+core_name=$(target)) batch-mode=1 elf_file=$(elf) \ report_file=$(log).yaml $(spike-yaml-makearg) # TODO: Add support for waveform collection. - $(tool_path)/spike-dasm --isa=$(variant) < $(path_var)/trace_rvfi_hart_00.dasm > $(log) - grep $(isspostrun_opts) $(path_var)/trace_rvfi_hart_00.dasm + $(foreach h,$(HART_IDS),$(tool_path)/spike-dasm --isa=$(variant) < ./trace_rvfi_hart_$(h).dasm > $(log)_hart_$(h).log &&) true + $(foreach h,$(HART_IDS),grep $(isspostrun_opts) ./trace_rvfi_hart_$(h).dasm &&) true ############################################################################### # Common targets and rules diff --git a/verif/sim/cva6.py b/verif/sim/cva6.py index dca0d765e0..45d8fd9a40 100644 --- a/verif/sim/cva6.py +++ b/verif/sim/cva6.py @@ -28,6 +28,7 @@ from dv.scripts.lib import * from verilator_log_to_trace_csv import * +from cva6_compare_text_traces import * from cva6_spike_log_to_trace_csv import * from dv.scripts.ovpsim_log_to_trace_csv import * from dv.scripts.whisper_log_trace_csv import * @@ -164,13 +165,14 @@ def parse_iss_yaml(iss, iss_yaml, isa, target, setting_dir, debug_cmd, priv, spi sys.exit(RET_FAIL) -def get_iss_cmd(base_cmd, elf, target, log): +def get_iss_cmd(base_cmd, elf, target, log, nr_harts): """Get the ISS simulation command Args: base_cmd : Original command template elf : ELF file to run ISS simualtion log : ISS simulation log name + nr_harts : number of harts Returns: cmd : Command for ISS simulation @@ -178,7 +180,8 @@ def get_iss_cmd(base_cmd, elf, target, log): cmd = re.sub(r"\", elf, base_cmd) cmd = re.sub(r"\", target, cmd) cmd = re.sub(r"\", log, cmd) - cmd += (" &> %s.iss" % log) + cmd += (" CVA6_NR_HARTS=%d" % nr_harts) + cmd += (" STDOUT=%s.out 2> %s.iss" % (log, log)) return cmd @@ -288,7 +291,7 @@ def do_simulate(sim_cmd, test_list, cwd, sim_opts, seed_gen, csr_file, (" +num_of_tests=%i " % test_cnt) + \ (" +start_idx=%d " % (i*batch_size)) + \ (" +asm_file_name=%s/asm_tests/%s " % (output_dir, test['test'])) + \ - (" -l %s/sim_%s_%d_%s.log " % (output_dir, test['test'], i, log_suffix)) + (" -l %s/sim_%s_%d_%s " % (output_dir, test['test'], i, log_suffix)) if verbose: cmd += "+UVM_VERBOSITY=UVM_HIGH " cmd = re.sub("", str(rand_seed), cmd) @@ -413,9 +416,12 @@ def gcc_compile(test_list, output_dir, isa, mabi, opts, debug_cmd, linker): -def tandem_postprocess(tandem_report, target, isa, test_name, log, testlist, iss, iterations = None): +def tandem_postprocess(tandem_report, target, isa, test_name, log, testlist, iss, + iterations = None, nr_harts=1, exit_on_ecall=True): analyze_tandem_report(tandem_report) - process_verilator_sim_log(log, log + ".csv") + for n in range(nr_harts): + logfile = log + ("_hart_%02g" % n) + ".log" + process_verilator_sim_log(logfile, logfile + ".csv", exit_on_ecall=exit_on_ecall) generate_yaml_report(tandem_report, target, isa, test_name, testlist, iss, False , iterations) def analyze_tandem_report(yaml_path): @@ -458,7 +464,9 @@ def generate_yaml_report(yaml_path, target, isa, test, testlist, iss, initial_cr def run_test(test, iss_yaml, isa, target, mabi, gcc_opts, iss_opts, output_dir, - setting_dir, debug_cmd, linker, priv, spike_params, test_name=None, iss_timeout=500, testlist="custom"): + setting_dir, debug_cmd, linker, priv, spike_params, test_name=None, + iss_timeout=500, testlist="custom", nr_harts=1, compare_outputs=False, + output_ref_file="", exit_on_ecall=True): """Run a directed test with ISS Args: @@ -478,6 +486,11 @@ def run_test(test, iss_yaml, isa, target, mabi, gcc_opts, iss_opts, output_dir, test_name : (Optional) Name of the test iss_timeout : Timeout for ISS simulation (default: 500) testlist : Test list identifier (default: "custom") + + nr_harts : Number of harts to simulate + compare_outputs: Perform stdout comparison across simulators + output_ref_file: If set, check simulator output against reference file + exit_on_ecall : Use ecalls as an exit marker """ if testlist != None: testlist = testlist.split('/')[-1].strip("testlist_").split('.')[0] @@ -519,6 +532,7 @@ def run_test(test, iss_yaml, isa, target, mabi, gcc_opts, iss_opts, output_dir, cmd= ("%s %s %s -o %s " % (get_env_var("RISCV_CC", debug_cmd = debug_cmd), test_path, gcc_opts, elf)) cmd += (" -march=%s" % isa) cmd += (" -mabi=%s" % mabi) + cmd += (" -DNUM_HARTS=%s" % nr_harts) logging.info("Compilation cmd: %s" % cmd) run_cmd(cmd, debug_cmd = debug_cmd) log_list = [] @@ -528,31 +542,36 @@ def run_test(test, iss_yaml, isa, target, mabi, gcc_opts, iss_opts, output_dir, tandem_sim = iss != "spike" and os.environ.get('SPIKE_TANDEM') != None run_cmd("mkdir -p %s/%s_sim" % (output_dir, iss)) if log_format == 1: - log = ("%s/%s_sim/%s_%d.%s.log" % (output_dir, iss, test_log_name, test_iteration, target)) + log = ("%s/%s_sim/%s_%d.%s" % (output_dir, iss, test_log_name, test_iteration, target)) else: - log = ("%s/%s_sim/%s.%s.log" % (output_dir, iss, test_log_name, target)) + log = ("%s/%s_sim/%s.%s" % (output_dir, iss, test_log_name, target)) yaml = ("%s/%s_sim/%s.%s.log.yaml" % (output_dir, iss, test_log_name, target)) log_list.append(log) base_cmd = parse_iss_yaml(iss, iss_yaml, isa, target, setting_dir, debug_cmd, priv, spike_params) - print(elf) - cmd = get_iss_cmd(base_cmd, elf, target, log) + cmd = get_iss_cmd(base_cmd, elf, target, log, nr_harts) logging.info("[%0s] Running ISS simulation: %s" % (iss, cmd)) if "spike" in iss: ratio = 10 else: ratio = 1 if tandem_sim: generate_yaml_report(yaml, target, isa, test_log_name, testlist, iss, True) run_cmd(cmd, iss_timeout//ratio, debug_cmd = debug_cmd) - logging.info("[%0s] Running ISS simulation: %s ...done" % (iss, elf)) + logging.info("[%0s] Running ISS simulation: %s ...done\n" % (iss, elf)) if tandem_sim: - tandem_postprocess(yaml, target, isa, test_log_name, log, testlist, iss) + tandem_postprocess(yaml, target, isa, test_log_name, log, testlist, iss, + nr_harts=nr_harts, exit_on_ecall=exit_on_ecall) if len(iss_list) == 2: - compare_iss_log(iss_list, log_list, report) + compare_iss_log(iss_list, log_list, report, nr_harts=nr_harts, exit_on_ecall=exit_on_ecall) + if (compare_outputs): + compare_output_logs(iss_list, log_list, report) + if output_ref_file: + compare_output_regexp(iss_list, log_list, os.path.abspath(output_ref_file), report) def iss_sim(test_list, output_dir, iss_list, iss_yaml, iss_opts, - isa, target, setting_dir, timeout_s, debug_cmd, priv, spike_params): + isa, target, setting_dir, timeout_s, debug_cmd, priv, spike_params, + nr_harts): """Run ISS simulation with the generated test program Args: @@ -579,8 +598,8 @@ def iss_sim(test_list, output_dir, iss_list, iss_yaml, iss_opts, for i in range(0, test['iterations']): prefix = ("%s/asm_tests/%s_%d" % (output_dir, test['test'], i)) elf = prefix + ".o" - log = ("%s/%s_%d.%s.log" % (log_dir, test['test'], i, target)) - cmd = get_iss_cmd(base_cmd, elf, target, log) + log = ("%s/%s_%d.%s" % (log_dir, test['test'], i, target)) + cmd = get_iss_cmd(base_cmd, elf, target, log,nr_harts) yaml = ("%s/%s_%s.%s.log.yaml" % (log_dir, test['test'], i, target)) if 'iss_opts' in test: cmd += ' ' @@ -597,7 +616,8 @@ def iss_sim(test_list, output_dir, iss_list, iss_yaml, iss_opts, tandem_postprocess(yaml, target, isa, test['test'], log, "generated tests", iss, i) -def iss_cmp(test_list, iss, target, output_dir, stop_on_first_error, exp, debug_cmd): +def iss_cmp(test_list, iss, target, output_dir, stop_on_first_error, exp, debug_cmd, + nr_harts=1, compare_outputs=False, output_ref_file="", exit_on_ecall=True): """Compare ISS simulation reult Args: @@ -607,13 +627,18 @@ def iss_cmp(test_list, iss, target, output_dir, stop_on_first_error, exp, debug_ stop_on_first_error : will end run on first error detected exp : Use experimental version debug_cmd : Produce the debug cmd log without running + compare_outputs: Compare outputs generated by the ISSs + output_ref_file: Compare output of the ISS against a reference file + exit_on_ecall : Use ecalls as an exit marker """ if debug_cmd: return iss_list = iss.split(",") + report = ("%s/iss_regr.log" % output_dir).rstrip() if len(iss_list) != 2: + if len(iss_list) == 1 and (compare_outputs or output_ref_file): + save_regr_report(report) return - report = ("%s/iss_regr.log" % output_dir).rstrip() for test in test_list: for i in range(0, test['iterations']): elf = ("%s/asm_tests/%s_%d.o" % (output_dir, test['test'], i)) @@ -622,26 +647,34 @@ def iss_cmp(test_list, iss, target, output_dir, stop_on_first_error, exp, debug_ log_list = [] run_cmd(("echo 'Test binary: %s' >> %s" % (elf, report))) for iss in iss_list: - log_list.append("%s/%s_sim/%s_%d.%s.log" % (output_dir, iss, test['test'], i, target)) - compare_iss_log(iss_list, log_list, report, stop_on_first_error, exp) + log_list.append("%s/%s_sim/%s_%d.%s" % (output_dir, iss, test['test'], i, target)) + compare_iss_log(iss_list, log_list, report, stop_on_first_error, exp, + nr_harts=nr_harts, exit_on_ecall=exit_on_ecall) save_regr_report(report) - -def compare_iss_log(iss_list, log_list, report, stop_on_first_error=0, exp=False): +def compare_iss_log(iss_list, log_list, report, stop_on_first_error=0, exp=False, + nr_harts=1, exit_on_ecall=True): if (len(iss_list) != 2 or len(log_list) != 2): logging.error("Only support comparing two ISS logs") logging.info("len(iss_list) = %s len(log_list) = %s" % (len(iss_list), len(log_list))) - else: + return + + for n in range(nr_harts): + logging.info("[Hart #%02g]" % n) + csv_list = [] for i in range(2): - log = log_list[i] + log = log_list[i] + ("_hart_%02g" % n) + ".log" csv = log.replace(".log", ".csv"); iss = iss_list[i] csv_list.append(csv) if iss == "spike": - process_spike_sim_log(log, csv) + if (n == 0): + process_spike_sim_log(log, csv, exit_on_ecall=exit_on_ecall) + else: + pass # Spike log in monocore only elif "veri" in iss or "vsim" in iss or "vcs" in iss or "questa" in iss: - process_verilator_sim_log(log, csv) + process_verilator_sim_log(log, csv, exit_on_ecall=exit_on_ecall) elif iss == "ovpsim": process_ovpsim_sim_log(log, csv, stop_on_first_error) elif iss == "sail": @@ -651,22 +684,45 @@ def compare_iss_log(iss_list, log_list, report, stop_on_first_error=0, exp=False else: logging.error("Unsupported ISS" % iss) sys.exit(RET_FAIL) - result = compare_trace_csv(csv_list[0], csv_list[1], iss_list[0], iss_list[1], report) - logging.info(result) + if (n == 0 or "spike" not in iss_list): + result = compare_trace_csv(csv_list[0], csv_list[1], iss_list[0], iss_list[1], report) + logging.info(result) + else: + logging.info("Skipping comparison for hart id > 0 (incompatibility with spike)\n") + +def compare_output_logs(iss_list, log_list, report): + logging.info("[Test outputs]") + logging.info("Comparing outputs:") + logging.info("%s : %s" % (iss_list[0], log_list[0] + ".out")) + logging.info("%s : %s" % (iss_list[1], log_list[1] + ".out")) + result = compare_text_traces(log_list[0] + ".out", log_list[1] + ".out", iss_list[0], iss_list[1], + report) + logging.info(result) + +def compare_output_regexp(iss_list, log_list, ref_file, report): + for iss, log in zip(iss_list, log_list): + logging.info("[Test %s against reference]" % iss) + logging.info("Comparing:") + logging.info("%s : %s" % (iss, log + ".out")) + logging.info("Reference : %s" % ref_file) + + result = compare_text_regexp(log + ".out", ref_file, iss, report) + logging.info(result) def save_regr_report(report): - passed_cnt = run_cmd(r"grep '\[PASSED\]' %s | wc -l" % report).strip() - failed_cnt = run_cmd(r"grep '\[FAILED\]' %s | wc -l" % report).strip() + passed_cnt = run_cmd(r"grep -E '\[PASSED\]' %s | wc -l" % report).strip() + failed_cnt = run_cmd(r"grep -E '\[FAILED\]' %s | wc -l" % report).strip() summary = ("%s PASSED, %s FAILED" % (passed_cnt, failed_cnt)) logging.info(summary) - run_cmd(("echo %s >> %s" % (summary, report))) + run_cmd(("echo -e '%s\\n\\n' >> %s" % (summary, report))) if failed_cnt != "0": - failed_details = run_cmd(r"sed -e 's,.*_sim/,,' %s | grep '\(csv\|matched\)' | uniq | sed -e 'N;s/\\n/ /g' | grep '\[FAILED\]'" % report).strip() - logging.info(failed_details) - run_cmd(("echo %s >> %s" % (failed_details, report))) + failed_details = run_cmd(r"sed -e 's,.*_sim/,,' %s | grep -E '(csv|matched)' | uniq | sed -e " + r"'N;s/\\n/ /g' | grep -E '\[FAILED\]'" % report).strip() + for line in failed_details.split("\n"): + logging.info(line) #sys.exit(RET_FAIL) #Do not return error code in case of test fail. - logging.info("ISS regression report is saved to %s" % report) + logging.info("ISS regression report is saved to %s\n" % report) def read_seed(arg): @@ -793,6 +849,14 @@ def parse_args(cwd): help="Choose additional z, s, x extensions") parser.add_argument("--spike_params", type=str, default="", help="Spike command line parameters, run spike --help and spike --print-params to see more") + parser.add_argument("--nr_harts", type=int, default=1, + help="Number of simulated harts") + parser.add_argument("--compare_outputs", action="store_true", + default=False, help="Also compares test stdout traces") + parser.add_argument("--no_ecall_exit", action="store_true", + default=False, help="Do not consider ecalls as exit markers") + parser.add_argument("--output_ref_file", type=str, default="", + help="Reference file to be checked against for output parsing") rsg = parser.add_argument_group('Random seeds', 'To control random seeds, use at most one ' 'of the --start_seed, --seed or --seed_yaml ' @@ -888,13 +952,13 @@ def load_config(args, cwd): base = args.target if base in ("cv64a6_imafdch_sv39", "cv64a6_imafdch_sv39_wb"): args.mabi = "lp64d" - args.isa = "rv64gch_zba_zbb_zbs_zbc" + args.isa = "rv64gch_zba_zbb_zbs_zbc_zicbom" elif base in ("cv64a6_imafdc_sv39_wb",): args.mabi = "lp64d" args.isa = "rv64gc_zba_zbb_zbs_zbc" elif base in ("cv64a6_imafdc_sv39", "cv64a6_imafdc_sv39_hpdcache", "cv64a6_imafdc_sv39_hpdcache_wb"): args.mabi = "lp64d" - args.isa = "rv64gc_zba_zbb_zbs_zbc_zbkb_zbkx_zkne_zknd_zknh" + args.isa = "rv64gc_zba_zbb_zbs_zbc_zbkb_zbkx_zkne_zknd_zknh_zicbom" elif base == "cv32a60x": args.mabi = "ilp32" args.isa = "rv32imc_zba_zbb_zbs_zbc" @@ -1186,7 +1250,9 @@ def main(): if os.path.isfile(full_path) or args.debug: run_test(full_path, args.iss_yaml, args.isa, args.target, args.mabi, args.gcc_opts, args.iss, output_dir, args.core_setting_dir, args.debug, args.linker, - args.priv, args.spike_params, iss_timeout=args.iss_timeout) + args.priv, args.spike_params, iss_timeout=args.iss_timeout, + nr_harts=args.nr_harts, compare_outputs=args.compare_outputs, + output_ref_file=args.output_ref_file, exit_on_ecall=not args.no_ecall_exit) else: logging.error('%s does not exist or is not a file' % full_path) sys.exit(RET_FAIL) @@ -1262,7 +1328,10 @@ def main(): if os.path.isfile(path_test): run_test(path_test, args.iss_yaml, args.isa, args.target, args.mabi, gcc_opts, args.iss, output_dir, args.core_setting_dir, args.debug, args.linker, - args.priv, args.spike_params, test_entry['test'], iss_timeout=args.iss_timeout, testlist=args.testlist) + args.priv, args.spike_params, test_entry['test'], iss_timeout=args.iss_timeout, + testlist=args.testlist, nr_harts=args.nr_harts, + compare_outputs=args.compare_outputs, output_ref_file=args.output_ref_file, + exit_on_ecall=not args.no_ecall_exit) else: if not args.debug: logging.error('%s does not exist' % path_test) @@ -1281,12 +1350,13 @@ def main(): if args.steps == "all" or re.match(".*iss_sim.*", args.steps): iss_sim(matched_list, output_dir, args.iss, args.iss_yaml, args.iss_opts, args.isa, args.target, args.core_setting_dir, args.iss_timeout, args.debug, - args.priv, args.spike_params) + args.priv, args.spike_params, args.nr_harts) # Compare ISS simulation result if args.steps == "all" or re.match(".*iss_cmp.*", args.steps): iss_cmp(matched_list, args.iss, args.target, output_dir, args.stop_on_first_error, - args.exp, args.debug) + args.exp, args.debug, args.nr_harts, compare_outputs=args.compare_outputs, + output_ref_file=args.output_ref_file, exit_on_ecall=(not args.no_ecall_exit)) sys.exit(RET_SUCCESS) except KeyboardInterrupt: diff --git a/verif/sim/cva6_compare_text_traces.py b/verif/sim/cva6_compare_text_traces.py new file mode 100644 index 0000000000..343c252ca2 --- /dev/null +++ b/verif/sim/cva6_compare_text_traces.py @@ -0,0 +1,99 @@ +import re + +def read_line(fd, line_nb): + ret = fd.readline() + line_nb += 1 + while (ret.startswith("UVM_") or ret.startswith("[SPIKE]") or + ret.startswith("*** [rvfi_tracer") or ret.startswith("CPU time used:") or + ret.startswith("Wall clock time passed:")): + ret = fd.readline() + line_nb += 1 + return ret, line_nb + +def compare_text_traces(file1, file2, name1, name2, log): + """Compare two generic text file""" + matched_cnt = 0 + mismatch_cnt = 0 + file1_index = 0 + file2_index = 0 + + if log: + fd = open(log, 'a+') + else: + fd = sys.stdout + + fd.write("{} : {}\n".format(name1, file1)) + fd.write("{} : {}\n".format(name2, file2)) + with open(file1, "r") as fd1, open(file2, "r") as fd2: + while (True): + line1, file1_index = read_line(fd1, file1_index) + line2, file2_index = read_line(fd2, file2_index) + if (line1 == "" and line2 == ""): + break + if line1 != line2: + mismatch_cnt += 1 + fd.write("\nMismatch[%s]:\n" % mismatch_cnt) + if (line1 == ""): + fd.write("%s : \n" % name1) + else: + fd.write("{}[{}] : {}\n".format(name1, file1_index, line1.strip())) + if (line2 == ""): + fd.write("%s : \n" % name2) + else: + fd.write("{}[{}] : {}\n".format(name2, file2_index, line2.strip())) + else: + matched_cnt += 1 + + if mismatch_cnt == 0: + compare_result = "[PASSED]: {} matched\n".format(matched_cnt) + else: + compare_result = "[FAILED]: {} matched, {} mismatch\n".format( + matched_cnt, mismatch_cnt) + fd.write(compare_result + "\n") + if log: + fd.close() + return compare_result + +def compare_text_regexp(file, ref_file, name, log): + """Compare two generic text file""" + matched_cnt = 0 + mismatch_cnt = 0 + file_index = 0 + ref_index = 0 + + if log: + fd = open(log, 'a+') + else: + fd = sys.stdout + + fd.write("{} : {}\n".format(name, file)) + fd.write("Reference : %s\n" % ref_file) + with open(file, "r") as fd1, open(ref_file, "r") as fd2: + while (True): + line, file_index = read_line(fd1, file_index) + regexp, ref_index = read_line(fd2, ref_index) + if (line == "" and regexp == ""): + break + if re.match(regexp.strip(), line.strip()) is None: + mismatch_cnt += 1 + fd.write("\nMismatch[%s]:\n" % mismatch_cnt) + if (line == ""): + fd.write("%s : \n" % name) + else: + fd.write("{}[{}] : {}\n".format(name, file_index, line.strip())) + if (regexp == ""): + fd.write("%s : \n" % name2) + else: + fd.write("Expected[{}] : {}\n".format(ref_index, regexp.strip())) + else: + matched_cnt += 1 + + if mismatch_cnt == 0: + compare_result = "[PASSED]: {} matched\n".format(matched_cnt) + else: + compare_result = "[FAILED]: {} matched, {} mismatch\n".format( + matched_cnt, mismatch_cnt) + fd.write(compare_result + "\n") + if log: + fd.close() + return compare_result diff --git a/verif/sim/cva6_spike_log_to_trace_csv.py b/verif/sim/cva6_spike_log_to_trace_csv.py index f7558a4382..be5f5d4069 100644 --- a/verif/sim/cva6_spike_log_to_trace_csv.py +++ b/verif/sim/cva6_spike_log_to_trace_csv.py @@ -28,7 +28,7 @@ from lib import * RD_RE = re.compile(r"(core\s+\d+:\s+)?(?P\d) 0x(?P[a-f0-9]+?) " \ - "\((?P.*?)\)(( c\S* 0x[a-f0-9]+)*) (?P[xf]\s*\d*?)\s*0x(?P[a-f0-9]+)") + r"\((?P.*?)\)(( c\S* 0x[a-f0-9]+)*) (?P[xf]\s*\d*?)\s*0x(?P[a-f0-9]+)") CORE_RE = re.compile( r"core\s+\d+:\s+0x(?P[a-f0-9]+?) \(0x(?P.*?)\) (?P.*?)$") @@ -88,7 +88,7 @@ def read_spike_instr(match, full_trace): return instr -def read_spike_trace(path, full_trace): +def read_spike_trace(path, full_trace, exit_on_ecall=True): """Read a Spike simulation log at , yielding executed instructions. This assumes that the log was generated with the -l and --log-commits options @@ -148,7 +148,7 @@ def read_spike_trace(path, full_trace): instr = read_spike_instr(instr_match, full_trace) # If instr.instr_str is 'ecall', we should stop. - if instr.instr_str == 'ecall': + if exit_on_ecall and instr.instr_str == 'ecall': break continue @@ -161,7 +161,7 @@ def read_spike_trace(path, full_trace): if instr_match: yield instr, False instr = read_spike_instr(instr_match, full_trace) - if instr.instr_str == 'ecall': + if exit_on_ecall and instr.instr_str == 'ecall': break continue @@ -186,7 +186,7 @@ def read_spike_trace(path, full_trace): yield (instr, False) -def process_spike_sim_log(spike_log, csv, full_trace=0): +def process_spike_sim_log(spike_log, csv, full_trace=0, exit_on_ecall=True): """Process SPIKE simulation log. Extract instruction and affected register information from spike simulation @@ -202,7 +202,7 @@ def process_spike_sim_log(spike_log, csv, full_trace=0): trace_csv = RiscvInstructionTraceCsv(csv_fd) trace_csv.start_new_trace() - for (entry, illegal) in read_spike_trace(spike_log, full_trace): + for (entry, illegal) in read_spike_trace(spike_log, full_trace, exit_on_ecall): instrs_in += 1 if illegal and full_trace: logging.debug("Illegal instruction: {}, opcode:{}" diff --git a/verif/sim/verilator_log_to_trace_csv.py b/verif/sim/verilator_log_to_trace_csv.py index ab35106373..075d36535d 100644 --- a/verif/sim/verilator_log_to_trace_csv.py +++ b/verif/sim/verilator_log_to_trace_csv.py @@ -28,8 +28,8 @@ from riscv_trace_csv import * from lib import * -RD_RE = re.compile(r"(?P\d) 0x(?P[a-f0-9]+?) " \ - "\((?P.*?)\) (?P[xf]\s*\d*?) 0x(?P[a-f0-9]+)") +RD_RE = re.compile(r"(?P\d) 0x(?P[a-f0-9]+?) " + r"\((?P.*?)\) (?P[xf]\s*\d*?) 0x(?P[a-f0-9]+)") CORE_RE = re.compile(r"core.*0x(?P[a-f0-9]+?) \(0x(?P.*?)\) (?P.*?)$") ILLE_RE = re.compile(r"trap_illegal_instruction") @@ -81,7 +81,7 @@ def read_verilator_instr(match, full_trace): return instr -def read_verilator_trace(path, full_trace): +def read_verilator_trace(path, full_trace, exit_on_ecall=True): '''Read a Spike simulation log at , yielding executed instructions. This assumes that the log was generated with the -l and --log-commits options @@ -151,7 +151,7 @@ def read_verilator_trace(path, full_trace): instr = read_verilator_instr(instr_match, full_trace) # If instr.instr_str is 'ecall', we should stop. - if instr.instr_str == 'ecall': + if exit_on_ecall and instr.instr_str == 'ecall': break continue @@ -164,7 +164,7 @@ def read_verilator_trace(path, full_trace): if instr_match: yield (instr, False) instr = read_verilator_instr(instr_match, full_trace) - if instr.instr_str == 'ecall': + if exit_on_ecall and instr.instr_str == 'ecall': break continue @@ -189,7 +189,7 @@ def read_verilator_trace(path, full_trace): yield (instr, False) -def process_verilator_sim_log(verilator_log, csv, full_trace = 0): +def process_verilator_sim_log(verilator_log, csv, full_trace = 0, exit_on_ecall=True): """Process VERILATOR simulation log. Extract instruction and affected register information from verilator simulation @@ -206,7 +206,7 @@ def process_verilator_sim_log(verilator_log, csv, full_trace = 0): trace_csv = RiscvInstructionTraceCsv(csv_fd) trace_csv.start_new_trace() - for (entry, illegal) in read_verilator_trace(verilator_log, full_trace): + for (entry, illegal) in read_verilator_trace(verilator_log, full_trace, exit_on_ecall): instrs_in += 1 if illegal and full_trace: diff --git a/verif/tests/custom/common/syscalls.c b/verif/tests/custom/common/syscalls.c index 045f35c4d1..4c060f2fb7 100644 --- a/verif/tests/custom/common/syscalls.c +++ b/verif/tests/custom/common/syscalls.c @@ -46,9 +46,23 @@ static uintptr_t syscall(uintptr_t which, uintptr_t arg0, uintptr_t arg1, uintpt // - the environment acknowledges the env request by writing 0 into tohost. // - the completion of the request is signalled by the environment through // a write of a non-zero value into fromhost. - tohost = (((uint64_t) ((unsigned long int) magic_mem)) << 16) >> 16; // clear the DEV and CMD bytes, clip payload. - while (fromhost == 0) - ; + tohost = (((uint64_t)((unsigned long int)magic_mem)) << 16) >> + 16; // clear the DEV and CMD bytes, clip payload. + + #ifdef __riscv_atomic + // Required for Verilator consistency, else `fromhost` will + // be fetched from the cache + invalidate_cacheline(&fromhost); + invalidate_cacheline(&magic_mem); +#endif + + while (fromhost == 0) { +#ifdef __riscv_atomic + // Idem + invalidate_cacheline(&fromhost); +#endif + } + fromhost = 0; #ifdef __riscv_atomic // __sync_synchronize requires A extension @@ -182,7 +196,7 @@ int putchar(int ch) buf[buflen++] = ch; - if (ch == '\n' || buflen == sizeof(buf)) + if (ch == '\n' || ch == ' ' || buflen == sizeof(buf)) { syscall(SYS_write, 1, (uintptr_t)buf, buflen); buflen = 0; @@ -279,7 +293,7 @@ static void vprintfmt(void (*putch)(int, void**), void **putdat, const char *fmt case '-': padc = '-'; goto reswitch; - + // flag to pad with 0's instead of spaces case '0': padc = '0'; @@ -388,7 +402,7 @@ static void vprintfmt(void (*putch)(int, void**), void **putdat, const char *fmt case '%': putch(ch, putdat); break; - + // unrecognized escape sequence - just print it literally default: putch('%', putdat); diff --git a/verif/tests/custom/common/util.h b/verif/tests/custom/common/util.h index 78fa62401a..165619d5ff 100644 --- a/verif/tests/custom/common/util.h +++ b/verif/tests/custom/common/util.h @@ -89,4 +89,10 @@ static uintptr_t insn_len(uintptr_t pc) stringify(code), _c, _c/iter, 10*_c/iter%10, _c/_i, 10*_c/_i%10); \ } while(0) +static inline void invalidate_cacheline(volatile void *addr) { +#ifdef __riscv_cbo + __asm__ volatile("cbo.inval (%0)" ::"r"(addr) : "memory"); +#endif +} + #endif //__UTIL_H diff --git a/verif/tests/custom/rvh/call.c b/verif/tests/custom/rvh/call.c new file mode 100644 index 0000000000..c6f91b9f51 --- /dev/null +++ b/verif/tests/custom/rvh/call.c @@ -0,0 +1,109 @@ +#include + +#include "utils.h" + +#include "sbi.h" +#include "csr.h" +#include "trampoline.h" +#include "vm.h" +#include "call.h" + +void change_mode() { + struct sbiret r = sbi_ecall(SBI_EXT_EXTRA, SBI_EXTRA_CHANGE_MODE, 0, 0, 0, 0, 0, 0); + if (r.error) { + printf("%s : ecall returned an error\n", __FUNCTION__); + panic(); + } +} + +void call_flush_tlb(void) { + struct sbiret r = sbi_ecall(SBI_EXT_EXTRA, SBI_EXTRA_FLUSH_TLB, 0, 0, 0, 0, 0, 0); + if (r.error) { + printf("%s : ecall returned an error\n", __FUNCTION__); + panic(); + } +} + +void call_flush_htlb(void) { + struct sbiret r = sbi_ecall(SBI_EXT_EXTRA, SBI_EXTRA_FLUSH_HTLB, 0, 0, 0, 0, 0, 0); + if (r.error) { + printf("%s : ecall returned an error\n", __FUNCTION__); + panic(); + } +} + +void call_set_stvec(vec_f v) { + struct sbiret r = sbi_ecall(SBI_EXT_EXTRA, SBI_EXTRA_SET_STVEC, (uint64_t)v, 0, 0, 0, 0, 0); + if (r.error) { + printf("%s : ecall returned an error\n", __FUNCTION__); + panic(); + } +} + +void forward_ecall(struct trap_context *tc) { + struct sbiret r = sbi_ecall(tc->a7, tc->a6, tc->a0, tc->a1, tc->a2, tc->a3, tc->a4, tc->a5); + tc->a0 = r.error; + tc->a1 = r.value; +} + +uint64_t handle_vs_call(void) { + struct trap_context *tc = GET_TRAP_CONTEXT(); + switch (tc->a7) { + case SBI_EXT_DBCN: { + switch (tc->a6) { + case SBI_EXT_DBCN_CONSOLE_READ: + case SBI_EXT_DBCN_CONSOLE_WRITE: + case SBI_EXT_DBCN_CONSOLE_WRITE_BYTE: { + forward_ecall(tc); + break; + } + default: { + tc->a0 = EXTRA_UNKNOWN; + goto unknown; + } + } + break; + } + case SBI_EXT_EXTRA: { + switch (tc->a6) { + case SBI_EXTRA_CHANGE_MODE: { + tc->a0 = EXTRA_SUCCESS; + csr_write(CSR_SSCRATCH, NEXT_INSTRUCTION(csr_read(CSR_SEPC), 1)); + RESTORE_CONTEXT(); + asm volatile("csrr t0, sscratch\n" + "jr t0\n"); + break; + } + case SBI_EXTRA_FLUSH_TLB: { + flush_tlb(); + tc->a0 = EXTRA_SUCCESS; + break; + } + case SBI_EXTRA_FLUSH_HTLB: { + flush_htlb(); + tc->a0 = EXTRA_SUCCESS; + break; + } + case SBI_EXTRA_SET_STVEC: { + asm volatile("csrw stvec, %0" ::"r"(tc->a0) : "memory"); + tc->a0 = EXTRA_SUCCESS; + break; + } + default: { + tc->a0 = EXTRA_ERROR; + goto error; + } + } + break; + } + default: { + tc->a0 = EXTRA_UNKNOWN; + goto unknown; + } + } + return EXTRA_SUCCESS; +error: + return EXTRA_ERROR; +unknown: + return EXTRA_UNKNOWN; +} diff --git a/verif/tests/custom/rvh/call.h b/verif/tests/custom/rvh/call.h new file mode 100644 index 0000000000..498d80508e --- /dev/null +++ b/verif/tests/custom/rvh/call.h @@ -0,0 +1,24 @@ +#ifndef HMODE_CALL_H +#define HMODE_CALL_H + +#include "util.h" +#include "types.h" + +#define SBI_EXT_EXTRA 0x08000000 + +enum sbi_ext_extra { + SBI_EXTRA_CHANGE_MODE, + SBI_EXTRA_FLUSH_TLB, + SBI_EXTRA_FLUSH_HTLB, + SBI_EXTRA_SET_STVEC, +}; + +enum sbi_ext_extra_error { EXTRA_SUCCESS = 0, EXTRA_ERROR, EXTRA_UNKNOWN }; + +void change_mode(void); +void call_flush_tlb(void); +void call_flush_htlb(void); +void call_set_stvec(vec_f); +uint64_t handle_vs_call(void); + +#endif //HMODE_CALL_H diff --git a/verif/tests/custom/rvh/csr.h b/verif/tests/custom/rvh/csr.h new file mode 100644 index 0000000000..7701fac3a9 --- /dev/null +++ b/verif/tests/custom/rvh/csr.h @@ -0,0 +1,518 @@ +#ifndef HMODE_CSR_H +#define HMODE_CSR_H + +// mostly stolen from Linux "arch/riscv/include/asm/csr.h" + +#define __ASM_STR(x) #x + +/* Status register flags */ +#define SR_SIE 0x00000002 /* Supervisor Interrupt Enable */ +#define SR_MIE 0x00000008 /* Machine Interrupt Enable */ +#define SR_SPIE 0x00000020 /* Previous Supervisor IE */ +#define SR_MPIE 0x00000080 /* Previous Machine IE */ +#define SR_SPP 0x00000100 /* Previously Supervisor */ +#define SR_MPP 0x00001800 /* Previously Machine */ +#define SR_MPP_U 0x00000000 +#define SR_MPP_S 0x00000800 +#define SR_MPP_M 0x00001800 +#define SR_SUM 0x00040000 /* Supervisor User Memory Access */ + +#define SR_FS 0x00006000 /* Floating-point Status */ +#define SR_FS_OFF 0x00000000 +#define SR_FS_INITIAL 0x00002000 +#define SR_FS_CLEAN 0x00004000 +#define SR_FS_DIRTY 0x00006000 + +#define SR_VS 0x00000600 /* Vector Status */ +#define SR_VS_OFF 0x00000000 +#define SR_VS_INITIAL 0x00000200 +#define SR_VS_CLEAN 0x00000400 +#define SR_VS_DIRTY 0x00000600 + +#define SR_XS 0x00018000 /* Extension Status */ +#define SR_XS_OFF 0x00000000 +#define SR_XS_INITIAL 0x00008000 +#define SR_XS_CLEAN 0x00010000 +#define SR_XS_DIRTY 0x00018000 + +#define SR_MPRV 0x00020000 /* M-mode specific */ + +#define SR_FS_VS (SR_FS | SR_VS) /* Vector and Floating-Point Unit */ + +#define SR_SD 0x8000000000000000 /* FS/VS/XS dirty */ + +#define SR_UXL 0x300000000 /* XLEN mask for U-mode */ +#define SR_UXL_32 0x100000000 /* XLEN = 32 for U-mode */ +#define SR_UXL_64 0x200000000 /* XLEN = 64 for U-mode */ +#define SR_SXL 0xc00000000 /* XLEN mask for S-mode */ +#define SR_SXL_32 0x400000000 /* XLEN = 32 for S-mode */ +#define SR_SXL_64 0x800000000 /* XLEN = 64 for S-mode */ + +/* SATP flags */ +#define SATP_PPN 0x00000FFFFFFFFFFF +#define SATP_MODE_39 0x8000000000000000 +#define SATP_MODE_48 0x9000000000000000 +#define SATP_MODE_57 0xa000000000000000 +#define SATP_MODE_SHIFT 60 +#define SATP_ASID_BITS 16 +#define SATP_ASID_SHIFT 44 +#define SATP_ASID_MASK 0xFFFF + +#define MAKE_HGATP(root, mode) ((((root) >> 12) & SATP_PPN) | (mode)) +#define MAKE_SATP(root, mode) ((((root) >> 12) & SATP_PPN) | (mode)) + +/* Exception cause high bit - is an interrupt if set */ +#define CAUSE_IRQ_FLAG (1ul << 63) + +/* Interrupt causes (minus the high bit) */ +#define IRQ_S_SOFT 1 +#define IRQ_VS_SOFT 2 +#define IRQ_M_SOFT 3 +#define IRQ_S_TIMER 5 +#define IRQ_VS_TIMER 6 +#define IRQ_M_TIMER 7 +#define IRQ_S_EXT 9 +#define IRQ_VS_EXT 10 +#define IRQ_M_EXT 11 +#define IRQ_S_GEXT 12 +#define IRQ_PMU_OVF 13 +#define IRQ_LOCAL_MAX (IRQ_PMU_OVF + 1) +//#define IRQ_LOCAL_MASK GENMASK((IRQ_LOCAL_MAX - 1), 0) + +/* Exception causes */ +#define EXC_INST_MISALIGNED 0 +#define EXC_INST_ACCESS 1 +#define EXC_INST_ILLEGAL 2 +#define EXC_BREAKPOINT 3 +#define EXC_LOAD_MISALIGNED 4 +#define EXC_LOAD_ACCESS 5 +#define EXC_STORE_MISALIGNED 6 +#define EXC_STORE_ACCESS 7 +#define EXC_U_VU_SYSCALL 8 +#define EXC_HS_SYSCALL 9 +#define EXC_VS_SYSCALL 10 +#define EXC_M_SYSCALL 11 +#define EXC_INST_PAGE_FAULT 12 +#define EXC_LOAD_PAGE_FAULT 13 +#define EXC_STORE_PAGE_FAULT 15 +#define EXC_INST_GUEST_PAGE_FAULT 20 +#define EXC_LOAD_GUEST_PAGE_FAULT 21 +#define EXC_VIRTUAL_INST_FAULT 22 +#define EXC_STORE_GUEST_PAGE_FAULT 23 + +/* PMP configuration */ +#define PMP_R 0x01 +#define PMP_W 0x02 +#define PMP_X 0x04 +#define PMP_A 0x18 +#define PMP_A_TOR 0x08 +#define PMP_A_NA4 0x10 +#define PMP_A_NAPOT 0x18 +#define PMP_L 0x80 + +/* HSTATUS flags */ +#define HSTATUS_VSXL 0x300000000 +#define HSTATUS_VSXL_SHIFT 32 +#define HSTATUS_VTSR 0x00400000 +#define HSTATUS_VTW 0x00200000 +#define HSTATUS_VTVM 0x00100000 +#define HSTATUS_VGEIN 0x0003f000 +#define HSTATUS_VGEIN_SHIFT 12 +#define HSTATUS_HU 0x00000200 +#define HSTATUS_SPVP 0x00000100 +#define HSTATUS_SPV 0x00000080 +#define HSTATUS_GVA 0x00000040 +#define HSTATUS_VSBE 0x00000020 + +/* HGATP flags */ +#define HGATP_MODE_OFF 0 +#define HGATP_MODE_SV32X4 1 +#define HGATP_MODE_SV39X4 8 +#define HGATP_MODE_SV48X4 9 +#define HGATP_MODE_SV57X4 10 + +#define HGATP32_MODE_SHIFT 31 +#define HGATP32_VMID_SHIFT 22 +#define HGATP32_VMID GENMASK(28, 22) +#define HGATP32_PPN GENMASK(21, 0) + +#define HGATP64_MODE_SHIFT 60 +#define HGATP64_VMID_SHIFT 44 +#define HGATP64_VMID GENMASK(57, 44) +#define HGATP64_PPN GENMASK(43, 0) + +#define HGATP_PAGE_SHIFT 12 + +#define HGATP_PPN HGATP64_PPN +#define HGATP_VMID_SHIFT HGATP64_VMID_SHIFT +#define HGATP_VMID HGATP64_VMID +#define HGATP_MODE_SHIFT HGATP64_MODE_SHIFT + +/* VSIP & HVIP relation */ +#define VSIP_TO_HVIP_SHIFT (IRQ_VS_SOFT - IRQ_S_SOFT) +#define VSIP_VALID_MASK ((1 << IRQ_S_SOFT) | (1 << IRQ_S_TIMER) | (1 << IRQ_S_EXT)) + +/* AIA CSR bits */ +#define TOPI_IID_SHIFT 16 +#define TOPI_IID_MASK GENMASK(11, 0) +#define TOPI_IPRIO_MASK GENMASK(7, 0) +#define TOPI_IPRIO_BITS 8 + +#define TOPEI_ID_SHIFT 16 +#define TOPEI_ID_MASK GENMASK(10, 0) +#define TOPEI_PRIO_MASK GENMASK(10, 0) + +#define ISELECT_IPRIO0 0x30 +#define ISELECT_IPRIO15 0x3f +#define ISELECT_MASK GENMASK(8, 0) + +#define HVICTL_VTI BIT(30) +#define HVICTL_IID GENMASK(27, 16) +#define HVICTL_IID_SHIFT 16 +#define HVICTL_DPR BIT(9) +#define HVICTL_IPRIOM BIT(8) +#define HVICTL_IPRIO GENMASK(7, 0) + +/* xENVCFG flags */ +#define ENVCFG_STCE (1 << 63) +#define ENVCFG_PBMTE (1 << 62) +#define ENVCFG_CBZE (1 << 7) +#define ENVCFG_CBCFE (1 << 6) +#define ENVCFG_CBIE_SHIFT 4 +#define ENVCFG_CBIE (0x3 << ENVCFG_CBIE_SHIFT) +#define ENVCFG_CBIE_ILL 0x0 +#define ENVCFG_CBIE_FLUSH 0x1 +#define ENVCFG_CBIE_INV 0x3 +#define ENVCFG_FIOM 0x1 + +/* symbolic CSR names: */ +#define CSR_CYCLE 0xc00 +#define CSR_TIME 0xc01 +#define CSR_INSTRET 0xc02 +#define CSR_HPMCOUNTER3 0xc03 +#define CSR_HPMCOUNTER4 0xc04 +#define CSR_HPMCOUNTER5 0xc05 +#define CSR_HPMCOUNTER6 0xc06 +#define CSR_HPMCOUNTER7 0xc07 +#define CSR_HPMCOUNTER8 0xc08 +#define CSR_HPMCOUNTER9 0xc09 +#define CSR_HPMCOUNTER10 0xc0a +#define CSR_HPMCOUNTER11 0xc0b +#define CSR_HPMCOUNTER12 0xc0c +#define CSR_HPMCOUNTER13 0xc0d +#define CSR_HPMCOUNTER14 0xc0e +#define CSR_HPMCOUNTER15 0xc0f +#define CSR_HPMCOUNTER16 0xc10 +#define CSR_HPMCOUNTER17 0xc11 +#define CSR_HPMCOUNTER18 0xc12 +#define CSR_HPMCOUNTER19 0xc13 +#define CSR_HPMCOUNTER20 0xc14 +#define CSR_HPMCOUNTER21 0xc15 +#define CSR_HPMCOUNTER22 0xc16 +#define CSR_HPMCOUNTER23 0xc17 +#define CSR_HPMCOUNTER24 0xc18 +#define CSR_HPMCOUNTER25 0xc19 +#define CSR_HPMCOUNTER26 0xc1a +#define CSR_HPMCOUNTER27 0xc1b +#define CSR_HPMCOUNTER28 0xc1c +#define CSR_HPMCOUNTER29 0xc1d +#define CSR_HPMCOUNTER30 0xc1e +#define CSR_HPMCOUNTER31 0xc1f +#define CSR_CYCLEH 0xc80 +#define CSR_TIMEH 0xc81 +#define CSR_INSTRETH 0xc82 +#define CSR_HPMCOUNTER3H 0xc83 +#define CSR_HPMCOUNTER4H 0xc84 +#define CSR_HPMCOUNTER5H 0xc85 +#define CSR_HPMCOUNTER6H 0xc86 +#define CSR_HPMCOUNTER7H 0xc87 +#define CSR_HPMCOUNTER8H 0xc88 +#define CSR_HPMCOUNTER9H 0xc89 +#define CSR_HPMCOUNTER10H 0xc8a +#define CSR_HPMCOUNTER11H 0xc8b +#define CSR_HPMCOUNTER12H 0xc8c +#define CSR_HPMCOUNTER13H 0xc8d +#define CSR_HPMCOUNTER14H 0xc8e +#define CSR_HPMCOUNTER15H 0xc8f +#define CSR_HPMCOUNTER16H 0xc90 +#define CSR_HPMCOUNTER17H 0xc91 +#define CSR_HPMCOUNTER18H 0xc92 +#define CSR_HPMCOUNTER19H 0xc93 +#define CSR_HPMCOUNTER20H 0xc94 +#define CSR_HPMCOUNTER21H 0xc95 +#define CSR_HPMCOUNTER22H 0xc96 +#define CSR_HPMCOUNTER23H 0xc97 +#define CSR_HPMCOUNTER24H 0xc98 +#define CSR_HPMCOUNTER25H 0xc99 +#define CSR_HPMCOUNTER26H 0xc9a +#define CSR_HPMCOUNTER27H 0xc9b +#define CSR_HPMCOUNTER28H 0xc9c +#define CSR_HPMCOUNTER29H 0xc9d +#define CSR_HPMCOUNTER30H 0xc9e +#define CSR_HPMCOUNTER31H 0xc9f + +#define CSR_SSCOUNTOVF 0xda0 + +#define CSR_SSTATUS 0x100 +#define CSR_SIE 0x104 +#define CSR_STVEC 0x105 +#define CSR_SCOUNTEREN 0x106 +#define CSR_SENVCFG 0x10a +#define CSR_SSCRATCH 0x140 +#define CSR_SEPC 0x141 +#define CSR_SCAUSE 0x142 +#define CSR_STVAL 0x143 +#define CSR_SIP 0x144 +#define CSR_SATP 0x180 + +#define CSR_STIMECMP 0x14D +#define CSR_STIMECMPH 0x15D + +/* Supervisor-Level Window to Indirectly Accessed Registers (AIA) */ +#define CSR_SISELECT 0x150 +#define CSR_SIREG 0x151 + +/* Supervisor-Level Interrupts (AIA) */ +#define CSR_STOPEI 0x15c +#define CSR_STOPI 0xdb0 + +/* Supervisor-Level High-Half CSRs (AIA) */ +#define CSR_SIEH 0x114 +#define CSR_SIPH 0x154 + +#define CSR_VSSTATUS 0x200 +#define CSR_VSIE 0x204 +#define CSR_VSTVEC 0x205 +#define CSR_VSSCRATCH 0x240 +#define CSR_VSEPC 0x241 +#define CSR_VSCAUSE 0x242 +#define CSR_VSTVAL 0x243 +#define CSR_VSIP 0x244 +#define CSR_VSATP 0x280 +#define CSR_VSTIMECMP 0x24D +#define CSR_VSTIMECMPH 0x25D + +#define CSR_HSTATUS 0x600 +#define CSR_HEDELEG 0x602 +#define CSR_HIDELEG 0x603 +#define CSR_HIE 0x604 +#define CSR_HTIMEDELTA 0x605 +#define CSR_HCOUNTEREN 0x606 +#define CSR_HGEIE 0x607 +#define CSR_HENVCFG 0x60a +#define CSR_HTIMEDELTAH 0x615 +#define CSR_HENVCFGH 0x61a +#define CSR_HTVAL 0x643 +#define CSR_HIP 0x644 +#define CSR_HVIP 0x645 +#define CSR_HTINST 0x64a +#define CSR_HGATP 0x680 +#define CSR_HGEIP 0xe12 + +/* Virtual Interrupts and Interrupt Priorities (H-extension with AIA) */ +#define CSR_HVIEN 0x608 +#define CSR_HVICTL 0x609 +#define CSR_HVIPRIO1 0x646 +#define CSR_HVIPRIO2 0x647 + +/* VS-Level Window to Indirectly Accessed Registers (H-extension with AIA) */ +#define CSR_VSISELECT 0x250 +#define CSR_VSIREG 0x251 + +/* VS-Level Interrupts (H-extension with AIA) */ +#define CSR_VSTOPEI 0x25c +#define CSR_VSTOPI 0xeb0 + +/* Hypervisor and VS-Level High-Half CSRs (H-extension with AIA) */ +#define CSR_HIDELEGH 0x613 +#define CSR_HVIENH 0x618 +#define CSR_HVIPH 0x655 +#define CSR_HVIPRIO1H 0x656 +#define CSR_HVIPRIO2H 0x657 +#define CSR_VSIEH 0x214 +#define CSR_VSIPH 0x254 + +#define CSR_MSTATUS 0x300 +#define CSR_MISA 0x301 +#define CSR_MEDELEG 0x302 +#define CSR_MIDELEG 0x303 +#define CSR_MIE 0x304 +#define CSR_MTVEC 0x305 +#define CSR_MENVCFG 0x30a +#define CSR_MENVCFGH 0x31a +#define CSR_MSCRATCH 0x340 +#define CSR_MEPC 0x341 +#define CSR_MCAUSE 0x342 +#define CSR_MTVAL 0x343 +#define CSR_MIP 0x344 +#define CSR_MSECCFG 0x747 +#define CSR_PMPCFG0 0x3a0 +#define CSR_PMPCFG1 0x3a1 // RV32 only +#define CSR_PMPCFG2 0x3a2 +#define CSR_PMPCFG3 0x3a3 // RV32 only +#define CSR_PMPCFG4 0x3a4 +#define CSR_PMPCFG5 0x3a5 // RV32 only +#define CSR_PMPCFG6 0x3a6 +#define CSR_PMPCFG7 0x3a7 // RV32 only +#define CSR_PMPCFG8 0x3a8 +#define CSR_PMPCFG9 0x3a9 // RV32 only +#define CSR_PMPCFG10 0x3aa +#define CSR_PMPCFG11 0x3ab // RV32 only +#define CSR_PMPCFG12 0x3ac +#define CSR_PMPCFG13 0x3ad // RV32 only +#define CSR_PMPCFG14 0x3ae +#define CSR_PMPCFG15 0x3af // RV32 only +#define CSR_PMPADDR0 0x3b0 +#define CSR_PMPADDR1 0x3b1 +#define CSR_PMPADDR2 0x3b2 +#define CSR_PMPADDR3 0x3b3 +#define CSR_PMPADDR4 0x3b4 +#define CSR_PMPADDR5 0x3b5 +#define CSR_PMPADDR6 0x3b6 +#define CSR_PMPADDR7 0x3b7 +#define CSR_PMPADDR8 0x3b8 +#define CSR_PMPADDR9 0x3b9 +#define CSR_PMPADDR10 0x3ba +#define CSR_PMPADDR11 0x3bb +#define CSR_PMPADDR12 0x3bc +#define CSR_PMPADDR13 0x3bd +#define CSR_PMPADDR14 0x3be +#define CSR_PMPADDR15 0x3bf +#define CSR_PMPADDR16 0x3c0 +#define CSR_PMPADDR17 0x3c1 +#define CSR_PMPADDR18 0x3c2 +#define CSR_PMPADDR19 0x3c3 +#define CSR_PMPADDR20 0x3c4 +#define CSR_PMPADDR21 0x3c5 +#define CSR_PMPADDR22 0x3c6 +#define CSR_PMPADDR23 0x3c7 +#define CSR_PMPADDR24 0x3c8 +#define CSR_PMPADDR25 0x3c9 +#define CSR_PMPADDR26 0x3ca +#define CSR_PMPADDR27 0x3cb +#define CSR_PMPADDR28 0x3cc +#define CSR_PMPADDR29 0x3cd +#define CSR_PMPADDR30 0x3ce +#define CSR_PMPADDR31 0x3cf +#define CSR_PMPADDR32 0x3d0 +#define CSR_PMPADDR33 0x3d1 +#define CSR_PMPADDR34 0x3d2 +#define CSR_PMPADDR35 0x3d3 +#define CSR_PMPADDR36 0x3d4 +#define CSR_PMPADDR37 0x3d5 +#define CSR_PMPADDR38 0x3d6 +#define CSR_PMPADDR39 0x3d7 +#define CSR_PMPADDR40 0x3d8 +#define CSR_PMPADDR41 0x3d9 +#define CSR_PMPADDR42 0x3da +#define CSR_PMPADDR43 0x3db +#define CSR_PMPADDR44 0x3dc +#define CSR_PMPADDR45 0x3dd +#define CSR_PMPADDR46 0x3de +#define CSR_PMPADDR47 0x3df +#define CSR_PMPADDR48 0x3e0 +#define CSR_PMPADDR49 0x3e1 +#define CSR_PMPADDR50 0x3e2 +#define CSR_PMPADDR51 0x3e3 +#define CSR_PMPADDR52 0x3e4 +#define CSR_PMPADDR53 0x3e5 +#define CSR_PMPADDR54 0x3e6 +#define CSR_PMPADDR55 0x3e7 +#define CSR_PMPADDR56 0x3e8 +#define CSR_PMPADDR57 0x3e9 +#define CSR_PMPADDR58 0x3ea +#define CSR_PMPADDR59 0x3eb +#define CSR_PMPADDR60 0x3ec +#define CSR_PMPADDR61 0x3ed +#define CSR_PMPADDR62 0x3ee +#define CSR_PMPADDR63 0x3ef +#define CSR_MVENDORID 0xf11 +#define CSR_MARCHID 0xf12 +#define CSR_MIMPID 0xf13 +#define CSR_MHARTID 0xf14 + +/* Machine-Level Window to Indirectly Accessed Registers (AIA) */ +#define CSR_MISELECT 0x350 +#define CSR_MIREG 0x351 + +/* Machine-Level Interrupts (AIA) */ +#define CSR_MTOPEI 0x35c +#define CSR_MTOPI 0xfb0 + +/* Virtual Interrupts for Supervisor Level (AIA) */ +#define CSR_MVIEN 0x308 +#define CSR_MVIP 0x309 + +/* Machine-Level High-Half CSRs (AIA) */ +#define CSR_MIDELEGH 0x313 +#define CSR_MIEH 0x314 +#define CSR_MVIENH 0x318 +#define CSR_MVIPH 0x319 +#define CSR_MIPH 0x354 + +#define CSR_VSTART 0x8 +#define CSR_VCSR 0xf +#define CSR_VL 0xc20 +#define CSR_VTYPE 0xc21 +#define CSR_VLENB 0xc22 + +#define RV_IRQ_PMU IRQ_PMU_OVF +#define SIP_LCOFIP (0x1 << IRQ_PMU_OVF) + +/* IE/IP (Supervisor/Machine Interrupt Enable/Pending) flags */ +#define IE_M_SIE (0x1 << IRQ_M_SOFT) +#define IE_M_TIE (0x1 << IRQ_M_TIMER) +#define IE_M_EIE (0x1 << IRQ_M_EXT) +#define IE_S_SIE (0x1 << IRQ_S_SOFT) +#define IE_S_TIE (0x1 << IRQ_S_TIMER) +#define IE_S_EIE (0x1 << IRQ_S_EXT) + +#define bit(obj) (1ul << (obj)) + +#define csr_swap(csr, val) \ + ({ \ + unsigned long __v = (unsigned long)(val); \ + __asm__ __volatile__("csrrw %0, " __ASM_STR(csr) ", %1" : "=r"(__v) : "rK"(__v) : "memory"); \ + __v; \ + }) + +#define csr_read(csr) \ + ({ \ + register unsigned long __v; \ + __asm__ __volatile__("csrr %0, " __ASM_STR(csr) : "=r"(__v) : : "memory"); \ + __v; \ + }) + +#define csr_write(csr, val) \ + ({ \ + unsigned long __v = (unsigned long)(val); \ + __asm__ __volatile__("csrw " __ASM_STR(csr) ", %0" : : "rK"(__v) : "memory"); \ + }) + +#define csr_read_set(csr, val) \ + ({ \ + unsigned long __v = (unsigned long)(val); \ + __asm__ __volatile__("csrrs %0, " __ASM_STR(csr) ", %1" : "=r"(__v) : "rK"(__v) : "memory"); \ + __v; \ + }) + +#define csr_set(csr, val) \ + ({ \ + unsigned long __v = (unsigned long)(val); \ + __asm__ __volatile__("csrs " __ASM_STR(csr) ", %0" : : "rK"(__v) : "memory"); \ + }) + +#define csr_read_clear(csr, val) \ + ({ \ + unsigned long __v = (unsigned long)(val); \ + __asm__ __volatile__("csrrc %0, " __ASM_STR(csr) ", %1" : "=r"(__v) : "rK"(__v) : "memory"); \ + __v; \ + }) + +#define csr_clear(csr, val) \ + ({ \ + unsigned long __v = (unsigned long)(val); \ + __asm__ __volatile__("csrc " __ASM_STR(csr) ", %0" : : "rK"(__v) : "memory"); \ + }) + +#endif //HMODE_CSR_H diff --git a/verif/tests/custom/rvh/main.h b/verif/tests/custom/rvh/main.h new file mode 100644 index 0000000000..78a82c4841 --- /dev/null +++ b/verif/tests/custom/rvh/main.h @@ -0,0 +1,6 @@ +#ifndef HMODE_MAIN_H +#define HMODE_MAIN_H + +void test_routine(void); + +#endif //HMODE_MAIN_H \ No newline at end of file diff --git a/verif/tests/custom/rvh/main.ld b/verif/tests/custom/rvh/main.ld new file mode 100644 index 0000000000..ce99294e70 --- /dev/null +++ b/verif/tests/custom/rvh/main.ld @@ -0,0 +1,53 @@ +OUTPUT_ARCH( "riscv" ) +ENTRY(_start) + +SECTIONS +{ + . = 0x80000000; + _start_text = .; + .text.init : { *(.text.init) } + . = ALIGN(0x1000); + .tohost : { *(.tohost) } + . = ALIGN(0x1000); + .uvmif : { *(.uvmif) } + . = ALIGN(0x1000); + .text : { *(.text) } + . = ALIGN(0x1000); + .text.startup : { *(.text.startup) } + . = ALIGN(0x1000); + _end_text = .; + . = ALIGN(0x1000); + .rodata : { *(.rodata*)} + . = ALIGN(0x8); + . = ALIGN(0x1000); + .page_table : { *(.page_table) } + .user_stack : { *(.user_stack) } + .kernel_data : { *(.kernel_data) } + .kernel_stack : { *(.kernel_stack) } + .data : { *(.data) } + .sdata : { + __global_pointer$ = . + 0x800; + *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) *(.srodata*) + *(.sdata .sdata.* .gnu.linkonce.s.*) + } + .bss : { *(.bss) } + .tdata : + { + _tdata_begin = .; + *(.tdata) + _tdata_end = .; + } + .tbss : + { + *(.tbss) + _tbss_end = .; + } + .trampoline ALIGN(4096) : { + trampoline_start = .; + *(.trampoline) + trampoline_end = .; + } + _end = .; +} + +ASSERT(trampoline_end - trampoline_start <= 4096, "Section .trampoline is larger than a page !"); diff --git a/verif/tests/custom/rvh/main_hgatp.c b/verif/tests/custom/rvh/main_hgatp.c new file mode 100644 index 0000000000..4d3dcb2c0b --- /dev/null +++ b/verif/tests/custom/rvh/main_hgatp.c @@ -0,0 +1,23 @@ +#include + +#include "main.h" +#include "tests.h" +#include "utils.h" +#include "mmode.h" +#include "vm.h" + +int main(int argc, char* arg[]) { + firmware_init(); + test_routine(); +} + +void test_routine(void) { + build_page_tables(); + printf("\n============\n"); + reset(); + test_hgatp_simple(); + reset(); + test_hgatp_full(); + reset(); + printf("============\n"); +} diff --git a/verif/tests/custom/rvh/main_satp.c b/verif/tests/custom/rvh/main_satp.c new file mode 100644 index 0000000000..72e58584fe --- /dev/null +++ b/verif/tests/custom/rvh/main_satp.c @@ -0,0 +1,20 @@ +#include + +#include "main.h" +#include "tests.h" +#include "utils.h" +#include "mmode.h" +#include "vm.h" + +int main(int argc, char* arg[]) { + firmware_init(); + test_routine(); +} + +void test_routine(void) { + build_page_tables(); + printf("\n============\n"); + reset(); + test_satp(); + printf("============\n"); +} diff --git a/verif/tests/custom/rvh/main_timer.c b/verif/tests/custom/rvh/main_timer.c new file mode 100644 index 0000000000..c324ae680e --- /dev/null +++ b/verif/tests/custom/rvh/main_timer.c @@ -0,0 +1,21 @@ +#include + +#include "main.h" +#include "tests.h" +#include "utils.h" +#include "mmode.h" +#include "vm.h" + +int main(int argc, char* arg[]) { + firmware_init(); + test_routine(); +} + +void test_routine(void) { + build_page_tables(); + printf("\n============\n"); + reset(); + test_timer(); + reset(); + printf("============\n"); +} diff --git a/verif/tests/custom/rvh/main_u.c b/verif/tests/custom/rvh/main_u.c new file mode 100644 index 0000000000..adcbc5eae5 --- /dev/null +++ b/verif/tests/custom/rvh/main_u.c @@ -0,0 +1,21 @@ +#include + +#include "main.h" +#include "tests.h" +#include "utils.h" +#include "mmode.h" +#include "vm.h" + +int main(int argc, char* arg[]) { + firmware_init(); + test_routine(); +} + +void test_routine(void) { + build_page_tables(); + printf("\n============\n"); + reset(); + test_u_mode(); + reset(); + printf("============\n"); +} diff --git a/verif/tests/custom/rvh/main_vs.c b/verif/tests/custom/rvh/main_vs.c new file mode 100644 index 0000000000..ba9691b8ea --- /dev/null +++ b/verif/tests/custom/rvh/main_vs.c @@ -0,0 +1,29 @@ +#include + +#include "main.h" +#include "tests.h" +#include "utils.h" +#include "mmode.h" +#include "vm.h" + +int main(int argc, char* arg[]) { + firmware_init(); + test_routine(); +} + +void test_routine(void) { + build_page_tables(); + printf("\n============\n"); + reset(); + test_vs_mode_1(); + reset(); + test_vs_mode_2(); + reset(); + test_vs_mode_3(); + reset(); + test_vs_mode_4(); + reset(); + test_vs_mode_5(); + reset(); + printf("============\n"); +} diff --git a/verif/tests/custom/rvh/main_vsatp.c b/verif/tests/custom/rvh/main_vsatp.c new file mode 100644 index 0000000000..324ebcf743 --- /dev/null +++ b/verif/tests/custom/rvh/main_vsatp.c @@ -0,0 +1,23 @@ +#include + +#include "main.h" +#include "tests.h" +#include "utils.h" +#include "mmode.h" +#include "vm.h" + +int main(int argc, char* arg[]) { + firmware_init(); + test_routine(); +} + +void test_routine(void) { + build_page_tables(); + printf("\n============\n"); + reset(); + test_vsatp_simple(); + reset(); + test_vsatp_full(); + reset(); + printf("============\n"); +} diff --git a/verif/tests/custom/rvh/mmode.c b/verif/tests/custom/rvh/mmode.c new file mode 100644 index 0000000000..e784d6591a --- /dev/null +++ b/verif/tests/custom/rvh/mmode.c @@ -0,0 +1,42 @@ +#include + +#include "mmode.h" +#include "call.h" +#include "csr.h" +#include "trampoline.h" +#include "sbi.h" +#include "utils.h" + +__thread struct trap_context* __tc; + +__attribute__((naked, aligned(4))) void mvec(void) { + SAVE_CONTEXT(); + printf("M-mode : panicked (mcause = 0x%lx mepc = 0x%lx mstatus = 0x%lx)\n", csr_read(CSR_MCAUSE), csr_read(CSR_MEPC), csr_read(CSR_MSTATUS)); + panic(); +} + +void firmware_init(void) { + csr_write(CSR_MTVEC, (uint64_t)&mvec); + csr_clear(CSR_MSTATUS, SR_MPP | SR_FS | SR_VS | SR_XS | SR_MPRV); + csr_set(CSR_MSTATUS, SR_MPP_S | SR_FS_INITIAL | SR_VS_INITIAL | SR_XS_INITIAL); // SR_SXL_64 SR_UXL_64 SR_MPRV + csr_write(CSR_MENVCFG, ENVCFG_CBIE | ENVCFG_CBCFE | ENVCFG_CBZE); + csr_write(CSR_MIE, 0ul); + csr_write(CSR_PMPADDR0, ~0ul); + csr_write(CSR_MEDELEG, + bit(EXC_INST_MISALIGNED) | bit(EXC_INST_ACCESS) | bit(EXC_INST_ILLEGAL) | bit(EXC_BREAKPOINT) | bit(EXC_LOAD_MISALIGNED) | bit(EXC_LOAD_ACCESS) | + bit(EXC_STORE_MISALIGNED) | bit(EXC_STORE_ACCESS) | bit(EXC_U_VU_SYSCALL) | bit(EXC_VS_SYSCALL) | bit(EXC_INST_PAGE_FAULT) | + bit(EXC_LOAD_PAGE_FAULT) | bit(EXC_STORE_PAGE_FAULT) | bit(EXC_INST_GUEST_PAGE_FAULT) | bit(EXC_LOAD_GUEST_PAGE_FAULT) | + bit(EXC_VIRTUAL_INST_FAULT) | bit(EXC_STORE_GUEST_PAGE_FAULT)); + csr_write(CSR_MIDELEG, bit(IRQ_S_SOFT) | bit(IRQ_S_TIMER) | bit(IRQ_S_EXT) | bit(IRQ_S_GEXT) | bit(IRQ_VS_SOFT) | bit(IRQ_VS_TIMER) | bit(IRQ_VS_EXT)); + csr_write(CSR_PMPCFG0, PMP_R | PMP_W | PMP_X | PMP_A_NAPOT); + reset(); + + csr_set(CSR_MIP, bit(IRQ_S_TIMER)); // so the timer interrupt test passes + + asm volatile("la a6, 1f\n" + "csrw mepc, a6" :: + : "memory", "a6"); + + asm volatile("mret\n" + "1:\n"); +} diff --git a/verif/tests/custom/rvh/mmode.h b/verif/tests/custom/rvh/mmode.h new file mode 100644 index 0000000000..df732df141 --- /dev/null +++ b/verif/tests/custom/rvh/mmode.h @@ -0,0 +1,6 @@ +#ifndef HMODE_MMODE_H +#define HMODE_MMODE_H + +void firmware_init(void); + +#endif //HMODE_MMODE_H diff --git a/verif/tests/custom/rvh/page_table.s b/verif/tests/custom/rvh/page_table.s new file mode 100644 index 0000000000..305e396dae --- /dev/null +++ b/verif/tests/custom/rvh/page_table.s @@ -0,0 +1,41 @@ +.section .page_table, "aw", @progbits + +.balign 4096 + +.globl __satp_lvl3 +__satp_lvl3: +.type __satp_lvl3, @object +.size __satp_lvl3, 4096 +.space 4096 + +.globl __satp_lvl2 +__satp_lvl2: +.type __satp_lvl2, @object +.size __satp_lvl2, 4096 +.space 4096 + +.globl __satp_lvl1 +__satp_lvl1: +.type __satp_lvl1, @object +.size __satp_lvl1, 4096 +.space 4096 + +.balign 4096*4 + +.globl __hgatp_lvl3 +__hgatp_lvl3: +.type __hgatp_lvl3, @object +.size __hgatp_lvl3, 4096*4 +.space 4096*4 + +.globl __hgatp_lvl2 +__hgatp_lvl2: +.type __hgatp_lvl2, @object +.size __hgatp_lvl2, 4096 +.space 4096 + +.globl __hgatp_lvl1 +__hgatp_lvl1: +.type __hgatp_lvl1, @object +.size __hgatp_lvl1, 4096 +.space 4096 diff --git a/verif/tests/custom/rvh/sbi.h b/verif/tests/custom/rvh/sbi.h new file mode 100644 index 0000000000..efad3c616e --- /dev/null +++ b/verif/tests/custom/rvh/sbi.h @@ -0,0 +1,222 @@ +#ifndef HMODE_SBI_H +#define HMODE_SBI_H + +#include "types.h" + +// Content from this file is mostly stolen from Linux (arch/riscv/include/asm/sbi.h) + +struct sbiret { + long error; + long value; +}; + +enum sbi_ext_id { + SBI_EXT_BASE = 0x10, + SBI_EXT_TIME = 0x54494D45, + SBI_EXT_IPI = 0x735049, + SBI_EXT_RFENCE = 0x52464E43, + SBI_EXT_HSM = 0x48534D, + SBI_EXT_SRST = 0x53525354, + SBI_EXT_PMU = 0x504D55, + SBI_EXT_DBCN = 0x4442434E +}; + +enum sbi_ext_base_fid { + SBI_EXT_BASE_GET_SPEC_VERSION = 0, + SBI_EXT_BASE_GET_IMP_ID, + SBI_EXT_BASE_GET_IMP_VERSION, + SBI_EXT_BASE_PROBE_EXT, + SBI_EXT_BASE_GET_MVENDORID, + SBI_EXT_BASE_GET_MARCHID, + SBI_EXT_BASE_GET_MIMPID, +}; + +enum sbi_ext_time_fid { + SBI_EXT_TIME_SET_TIMER = 0, +}; + +enum sbi_ext_ipi_fid { + SBI_EXT_IPI_SEND_IPI = 0, +}; + +enum sbi_ext_rfence_fid { + SBI_EXT_RFENCE_REMOTE_FENCE_I = 0, + SBI_EXT_RFENCE_REMOTE_SFENCE_VMA, + SBI_EXT_RFENCE_REMOTE_SFENCE_VMA_ASID, + SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA_VMID, + SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA, + SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA_ASID, + SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA, +}; + +enum sbi_ext_hsm_fid { + SBI_EXT_HSM_HART_START = 0, + SBI_EXT_HSM_HART_STOP, + SBI_EXT_HSM_HART_STATUS, + SBI_EXT_HSM_HART_SUSPEND, +}; + +enum sbi_hsm_hart_state { + SBI_HSM_STATE_STARTED = 0, + SBI_HSM_STATE_STOPPED, + SBI_HSM_STATE_START_PENDING, + SBI_HSM_STATE_STOP_PENDING, + SBI_HSM_STATE_SUSPENDED, + SBI_HSM_STATE_SUSPEND_PENDING, + SBI_HSM_STATE_RESUME_PENDING, +}; + +enum sbi_ext_srst_fid { + SBI_EXT_SRST_RESET = 0, +}; + +enum sbi_srst_reset_type { + SBI_SRST_RESET_TYPE_SHUTDOWN = 0, + SBI_SRST_RESET_TYPE_COLD_REBOOT, + SBI_SRST_RESET_TYPE_WARM_REBOOT, +}; + +enum sbi_srst_reset_reason { + SBI_SRST_RESET_REASON_NONE = 0, + SBI_SRST_RESET_REASON_SYS_FAILURE, +}; + +enum sbi_ext_pmu_fid { + SBI_EXT_PMU_NUM_COUNTERS = 0, + SBI_EXT_PMU_COUNTER_GET_INFO, + SBI_EXT_PMU_COUNTER_CFG_MATCH, + SBI_EXT_PMU_COUNTER_START, + SBI_EXT_PMU_COUNTER_STOP, + SBI_EXT_PMU_COUNTER_FW_READ, +}; + +/** General pmu event codes specified in SBI PMU extension */ +enum sbi_pmu_hw_generic_events_t { + SBI_PMU_HW_NO_EVENT = 0, + SBI_PMU_HW_CPU_CYCLES = 1, + SBI_PMU_HW_INSTRUCTIONS = 2, + SBI_PMU_HW_CACHE_REFERENCES = 3, + SBI_PMU_HW_CACHE_MISSES = 4, + SBI_PMU_HW_BRANCH_INSTRUCTIONS = 5, + SBI_PMU_HW_BRANCH_MISSES = 6, + SBI_PMU_HW_BUS_CYCLES = 7, + SBI_PMU_HW_STALLED_CYCLES_FRONTEND = 8, + SBI_PMU_HW_STALLED_CYCLES_BACKEND = 9, + SBI_PMU_HW_REF_CPU_CYCLES = 10, + + SBI_PMU_HW_GENERAL_MAX, +}; + +/** + * Special "firmware" events provided by the firmware, even if the hardware + * does not support performance events. These events are encoded as a raw + * event type in Linux kernel perf framework. + */ +enum sbi_pmu_fw_generic_events_t { + SBI_PMU_FW_MISALIGNED_LOAD = 0, + SBI_PMU_FW_MISALIGNED_STORE = 1, + SBI_PMU_FW_ACCESS_LOAD = 2, + SBI_PMU_FW_ACCESS_STORE = 3, + SBI_PMU_FW_ILLEGAL_INSN = 4, + SBI_PMU_FW_SET_TIMER = 5, + SBI_PMU_FW_IPI_SENT = 6, + SBI_PMU_FW_IPI_RCVD = 7, + SBI_PMU_FW_FENCE_I_SENT = 8, + SBI_PMU_FW_FENCE_I_RCVD = 9, + SBI_PMU_FW_SFENCE_VMA_SENT = 10, + SBI_PMU_FW_SFENCE_VMA_RCVD = 11, + SBI_PMU_FW_SFENCE_VMA_ASID_SENT = 12, + SBI_PMU_FW_SFENCE_VMA_ASID_RCVD = 13, + + SBI_PMU_FW_HFENCE_GVMA_SENT = 14, + SBI_PMU_FW_HFENCE_GVMA_RCVD = 15, + SBI_PMU_FW_HFENCE_GVMA_VMID_SENT = 16, + SBI_PMU_FW_HFENCE_GVMA_VMID_RCVD = 17, + + SBI_PMU_FW_HFENCE_VVMA_SENT = 18, + SBI_PMU_FW_HFENCE_VVMA_RCVD = 19, + SBI_PMU_FW_HFENCE_VVMA_ASID_SENT = 20, + SBI_PMU_FW_HFENCE_VVMA_ASID_RCVD = 21, + SBI_PMU_FW_MAX, +}; + +/* SBI PMU event types */ +enum sbi_pmu_event_type { + SBI_PMU_EVENT_TYPE_HW = 0x0, + SBI_PMU_EVENT_TYPE_CACHE = 0x1, + SBI_PMU_EVENT_TYPE_RAW = 0x2, + SBI_PMU_EVENT_TYPE_FW = 0xf, +}; + +/* SBI PMU event types */ +enum sbi_pmu_ctr_type { + SBI_PMU_CTR_TYPE_HW = 0x0, + SBI_PMU_CTR_TYPE_FW, +}; + +enum sbi_ext_dbcn { + SBI_EXT_DBCN_CONSOLE_WRITE = 0x0, + SBI_EXT_DBCN_CONSOLE_READ = 0x1, + SBI_EXT_DBCN_CONSOLE_WRITE_BYTE = 0x2 + +}; + +static inline struct sbiret sbi_ecall(enum sbi_ext_id ext, int fid, uint64_t arg0, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5) { + struct sbiret ret; + register uint64_t a0 asm("a0") = (uint64_t)(arg0); + register uint64_t a1 asm("a1") = (uint64_t)(arg1); + register uint64_t a2 asm("a2") = (uint64_t)(arg2); + register uint64_t a3 asm("a3") = (uint64_t)(arg3); + register uint64_t a4 asm("a4") = (uint64_t)(arg4); + register uint64_t a5 asm("a5") = (uint64_t)(arg5); + register uint64_t a6 asm("a6") = (uint64_t)(fid); + register uint64_t a7 asm("a7") = (uint64_t)(ext); + asm volatile("ecall" : "+r"(a0), "+r"(a1) : "r"(a2), "r"(a3), "r"(a4), "r"(a5), "r"(a6), "r"(a7) : "memory"); + register uint64_t sp asm("sp"); + ret.error = (long)a0; + ret.value = (long)a1; + return ret; +} + +// PMU extension +static inline long sbi_pmu_num_counters(void) { + struct sbiret r = sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_NUM_COUNTERS, 0, 0, 0, 0, 0, 0); + return r.value; +} +static inline struct sbiret sbi_pmu_counter_get_info(struct sbiret *ret, uint64_t counter_idx) { + return sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_GET_INFO, counter_idx, 0, 0, 0, 0, 0); +} +static inline struct sbiret sbi_pmu_counter_config_matching(struct sbiret *ret, uint64_t counter_idx_base, uint64_t counter_idx_mask, uint64_t config_flags, uint64_t event_idx, uint64_t event_data) { + return sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_CFG_MATCH, counter_idx_base, counter_idx_mask, config_flags, event_idx, event_data, 0); +} +static inline struct sbiret sbi_pmu_counter_start(struct sbiret *ret, uint64_t counter_idx_base, uint64_t counter_idx_mask, uint64_t start_flags, uint64_t initial_value) { + return sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_START, counter_idx_base, counter_idx_mask, start_flags, initial_value, 0, 0); +} +static inline struct sbiret sbi_pmu_counter_stop(struct sbiret *ret, uint64_t counter_idx_base, uint64_t counter_idx_mask, uint64_t stop_flags) { + return sbi_ecall(SBI_EXT_PMU, SBI_EXT_PMU_COUNTER_STOP, counter_idx_base, counter_idx_mask, stop_flags, 0, 0, 0); +} + +// RFENCE extension +static inline struct sbiret sbi_remote_fence_i(struct sbiret *ret, uint64_t mask, uint64_t base) { + return sbi_ecall(SBI_EXT_RFENCE, SBI_EXT_RFENCE_REMOTE_FENCE_I, mask, base, 0, 0, 0, 0); +} +static inline struct sbiret sbi_remote_sfence_vma(struct sbiret *ret, uint64_t mask, uint64_t base, uint64_t start_addr, uint64_t size) { + return sbi_ecall(SBI_EXT_RFENCE, SBI_EXT_RFENCE_REMOTE_SFENCE_VMA, mask, base, start_addr, size, 0, 0); +} +static inline struct sbiret sbi_remote_sfence_vma_asid(struct sbiret *ret, uint64_t mask, uint64_t base, uint64_t start_addr, uint64_t size, uint64_t asid) { + return sbi_ecall(SBI_EXT_RFENCE, SBI_EXT_RFENCE_REMOTE_SFENCE_VMA_ASID, mask, base, start_addr, size, asid, 0); +} +static inline struct sbiret sbi_remote_hfence_gvma(struct sbiret *ret, uint64_t mask, uint64_t base, uint64_t start_addr, uint64_t size) { + return sbi_ecall(SBI_EXT_RFENCE, SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA, mask, base, start_addr, size, 0, 0); +} +static inline struct sbiret sbi_remote_hfence_gvma_vmid(struct sbiret *ret, uint64_t mask, uint64_t base, uint64_t start_addr, uint64_t size, uint64_t vmid) { + return sbi_ecall(SBI_EXT_RFENCE, SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA_VMID, mask, base, start_addr, size, vmid, 0); +} +static inline struct sbiret sbi_remote_hfence_vvma(struct sbiret *ret, uint64_t mask, uint64_t base, uint64_t start_addr, uint64_t size) { + return sbi_ecall(SBI_EXT_RFENCE, SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA, mask, base, start_addr, size, 0, 0); +} +static inline struct sbiret sbi_remote_hfence_vvma_asid(struct sbiret *ret, uint64_t mask, uint64_t base, uint64_t start_addr, uint64_t size, uint64_t asid) { + return sbi_ecall(SBI_EXT_RFENCE, SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA_ASID, mask, base, start_addr, size, asid, 0); +} + +#endif // HMODE_SBI_H diff --git a/verif/tests/custom/rvh/tests.c b/verif/tests/custom/rvh/tests.c new file mode 100644 index 0000000000..32d28e5317 --- /dev/null +++ b/verif/tests/custom/rvh/tests.c @@ -0,0 +1,1058 @@ +#include + +#include "call.h" +#include "csr.h" +#include "sbi.h" +#include "tests.h" +#include "trampoline.h" +#include "types.h" +#include "utils.h" +#include "vm.h" + +void notice_success(void) { + printf("Succeeded\n"); +} + +void notice_failure(void) { + printf("Failed\n"); +} + +void notice_failure_detailed(char *msg) { + printf("Failed (%s)\n", msg); +} + +void test_timer(void) { + printf("Starting test: HS-mode (timer interrupt)\n"); + + printf("\t- Run: "); + asm volatile goto("la a6, 1f\n" + "csrw stvec, a6" :: + : "memory", "a6" + : entry_point); + csr_set(CSR_SIE, IE_S_TIE); // enable timer interrupts + csr_set(CSR_SSTATUS, SR_SIE); // enable interrupts (general switch) + asm volatile("wfi"); + notice_failure(); + goto end; + +entry_point: + asm volatile(".align 4\n1:"); + uint64_t scause = csr_read(CSR_SCAUSE); + if (scause & CAUSE_IRQ_FLAG) { + switch (scause & ~CAUSE_IRQ_FLAG) { + case IRQ_S_TIMER: { // optionally you can give a second chance in case another interrupt is caught + notice_success(); + goto end; + } + } + } + notice_failure_detailed("wrong signal received"); + +end: + return; +} + +void test_u_mode(void) { + printf("Starting test: U-mode (ecall) \n"); + + printf("\t- Run: "); + asm volatile goto("la a6, 1f\n" + "csrw stvec, a6" :: + : "memory", "a6" + : smode_entry_point); + csr_write(CSR_SEPC, &&umode_entry_point); + csr_clear(CSR_SSTATUS, SR_SPP | SR_SPIE); // u-mode without interrupts + asm volatile("sret"); + +umode_entry_point: + // random ecall + asm volatile goto("ecall" : : : "memory", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7" : smode_entry_point); + + notice_failure_detailed("ecall returned"); + goto end; + +smode_entry_point: + asm volatile(".align 4\n1:"); + uint64_t scause = csr_read(CSR_SCAUSE); + if (!(scause & CAUSE_IRQ_FLAG)) { + switch (scause) { + case EXC_U_VU_SYSCALL: { + if (!(csr_read(CSR_SSTATUS) & SR_SPP)) { + notice_success(); + goto end; + } + } + } + } + notice_failure_detailed("wrong signal received"); + +end: + return; +} + +void test_vs_mode_1(void) { + printf("Starting test: VS mode (ecall) - no G/S translation\n"); + + printf("\t- Run: "); + asm volatile goto("la a6, 1f\n" + "csrw stvec, a6" :: + : "memory", "a6" + : host_entry_point); + csr_set(CSR_VSSTATUS, csr_read(CSR_SSTATUS)); + csr_write(CSR_VSTVEC, &panic); // VS interrupt vector + csr_write(CSR_VSATP, 0ul); // vs page table + csr_write(CSR_VSIE, 0ul); // interrupts vs control + csr_write(CSR_HIE, 0ul); // interrupts h control + csr_write(CSR_HIDELEG, 0ul); // hypervisor interrupt delegation + csr_write(CSR_HEDELEG, 0ul); // hypervisor exception delegation + csr_write(CSR_HCOUNTEREN, 0ul); // hypervisor counter controls + csr_write(CSR_HENVCFG, ENVCFG_CBIE | ENVCFG_CBCFE | ENVCFG_CBZE); // hypervisor environment controls + csr_write(CSR_HGATP, 0ul); // nested page table + flush_htlb(); + + csr_write(CSR_SEPC, &&guest_entry_point); + csr_write(CSR_HSTATUS, HSTATUS_SPV | HSTATUS_SPVP); // hypervisor status configuration + csr_set(CSR_SSTATUS, SR_SPP); // making sure the guest will start in VS mode + + asm volatile("sret"); + +guest_entry_point: + register uint64_t a7 asm("a7") = SBI_EXT_BASE; + register uint64_t a6 asm("a6") = SBI_EXT_BASE_GET_SPEC_VERSION; + asm volatile goto("ecall" : : "r"(a6), "r"(a7) : "memory", "a0", "a1", "a2", "a3", "a4", "a5" : host_entry_point); + + notice_failure_detailed("ecall returned"); + goto end; + +host_entry_point: + asm volatile(".align 4\n1:"); + uint64_t scause = csr_read(CSR_SCAUSE); + if (!(scause & CAUSE_IRQ_FLAG)) { + switch (scause) { + case EXC_VS_SYSCALL: { + uint64_t hstatus = csr_read(CSR_HSTATUS); + if ((hstatus & (HSTATUS_SPV | HSTATUS_SPVP)) == (HSTATUS_SPV | HSTATUS_SPVP)) { + notice_success(); + } else { + notice_failure(); // wrong hstatus + } + goto end; + } + } + } + notice_failure_detailed("wrong signal received"); + +end: + return; +} + +void test_vs_mode_2(void) { + printf("Starting test: VS mode (ecall) - only G translation (no U flag)\n"); + + printf("\t- Run: "); + asm volatile goto("la a6, 1f\n" + "csrw stvec, a6" :: + : "memory", "a6" + : host_entry_point); + csr_set(CSR_VSSTATUS, csr_read(CSR_SSTATUS)); + csr_write(CSR_VSTVEC, &panic); // VS interrupt vector + csr_write(CSR_VSATP, 0ul); // vs page table + csr_write(CSR_VSIE, 0ul); // interrupts vs control + csr_write(CSR_HIE, 0ul); // interrupts h control + csr_write(CSR_HIDELEG, 0ul); // hypervisor interrupt delegation + csr_write(CSR_HEDELEG, 0ul); // hypervisor exception delegation + csr_write(CSR_HCOUNTEREN, 0ul); // hypervisor counter controls + csr_write(CSR_HENVCFG, ENVCFG_CBIE | ENVCFG_CBCFE | ENVCFG_CBZE); // hypervisor environment controls + csr_write(CSR_HGATP, MAKE_SATP(SATP_ROOT, SATP_MODE_39)); // nested page table + flush_htlb(); + + csr_write(CSR_SEPC, &&guest_entry_point); + csr_set(CSR_SSTATUS, SR_SPP); // making sure the guest will start in VS mode + csr_write(CSR_HSTATUS, HSTATUS_SPV | HSTATUS_SPVP); // hypervisor status configuration + + asm volatile("sret"); + +guest_entry_point: + register uint64_t a7 asm("a7") = SBI_EXT_BASE; + register uint64_t a6 asm("a6") = SBI_EXT_BASE_GET_SPEC_VERSION; + asm volatile goto("ecall" : : "r"(a6), "r"(a7) : "memory", "a0", "a1", "a2", "a3", "a4", "a5" : host_entry_point); + + notice_failure_detailed("ecall returned"); + goto end; + +host_entry_point: + asm volatile(".align 4\n1:"); + uint64_t scause = csr_read(CSR_SCAUSE); + if (!(scause & CAUSE_IRQ_FLAG)) { + switch (scause) { + case EXC_VS_SYSCALL: { + notice_failure(); // wrong hstatus + goto end; + } + case EXC_INST_GUEST_PAGE_FAULT: { + uint64_t hstatus = csr_read(CSR_HSTATUS); + if ((hstatus & (HSTATUS_SPV | HSTATUS_SPVP)) == (HSTATUS_SPV | HSTATUS_SPVP)) { + notice_success(); + } else { + notice_failure(); // wrong hstatus + } + goto end; + } + } + } + notice_failure_detailed("wrong signal received"); + +end: + return; +} + +void test_vs_mode_3(void) { + printf("Starting test: VS mode (ecall) - only G translation (valid)\n"); + + printf("\t- Run: "); + asm volatile goto("la a6, 1f\n" + "csrw stvec, a6" :: + : "memory", "a6" + : host_entry_point); + csr_set(CSR_VSSTATUS, csr_read(CSR_SSTATUS)); + csr_write(CSR_VSTVEC, &panic); // VS interrupt vector + csr_write(CSR_VSATP, 0ul); // vs page table + csr_write(CSR_VSIE, 0ul); // interrupts vs control + csr_write(CSR_HIE, 0ul); // interrupts h control + csr_write(CSR_HIDELEG, 0ul); // hypervisor interrupt delegation + csr_write(CSR_HEDELEG, 0ul); // hypervisor exception delegation + csr_write(CSR_HCOUNTEREN, 0ul); // hypervisor counter controls + csr_write(CSR_HENVCFG, ENVCFG_CBIE | ENVCFG_CBCFE | ENVCFG_CBZE); // hypervisor environment controls + csr_write(CSR_HGATP, MAKE_SATP(HGATP_ROOT, SATP_MODE_39)); // nested page table + flush_htlb(); + + csr_write(CSR_SEPC, &&guest_entry_point); + csr_set(CSR_SSTATUS, SR_SPP); // making sure the guest will start in VS mode + csr_write(CSR_HSTATUS, HSTATUS_SPV | HSTATUS_SPVP); // hypervisor status configuration + + asm volatile("sret"); + +guest_entry_point: + register uint64_t a7 asm("a7") = SBI_EXT_BASE; + register uint64_t a6 asm("a6") = SBI_EXT_BASE_GET_SPEC_VERSION; + asm volatile goto("ecall" : : "r"(a6), "r"(a7) : "memory", "a0", "a1", "a2", "a3", "a4", "a5" : host_entry_point); + + notice_failure_detailed("ecall returned"); + goto end; + +host_entry_point: + asm volatile(".align 4\n1:"); + uint64_t scause = csr_read(CSR_SCAUSE); + if (!(scause & CAUSE_IRQ_FLAG)) { + switch (scause) { + case EXC_VS_SYSCALL: { + uint64_t hstatus = csr_read(CSR_HSTATUS); + if ((hstatus & (HSTATUS_SPV | HSTATUS_SPVP)) == (HSTATUS_SPV | HSTATUS_SPVP)) { + notice_success(); + } else { + notice_failure(); // wrong hstatus + } + goto end; + } + } + } + notice_failure_detailed("wrong signal received"); + +end: + return; +} + +void test_vs_mode_4(void) { + printf("Starting test: VS mode (ecall) - only S translation\n"); + + printf("\t- Run: "); + asm volatile goto("la a6, 1f\n" + "csrw stvec, a6" :: + : "memory", "a6" + : host_entry_point); + csr_set(CSR_VSSTATUS, csr_read(CSR_SSTATUS)); + csr_write(CSR_VSTVEC, &panic); // VS interrupt vector + csr_write(CSR_VSIE, 0ul); // interrupts vs control + csr_write(CSR_HIE, 0ul); // interrupts h control + csr_write(CSR_HIDELEG, 0ul); // hypervisor interrupt delegation + csr_write(CSR_HEDELEG, 0ul); // hypervisor exception delegation + csr_write(CSR_HCOUNTEREN, 0ul); // hypervisor counter controls + csr_write(CSR_HENVCFG, 0ul); // hypervisor environment controls + csr_write(CSR_HGATP, 0ul); // nested page table + flush_htlb(); + csr_write(CSR_VSATP, MAKE_SATP(SATP_ROOT, SATP_MODE_39)); // vs page table + flush_vtlb(); + + csr_write(CSR_SEPC, &&guest_entry_point); + csr_set(CSR_SSTATUS, SR_SPP); // making sure the guest will start in VS mode + csr_write(CSR_HSTATUS, HSTATUS_SPV | HSTATUS_SPVP); // hypervisor status configuration + + asm volatile("sret"); + +guest_entry_point: + register uint64_t a7 asm("a7") = SBI_EXT_BASE; + register uint64_t a6 asm("a6") = SBI_EXT_BASE_GET_SPEC_VERSION; + asm volatile goto("ecall" : : "r"(a6), "r"(a7) : "memory", "a0", "a1", "a2", "a3", "a4", "a5" : host_entry_point); + + notice_failure_detailed("ecall returned"); + goto end; + +host_entry_point: + asm volatile(".align 4\n1:"); + uint64_t scause = csr_read(CSR_SCAUSE); + if (!(scause & CAUSE_IRQ_FLAG)) { + switch (scause) { + case EXC_VS_SYSCALL: { + uint64_t hstatus = csr_read(CSR_HSTATUS); + if ((hstatus & (HSTATUS_SPV | HSTATUS_SPVP)) == (HSTATUS_SPV | HSTATUS_SPVP)) { + notice_success(); + } else { + notice_failure(); // wrong hstatus + } + goto end; + } + case EXC_INST_PAGE_FAULT: { + notice_failure_detailed("instruction fault"); + goto end; + } + } + } + notice_failure_detailed("wrong signal received"); + +end: + return; +} + +void test_vs_mode_5(void) { + printf("Starting test: VS mode (ecall) - both G/S translations\n"); + + printf("\t- Run: "); + asm volatile goto("la a6, 1f\n" + "csrw stvec, a6" :: + : "memory", "a6" + : host_entry_point); + csr_set(CSR_VSSTATUS, csr_read(CSR_SSTATUS)); + csr_write(CSR_VSTVEC, &panic); // VS interrupt vector + csr_write(CSR_VSIE, 0ul); // interrupts vs control + csr_write(CSR_HIE, 0ul); // interrupts h control + csr_write(CSR_HIDELEG, 0ul); // hypervisor interrupt delegation + csr_write(CSR_HEDELEG, 0ul); // hypervisor exception delegation + csr_write(CSR_HCOUNTEREN, 0ul); // hypervisor counter controls + csr_write(CSR_HENVCFG, ENVCFG_CBIE | ENVCFG_CBCFE | ENVCFG_CBZE); // hypervisor environment controls + csr_write(CSR_HGATP, MAKE_SATP(HGATP_ROOT, SATP_MODE_39)); // nested page table + flush_htlb(); + csr_write(CSR_VSATP, MAKE_SATP(SATP_ROOT, SATP_MODE_39)); // vs page table + flush_vtlb(); + + csr_write(CSR_SEPC, &&guest_entry_point); + csr_set(CSR_SSTATUS, SR_SPP); // making sure the guest will start in VS mode + csr_write(CSR_HSTATUS, HSTATUS_SPV | HSTATUS_SPVP); // hypervisor status configuration + + asm volatile("sret"); + +guest_entry_point: + register uint64_t a7 asm("a7") = SBI_EXT_BASE; + register uint64_t a6 asm("a6") = SBI_EXT_BASE_GET_SPEC_VERSION; + asm volatile goto("ecall" : : "r"(a6), "r"(a7) : "memory", "a0", "a1", "a2", "a3", "a4", "a5" : host_entry_point); + + notice_failure_detailed("ecall returned"); + goto end; + +host_entry_point: + asm volatile(".align 4\n1:"); + uint64_t scause = csr_read(CSR_SCAUSE); + if (!(scause & CAUSE_IRQ_FLAG)) { + switch (scause) { + case EXC_VS_SYSCALL: { + uint64_t hstatus = csr_read(CSR_HSTATUS); + if ((hstatus & (HSTATUS_SPV | HSTATUS_SPVP)) == (HSTATUS_SPV | HSTATUS_SPVP)) { + notice_success(); + } else { + notice_failure(); // wrong hstatus + } + goto end; + } + case EXC_INST_PAGE_FAULT: { + notice_failure_detailed("instruction fault"); + goto end; + } + } + } + notice_failure_detailed("wrong signal received"); + +end: + return; +} + +void test_satp_noread(enum mapping mapping_type) { + asm volatile("ld a5, 0(%0)\n" + "call notice_failure\n" + ::"r"(get_translated_symbol((uint64_t)&flag, mapping_type, __FUNCTION__, __FILE__, __LINE__)) + : "memory", "a5"); +} + +void test_satp_nowrite(enum mapping mapping_type) { + __sync_store(&flag, 42); + asm volatile("sd %0, 0(%1)\n" + "call notice_failure\n" + ::"r"(0), + "r"(get_translated_symbol((uint64_t)&flag, mapping_type, __FUNCTION__, __FILE__, __LINE__)) + : "memory"); +} + +void test_satp_noexec(enum mapping mapping_type) { + asm volatile("jalr %0\n" + "call notice_failure\n" + ::"r"(get_translated_symbol((uint64_t)&test_ret, mapping_type, __FUNCTION__, __FILE__, __LINE__)) + : "memory", "ra"); +} + +void test_satp_read(enum mapping mapping_type) { + uint64_t* symb_addr = (uint64_t*) get_translated_symbol((uint64_t)&flag, mapping_type, __FUNCTION__, __FILE__, __LINE__); + __sync_store(&flag, 0xabacadaba); + uint64_t test_flag = __sync_load(symb_addr); + // Failure is notified in interrupt handler + if (test_flag == 0xabacadaba) { + notice_success(); + } +} + +void test_satp_write(enum mapping mapping_type) { + __sync_store(&flag, 0); + __sync_store((uint64_t *)get_translated_symbol((uint64_t)&flag, mapping_type, __FUNCTION__, __FILE__, __LINE__), 451); + // Failure is notified in interrupt handler + if (__sync_load(&flag) == 451) { + notice_success(); + } +} + +void test_satp_exec(enum mapping mapping_type) { + asm volatile("jalr ra,0(%0)\n" + "call notice_success" ::"r"(get_translated_symbol((uint64_t)&test_ret, mapping_type, __FUNCTION__, __FILE__, __LINE__)) + : "memory"); +} + +__attribute__((naked, aligned(4))) void noread_entry_point(void) { + SAVE_CONTEXT(); + uint64_t scause = csr_read(CSR_SCAUSE); + switch (scause) { + case EXC_LOAD_GUEST_PAGE_FAULT: + case EXC_LOAD_PAGE_FAULT: { + notice_success(); + csr_write(CSR_SEPC, NEXT_INSTRUCTION(csr_read(CSR_SEPC), 2)); + break; + } + case EXC_STORE_GUEST_PAGE_FAULT: + case EXC_STORE_PAGE_FAULT: { + notice_failure(); + printf("Unexpected store page fault at 0x%lx from 0x%lx\n", csr_read(CSR_STVAL), csr_read(CSR_SEPC)); + csr_write(CSR_SEPC, NEXT_INSTRUCTION(csr_read(CSR_SEPC), 1)); + break; + } + case EXC_VS_SYSCALL: { + if (handle_vs_call() == EXTRA_UNKNOWN) { + panic(); + } + csr_write(CSR_SEPC, NEXT_INSTRUCTION(csr_read(CSR_SEPC), 1)); + break; + } + default: { + notice_failure(); + printf("Unexpected exception at 0x%lx: 0x%lx\n", csr_read(CSR_SEPC), scause); + csr_write(CSR_SEPC, NEXT_INSTRUCTION(csr_read(CSR_SEPC), 1)); + break; + } + } + RESTORE_CONTEXT(sret); +} + +__attribute__((naked, aligned(4))) void nowrite_entry_point(void) { + SAVE_CONTEXT(); + uint64_t scause = csr_read(CSR_SCAUSE); + switch (scause) { + case EXC_STORE_GUEST_PAGE_FAULT: + case EXC_STORE_PAGE_FAULT: { + if (__sync_load(&flag) != 0) { + notice_success(); + csr_write(CSR_SEPC, NEXT_INSTRUCTION(csr_read(CSR_SEPC), 2)); + } else { + csr_write(CSR_SEPC, NEXT_INSTRUCTION(csr_read(CSR_SEPC), 1)); + } + break; + } + case EXC_LOAD_GUEST_PAGE_FAULT: + case EXC_LOAD_PAGE_FAULT: { + notice_failure(); + printf("Unexpected load page fault at 0x%lx from 0x%lx\n", csr_read(CSR_STVAL), csr_read(CSR_SEPC)); + csr_write(CSR_SEPC, NEXT_INSTRUCTION(csr_read(CSR_SEPC), 1)); + break; + } + + case EXC_VS_SYSCALL: { + if (handle_vs_call() == EXTRA_ERROR) { + panic(); + } + csr_write(CSR_SEPC, NEXT_INSTRUCTION(csr_read(CSR_SEPC), 1)); + break; + } + default: { + notice_failure(); + printf("Unexpected exception at 0x%lx: 0x%lx\n", csr_read(CSR_SEPC), scause); + csr_write(CSR_SEPC, NEXT_INSTRUCTION(csr_read(CSR_SEPC), 1)); + break; + } + } + RESTORE_CONTEXT(sret); +} + +__attribute__((naked, aligned(4))) void noexec_entry_point(void) { + SAVE_CONTEXT(); + struct trap_context *tc = GET_TRAP_CONTEXT(); + uint64_t scause = csr_read(CSR_SCAUSE); + switch (scause) { + case EXC_INST_GUEST_PAGE_FAULT: + case EXC_INST_PAGE_FAULT: { + notice_success(); + csr_write(CSR_SEPC, NEXT_INSTRUCTION(tc->ra, 1)); + break; + } + case EXC_LOAD_GUEST_PAGE_FAULT: + case EXC_LOAD_PAGE_FAULT: { + notice_failure(); + printf("Unexpected load page fault at 0x%lx from 0x%lx\n", csr_read(CSR_STVAL), csr_read(CSR_SEPC)); + csr_write(CSR_SEPC, NEXT_INSTRUCTION(csr_read(CSR_SEPC), 1)); + break; + } + case EXC_STORE_GUEST_PAGE_FAULT: + case EXC_STORE_PAGE_FAULT: { + notice_failure(); + printf("Unexpected store page fault at 0x%lx from 0x%lx\n", csr_read(CSR_STVAL), csr_read(CSR_SEPC)); + csr_write(CSR_SEPC, NEXT_INSTRUCTION(csr_read(CSR_SEPC), 1)); + break; + } + case EXC_VS_SYSCALL: { + if (handle_vs_call() == EXTRA_UNKNOWN) { + panic(); + } + csr_write(CSR_SEPC, NEXT_INSTRUCTION(csr_read(CSR_SEPC), 1)); + break; + } + default: { + panic(); + } + } + RESTORE_CONTEXT(sret); +} + +__attribute__((naked, aligned(4))) void exec_nowrite_entry_point(void) { + SAVE_CONTEXT(); + struct trap_context *tc = GET_TRAP_CONTEXT(); + uint64_t scause = csr_read(CSR_SCAUSE); + switch (scause) { + case EXC_INST_GUEST_PAGE_FAULT: + case EXC_INST_PAGE_FAULT: { + notice_failure(); + printf("Unexpected load page fault at 0x%lx from 0x%lx\n", csr_read(CSR_STVAL), csr_read(CSR_SEPC)); + csr_write(CSR_SEPC, NEXT_INSTRUCTION(tc->ra, 1)); + break; + } + case EXC_STORE_GUEST_PAGE_FAULT: + case EXC_STORE_PAGE_FAULT: { + notice_success(); + csr_write(CSR_SEPC, NEXT_INSTRUCTION(csr_read(CSR_SEPC), 2)); + break; + } + case EXC_VS_SYSCALL: { + if (handle_vs_call() == EXTRA_UNKNOWN) { + panic(); + } + csr_write(CSR_SEPC, NEXT_INSTRUCTION(csr_read(CSR_SEPC), 1)); + break; + } + default: { + notice_failure(); + printf("Unexpected interrupt at 0x%lx: 0x%lx\n", csr_read(CSR_SEPC), scause); + csr_write(CSR_SEPC, NEXT_INSTRUCTION(csr_read(CSR_SEPC), 1)); + } + } + RESTORE_CONTEXT(sret); +} + +__attribute__((naked, aligned(4))) void exec_noread_nowrite_entry_point(void) { + SAVE_CONTEXT(); + struct trap_context *tc = GET_TRAP_CONTEXT(); + uint64_t scause = csr_read(CSR_SCAUSE); + switch (scause) { + case EXC_INST_GUEST_PAGE_FAULT: + case EXC_INST_PAGE_FAULT: { + notice_failure(); + printf("Unexpected load page fault at 0x%lx from 0x%lx\n", csr_read(CSR_STVAL), csr_read(CSR_SEPC)); + csr_write(CSR_SEPC, NEXT_INSTRUCTION(tc->ra, 1)); + break; + } + case EXC_STORE_GUEST_PAGE_FAULT: + case EXC_STORE_PAGE_FAULT: { + notice_success(); + csr_write(CSR_SEPC, NEXT_INSTRUCTION(csr_read(CSR_SEPC), 2)); + break; + } + case EXC_LOAD_GUEST_PAGE_FAULT: + case EXC_LOAD_PAGE_FAULT: { + notice_success(); + csr_write(CSR_SEPC, NEXT_INSTRUCTION(csr_read(CSR_SEPC), 2)); + break; + } + case EXC_VS_SYSCALL: { + if (handle_vs_call() == EXTRA_UNKNOWN) { + panic(); + } + csr_write(CSR_SEPC, NEXT_INSTRUCTION(csr_read(CSR_SEPC), 1)); + break; + } + default: { + notice_failure(); + printf("Unexpected interrupt at 0x%lx: 0x%lx\n", csr_read(CSR_SEPC), scause); + csr_write(CSR_SEPC, NEXT_INSTRUCTION(csr_read(CSR_SEPC), 1)); + break; + } + } + RESTORE_CONTEXT(sret); +} + +__attribute__((naked, aligned(4))) void only_call_entry_point(void) { + SAVE_CONTEXT(); + uint64_t scause = csr_read(CSR_SCAUSE); + switch (scause) { + case EXC_VS_SYSCALL: { + if (handle_vs_call() == EXTRA_UNKNOWN) { + panic(); + } + csr_write(CSR_SEPC, NEXT_INSTRUCTION(csr_read(CSR_SEPC), 1)); + break; + } + default: { + panic(); + break; + } + } + RESTORE_CONTEXT(); + asm volatile("sret"); +} + +void set_stvec_s(vec_f entry_point) { + asm volatile("csrw stvec, %0" ::"r"(entry_point) : "memory"); +} + +void set_stvec_u(vec_f entry_point) { + call_set_stvec(entry_point); +} + +void set_stvec_vs(vec_f entry_point) { + call_set_stvec(entry_point); +} + +void flush_tlb_s(void) { + flush_tlb(); +} + +void flush_tlb_u(void) { + call_flush_tlb(); +} + +void flush_htlb_vs(void) { + call_flush_htlb(); +} + +/** + * Tries to read but entries are invalid + */ +void test_satp_invalid(struct test_env *env, tlb_f flush_tlb, uint64_t flags) { + env->set_stvec(noread_entry_point); + + for (uint i = env->count - 1; i < env->count; i++) + FLAG_SET(mapping[env->mapping_entries[i]].associated_pte, flags); + env->flush_tlb(); + + char *words[2] = {"Valid", "User"}; + int j = 0; + if (flags & FLAG_VALID) + j = 1; + + for (uint i = env->count - 1; i < env->count; i++) { + printf("\t- %s required (%s): ", words[j], mapping[env->mapping_entries[i]].comment); + test_satp_noread(env->mapping_entries[i]); + } +} + +/** + * Tries to read and write but entries are read-only + */ +void test_satp_rodata(struct test_env *env, tlb_f flush_tlb, uint64_t flags) { + env->set_stvec(nowrite_entry_point); + + for (uint i = 0; i < env->count; i++) + FLAG_SET(mapping[env->mapping_entries[i]].associated_pte, flags); + env->flush_tlb(); + + for (uint i = 0; i < env->count; i++) { + printf("\t- Read works (%s): ", mapping[env->mapping_entries[i]].comment); + test_satp_read(env->mapping_entries[i]); + } + + for (uint i = 0; i < env->count; i++) { + printf("\t- Store required (%s): ", mapping[env->mapping_entries[i]].comment); + test_satp_nowrite(env->mapping_entries[i]); + } +} + +/** + * Tries to write and exec but entries are read/write only + */ +void test_satp_data(struct test_env *env, tlb_f flush_tlb, uint64_t flags) { + env->set_stvec(noexec_entry_point); + + for (uint i = 0; i < env->count; i++) + FLAG_SET(mapping[env->mapping_entries[i]].associated_pte, flags); + env->flush_tlb(); + + for (uint i = 0; i < env->count; i++) { + printf("\t- Store works (%s): ", mapping[env->mapping_entries[i]].comment); + test_satp_write(env->mapping_entries[i]); + } + + for (uint i = 0; i < env->count; i++) { + printf("\t- Exec required (%s): ", mapping[env->mapping_entries[i]].comment); + test_satp_noexec(env->mapping_entries[i]); + } +} + +/** + * Tries to read, write and exec but entries are read/exec only + */ +void test_satp_code(struct test_env *env, tlb_f flush_tlb, uint64_t flags) { + env->set_stvec(exec_nowrite_entry_point); + + for (uint i = 0; i < env->count; i++) + FLAG_SET(mapping[env->mapping_entries[i]].associated_pte, flags); + env->flush_tlb(); + + for (uint i = 0; i < env->count; i++) { + printf("\t- Exec works (%s): ", mapping[env->mapping_entries[i]].comment); + test_satp_exec(env->mapping_entries[i]); + } + + for (uint i = 0; i < env->count; i++) { + printf("\t- Read works (%s): ", mapping[env->mapping_entries[i]].comment); + test_satp_read(env->mapping_entries[i]); + } + + for (uint i = 0; i < env->count; i++) { + printf("\t- Store required (%s): ", mapping[env->mapping_entries[i]].comment); + test_satp_nowrite(env->mapping_entries[i]); + } +} + +/** + * Tries to read, write and exec but entries are exec only + */ +void test_satp_private_code(struct test_env *env, tlb_f flush_tlb, uint64_t flags) { + env->set_stvec(exec_noread_nowrite_entry_point); + + for (uint i = 0; i < env->count; i++) + FLAG_SET(mapping[env->mapping_entries[i]].associated_pte, flags); + env->flush_tlb(); + + for (uint i = 0; i < env->count; i++) { + printf("\t- Exec works (%s): ", mapping[env->mapping_entries[i]].comment); + test_satp_exec(env->mapping_entries[i]); + } + + for (uint i = 0; i < env->count; i++) { + printf("\t- Read required (%s): ", mapping[env->mapping_entries[i]].comment); + test_satp_noread(env->mapping_entries[i]); + } + + for (uint i = 0; i < env->count; i++) { + printf("\t- Store required (%s): ", mapping[env->mapping_entries[i]].comment); + test_satp_nowrite(env->mapping_entries[i]); + } +} + +void test_satp(void) { + uint64_t selected_mappings[] = {MAPPING_S_LVL3, MAPPING_S_LVL2, MAPPING_S_LVL1}; + struct test_env env = { + .set_stvec = set_stvec_s, + .flush_tlb = flush_tlb_s, + .count = LENGTH(selected_mappings), + .mapping_entries = selected_mappings, + }; + + printf("Starting test: HS-mode (satp)\n"); + + csr_clear(CSR_SCAUSE, SR_SIE); + csr_write(CSR_SATP, MAKE_SATP(SATP_ROOT, SATP_MODE_39)); + flush_tlb(); + + printf(" * invalid (R----) *\n"); + test_satp_invalid(&env, flush_tlb_s, FLAG_READ); + + printf(" * rodata (R---V) *\n"); + test_satp_rodata(&env, flush_tlb_s, FLAG_READ | FLAG_VALID); + + printf(" * data (RW--V) *\n"); + test_satp_data(&env, flush_tlb_s, FLAG_READ | FLAG_WRITE | FLAG_VALID); + + printf(" * code (R-X-V) *\n"); + test_satp_code(&env, flush_tlb_s, FLAG_READ | FLAG_EXEC | FLAG_VALID); + + printf(" * code (--X-V) *\n"); + test_satp_private_code(&env, flush_tlb_s, FLAG_EXEC | FLAG_VALID); +} + +void test_vsatp_simple(void) { + uint64_t selected_mappings[] = {MAPPING_S_LVL3, MAPPING_S_LVL2, MAPPING_S_LVL1}; + struct test_env env = { + .set_stvec = set_stvec_s, + .flush_tlb = flush_tlb_s, + .count = LENGTH(selected_mappings), + .mapping_entries = selected_mappings, + }; + + printf("Starting test: VS-mode (vsatp - only S)\n"); + + uint64_t satp_root = MAKE_SATP(SATP_ROOT, SATP_MODE_39); + + csr_write(CSR_STVEC, &only_call_entry_point); + csr_set(CSR_VSSTATUS, csr_read(CSR_SSTATUS)); + csr_write(CSR_VSTVEC, &panic); // VS interrupt vector + csr_write(CSR_VSATP, 0ul); // vs page table + csr_write(CSR_VSIE, 0ul); // interrupts vs control + csr_write(CSR_HIE, 0ul); // interrupts h control + csr_write(CSR_HIDELEG, 0ul); // hypervisor interrupt delegation + csr_write(CSR_HEDELEG, + bit(EXC_LOAD_MISALIGNED) | bit(EXC_LOAD_ACCESS) | bit(EXC_STORE_MISALIGNED) | bit(EXC_STORE_ACCESS) | bit(EXC_LOAD_PAGE_FAULT) | + bit(EXC_STORE_PAGE_FAULT) | bit(EXC_INST_PAGE_FAULT)); // hypervisor exception delegation + csr_write(CSR_HCOUNTEREN, 0ul); // hypervisor counter controls + csr_write(CSR_HENVCFG, ENVCFG_CBIE | ENVCFG_CBCFE | ENVCFG_CBZE); // hypervisor environment controls + csr_write(CSR_SEPC, &&vm_land); // vm entry point + csr_set(CSR_SSTATUS, SR_SPP); // making sure the guest will start in VS mode + csr_write(CSR_HSTATUS, HSTATUS_SPV | HSTATUS_SPVP); // hypervisor status configuration + csr_write(CSR_HGATP, 0ul); // nested page table + flush_htlb(); + asm volatile("sret"); + +vm_land: + // From here we are running in VS-mode + csr_write(CSR_STVEC, &panic_vector); + csr_write(CSR_SATP, satp_root); + flush_tlb(); + + printf(" * nouser (R----) *\n"); + test_satp_invalid(&env, flush_tlb_s, FLAG_READ); + + printf(" * rodata (R---V) *\n"); + test_satp_rodata(&env, flush_tlb_s, FLAG_READ | FLAG_VALID); + + printf(" * data (RW--V) *\n"); + test_satp_data(&env, flush_tlb_s, FLAG_READ | FLAG_WRITE | FLAG_VALID); + + printf(" * code (R-X-V) *\n"); + test_satp_code(&env, flush_tlb_s, FLAG_READ | FLAG_EXEC | FLAG_VALID); + + printf(" * code (--X-V) *\n"); + test_satp_private_code(&env, flush_tlb_s, FLAG_EXEC | FLAG_VALID); + + change_mode(); // from here we are running in HS-mode + + printf(" * satp and vsatp coherency *\n"); + printf("\t- Check: "); + if (csr_read(CSR_SATP) == 0 && csr_read(CSR_VSATP) == satp_root) { + notice_success(); + } else { + notice_failure(); + } + return; +} + +void test_hgatp_simple(void) { + uint64_t selected_mappings[] = {MAPPING_H_LVL3, MAPPING_H_LVL2, MAPPING_H_LVL1}; + struct test_env env = { + .set_stvec = set_stvec_vs, + .flush_tlb = flush_htlb_vs, + .count = LENGTH(selected_mappings), + .mapping_entries = selected_mappings, + }; + + printf("Starting test: VS-mode (hgatp - only G)\n"); + + csr_write(CSR_STVEC, &only_call_entry_point); + csr_set(CSR_VSSTATUS, csr_read(CSR_SSTATUS)); + csr_write(CSR_VSTVEC, &panic); // VS interrupt vector + csr_write(CSR_VSATP, 0ul); // vs page table + csr_write(CSR_VSIE, 0ul); // interrupts vs control + csr_write(CSR_HIE, 0ul); // interrupts h control + csr_write(CSR_HIDELEG, 0ul); // hypervisor interrupt delegation + csr_write(CSR_HEDELEG, + bit(EXC_LOAD_MISALIGNED) | bit(EXC_LOAD_ACCESS) | bit(EXC_STORE_MISALIGNED) | bit(EXC_STORE_ACCESS) | bit(EXC_LOAD_PAGE_FAULT) | + bit(EXC_STORE_PAGE_FAULT) | bit(EXC_INST_PAGE_FAULT)); // hypervisor exception delegation + csr_write(CSR_HCOUNTEREN, 0ul); // hypervisor counter controls + csr_write(CSR_HENVCFG, ENVCFG_CBIE | ENVCFG_CBCFE | ENVCFG_CBZE); // hypervisor environment controls + csr_write(CSR_SEPC, &&vm_land); // vm entry point + csr_set(CSR_SSTATUS, SR_SPP); // making sure the guest will start in VS mode + csr_write(CSR_HSTATUS, HSTATUS_SPV | HSTATUS_SPVP); // hypervisor status configuration + csr_write(CSR_VSATP, 0ul); + csr_write(CSR_HGATP, MAKE_SATP(HGATP_ROOT, SATP_MODE_39)); // nested page table + flush_htlb(); + asm volatile("sret"); + +vm_land: + // From here we are running in VS-mode + csr_write(CSR_STVEC, &panic_vector); + flush_tlb(); + + printf(" * nouser (R--U-) *\n"); + test_satp_invalid(&env, flush_htlb_vs, FLAG_USER | FLAG_READ); + + printf(" * nouser (R---V) *\n"); + test_satp_invalid(&env, flush_htlb_vs, FLAG_READ | FLAG_VALID); + + printf(" * rodata (R--UV) *\n"); + test_satp_rodata(&env, flush_htlb_vs, FLAG_USER | FLAG_READ | FLAG_VALID); + + printf(" * data (RW-UV) *\n"); + test_satp_data(&env, flush_htlb_vs, FLAG_USER | FLAG_READ | FLAG_WRITE | FLAG_VALID); + + printf(" * code (R-XUV) *\n"); + test_satp_code(&env, flush_htlb_vs, FLAG_USER | FLAG_READ | FLAG_EXEC | FLAG_VALID); + + printf(" * code (--XUV) *\n"); + test_satp_private_code(&env, flush_htlb_vs, FLAG_USER | FLAG_EXEC | FLAG_VALID); + + change_mode(); + // From here we are running in HS-mode +} + +void test_vsatp_full(void) { + uint64_t selected_mappings[] = {MAPPING_S_LVL3, MAPPING_S_LVL2, MAPPING_S_LVL1}; + struct test_env env = { + .set_stvec = set_stvec_s, + .flush_tlb = flush_tlb_s, + .count = LENGTH(selected_mappings), + .mapping_entries = selected_mappings, + }; + + printf("Starting test: VS-mode (vsatp - G/S)\n"); + + uint64_t satp_root = MAKE_SATP(SATP_ROOT, SATP_MODE_39); + + csr_write(CSR_STVEC, &only_call_entry_point); + csr_set(CSR_VSSTATUS, csr_read(CSR_SSTATUS)); + csr_write(CSR_VSTVEC, &panic); // VS interrupt vector + csr_write(CSR_VSATP, 0ul); // vs page table + csr_write(CSR_VSIE, 0ul); // interrupts vs control + csr_write(CSR_HIE, 0ul); // interrupts h control + csr_write(CSR_HIDELEG, 0ul); // hypervisor interrupt delegation + csr_write(CSR_HEDELEG, + bit(EXC_LOAD_MISALIGNED) | bit(EXC_LOAD_ACCESS) | bit(EXC_STORE_MISALIGNED) | bit(EXC_STORE_ACCESS) | bit(EXC_LOAD_PAGE_FAULT) | + bit(EXC_STORE_PAGE_FAULT) | bit(EXC_INST_PAGE_FAULT)); // hypervisor exception delegation + csr_write(CSR_HCOUNTEREN, 0ul); // hypervisor counter controls + csr_write(CSR_SENVCFG, ENVCFG_CBIE | ENVCFG_CBCFE | ENVCFG_CBZE); + csr_write(CSR_HENVCFG, ENVCFG_CBIE | ENVCFG_CBCFE | ENVCFG_CBZE); + csr_write(CSR_SEPC, &&vm_land); // vm entry point + csr_set(CSR_SSTATUS, SR_SPP); // making sure the guest will start in VS mode + csr_write(CSR_HSTATUS, HSTATUS_SPV | HSTATUS_SPVP); // hypervisor status configuration + csr_write(CSR_HGATP, MAKE_SATP(HGATP_ROOT, SATP_MODE_39)); // nested page table + flush_htlb(); + + asm volatile("sret"); + +vm_land: + // From here we are running in VS-mode + csr_write(CSR_STVEC, &panic_vector); + csr_write(CSR_SATP, satp_root); + flush_tlb(); + + printf(" * nouser (R----) *\n"); + test_satp_invalid(&env, &noread_entry_point, FLAG_READ); + + printf(" * rodata (R---V) *\n"); + test_satp_rodata(&env, &nowrite_entry_point, FLAG_READ | FLAG_VALID); + + printf(" * data (RW--V) *\n"); + test_satp_data(&env, &noexec_entry_point, FLAG_READ | FLAG_WRITE | FLAG_VALID); + + printf(" * code (R-X-V) *\n"); + test_satp_code(&env, &exec_nowrite_entry_point, FLAG_READ | FLAG_EXEC | FLAG_VALID); + + printf(" * code (--X-V) *\n"); + test_satp_private_code(&env, &exec_noread_nowrite_entry_point, FLAG_EXEC | FLAG_VALID); + + change_mode(); + // From here we are running in HS-mode +} + +void test_hgatp_full(void) { + uint64_t selected_mappings[] = {MAPPING_VS_LVL3, MAPPING_VS_LVL2, MAPPING_VS_LVL1}; + struct test_env env = { + .set_stvec = set_stvec_vs, + .flush_tlb = flush_htlb_vs, + .count = LENGTH(selected_mappings), + .mapping_entries = selected_mappings, + }; + + printf("Starting test: VS-mode (hgatp - G/S)\n"); + + csr_write(CSR_STVEC, &only_call_entry_point); + csr_set(CSR_VSSTATUS, csr_read(CSR_SSTATUS)); + csr_write(CSR_VSTVEC, &panic); // VS interrupt vector + csr_write(CSR_VSATP, 0ul); // vs page table + csr_write(CSR_VSIE, 0ul); // interrupts vs control + csr_write(CSR_HIE, 0ul); // interrupts h control + csr_write(CSR_HIDELEG, 0ul); // hypervisor interrupt delegation + csr_write(CSR_HEDELEG, + bit(EXC_LOAD_MISALIGNED) | bit(EXC_LOAD_ACCESS) | bit(EXC_STORE_MISALIGNED) | bit(EXC_STORE_ACCESS) | bit(EXC_LOAD_PAGE_FAULT) | + bit(EXC_STORE_PAGE_FAULT) | bit(EXC_INST_PAGE_FAULT)); // hypervisor exception delegation + csr_write(CSR_HCOUNTEREN, 0ul); // hypervisor counter controls + csr_write(CSR_SENVCFG, ENVCFG_CBIE | ENVCFG_CBCFE | ENVCFG_CBZE); + csr_write(CSR_HENVCFG, ENVCFG_CBIE | ENVCFG_CBCFE | ENVCFG_CBZE); + csr_write(CSR_SEPC, &&vm_land); // vm entry point + csr_set(CSR_SSTATUS, SR_SPP); // making sure the guest will start in VS mode + csr_write(CSR_HSTATUS, HSTATUS_SPV | HSTATUS_SPVP); // hypervisor status configuration + csr_write(CSR_VSATP, 0ul); + csr_write(CSR_HGATP, MAKE_SATP(HGATP_ROOT, SATP_MODE_39)); // nested page table + flush_htlb(); + + asm volatile("sret"); + +vm_land: + // From here we are running in VS-mode + csr_write(CSR_STVEC, &panic_vector); + csr_write(CSR_SATP, MAKE_SATP(SATP_ROOT, SATP_MODE_39)); + flush_tlb(); + // TODO: make an ecall to flush using flush_htlb_addr() on selected GPA + // to test it + + printf(" * novalid (R--U-) *\n"); + test_satp_invalid(&env, &noread_entry_point, FLAG_READ | FLAG_USER); + + printf(" * nouser (R---V) *\n"); + test_satp_invalid(&env, &noread_entry_point, FLAG_READ | FLAG_VALID); + + printf(" * rodata (R--UV) *\n"); + test_satp_rodata(&env, &nowrite_entry_point, FLAG_READ | FLAG_VALID | FLAG_USER); + + printf(" * data (RW-UV) *\n"); + test_satp_data(&env, &noexec_entry_point, FLAG_READ | FLAG_WRITE | FLAG_VALID | FLAG_USER); + + printf(" * code (R-XUV) *\n"); + test_satp_code(&env, &exec_nowrite_entry_point, FLAG_READ | FLAG_EXEC | FLAG_VALID | FLAG_USER); + + printf(" * code (--XUV) *\n"); + test_satp_private_code(&env, &exec_noread_nowrite_entry_point, FLAG_EXEC | FLAG_VALID | FLAG_USER); + + printf(" * novalid (R--U-) *\n"); + test_satp_invalid(&env, &noread_entry_point, FLAG_READ | FLAG_USER); + + change_mode(); + // From here we are running in HS-mode +} diff --git a/verif/tests/custom/rvh/tests.h b/verif/tests/custom/rvh/tests.h new file mode 100644 index 0000000000..8a7c5fcd41 --- /dev/null +++ b/verif/tests/custom/rvh/tests.h @@ -0,0 +1,26 @@ +#ifndef HMODE_TESTS_H +#define HMODE_TESTS_H + +#include "types.h" + +struct test_env { + stvec_f set_stvec; + tlb_f flush_tlb; + int count; + const uint64_t *const mapping_entries; +}; + +void test_timer(void); +void test_u_mode(void); +void test_vs_mode_1(void); +void test_vs_mode_2(void); +void test_vs_mode_3(void); +void test_vs_mode_4(void); +void test_vs_mode_5(void); +void test_satp(void); +void test_vsatp_simple(void); +void test_hgatp_simple(void); +void test_vsatp_full(void); +void test_hgatp_full(void); + +#endif //HMODE_TESTS_H diff --git a/verif/tests/custom/rvh/trampoline.S b/verif/tests/custom/rvh/trampoline.S new file mode 100644 index 0000000000..e661edae7b --- /dev/null +++ b/verif/tests/custom/rvh/trampoline.S @@ -0,0 +1,27 @@ +#define SBI_EXT_BASE 0x10 +#define SBI_EXT_BASE_GET_SPEC_VERSION 0x0 + +.section .trampoline, "aw", @progbits + +.globl test_ret +test_ret: +.type test_ret, @function +.size test_ret, .Lend_test_ret - test_ret +ret +.Lend_test_ret: + +.globl test_ecall +test_ecall: +.type test_ecall, @function +.size test_ecall, .Lend_test_ecall - test_ecall +li a7, SBI_EXT_BASE +li a6, SBI_EXT_BASE_GET_SPEC_VERSION +ecall +.Lend_test_ecall: + +.balign 8 +.globl flag +flag: +.type flag, @object +.size flag, 8 +.quad 0x0 diff --git a/verif/tests/custom/rvh/trampoline.h b/verif/tests/custom/rvh/trampoline.h new file mode 100644 index 0000000000..5cff273699 --- /dev/null +++ b/verif/tests/custom/rvh/trampoline.h @@ -0,0 +1,141 @@ +#ifndef HMODE_TRAMPOLINE_H +#define HMODE_TRAMPOLINE_H + +#include + +struct trap_context { + /* Return address */ + /* 0 */ uint64_t ra; + /* Global pointer */ + /* 1 */ uint64_t gp; + /* Thread pointer */ + /* 2 */ uint64_t tp; + /* Function arguments and return values */ + /* 3 */ uint64_t a0; + /* 4 */ uint64_t a1; + /* 5 */ uint64_t a2; + /* 6 */ uint64_t a3; + /* 7 */ uint64_t a4; + /* 8 */ uint64_t a5; + /* 9 */ uint64_t a6; + /* 10 */ uint64_t a7; + /* Temporaries and alternate link registers */ + /* 11 */ uint64_t t0; + /* 12 */ uint64_t t1; + /* 13 */ uint64_t t2; + /* 14 */ uint64_t t3; + /* 15 */ uint64_t t4; + /* 16 */ uint64_t t5; + /* 17 */ uint64_t t6; + /* Saved registers */ + /* 19 */ uint64_t s0; /* Saved Frame pointer */ + /* 19 */ uint64_t s1; + /* 20 */ uint64_t s2; + /* 21 */ uint64_t s3; + /* 22 */ uint64_t s4; + /* 23 */ uint64_t s5; + /* 24 */ uint64_t s6; + /* 25 */ uint64_t s7; + /* 26 */ uint64_t s8; + /* 27 */ uint64_t s9; + /* 28 */ uint64_t s10; + /* 29 */ uint64_t s11; +} __attribute__((packed)); + +extern __thread struct trap_context* __tc; + +#define SAVE_CONTEXT() \ + asm volatile("addi sp, sp, -256\n" \ + "sd ra, 0*8(sp)\n" \ + "sd gp, 1*8(sp)\n" \ + "sd tp, 2*8(sp)\n" \ + "sd a0, 3*8(sp)\n" \ + "sd a1, 4*8(sp)\n" \ + "sd a2, 5*8(sp)\n" \ + "sd a3, 6*8(sp)\n" \ + "sd a4, 7*8(sp)\n" \ + "sd a5, 8*8(sp)\n" \ + "sd a6, 9*8(sp)\n" \ + "sd a7, 10*8(sp)\n" \ + "sd t0, 11*8(sp)\n" \ + "sd t1, 12*8(sp)\n" \ + "sd t2, 13*8(sp)\n" \ + "sd t3, 14*8(sp)\n" \ + "sd t4, 15*8(sp)\n" \ + "sd t5, 16*8(sp)\n" \ + "sd t6, 17*8(sp)\n" \ + "sd s0, 18*8(sp)\n" \ + "sd s1, 19*8(sp)\n" \ + "sd s2, 20*8(sp)\n" \ + "sd s3, 21*8(sp)\n" \ + "sd s4, 22*8(sp)\n" \ + "sd s5, 23*8(sp)\n" \ + "sd s6, 24*8(sp)\n" \ + "sd s7, 25*8(sp)\n" \ + "sd s8, 26*8(sp)\n" \ + "sd s9, 27*8(sp)\n" \ + "sd s10, 28*8(sp)\n" \ + "sd s11, 29*8(sp)\n" \ + /* Store thread_context to TLS */ \ + "lui a5, %%tprel_hi(__tc)\n" \ + "add a5, a5, tp, %%tprel_add(__tc)\n"\ + "sd sp, %%tprel_lo(__tc)(a5)\n" \ + ::: "memory") + +#define RESTORE_CONTEXT(ret) \ + asm volatile(/* Store thread_context to TLS */ \ + "lui a5, %%tprel_hi(__tc)\n" \ + "add a5, a5, tp, %%tprel_add(__tc)\n"\ + "ld sp, %%tprel_lo(__tc)(a5)\n" \ + "ld ra, 0*8(sp)\n" \ + "ld gp, 1*8(sp)\n" \ + "ld tp, 2*8(sp)\n" \ + "ld a0, 3*8(sp)\n" \ + "ld a1, 4*8(sp)\n" \ + "ld a2, 5*8(sp)\n" \ + "ld a3, 6*8(sp)\n" \ + "ld a4, 7*8(sp)\n" \ + "ld a5, 8*8(sp)\n" \ + "ld a6, 9*8(sp)\n" \ + "ld a7, 10*8(sp)\n" \ + "ld t0, 11*8(sp)\n" \ + "ld t1, 12*8(sp)\n" \ + "ld t2, 13*8(sp)\n" \ + "ld t3, 14*8(sp)\n" \ + "ld t4, 15*8(sp)\n" \ + "ld t5, 16*8(sp)\n" \ + "ld t6, 17*8(sp)\n" \ + "ld s0, 18*8(sp)\n" \ + "ld s1, 19*8(sp)\n" \ + "ld s2, 20*8(sp)\n" \ + "ld s3, 21*8(sp)\n" \ + "ld s4, 22*8(sp)\n" \ + "ld s5, 23*8(sp)\n" \ + "ld s6, 24*8(sp)\n" \ + "ld s7, 25*8(sp)\n" \ + "ld s8, 26*8(sp)\n" \ + "ld s9, 27*8(sp)\n" \ + "ld s10, 28*8(sp)\n" \ + "ld s11, 29*8(sp)\n" \ + "addi sp, sp, 256\n" \ + #ret ::: "memory") + +#define GET_TRAP_CONTEXT() (__tc) + +void test_ret(void); +void test_ecall(void); +extern uint64_t flag; + +#define RV_INS_LEN(ins) ((((ins) & 0x3) != 0x3) ? 2 : ((((ins) & 0x1f) != 0x1f) ? 4 : ((((ins) & 0x3f) != 0x3f) ? 6 : 8))) + +#define NEXT_INSTRUCTION(inst, n) \ + ({ \ + uint64_t __pc = (inst); \ + for (int i = 0; i < (n); i++) { \ + __pc += RV_INS_LEN(*(uint8_t *)(__pc)); \ + }; \ + __pc; \ + }) + + +#endif //HMODE_TRAMPOLINE_H diff --git a/verif/tests/custom/rvh/types.h b/verif/tests/custom/rvh/types.h new file mode 100644 index 0000000000..add70d56a1 --- /dev/null +++ b/verif/tests/custom/rvh/types.h @@ -0,0 +1,13 @@ +#ifndef HMODE_TYPES_H +#define HMODE_TYPES_H + +#define LENGTH(array) (sizeof(array) / sizeof(array[0])) + +typedef unsigned long uint64_t; +typedef unsigned int uint; + +typedef void (*vec_f)(void); +typedef void (*stvec_f)(vec_f); +typedef void (*tlb_f)(void); + +#endif // HMODE_TYPES_H diff --git a/verif/tests/custom/rvh/utils.c b/verif/tests/custom/rvh/utils.c new file mode 100644 index 0000000000..0faa7ce60f --- /dev/null +++ b/verif/tests/custom/rvh/utils.c @@ -0,0 +1,161 @@ +#include + +#include "utils.h" + +#include "csr.h" +#include "vm.h" + +__attribute__((naked, noreturn, aligned(4))) void panic(void) { + exit(1); +} + +void reset(void) { + csr_write(CSR_STVEC, &panic_vector); + csr_write(CSR_SEPC, &panic); + csr_write(CSR_SIE, 0ul); + csr_clear(CSR_SSTATUS, SR_SIE); + csr_write(CSR_HSTATUS, 0ul); + csr_write(CSR_HIE, 0ul); + csr_write(CSR_HIDELEG, 0ul); + csr_write(CSR_HEDELEG, 0ul); + csr_write(CSR_HCOUNTEREN, 0ul); + // Activate CBO + csr_write(CSR_SENVCFG, ENVCFG_CBIE | ENVCFG_CBCFE | ENVCFG_CBZE); + csr_write(CSR_HENVCFG, ENVCFG_CBIE | ENVCFG_CBCFE | ENVCFG_CBZE); + csr_write(CSR_HGATP, 0ul); + flush_htlb(); + csr_write(CSR_SATP, 0ul); + flush_tlb(); +} + +__attribute__((noreturn, naked, aligned(4))) void panic_vector(void) { + printf("Panic: "); + if (csr_read(CSR_SCAUSE) & CAUSE_IRQ_FLAG) { + switch (csr_read(CSR_SCAUSE) & ~CAUSE_IRQ_FLAG) { + case 1: { + printf("Software Interrupt (HS-mode)"); + break; + } + case 2: { + printf("Software Interrupt (VS-mode)"); + break; + } + case 3: { + printf("Software Interrupt (M-mode)"); + break; + } + case 5: { + printf("Timer Interrupt (HS-mode)"); + break; + } + case 6: { + printf("Timer Interrupt (VS-mode)"); + break; + } + case 7: { + printf("Timer Interrupt (M-mode)"); + break; + } + case 9: { + printf("External Interrupt (HS-mode)"); + break; + } + case 10: { + printf("External Interrupt (VS-mode)"); + break; + } + case 11: { + printf("External Interrupt (M-mode)"); + break; + } + default: { + printf("Unsupported Interrupt"); + } + } + } else { + switch (csr_read(CSR_SCAUSE)) { + case 0: { + printf("Instruction address misaligned"); + break; + } + case 1: { + printf("Instruction access fault"); + break; + } + case 2: { + printf("Illegal instruction"); + break; + } + case 3: { + printf("Breakpoint"); + break; + } + case 4: { + printf("Load address misaligned"); + break; + } + case 5: { + printf("Load access fault"); + break; + } + case 6: { + printf("Store/AMO address misaligned"); + break; + } + case 7: { + printf("Store/AMO access fault"); + break; + } + case 8: { + printf("Environment call from U-mode or VU-mode"); + break; + } + case 9: { + printf("Environment call from HS-mode"); + break; + } + case 10: { + printf("Environment call from VS-mode"); + break; + } + case 11: { + printf("Environment call from M-mode"); + break; + } + case 12: { + printf("Instruction page fault"); + break; + } + case 13: { + printf("Load page fault"); + break; + } + case 15: { + printf("Store/AMO page fault"); + break; + } + case 20: { + printf("Instruction guest-page fault"); + break; + } + case 21: { + printf("Load guest-page fault"); + break; + } + case 22: { + printf("Virtual instruction"); + break; + } + case 23: { + printf("Store/AMO guest-page fault"); + break; + } + default: { + printf("Unsupported exception"); + } + } + } + printf("\n"); + printf("S-mode : panicked (scause = 0x%lx sepc = 0x%lx stval = 0x%lx)\n", csr_read(CSR_SCAUSE) , csr_read(CSR_SEPC), csr_read(CSR_STVAL)); + panic(); +} diff --git a/verif/tests/custom/rvh/utils.h b/verif/tests/custom/rvh/utils.h new file mode 100644 index 0000000000..2ec1c98982 --- /dev/null +++ b/verif/tests/custom/rvh/utils.h @@ -0,0 +1,17 @@ +#ifndef HMODE_PANIC_H +#define HMODE_PANIC_H + +#include "util.h" + +__attribute__((noreturn)) void panic(void); +__attribute__((noreturn)) void panic_vector(void); + +#define __sync_load(a) __atomic_load_n(a, __ATOMIC_ACQUIRE) +#define __sync_store(a, val) __atomic_store_n(a, val, __ATOMIC_RELEASE) +#define __sync_compare_and_swap_n(a, old_val, new_val) __atomic_compare_exchange_n(a, old_val, new_val, 0, __ATOMIC_RELEASE, __ATOMIC_ACQUIRE) +#define __sync_fetch_and_add(a, inc) __atomic_fetch_add(a, inc, __ATOMIC_ACQUIRE) + +void reset(void); +void exit(int); // defined in `../common/syscalls.c` + +#endif //HMODE_PANIC_H diff --git a/verif/tests/custom/rvh/vm.c b/verif/tests/custom/rvh/vm.c new file mode 100644 index 0000000000..cef07e3f45 --- /dev/null +++ b/verif/tests/custom/rvh/vm.c @@ -0,0 +1,164 @@ +#include + +#include "utils.h" +#include "types.h" +#include "vm.h" +#define TRAMPOLINE_SIZE ((uint64_t) & trampoline_end - (uint64_t) & trampoline_start) + +extern uint64_t _start_text; +extern uint64_t _end_text; +extern uint64_t trampoline_start; +extern uint64_t trampoline_end; + +uint64_t *const satp_lvl3 = &__satp_lvl3; +uint64_t *const satp_lvl2 = &__satp_lvl2; +uint64_t *const satp_lvl1 = &__satp_lvl1; +uint64_t *const hgatp_lvl3 = &__hgatp_lvl3; +uint64_t *const hgatp_lvl2 = &__hgatp_lvl2; +uint64_t *const hgatp_lvl1 = &__hgatp_lvl1; + +const char comment_1GB[] = "1GB"; +const char comment_2MB[] = "2MB"; +const char comment_4KB[] = "4KB"; + +struct segment mapping[MAPPING_MAX]; + +uint64_t get_aligned_address(uint64_t address, uint size, int level) { + if (level <= 0 || level > 5) { + printf("%s : Error (%s:%d)\n", __FUNCTION__, __FILE__, __LINE__); + goto error; + } + uint64_t aligned = ALIGN_ADDRESS(address, level); + uint64_t end_address; + if (__builtin_add_overflow(address, size - 1, &end_address)) { + printf("%s : Error (%s:%d)\n", __FUNCTION__, __FILE__, __LINE__); + goto error; + } + if (aligned + ENTRY_MASK_LOWER(level) < end_address) { + printf("%s : Error (%s:%d)\n", __FUNCTION__, __FILE__, __LINE__); + goto error; + } + return aligned; + +error: + panic(); + return 0ul; +} + +void build_page_tables(void) { + for (int i = 0; i < 512; i++) { + satp_lvl3[i] = 0; + satp_lvl2[i] = 0; + satp_lvl1[i] = 0; + hgatp_lvl3[i] = 0; + hgatp_lvl3[i + 512] = 0; + hgatp_lvl3[i + 1024] = 0; + hgatp_lvl3[i + 1536] = 0; + hgatp_lvl2[i] = 0; + hgatp_lvl1[i] = 0; + } + // range 0x8000_0000 - 0xcfff_ffff (1-1 mapping for compatibility with baremetal) + satp_lvl3[0x2] = + BUILD_PTE(get_aligned_address((uint64_t)&_start_text, (uint64_t)&_end_text - (uint64_t)&_start_text, 3), FLAG_VALID | FLAG_READ | FLAG_WRITE | FLAG_EXEC | DEFAULT_FLAGS); + mapping[MAPPING_S_LVL3].base = 0xffffffc000000000; + mapping[MAPPING_S_LVL3].mask = ENTRY_MASK_LOWER(3); // up to 0xffffffc03fffffff + mapping[MAPPING_S_LVL3].associated_pa = get_aligned_address((uint64_t)&trampoline_start, TRAMPOLINE_SIZE, 3); + mapping[MAPPING_S_LVL3].associated_pte = &satp_lvl3[0x100]; // Mapping a 0xffff'ffc0'YXXX'XXXX + *mapping[MAPPING_S_LVL3].associated_pte = BUILD_PTE(mapping[MAPPING_S_LVL3].associated_pa, FLAG_NONE); + mapping[MAPPING_S_LVL3].comment = comment_1GB; + + // range 0xffff_ffff_c000_0000 - 0xffff_ffff_ffff_ffff (provides lvl2 mapping) + satp_lvl3[0x1ff] = BUILD_PTE((uint64_t)satp_lvl2, FLAG_VALID); + mapping[MAPPING_S_LVL2].base = 0xffffffffc0000000; + mapping[MAPPING_S_LVL2].mask = ENTRY_MASK_LOWER(2); // up to 0xffffffffc01fffff + mapping[MAPPING_S_LVL2].associated_pa = get_aligned_address((uint64_t)&trampoline_start, TRAMPOLINE_SIZE, 2); + mapping[MAPPING_S_LVL2].associated_pte = &satp_lvl2[0x0]; // Mapping at 0xffff'ffff'c0YX'XXXX + *mapping[MAPPING_S_LVL2].associated_pte = BUILD_PTE(mapping[MAPPING_S_LVL2].associated_pa, FLAG_NONE); + mapping[MAPPING_S_LVL2].comment = comment_2MB; + + // range 0xffff_ffff_ffe0_0000 - 0xffff_ffff_ffff_ffff (provides lvl1 mapping) + satp_lvl2[0x1ff] = BUILD_PTE((uint64_t)satp_lvl1, FLAG_VALID); + mapping[MAPPING_S_LVL1].base = 0xfffffffffffff000; + mapping[MAPPING_S_LVL1].mask = ENTRY_MASK_LOWER(1); // up to 0xffffffffffffffff + mapping[MAPPING_S_LVL1].associated_pa = get_aligned_address((uint64_t)&trampoline_start, TRAMPOLINE_SIZE, 1); + mapping[MAPPING_S_LVL1].associated_pte = &satp_lvl1[0x1ff]; + *mapping[MAPPING_S_LVL1].associated_pte = BUILD_PTE(mapping[MAPPING_S_LVL1].associated_pa, FLAG_NONE); + mapping[MAPPING_S_LVL1].comment = comment_4KB; + + // range 0x4000_0000 - 0x403f_ffff (1-1 mapping for compatibility with baremetal) + hgatp_lvl3[0x2] = BUILD_PTE(get_aligned_address((uint64_t)&_start_text, (uint64_t)&_end_text - (uint64_t)&_start_text, 3), + FLAG_VALID | FLAG_READ | FLAG_WRITE | FLAG_EXEC | FLAG_USER | DEFAULT_FLAGS); + mapping[MAPPING_H_LVL3].base = 0x4000000000; + mapping[MAPPING_H_LVL3].mask = ENTRY_MASK_LOWER(3); // up to 0x403fffffff + mapping[MAPPING_H_LVL3].associated_pa = get_aligned_address((uint64_t)&trampoline_start, TRAMPOLINE_SIZE, 3); + mapping[MAPPING_H_LVL3].associated_pte = &hgatp_lvl3[0x100]; + *mapping[MAPPING_H_LVL3].associated_pte = BUILD_PTE(mapping[MAPPING_H_LVL3].associated_pa, FLAG_NONE); + mapping[MAPPING_H_LVL3].comment = comment_1GB; + + // range 0x7f_c000_0000 - 0x7f_c01f_ffff (provides lvl2 mapping) + hgatp_lvl3[0x1ff] = BUILD_PTE((uint64_t)hgatp_lvl2, FLAG_VALID); + mapping[MAPPING_H_LVL2].base = 0x7fc0000000; + mapping[MAPPING_H_LVL2].mask = ENTRY_MASK_LOWER(2); // up to 0x7f_c01f_ffff + mapping[MAPPING_H_LVL2].associated_pa = get_aligned_address((uint64_t)&trampoline_start, TRAMPOLINE_SIZE, 2); + mapping[MAPPING_H_LVL2].associated_pte = &hgatp_lvl2[0x0]; + *mapping[MAPPING_H_LVL2].associated_pte = BUILD_PTE(mapping[MAPPING_H_LVL2].associated_pa, FLAG_NONE); + mapping[MAPPING_H_LVL2].comment = comment_2MB; + + // range 0x7f_ffe0_0000 - 0x7f_ffff_ffff (provides lvl1 mapping) + hgatp_lvl2[0x1ff] = BUILD_PTE((uint64_t)hgatp_lvl1, FLAG_VALID); + mapping[MAPPING_H_LVL1].base = 0x7ffffff000; + mapping[MAPPING_H_LVL1].mask = ENTRY_MASK_LOWER(1); // up to 0x7ff_fff_ffff + mapping[MAPPING_H_LVL1].associated_pa = get_aligned_address((uint64_t)&trampoline_start, TRAMPOLINE_SIZE, 1); + mapping[MAPPING_H_LVL1].associated_pte = &hgatp_lvl1[0x1ff]; + *mapping[MAPPING_H_LVL1].associated_pte = BUILD_PTE(mapping[MAPPING_H_LVL1].associated_pa, FLAG_NONE); + mapping[MAPPING_H_LVL1].comment = comment_4KB; + + // range 0x3fc0000000 - 0x3fffffffff + satp_lvl3[0xff] = BUILD_PTE(mapping[MAPPING_H_LVL3].base, + FLAG_VALID | FLAG_READ | FLAG_WRITE | FLAG_EXEC | DEFAULT_FLAGS); + mapping[MAPPING_VS_LVL3].base = 0x3fc0000000; + mapping[MAPPING_VS_LVL3].mask = mapping[MAPPING_H_LVL3].mask; // up to 0x3fffffffff + mapping[MAPPING_VS_LVL3].associated_pa = mapping[MAPPING_H_LVL3].associated_pa; + mapping[MAPPING_VS_LVL3].associated_pte = mapping[MAPPING_H_LVL3].associated_pte; + mapping[MAPPING_VS_LVL3].comment = mapping[MAPPING_H_LVL3].comment; + + // range 0x3f80000000 - 0x3fbfffffff + satp_lvl3[0xfe] = BUILD_PTE((uint64_t)satp_lvl2, FLAG_VALID); + // range 0x3fa0000000 - 0x3fa01ff000 + satp_lvl2[0x100] = BUILD_PTE(mapping[MAPPING_H_LVL2].base, + FLAG_VALID | FLAG_READ | FLAG_WRITE | FLAG_EXEC | DEFAULT_FLAGS); + mapping[MAPPING_VS_LVL2].base = 0x3fa0000000; + mapping[MAPPING_VS_LVL2].mask = mapping[MAPPING_H_LVL2].mask; // up to 0x3fa01ff000 + mapping[MAPPING_VS_LVL2].associated_pa = mapping[MAPPING_H_LVL2].associated_pa; + mapping[MAPPING_VS_LVL2].associated_pte = mapping[MAPPING_H_LVL2].associated_pte; + mapping[MAPPING_VS_LVL2].comment = mapping[MAPPING_H_LVL2].comment; + + // range 0x3fbff00000 - 0x3fbff00fff + satp_lvl1[0x100] = BUILD_PTE(mapping[MAPPING_H_LVL1].base, + FLAG_VALID | FLAG_READ | FLAG_WRITE | FLAG_EXEC | DEFAULT_FLAGS); + mapping[MAPPING_VS_LVL1].base = 0x3fbff00000; + mapping[MAPPING_VS_LVL1].mask = mapping[MAPPING_H_LVL1].mask; // up to 0x3fbff00fff + mapping[MAPPING_VS_LVL1].associated_pa = mapping[MAPPING_H_LVL1].associated_pa; + mapping[MAPPING_VS_LVL1].associated_pte = mapping[MAPPING_H_LVL1].associated_pte; + mapping[MAPPING_VS_LVL1].comment = mapping[MAPPING_H_LVL1].comment; +} + +uint64_t get_translated_symbol(uint64_t symbol_address, enum mapping id, const char func[], const char file[], int line) { + struct segment *s = &mapping[id]; + if (symbol_address < s->associated_pa || symbol_address > s->associated_pa + s->mask) { + printf("%s : Error symbol 0x%lx is not inside segment 0x%lx-0x%lx (mapping %d) (%s:%d)\n", + func, + symbol_address, + s->associated_pa, + s->associated_pa + s->mask, + id, + file, + line); + goto error; + } + return s->base + (symbol_address & s->mask); + +error: + panic(); + return 0ul; +} diff --git a/verif/tests/custom/rvh/vm.h b/verif/tests/custom/rvh/vm.h new file mode 100644 index 0000000000..a960871eab --- /dev/null +++ b/verif/tests/custom/rvh/vm.h @@ -0,0 +1,107 @@ +#ifndef HMODE_VM_H +#define HMODE_VM_H + +#include "types.h" + +#define PAGE_SIZE (0x1000) +#define FLAG_MASK (0xE0000000000003FF) +#define BUILD_PTE(destination, flags) ((((destination) >> 2) & (0xFFFFFFFFFFF000 >> 2)) | ((flags) & (FLAG_MASK))) +#define FLAG_NONE (0ul) +#define FLAG_VALID (1ul) +#define FLAG_READ (1ul << 1) +#define FLAG_WRITE (1ul << 2) +#define FLAG_EXEC (1ul << 3) +#define FLAG_USER (1ul << 4) +#define FLAG_GLOBAL (1ul << 5) +#define FLAG_ACCESS (1ul << 6) +#define FLAG_DIRTY (1ul << 7) +#define ENTRY_SIZE(level) (1ul << ((level) * 9 + 3)) +#define ENTRY_MASK(level) (0x1fful << ((level) * 9 + 3)) +#define ENTRY_MASK_LOWER(level) (ENTRY_SIZE(level) - 1) +#define ALIGN_ADDRESS(address, level) ((address) & (~ENTRY_MASK_LOWER(level))) +#define DEFAULT_FLAGS (FLAG_ACCESS | FLAG_DIRTY) +#define FLAG_SET(pte, flags) \ + ({ \ + (*pte) &= ~FLAG_MASK; \ + (*pte) |= (((flags) | DEFAULT_FLAGS) & FLAG_MASK); \ + }) + +static inline void flush_tlb(void) { + asm volatile("sfence.vma" : : : "memory"); +} +static inline void flush_tlb_addr(uint64_t va) { + asm volatile("sfence.vma %0" : : "r"(va) : "memory"); +} +static inline void flush_tlb_asid(uint64_t asid) { + asm volatile("sfence.vma x0,%0" : : "r"(asid) : "memory"); +} +static inline void flush_tlb_asid_addr(uint64_t asid, uint64_t va) { + asm volatile("sfence.vma %0,%1" : : "r"(va), "r"(asid) : "memory"); +} +static inline void flush_htlb(void) { + asm volatile("hfence.gvma" : : : "memory"); +} +static inline void flush_htlb_addr(uint64_t gpa) { + asm volatile("hfence.gvma %0" : : "r"(gpa >> 2) : "memory"); +} +static inline void flush_htlb_vmid(uint64_t vmid) { + asm volatile("hfence.gvma x0,%0" : : "r"(vmid >> 2) : "memory"); +} +static inline void flush_htlb_vmid_addr(uint64_t vmid, uint64_t gpa) { + asm volatile("hfence.gvma %0,%1" : : "r"(gpa >> 2), "r"(vmid) : "memory"); +} +static inline void flush_vtlb(void) { + asm volatile("hfence.vvma" : : : "memory"); +} +static inline void flush_vtlb_addr(uint64_t gva) { + asm volatile("hfence.vvma %0" : : "r"(gva) : "memory"); +} +static inline void flush_vtlb_asid(uint64_t asid) { + asm volatile("hfence.vvma x0,%0" : : "r"(asid) : "memory"); +} +static inline void flush_vtlb_asid_addr(uint64_t asid, uint64_t gva) { + asm volatile("hfence.vvma %0,%1" : : "r"(gva), "r"(asid) : "memory"); +} + +struct segment { + uint64_t base; + uint64_t mask; // size + 1 + uint64_t *associated_pte; + uint64_t associated_pa; + const char *comment; +}; + +enum mapping { + MAPPING_S_LVL1, + MAPPING_S_LVL2, + MAPPING_S_LVL3, + MAPPING_H_LVL1, + MAPPING_H_LVL2, + MAPPING_H_LVL3, + MAPPING_VS_LVL1, + MAPPING_VS_LVL2, + MAPPING_VS_LVL3, + MAPPING_MAX +}; + +extern struct segment mapping[MAPPING_MAX]; + +void build_page_tables(void); +uint64_t get_translated_symbol(uint64_t symbol_address, enum mapping id, const char func[], const char file[], int line); + +extern uint64_t __satp_lvl3; +extern uint64_t __satp_lvl2; +extern uint64_t __satp_lvl1; +extern uint64_t *const satp_lvl3; +extern uint64_t *const satp_lvl2; +extern uint64_t *const satp_lvl1; +#define SATP_ROOT ((uint64_t)satp_lvl3) +extern uint64_t __hgatp_lvl3; +extern uint64_t __hgatp_lvl2; +extern uint64_t __hgatp_lvl1; +extern uint64_t *const hgatp_lvl3; +extern uint64_t *const hgatp_lvl2; +extern uint64_t *const hgatp_lvl1; +#define HGATP_ROOT ((uint64_t)hgatp_lvl3) + +#endif //HMODE_VM_H