diff --git a/.github/workflows/test-regression.yml b/.github/workflows/test-regression.yml index a4d22851df6..6342b3600a2 100644 --- a/.github/workflows/test-regression.yml +++ b/.github/workflows/test-regression.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - test: ["hello_world", "hello_world_dccm", "hello_world_iccm", "cmark", "cmark_dccm", "cmark_iccm", "dhry"] + test: ["hello_world", "hello_world_dccm", "hello_world_iccm", "cmark", "cmark_dccm", "cmark_iccm", "dhry", "pmp"] coverage: ["branch", "toggle"] #TODO: add functional coverage env: DEBIAN_FRONTEND: "noninteractive" diff --git a/.github/workflows/test-uarch.yml b/.github/workflows/test-uarch.yml index fcb77c189c6..ce9cee50ee5 100644 --- a/.github/workflows/test-uarch.yml +++ b/.github/workflows/test-uarch.yml @@ -24,6 +24,7 @@ jobs: - "block/iccm" - "block/dccm" - "block/lib_axi4_to_ahb" + - "block/pmp" env: CCACHE_DIR: "/opt/verification/.cache/" VERILATOR_VERSION: v5.010 diff --git a/.gitignore b/.gitignore index e2176adf9ef..21e8aba7968 100644 --- a/.gitignore +++ b/.gitignore @@ -6,12 +6,17 @@ obj_dir *.log *.exe *.swp +*.sym verilator-build program.hex snapshots __pycache__ sim_build +sim-build* venv results.xml verification/sim verilator-cocotb-build +*.dat +*.xml +*.json diff --git a/configs/veer.config b/configs/veer.config index f705eb1cf73..ea8f72bfdd8 100755 --- a/configs/veer.config +++ b/configs/veer.config @@ -199,6 +199,8 @@ Parameters that can be set by the end user: if 1, minimize clock-gating to facilitate FPGA builds -text_in_iccm = {0, 1} Don't add ICCM preload code in generated link.ld + -set=pmp_entries = {0, 1, ..., 64 } + number of PMP entries Additionally the following may be set for bus masters and slaves using the -set=var=value option: @@ -266,6 +268,7 @@ my $text_in_iccm = 0; my $lsu2dma = 0; +my $pmp_entries=16; $ret_stack_size=8; $btb_enable=1; @@ -815,13 +818,15 @@ my @perf_events = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 45, 46, 47, 48, 49, 50, 54, 55, 56, 512, 513, 514, 515, 516); -foreach my $i (0 .. 3) { - $csr{"pmpcfg$i"} = { "exists" => "false" }; -} +# FIXME: PMP CSR handling -foreach my $i (0 .. 15) { - $csr{"pmpaddr$i"} = { "exists" => "false" }; -} +#foreach my $i (0 .. 3) { +# $csr{"pmpcfg$i"} = { "exists" => "false" }; +#} + +#foreach my $i (0 .. 15) { +# $csr{"pmpaddr$i"} = { "exists" => "false" }; +#} # }}} # Main config hash, with default values @@ -1035,6 +1040,7 @@ our %config = (#{{{ "SDVT_AHB" => "$ahb", }, "protection" => { # Design parms, Overridable - static MPU + "pmp_entries" => "$pmp_entries", "inst_access_enable0" => "0x0", "inst_access_addr0" => "0x00000000", "inst_access_mask0" => "0xffffffff", @@ -1134,6 +1140,7 @@ our %verilog_parms = ( "bitmanip_zbr" => '1', "fast_interrupt_redirect" => '1', + "pmp_entries" => '7', "inst_access_enable0" => '1', "inst_access_addr0" => '32', "inst_access_mask0" => '32', @@ -1357,6 +1364,7 @@ if ($config{core}{div_new}==0 && $config{core}{div_bit}!=1) { die("$helpusage\n\nFAIL: div_new=0 requires div_bit=1 ILLEGAL !!!\n\n"); } +$c=$config{protection}{pmp_entries}; if (!($c==64 || $c==16 || $c==0)) { die("$helpusage\n\nFAIL: pmp_entries must be either 0, 16 or 64 !!!\n\n"); } $c=$config{protection}{inst_access_addr0}; if ((hex($c)&0x3f) != 0) { die("$helpusage\n\nFAIL: inst_access_addr0 lower 6b must be 0s $c !!!\n\n"); } $c=$config{protection}{inst_access_addr1}; if ((hex($c)&0x3f) != 0) { die("$helpusage\n\nFAIL: inst_access_addr1 lower 6b must be 0s !!!\n\n"); } $c=$config{protection}{inst_access_addr2}; if ((hex($c)&0x3f) != 0) { die("$helpusage\n\nFAIL: inst_access_addr2 lower 6b must be 0s !!!\n\n"); } diff --git a/design/dec/csrdecode b/design/dec/csrdecode index 3b80870aa6a..146c6db2f6f 100644 --- a/design/dec/csrdecode +++ b/design/dec/csrdecode @@ -75,6 +75,11 @@ csr_perfvi = [00110011....] csr_mcountinhibit = [001100100000] csr_mfdht = [011111001110] csr_mfdhs = [011111001111] +csr_pmpcfg = [00111010....] +csr_pmpaddr0 = [00111011....] +csr_pmpaddr16 = [00111100....] +csr_pmpaddr32 = [00111101....] +csr_pmpaddr48 = [00111110....] .input @@ -171,6 +176,11 @@ csr_dicad0h csr_dicad0 csr_dicad1 csr_dicago + csr_pmpcfg + csr_pmpaddr0 + csr_pmpaddr16 + csr_pmpaddr32 + csr_pmpaddr48 valid_only presync postsync @@ -256,4 +266,10 @@ csr[ csr_perfvg ] = { valid_only } csr[ csr_perfvh ] = { valid_only } csr[ csr_perfvi ] = { valid_only } +csr[ csr_pmpcfg ] = { csr_pmpcfg } +csr[ csr_pmpaddr0 ] = { csr_pmpaddr0 } +csr[ csr_pmpaddr16 ] = { csr_pmpaddr16 } +csr[ csr_pmpaddr32 ] = { csr_pmpaddr32 } +csr[ csr_pmpaddr48 ] = { csr_pmpaddr48 } + .end diff --git a/design/dec/el2_dec.sv b/design/dec/el2_dec.sv index d22997a0e07..06206c2c9ad 100644 --- a/design/dec/el2_dec.sv +++ b/design/dec/el2_dec.sv @@ -28,419 +28,432 @@ //******************************************************************************** module el2_dec -import el2_pkg::*; + import el2_pkg::*; #( -`include "el2_param.vh" - ) - ( - input logic clk, // Clock only while core active. Through one clock header. For flops with second clock header built in. Connected to ACTIVE_L2CLK. - input logic active_clk, // Clock only while core active. Through two clock headers. For flops without second clock header built in. - input logic free_clk, // Clock always. Through two clock headers. For flops without second clock header built in. - input logic free_l2clk, // Clock always. Through one clock header. For flops with second header built in. - - input logic lsu_fastint_stall_any, // needed by lsu for 2nd pass of dma with ecc correction, stall next cycle - - output logic dec_extint_stall, // Stall on external interrupt - - output logic dec_i0_decode_d, // Valid instruction at D-stage and not blocked - output logic dec_pause_state_cg, // to top for active state clock gating + `include "el2_param.vh" +) ( + input logic clk, // Clock only while core active. Through one clock header. For flops with second clock header built in. Connected to ACTIVE_L2CLK. + input logic active_clk, // Clock only while core active. Through two clock headers. For flops without second clock header built in. + input logic free_clk, // Clock always. Through two clock headers. For flops without second clock header built in. + input logic free_l2clk, // Clock always. Through one clock header. For flops with second header built in. + + input logic lsu_fastint_stall_any, // needed by lsu for 2nd pass of dma with ecc correction, stall next cycle + + output logic dec_extint_stall, // Stall on external interrupt + + output logic dec_i0_decode_d, // Valid instruction at D-stage and not blocked + output logic dec_pause_state_cg, // to top for active state clock gating - output logic dec_tlu_core_empty, - - input logic rst_l, // reset, active low - input logic [31:1] rst_vec, // reset vector, from core pins - - input logic nmi_int, // NMI pin - input logic [31:1] nmi_vec, // NMI vector, from pins + output logic dec_tlu_core_empty, + + input logic rst_l, // reset, active low + input logic [31:1] rst_vec, // reset vector, from core pins - input logic i_cpu_halt_req, // Asynchronous Halt request to CPU - input logic i_cpu_run_req, // Asynchronous Restart request to CPU + input logic nmi_int, // NMI pin + input logic [31:1] nmi_vec, // NMI vector, from pins - output logic o_cpu_halt_status, // Halt status of core (pmu/fw) - output logic o_cpu_halt_ack, // Halt request ack - output logic o_cpu_run_ack, // Run request ack - output logic o_debug_mode_status, // Core to the PMU that core is in debug mode. When core is in debug mode, the PMU should refrain from sendng a halt or run request + input logic i_cpu_halt_req, // Asynchronous Halt request to CPU + input logic i_cpu_run_req, // Asynchronous Restart request to CPU - input logic [31:4] core_id, // CORE ID + output logic o_cpu_halt_status, // Halt status of core (pmu/fw) + output logic o_cpu_halt_ack, // Halt request ack + output logic o_cpu_run_ack, // Run request ack + output logic o_debug_mode_status, // Core to the PMU that core is in debug mode. When core is in debug mode, the PMU should refrain from sendng a halt or run request - // external MPC halt/run interface - input logic mpc_debug_halt_req, // Async halt request - input logic mpc_debug_run_req, // Async run request - input logic mpc_reset_run_req, // Run/halt after reset - output logic mpc_debug_halt_ack, // Halt ack - output logic mpc_debug_run_ack, // Run ack - output logic debug_brkpt_status, // debug breakpoint + input logic [31:4] core_id, // CORE ID - input logic exu_pmu_i0_br_misp, // slot 0 branch misp - input logic exu_pmu_i0_br_ataken, // slot 0 branch actual taken - input logic exu_pmu_i0_pc4, // slot 0 4 byte branch + // external MPC halt/run interface + input logic mpc_debug_halt_req, // Async halt request + input logic mpc_debug_run_req, // Async run request + input logic mpc_reset_run_req, // Run/halt after reset + output logic mpc_debug_halt_ack, // Halt ack + output logic mpc_debug_run_ack, // Run ack + output logic debug_brkpt_status, // debug breakpoint + input logic exu_pmu_i0_br_misp, // slot 0 branch misp + input logic exu_pmu_i0_br_ataken, // slot 0 branch actual taken + input logic exu_pmu_i0_pc4, // slot 0 4 byte branch - input logic lsu_nonblock_load_valid_m, // valid nonblock load at m - input logic [pt.LSU_NUM_NBLOAD_WIDTH-1:0] lsu_nonblock_load_tag_m, // -> corresponding tag - input logic lsu_nonblock_load_inv_r, // invalidate request for nonblock load r - input logic [pt.LSU_NUM_NBLOAD_WIDTH-1:0] lsu_nonblock_load_inv_tag_r, // -> corresponding tag - input logic lsu_nonblock_load_data_valid, // valid nonblock load data back - input logic lsu_nonblock_load_data_error, // nonblock load bus error - input logic [pt.LSU_NUM_NBLOAD_WIDTH-1:0] lsu_nonblock_load_data_tag, // -> corresponding tag - input logic [31:0] lsu_nonblock_load_data, // nonblock load data - input logic lsu_pmu_bus_trxn, // D side bus transaction - input logic lsu_pmu_bus_misaligned, // D side bus misaligned - input logic lsu_pmu_bus_error, // D side bus error - input logic lsu_pmu_bus_busy, // D side bus busy - input logic lsu_pmu_misaligned_m, // D side load or store misaligned - input logic lsu_pmu_load_external_m, // D side bus load - input logic lsu_pmu_store_external_m, // D side bus store - input logic dma_pmu_dccm_read, // DMA DCCM read - input logic dma_pmu_dccm_write, // DMA DCCM write - input logic dma_pmu_any_read, // DMA read - input logic dma_pmu_any_write, // DMA write + input logic lsu_nonblock_load_valid_m, // valid nonblock load at m + input logic [pt.LSU_NUM_NBLOAD_WIDTH-1:0] lsu_nonblock_load_tag_m, // -> corresponding tag + input logic lsu_nonblock_load_inv_r, // invalidate request for nonblock load r + input logic [pt.LSU_NUM_NBLOAD_WIDTH-1:0] lsu_nonblock_load_inv_tag_r, // -> corresponding tag + input logic lsu_nonblock_load_data_valid, // valid nonblock load data back + input logic lsu_nonblock_load_data_error, // nonblock load bus error + input logic [pt.LSU_NUM_NBLOAD_WIDTH-1:0] lsu_nonblock_load_data_tag, // -> corresponding tag + input logic [31:0] lsu_nonblock_load_data, // nonblock load data - input logic [31:1] lsu_fir_addr, // Fast int address - input logic [1:0] lsu_fir_error, // Fast int lookup error + input logic lsu_pmu_bus_trxn, // D side bus transaction + input logic lsu_pmu_bus_misaligned, // D side bus misaligned + input logic lsu_pmu_bus_error, // D side bus error + input logic lsu_pmu_bus_busy, // D side bus busy + input logic lsu_pmu_misaligned_m, // D side load or store misaligned + input logic lsu_pmu_load_external_m, // D side bus load + input logic lsu_pmu_store_external_m, // D side bus store + input logic dma_pmu_dccm_read, // DMA DCCM read + input logic dma_pmu_dccm_write, // DMA DCCM write + input logic dma_pmu_any_read, // DMA read + input logic dma_pmu_any_write, // DMA write - input logic ifu_pmu_instr_aligned, // aligned instructions - input logic ifu_pmu_fetch_stall, // fetch unit stalled - input logic ifu_pmu_ic_miss, // icache miss - input logic ifu_pmu_ic_hit, // icache hit - input logic ifu_pmu_bus_error, // Instruction side bus error - input logic ifu_pmu_bus_busy, // Instruction side bus busy - input logic ifu_pmu_bus_trxn, // Instruction side bus transaction + input logic [31:1] lsu_fir_addr, // Fast int address + input logic [ 1:0] lsu_fir_error, // Fast int lookup error - input logic ifu_ic_error_start, // IC single bit error - input logic ifu_iccm_rd_ecc_single_err, // ICCM single bit error + input logic ifu_pmu_instr_aligned, // aligned instructions + input logic ifu_pmu_fetch_stall, // fetch unit stalled + input logic ifu_pmu_ic_miss, // icache miss + input logic ifu_pmu_ic_hit, // icache hit + input logic ifu_pmu_bus_error, // Instruction side bus error + input logic ifu_pmu_bus_busy, // Instruction side bus busy + input logic ifu_pmu_bus_trxn, // Instruction side bus transaction - input logic [3:0] lsu_trigger_match_m, - input logic dbg_cmd_valid, // debugger abstract command valid - input logic dbg_cmd_write, // command is a write - input logic [1:0] dbg_cmd_type, // command type - input logic [31:0] dbg_cmd_addr, // command address - input logic [1:0] dbg_cmd_wrdata, // command write data, for fence/fence_i + input logic ifu_ic_error_start, // IC single bit error + input logic ifu_iccm_rd_ecc_single_err, // ICCM single bit error + input logic [ 3:0] lsu_trigger_match_m, + input logic dbg_cmd_valid, // debugger abstract command valid + input logic dbg_cmd_write, // command is a write + input logic [ 1:0] dbg_cmd_type, // command type + input logic [31:0] dbg_cmd_addr, // command address + input logic [ 1:0] dbg_cmd_wrdata, // command write data, for fence/fence_i - input logic ifu_i0_icaf, // icache access fault - input logic [1:0] ifu_i0_icaf_type, // icache access fault type - input logic ifu_i0_icaf_second, // i0 has access fault on second 2B of 4B inst - input logic ifu_i0_dbecc, // icache/iccm double-bit error + input logic ifu_i0_icaf, // icache access fault + input logic [1:0] ifu_i0_icaf_type, // icache access fault type - input logic lsu_idle_any, // lsu idle for halting + input logic ifu_i0_icaf_second, // i0 has access fault on second 2B of 4B inst + input logic ifu_i0_dbecc, // icache/iccm double-bit error - input el2_br_pkt_t i0_brp, // branch packet - input logic [pt.BTB_ADDR_HI:pt.BTB_ADDR_LO] ifu_i0_bp_index, // BP index - input logic [pt.BHT_GHR_SIZE-1:0] ifu_i0_bp_fghr, // BP FGHR - input logic [pt.BTB_BTAG_SIZE-1:0] ifu_i0_bp_btag, // BP tag - input logic [$clog2(pt.BTB_SIZE)-1:0] ifu_i0_fa_index, // Fully associt btb index + input logic lsu_idle_any, // lsu idle for halting - input el2_lsu_error_pkt_t lsu_error_pkt_r, // LSU exception/error packet - input logic lsu_single_ecc_error_incr, // LSU inc SB error counter + input el2_br_pkt_t i0_brp, // branch packet + input logic [pt.BTB_ADDR_HI:pt.BTB_ADDR_LO] ifu_i0_bp_index, // BP index + input logic [ pt.BHT_GHR_SIZE-1:0] ifu_i0_bp_fghr, // BP FGHR + input logic [ pt.BTB_BTAG_SIZE-1:0] ifu_i0_bp_btag, // BP tag + input logic [ $clog2(pt.BTB_SIZE)-1:0] ifu_i0_fa_index, // Fully associt btb index - input logic lsu_imprecise_error_load_any, // LSU imprecise load bus error - input logic lsu_imprecise_error_store_any, // LSU imprecise store bus error - input logic [31:0] lsu_imprecise_error_addr_any, // LSU imprecise bus error address + input el2_lsu_error_pkt_t lsu_error_pkt_r, // LSU exception/error packet + input logic lsu_single_ecc_error_incr, // LSU inc SB error counter - input logic [31:0] exu_div_result, // final div result - input logic exu_div_wren, // Divide write enable to GPR + input logic lsu_imprecise_error_load_any, // LSU imprecise load bus error + input logic lsu_imprecise_error_store_any, // LSU imprecise store bus error + input logic [31:0] lsu_imprecise_error_addr_any, // LSU imprecise bus error address - input logic [31:0] exu_csr_rs1_x, // rs1 for csr instruction + input logic [31:0] exu_div_result, // final div result + input logic exu_div_wren, // Divide write enable to GPR - input logic [31:0] lsu_result_m, // load result - input logic [31:0] lsu_result_corr_r, // load result - corrected load data + input logic [31:0] exu_csr_rs1_x, // rs1 for csr instruction - input logic lsu_load_stall_any, // This is for blocking loads - input logic lsu_store_stall_any, // This is for blocking stores - input logic dma_dccm_stall_any, // stall any load/store at decode, pmu event - input logic dma_iccm_stall_any, // iccm stalled, pmu event + input logic [31:0] lsu_result_m, // load result + input logic [31:0] lsu_result_corr_r, // load result - corrected load data - input logic iccm_dma_sb_error, // ICCM DMA single bit error + input logic lsu_load_stall_any, // This is for blocking loads + input logic lsu_store_stall_any, // This is for blocking stores + input logic dma_dccm_stall_any, // stall any load/store at decode, pmu event + input logic dma_iccm_stall_any, // iccm stalled, pmu event - input logic exu_flush_final, // slot0 flush + input logic iccm_dma_sb_error, // ICCM DMA single bit error - input logic [31:1] exu_npc_r, // next PC + input logic exu_flush_final, // slot0 flush - input logic [31:0] exu_i0_result_x, // alu result x + input logic [31:1] exu_npc_r, // next PC + input logic [31:0] exu_i0_result_x, // alu result x - input logic ifu_i0_valid, // fetch valids to instruction buffer - input logic [31:0] ifu_i0_instr, // fetch inst's to instruction buffer - input logic [31:1] ifu_i0_pc, // pc's for instruction buffer - input logic ifu_i0_pc4, // indication of 4B or 2B for corresponding inst - input logic [31:1] exu_i0_pc_x, // pc's for e1 from the alu's - input logic mexintpend, // External interrupt pending - input logic timer_int, // Timer interrupt pending (from pin) - input logic soft_int, // Software interrupt pending (from pin) + input logic ifu_i0_valid, // fetch valids to instruction buffer + input logic [31:0] ifu_i0_instr, // fetch inst's to instruction buffer + input logic [31:1] ifu_i0_pc, // pc's for instruction buffer + input logic ifu_i0_pc4, // indication of 4B or 2B for corresponding inst + input logic [31:1] exu_i0_pc_x, // pc's for e1 from the alu's - input logic [7:0] pic_claimid, // PIC claimid - input logic [3:0] pic_pl, // PIC priv level - input logic mhwakeup, // High priority wakeup + input logic mexintpend, // External interrupt pending + input logic timer_int, // Timer interrupt pending (from pin) + input logic soft_int, // Software interrupt pending (from pin) - output logic [3:0] dec_tlu_meicurpl, // to PIC, Current priv level - output logic [3:0] dec_tlu_meipt, // to PIC + input logic [7:0] pic_claimid, // PIC claimid + input logic [3:0] pic_pl, // PIC priv level + input logic mhwakeup, // High priority wakeup - input logic [70:0] ifu_ic_debug_rd_data, // diagnostic icache read data - input logic ifu_ic_debug_rd_data_valid, // diagnostic icache read data valid - output el2_cache_debug_pkt_t dec_tlu_ic_diag_pkt, // packet of DICAWICS, DICAD0/1, DICAGO info for icache diagnostics + output logic [3:0] dec_tlu_meicurpl, // to PIC, Current priv level + output logic [3:0] dec_tlu_meipt, // to PIC + input logic [70:0] ifu_ic_debug_rd_data, // diagnostic icache read data + input logic ifu_ic_debug_rd_data_valid, // diagnostic icache read data valid + output el2_cache_debug_pkt_t dec_tlu_ic_diag_pkt, // packet of DICAWICS, DICAD0/1, DICAGO info for icache diagnostics -// Debug start - input logic dbg_halt_req, // DM requests a halt - input logic dbg_resume_req, // DM requests a resume - input logic ifu_miss_state_idle, // I-side miss buffer empty - output logic dec_tlu_dbg_halted, // Core is halted and ready for debug command - output logic dec_tlu_debug_mode, // Core is in debug mode - output logic dec_tlu_resume_ack, // Resume acknowledge - output logic dec_tlu_flush_noredir_r, // Tell fetch to idle on this flush - output logic dec_tlu_mpc_halted_only, // Core is halted only due to MPC - output logic dec_tlu_flush_leak_one_r, // single step - output logic dec_tlu_flush_err_r, // iside perr/ecc rfpc - output logic [31:2] dec_tlu_meihap, // Fast ext int base + // Debug start + input logic dbg_halt_req, // DM requests a halt + input logic dbg_resume_req, // DM requests a resume + input logic ifu_miss_state_idle, // I-side miss buffer empty - output logic dec_debug_wdata_rs1_d, // insert debug write data into rs1 at decode + output logic dec_tlu_dbg_halted, // Core is halted and ready for debug command + output logic dec_tlu_debug_mode, // Core is in debug mode + output logic dec_tlu_resume_ack, // Resume acknowledge + output logic dec_tlu_flush_noredir_r, // Tell fetch to idle on this flush + output logic dec_tlu_mpc_halted_only, // Core is halted only due to MPC + output logic dec_tlu_flush_leak_one_r, // single step + output logic dec_tlu_flush_err_r, // iside perr/ecc rfpc + output logic [31:2] dec_tlu_meihap, // Fast ext int base - output logic [31:0] dec_dbg_rddata, // debug command read data + output logic dec_debug_wdata_rs1_d, // insert debug write data into rs1 at decode - output logic dec_dbg_cmd_done, // abstract command is done - output logic dec_dbg_cmd_fail, // abstract command failed (illegal reg address) + output logic [31:0] dec_dbg_rddata, // debug command read data - output el2_trigger_pkt_t [3:0] trigger_pkt_any, // info needed by debug trigger blocks + output logic dec_dbg_cmd_done, // abstract command is done + output logic dec_dbg_cmd_fail, // abstract command failed (illegal reg address) - output logic dec_tlu_force_halt, // halt has been forced -// Debug end - // branch info from pipe0 for errors or counter updates - input logic [1:0] exu_i0_br_hist_r, // history - input logic exu_i0_br_error_r, // error - input logic exu_i0_br_start_error_r, // start error - input logic exu_i0_br_valid_r, // valid - input logic exu_i0_br_mp_r, // mispredict - input logic exu_i0_br_middle_r, // middle of bank + output el2_trigger_pkt_t [3:0] trigger_pkt_any, // info needed by debug trigger blocks - // branch info from pipe1 for errors or counter updates + output logic dec_tlu_force_halt, // halt has been forced + // Debug end + // branch info from pipe0 for errors or counter updates + input logic [1:0] exu_i0_br_hist_r, // history + input logic exu_i0_br_error_r, // error + input logic exu_i0_br_start_error_r, // start error + input logic exu_i0_br_valid_r, // valid + input logic exu_i0_br_mp_r, // mispredict + input logic exu_i0_br_middle_r, // middle of bank - input logic exu_i0_br_way_r, // way hit or repl + // branch info from pipe1 for errors or counter updates - output logic dec_i0_rs1_en_d, // Qualify GPR RS1 data - output logic dec_i0_rs2_en_d, // Qualify GPR RS2 data - output logic [31:0] gpr_i0_rs1_d, // gpr rs1 data - output logic [31:0] gpr_i0_rs2_d, // gpr rs2 data + input logic exu_i0_br_way_r, // way hit or repl - output logic [31:0] dec_i0_immed_d, // immediate data - output logic [12:1] dec_i0_br_immed_d, // br immediate data + output logic dec_i0_rs1_en_d, // Qualify GPR RS1 data + output logic dec_i0_rs2_en_d, // Qualify GPR RS2 data + output logic [31:0] gpr_i0_rs1_d, // gpr rs1 data + output logic [31:0] gpr_i0_rs2_d, // gpr rs2 data - output el2_alu_pkt_t i0_ap, // alu packet + output logic [31:0] dec_i0_immed_d, // immediate data + output logic [12:1] dec_i0_br_immed_d, // br immediate data - output logic dec_i0_alu_decode_d, // schedule on D-stage alu - output logic dec_i0_branch_d, // Branch in D-stage + output el2_alu_pkt_t i0_ap, // alu packet - output logic dec_i0_select_pc_d, // select pc onto rs1 for jal's + output logic dec_i0_alu_decode_d, // schedule on D-stage alu + output logic dec_i0_branch_d, // Branch in D-stage - output logic [31:1] dec_i0_pc_d, // pc's at decode - output logic [3:0] dec_i0_rs1_bypass_en_d, // rs1 bypass enable - output logic [3:0] dec_i0_rs2_bypass_en_d, // rs2 bypass enable + output logic dec_i0_select_pc_d, // select pc onto rs1 for jal's - output logic [31:0] dec_i0_result_r, // Result R-stage + output logic [31:1] dec_i0_pc_d, // pc's at decode + output logic [ 3:0] dec_i0_rs1_bypass_en_d, // rs1 bypass enable + output logic [ 3:0] dec_i0_rs2_bypass_en_d, // rs2 bypass enable - output el2_lsu_pkt_t lsu_p, // lsu packet - output logic dec_qual_lsu_d, // LSU instruction at D. Use to quiet LSU operands - output el2_mul_pkt_t mul_p, // mul packet - output el2_div_pkt_t div_p, // div packet - output logic dec_div_cancel, // cancel divide operation + output logic [31:0] dec_i0_result_r, // Result R-stage - output logic [11:0] dec_lsu_offset_d, // 12b offset for load/store addresses + output el2_lsu_pkt_t lsu_p, // lsu packet + output logic dec_qual_lsu_d, // LSU instruction at D. Use to quiet LSU operands + output el2_mul_pkt_t mul_p, // mul packet + output el2_div_pkt_t div_p, // div packet + output logic dec_div_cancel, // cancel divide operation - output logic dec_csr_ren_d, // CSR read enable - output logic [31:0] dec_csr_rddata_d, // CSR read data + output logic [11:0] dec_lsu_offset_d, // 12b offset for load/store addresses - output logic dec_tlu_flush_lower_r, // tlu flush due to late mp, exception, rfpc, or int - output logic dec_tlu_flush_lower_wb, - output logic [31:1] dec_tlu_flush_path_r, // tlu flush target - output logic dec_tlu_i0_kill_writeb_r, // I0 is flushed, don't writeback any results to arch state - output logic dec_tlu_fence_i_r, // flush is a fence_i rfnpc, flush icache + output logic dec_csr_ren_d, // CSR read enable + output logic [31:0] dec_csr_rddata_d, // CSR read data - output logic [31:1] pred_correct_npc_x, // npc if prediction is correct at e2 stage + output logic dec_tlu_flush_lower_r, // tlu flush due to late mp, exception, rfpc, or int + output logic dec_tlu_flush_lower_wb, + output logic [31:1] dec_tlu_flush_path_r, // tlu flush target + output logic dec_tlu_i0_kill_writeb_r, // I0 is flushed, don't writeback any results to arch state + output logic dec_tlu_fence_i_r, // flush is a fence_i rfnpc, flush icache - output el2_br_tlu_pkt_t dec_tlu_br0_r_pkt, // slot 0 branch predictor update packet + output logic [31:1] pred_correct_npc_x, // npc if prediction is correct at e2 stage - output logic dec_tlu_perfcnt0, // toggles when slot0 perf counter 0 has an event inc - output logic dec_tlu_perfcnt1, // toggles when slot0 perf counter 1 has an event inc - output logic dec_tlu_perfcnt2, // toggles when slot0 perf counter 2 has an event inc - output logic dec_tlu_perfcnt3, // toggles when slot0 perf counter 3 has an event inc + output el2_br_tlu_pkt_t dec_tlu_br0_r_pkt, // slot 0 branch predictor update packet - output el2_predict_pkt_t dec_i0_predict_p_d, // prediction packet to alus - output logic [pt.BHT_GHR_SIZE-1:0] i0_predict_fghr_d, // DEC predict fghr - output logic [pt.BTB_ADDR_HI:pt.BTB_ADDR_LO] i0_predict_index_d, // DEC predict index - output logic [pt.BTB_BTAG_SIZE-1:0] i0_predict_btag_d, // DEC predict branch tag + output logic dec_tlu_perfcnt0, // toggles when slot0 perf counter 0 has an event inc + output logic dec_tlu_perfcnt1, // toggles when slot0 perf counter 1 has an event inc + output logic dec_tlu_perfcnt2, // toggles when slot0 perf counter 2 has an event inc + output logic dec_tlu_perfcnt3, // toggles when slot0 perf counter 3 has an event inc - output logic [$clog2(pt.BTB_SIZE)-1:0] dec_fa_error_index, // Fully associt btb error index + output el2_predict_pkt_t dec_i0_predict_p_d, // prediction packet to alus + output logic [pt.BHT_GHR_SIZE-1:0] i0_predict_fghr_d, // DEC predict fghr + output logic [pt.BTB_ADDR_HI:pt.BTB_ADDR_LO] i0_predict_index_d, // DEC predict index + output logic [pt.BTB_BTAG_SIZE-1:0] i0_predict_btag_d, // DEC predict branch tag - output logic dec_lsu_valid_raw_d, + output logic [$clog2(pt.BTB_SIZE)-1:0] dec_fa_error_index, // Fully associt btb error index - output logic [31:0] dec_tlu_mrac_ff, // CSR for memory region control + output logic dec_lsu_valid_raw_d, - output logic [1:0] dec_data_en, // clock-gate control logic - output logic [1:0] dec_ctl_en, + output logic [31:0] dec_tlu_mrac_ff, // CSR for memory region control - input logic [15:0] ifu_i0_cinst, // 16b compressed instruction + output logic [1:0] dec_data_en, // clock-gate control logic + output logic [1:0] dec_ctl_en, - output el2_trace_pkt_t trace_rv_trace_pkt, // trace packet + input logic [15:0] ifu_i0_cinst, // 16b compressed instruction - // feature disable from mfdc - output logic dec_tlu_external_ldfwd_disable, // disable external load forwarding - output logic dec_tlu_sideeffect_posted_disable, // disable posted stores to side-effect address - output logic dec_tlu_core_ecc_disable, // disable core ECC - output logic dec_tlu_bpred_disable, // disable branch prediction - output logic dec_tlu_wb_coalescing_disable, // disable writebuffer coalescing - output logic [2:0] dec_tlu_dma_qos_prty, // DMA QoS priority coming from MFDC [18:16] + output el2_trace_pkt_t trace_rv_trace_pkt, // trace packet - // clock gating overrides from mcgc - output logic dec_tlu_misc_clk_override, // override misc clock domain gating - output logic dec_tlu_ifu_clk_override, // override fetch clock domain gating - output logic dec_tlu_lsu_clk_override, // override load/store clock domain gating - output logic dec_tlu_bus_clk_override, // override bus clock domain gating - output logic dec_tlu_pic_clk_override, // override PIC clock domain gating - output logic dec_tlu_picio_clk_override, // override PICIO clock domain gating - output logic dec_tlu_dccm_clk_override, // override DCCM clock domain gating - output logic dec_tlu_icm_clk_override, // override ICCM clock domain gating + // PMP signals + output el2_pmp_cfg_pkt_t pmp_pmpcfg [pt.PMP_ENTRIES], + output logic [31:0] pmp_pmpaddr[pt.PMP_ENTRIES], - output logic dec_tlu_i0_commit_cmt, // committed i0 instruction - input logic scan_mode // Flop scan mode control + // feature disable from mfdc + output logic dec_tlu_external_ldfwd_disable, // disable external load forwarding + output logic dec_tlu_sideeffect_posted_disable, // disable posted stores to side-effect address + output logic dec_tlu_core_ecc_disable, // disable core ECC + output logic dec_tlu_bpred_disable, // disable branch prediction + output logic dec_tlu_wb_coalescing_disable, // disable writebuffer coalescing + output logic [2:0] dec_tlu_dma_qos_prty, // DMA QoS priority coming from MFDC [18:16] - ); + // clock gating overrides from mcgc + output logic dec_tlu_misc_clk_override, // override misc clock domain gating + output logic dec_tlu_ifu_clk_override, // override fetch clock domain gating + output logic dec_tlu_lsu_clk_override, // override load/store clock domain gating + output logic dec_tlu_bus_clk_override, // override bus clock domain gating + output logic dec_tlu_pic_clk_override, // override PIC clock domain gating + output logic dec_tlu_picio_clk_override, // override PICIO clock domain gating + output logic dec_tlu_dccm_clk_override, // override DCCM clock domain gating + output logic dec_tlu_icm_clk_override, // override ICCM clock domain gating + output logic dec_tlu_i0_commit_cmt, // committed i0 instruction + input logic scan_mode // Flop scan mode control - logic dec_tlu_dec_clk_override; // to and from dec blocks - logic clk_override; +); - logic dec_ib0_valid_d; - logic dec_pmu_instr_decoded; - logic dec_pmu_decode_stall; - logic dec_pmu_presync_stall; - logic dec_pmu_postsync_stall; + logic dec_tlu_dec_clk_override; // to and from dec blocks + logic clk_override; - logic dec_tlu_wr_pause_r; // CSR write to pause reg is at R. + logic dec_ib0_valid_d; - logic [4:0] dec_i0_rs1_d; - logic [4:0] dec_i0_rs2_d; + logic dec_pmu_instr_decoded; + logic dec_pmu_decode_stall; + logic dec_pmu_presync_stall; + logic dec_pmu_postsync_stall; - logic [31:0] dec_i0_instr_d; + logic dec_tlu_wr_pause_r; // CSR write to pause reg is at R. - logic dec_tlu_trace_disable; - logic dec_tlu_pipelining_disable; + logic [4:0] dec_i0_rs1_d; + logic [4:0] dec_i0_rs2_d; + logic [31:0] dec_i0_instr_d; - logic [4:0] dec_i0_waddr_r; - logic dec_i0_wen_r; - logic [31:0] dec_i0_wdata_r; - logic dec_csr_wen_r; // csr write enable at wb - logic [11:0] dec_csr_wraddr_r; // write address for csryes - logic [31:0] dec_csr_wrdata_r; // csr write data at wb + logic dec_tlu_trace_disable; + logic dec_tlu_pipelining_disable; - logic [11:0] dec_csr_rdaddr_d; // read address for csr - logic dec_csr_legal_d; // csr indicates legal operation - logic dec_csr_wen_unq_d; // valid csr with write - for csr legal - logic dec_csr_any_unq_d; // valid csr - for csr legal - logic dec_csr_stall_int_ff; // csr is mie/mstatus + logic [4:0] dec_i0_waddr_r; + logic dec_i0_wen_r; + logic [31:0] dec_i0_wdata_r; + logic dec_csr_wen_r; // csr write enable at wb + logic [11:0] dec_csr_wraddr_r; // write address for csryes + logic [31:0] dec_csr_wrdata_r; // csr write data at wb - el2_trap_pkt_t dec_tlu_packet_r; + logic [11:0] dec_csr_rdaddr_d; // read address for csr + logic dec_csr_legal_d; // csr indicates legal operation - logic dec_i0_pc4_d; - logic dec_tlu_presync_d; - logic dec_tlu_postsync_d; - logic dec_tlu_debug_stall; + logic dec_csr_wen_unq_d; // valid csr with write - for csr legal + logic dec_csr_any_unq_d; // valid csr - for csr legal + logic dec_csr_stall_int_ff; // csr is mie/mstatus - logic [31:0] dec_illegal_inst; + el2_trap_pkt_t dec_tlu_packet_r; - logic dec_i0_icaf_d; + logic dec_i0_pc4_d; + logic dec_tlu_presync_d; + logic dec_tlu_postsync_d; + logic dec_tlu_debug_stall; - logic dec_i0_dbecc_d; - logic dec_i0_icaf_second_d; - logic [3:0] dec_i0_trigger_match_d; - logic dec_debug_fence_d; - logic dec_nonblock_load_wen; - logic [4:0] dec_nonblock_load_waddr; - logic dec_tlu_flush_pause_r; - el2_br_pkt_t dec_i0_brp; - logic [pt.BTB_ADDR_HI:pt.BTB_ADDR_LO] dec_i0_bp_index; - logic [pt.BHT_GHR_SIZE-1:0] dec_i0_bp_fghr; - logic [pt.BTB_BTAG_SIZE-1:0] dec_i0_bp_btag; - logic [$clog2(pt.BTB_SIZE)-1:0] dec_i0_bp_fa_index; // Fully associt btb index + logic [31:0] dec_illegal_inst; - logic [31:1] dec_tlu_i0_pc_r; - logic dec_tlu_i0_kill_writeb_wb; - logic dec_tlu_i0_valid_r; + logic dec_i0_icaf_d; - logic dec_pause_state; + logic dec_i0_dbecc_d; + logic dec_i0_icaf_second_d; + logic [3:0] dec_i0_trigger_match_d; + logic dec_debug_fence_d; + logic dec_nonblock_load_wen; + logic [4:0] dec_nonblock_load_waddr; + logic dec_tlu_flush_pause_r; + el2_br_pkt_t dec_i0_brp; + logic [pt.BTB_ADDR_HI:pt.BTB_ADDR_LO] dec_i0_bp_index; + logic [pt.BHT_GHR_SIZE-1:0] dec_i0_bp_fghr; + logic [pt.BTB_BTAG_SIZE-1:0] dec_i0_bp_btag; + logic [$clog2(pt.BTB_SIZE)-1:0] dec_i0_bp_fa_index; // Fully associt btb index - logic [1:0] dec_i0_icaf_type_d; // i0 instruction access fault type + logic [31:1] dec_tlu_i0_pc_r; + logic dec_tlu_i0_kill_writeb_wb; + logic dec_tlu_i0_valid_r; - logic dec_tlu_flush_extint; // Fast ext int started + logic dec_pause_state; - logic [31:0] dec_i0_inst_wb; - logic [31:1] dec_i0_pc_wb; - logic dec_tlu_i0_valid_wb1, dec_tlu_int_valid_wb1; - logic [4:0] dec_tlu_exc_cause_wb1; - logic [31:0] dec_tlu_mtval_wb1; - logic dec_tlu_i0_exc_valid_wb1; + logic [1:0] dec_i0_icaf_type_d; // i0 instruction access fault type - logic [4:0] div_waddr_wb; - logic dec_div_active; + logic dec_tlu_flush_extint; // Fast ext int started - logic dec_debug_valid_d; + logic [31:0] dec_i0_inst_wb; + logic [31:1] dec_i0_pc_wb; + logic dec_tlu_i0_valid_wb1, dec_tlu_int_valid_wb1; + logic [ 4:0] dec_tlu_exc_cause_wb1; + logic [31:0] dec_tlu_mtval_wb1; + logic dec_tlu_i0_exc_valid_wb1; - assign clk_override = dec_tlu_dec_clk_override; + logic [ 4:0] div_waddr_wb; + logic dec_div_active; + logic dec_debug_valid_d; - assign dec_dbg_rddata[31:0] = dec_i0_wdata_r[31:0]; + assign clk_override = dec_tlu_dec_clk_override; - el2_dec_ib_ctl #(.pt(pt)) instbuff (.*); + assign dec_dbg_rddata[31:0] = dec_i0_wdata_r[31:0]; - el2_dec_decode_ctl #(.pt(pt)) decode (.*); + el2_dec_ib_ctl #(.pt(pt)) instbuff (.*); - el2_dec_tlu_ctl #(.pt(pt)) tlu (.*); + el2_dec_decode_ctl #(.pt(pt)) decode (.*); - el2_dec_gpr_ctl #(.pt(pt)) arf (.*, - // inputs - .raddr0(dec_i0_rs1_d[4:0]), - .raddr1(dec_i0_rs2_d[4:0]), + el2_dec_tlu_ctl #(.pt(pt)) tlu (.*); - .wen0(dec_i0_wen_r), .waddr0(dec_i0_waddr_r[4:0]), .wd0(dec_i0_wdata_r[31:0]), - .wen1(dec_nonblock_load_wen), .waddr1(dec_nonblock_load_waddr[4:0]), .wd1(lsu_nonblock_load_data[31:0]), - .wen2(exu_div_wren), .waddr2(div_waddr_wb), .wd2(exu_div_result[31:0]), - // outputs - .rd0(gpr_i0_rs1_d[31:0]), .rd1(gpr_i0_rs2_d[31:0]) - ); + el2_dec_gpr_ctl #( + .pt(pt) + ) arf ( + .*, + // inputs + .raddr0(dec_i0_rs1_d[4:0]), + .raddr1(dec_i0_rs2_d[4:0]), + .wen0(dec_i0_wen_r), + .waddr0(dec_i0_waddr_r[4:0]), + .wd0(dec_i0_wdata_r[31:0]), + .wen1(dec_nonblock_load_wen), + .waddr1(dec_nonblock_load_waddr[4:0]), + .wd1(lsu_nonblock_load_data[31:0]), + .wen2(exu_div_wren), + .waddr2(div_waddr_wb), + .wd2(exu_div_result[31:0]), -// Trigger + // outputs + .rd0(gpr_i0_rs1_d[31:0]), + .rd1(gpr_i0_rs2_d[31:0]) + ); - el2_dec_trigger #(.pt(pt)) dec_trigger (.*); + // Trigger + el2_dec_trigger #(.pt(pt)) dec_trigger (.*); -// trace - assign trace_rv_trace_pkt.trace_rv_i_insn_ip = dec_i0_inst_wb[31:0]; - assign trace_rv_trace_pkt.trace_rv_i_address_ip = { dec_i0_pc_wb[31:1], 1'b0}; - assign trace_rv_trace_pkt.trace_rv_i_valid_ip = dec_tlu_int_valid_wb1 | dec_tlu_i0_valid_wb1 | dec_tlu_i0_exc_valid_wb1; - assign trace_rv_trace_pkt.trace_rv_i_exception_ip = dec_tlu_int_valid_wb1 | dec_tlu_i0_exc_valid_wb1; - assign trace_rv_trace_pkt.trace_rv_i_ecause_ip = dec_tlu_exc_cause_wb1[4:0]; // replicate across ports - assign trace_rv_trace_pkt.trace_rv_i_interrupt_ip = dec_tlu_int_valid_wb1; - assign trace_rv_trace_pkt.trace_rv_i_tval_ip = dec_tlu_mtval_wb1[31:0]; // replicate across ports + // trace + assign trace_rv_trace_pkt.trace_rv_i_insn_ip = dec_i0_inst_wb[31:0]; + assign trace_rv_trace_pkt.trace_rv_i_address_ip = {dec_i0_pc_wb[31:1], 1'b0}; + assign trace_rv_trace_pkt.trace_rv_i_valid_ip = dec_tlu_int_valid_wb1 | dec_tlu_i0_valid_wb1 | dec_tlu_i0_exc_valid_wb1; + assign trace_rv_trace_pkt.trace_rv_i_exception_ip = dec_tlu_int_valid_wb1 | dec_tlu_i0_exc_valid_wb1; + assign trace_rv_trace_pkt.trace_rv_i_ecause_ip = dec_tlu_exc_cause_wb1[4:0]; // replicate across ports + assign trace_rv_trace_pkt.trace_rv_i_interrupt_ip = dec_tlu_int_valid_wb1; + assign trace_rv_trace_pkt.trace_rv_i_tval_ip = dec_tlu_mtval_wb1[31:0]; // replicate across ports -// end trace -endmodule // el2_dec + // end trace + + +endmodule // el2_dec diff --git a/design/dec/el2_dec_pmp_ctl.sv b/design/dec/el2_dec_pmp_ctl.sv new file mode 100644 index 00000000000..537bcec0095 --- /dev/null +++ b/design/dec/el2_dec_pmp_ctl.sv @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Antmicro +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + +//******************************************************************************** +// el2_dec_pmp_ctl.sv +// +// +// Function: Physical Memory Protection CSRs +// Comments: +// +//******************************************************************************** + + +module el2_dec_pmp_ctl +import el2_pkg::*; +#( +`include "el2_param.vh" + ) + ( + input logic clk, + input logic free_l2clk, + input logic csr_wr_clk, + input logic rst_l, + input logic dec_csr_wen_r_mod, // csr write enable at wb + input logic [11:0] dec_csr_wraddr_r, // write address for csr + input logic [31:0] dec_csr_wrdata_r, // csr write data at wb + input logic [11:0] dec_csr_rdaddr_d, // read address for csr + + input logic csr_pmpcfg, + input logic csr_pmpaddr0, + input logic csr_pmpaddr16, + input logic csr_pmpaddr32, + input logic csr_pmpaddr48, + + input logic dec_pause_state, // Paused + input logic dec_tlu_pmu_fw_halted, // pmu/fw halted + input logic internal_dbg_halt_timers, // debug halted + + output logic [31:0] dec_pmp_rddata_d, // pmp CSR read data + output logic dec_pmp_read_d, // pmp CSR address match + + output el2_pmp_cfg_pkt_t pmp_pmpcfg [pt.PMP_ENTRIES], + output logic [31:0] pmp_pmpaddr [pt.PMP_ENTRIES], + + input logic scan_mode + ); + + logic wr_pmpcfg_r; + logic [3:0] wr_pmpcfg_group; + + logic wr_pmpaddr0_sel; + logic wr_pmpaddr16_sel; + logic wr_pmpaddr32_sel; + logic wr_pmpaddr48_sel; + logic wr_pmpaddr_r; + logic [1:0] wr_pmpaddr_quarter; + logic [5:0] wr_pmpaddr_address; + + logic [3:0] pmp_quarter_rdaddr; + logic [31:0] pmp_pmpcfg_rddata; + + // ---------------------------------------------------------------------- + // PMPCFGx (RW) + // [31:24] : PMP entry (x*4 + 3) configuration + // [23:16] : PMP entry (x*4 + 2) configuration + // [15:8] : PMP entry (x*4 + 1) configuration + // [7:0] : PMP entry (x*4 + 0) configuration + + localparam PMPCFG = 12'h3a0; + + assign wr_pmpcfg_r = dec_csr_wen_r_mod & (dec_csr_wraddr_r[11:4] == PMPCFG[11:4]); + assign wr_pmpcfg_group = dec_csr_wraddr_r[3:0]; // selects group of 4 pmpcfg entries (group 1 -> entries 4-7; up to 16 groups) + + for (genvar entry_idx = 0; entry_idx < pt.PMP_ENTRIES; entry_idx++) begin : gen_pmpcfg_ff + rvdffe #(8) pmpcfg_ff (.*, .clk(free_l2clk), + .en(wr_pmpcfg_r & (wr_pmpcfg_group == entry_idx[5:2]) & (~pmp_pmpcfg[entry_idx].lock)), + .din(dec_csr_wrdata_r[(entry_idx[1:0]*8)+7:(entry_idx[1:0]*8)+0] & 8'b10011111), + .dout(pmp_pmpcfg[entry_idx])); + end + + // ---------------------------------------------------------------------- + // PMPADDRx (RW) + // [31:0] : PMP entry (x) address selector (word addressing) + // + // NOTE: VeeR-EL2 uses 32-bit physical addressing, register bits 31:30 mapping + // to bits 33:32 of the physical address are always set to 0. (WARL) + + localparam PMPADDR0 = 12'h3b0; + localparam PMPADDR16 = 12'h3c0; + localparam PMPADDR32 = 12'h3d0; + localparam PMPADDR48 = 12'h3e0; + + assign wr_pmpaddr0_sel = dec_csr_wraddr_r[11:4] == PMPADDR0[11:4]; + assign wr_pmpaddr16_sel = dec_csr_wraddr_r[11:4] == PMPADDR16[11:4]; + assign wr_pmpaddr32_sel = dec_csr_wraddr_r[11:4] == PMPADDR32[11:4]; + assign wr_pmpaddr48_sel = dec_csr_wraddr_r[11:4] == PMPADDR48[11:4]; + assign wr_pmpaddr_r = dec_csr_wen_r_mod & (wr_pmpaddr0_sel | wr_pmpaddr16_sel | wr_pmpaddr32_sel | wr_pmpaddr48_sel); + + assign wr_pmpaddr_quarter[0] = wr_pmpaddr16_sel | wr_pmpaddr48_sel; + assign wr_pmpaddr_quarter[1] = wr_pmpaddr32_sel | wr_pmpaddr48_sel; + assign wr_pmpaddr_address = {wr_pmpaddr_quarter, dec_csr_wraddr_r[3:0]}; // entry address + + for (genvar entry_idx = 0; entry_idx < pt.PMP_ENTRIES; entry_idx++) begin : gen_pmpaddr_ff + logic pmpaddr_lock; + logic pmpaddr_lock_next; + assign pmpaddr_lock_next = ((entry_idx+1 < pt.PMP_ENTRIES) + ? (pmp_pmpcfg[entry_idx+1].lock + & pmp_pmpcfg[entry_idx+1].mode == TOR) + : 1'b0); + assign pmpaddr_lock = pmp_pmpcfg[entry_idx].lock | pmpaddr_lock_next; + assign pmp_pmpaddr[entry_idx][31:30] = 2'b00; + rvdffe #(30) pmpaddr_ff (.*, .clk(free_l2clk), + .en(wr_pmpaddr_r & (wr_pmpaddr_address == entry_idx) + & (~pmpaddr_lock)), + .din(dec_csr_wrdata_r[29:0]), + .dout(pmp_pmpaddr[entry_idx][29:0])); + end + + // CSR read mux + + assign pmp_quarter_rdaddr = dec_csr_rdaddr_d[3:0]; + assign pmp_pmpcfg_rddata = { pmp_pmpcfg[{pmp_quarter_rdaddr, 2'h3}], + pmp_pmpcfg[{pmp_quarter_rdaddr, 2'h2}], + pmp_pmpcfg[{pmp_quarter_rdaddr, 2'h1}], + pmp_pmpcfg[{pmp_quarter_rdaddr, 2'h0}] + }; + assign dec_pmp_read_d = csr_pmpcfg | csr_pmpaddr0 | csr_pmpaddr16 | csr_pmpaddr32 | csr_pmpaddr48; + assign dec_pmp_rddata_d[31:0] = ( ({32{csr_pmpcfg}} & pmp_pmpcfg_rddata) | + ({32{csr_pmpaddr0}} & pmp_pmpaddr[{2'h0, pmp_quarter_rdaddr}]) | + ({32{csr_pmpaddr16}} & pmp_pmpaddr[{2'h1, pmp_quarter_rdaddr}]) | + ({32{csr_pmpaddr32}} & pmp_pmpaddr[{2'h2, pmp_quarter_rdaddr}]) | + ({32{csr_pmpaddr48}} & pmp_pmpaddr[{2'h3, pmp_quarter_rdaddr}]) + ); + +endmodule // dec_pmp_ctl diff --git a/design/dec/el2_dec_tlu_ctl.sv b/design/dec/el2_dec_tlu_ctl.sv index 0e753606f85..38a994ddc24 100644 --- a/design/dec/el2_dec_tlu_ctl.sv +++ b/design/dec/el2_dec_tlu_ctl.sv @@ -233,7 +233,11 @@ import el2_pkg::*; output logic dec_tlu_pic_clk_override, // override PIC clock domain gating output logic dec_tlu_picio_clk_override,// override PICIO clock domain gating output logic dec_tlu_dccm_clk_override, // override DCCM clock domain gating - output logic dec_tlu_icm_clk_override // override ICCM clock domain gating + output logic dec_tlu_icm_clk_override, // override ICCM clock domain gating + + // pmp + output el2_pmp_cfg_pkt_t pmp_pmpcfg [pt.PMP_ENTRIES], + output logic [31:0] pmp_pmpaddr [pt.PMP_ENTRIES] ); @@ -376,6 +380,15 @@ import el2_pkg::*; logic csr_mitcnt0; logic csr_mitcnt1; + // PMP unit, isolated for size reasons + logic [31:0] dec_pmp_rddata_d; + logic dec_pmp_read_d; + logic csr_pmpcfg; + logic csr_pmpaddr0; + logic csr_pmpaddr16; + logic csr_pmpaddr32; + logic csr_pmpaddr48; + logic nmi_int_sync, timer_int_sync, soft_int_sync, i_cpu_halt_req_sync, i_cpu_run_req_sync, mpc_debug_halt_req_sync, mpc_debug_run_req_sync, mpc_debug_halt_req_sync_raw; logic csr_wr_clk; logic e4e5_clk, e4_valid, e5_valid, e4e5_valid, internal_dbg_halt_mode_f, internal_dbg_halt_mode_f2; @@ -412,8 +425,6 @@ import el2_pkg::*; el2_inst_pkt_t pmu_i0_itype_qual; - logic csr_mfdht; - logic csr_mfdhs; logic csr_misa; logic csr_mvendorid; logic csr_marchid; @@ -461,14 +472,19 @@ import el2_pkg::*; logic csr_mhpme6; logic csr_mcountinhibit; logic csr_mpmc; + logic csr_mcpc; + logic csr_mdeau; logic csr_micect; logic csr_miccmect; logic csr_mdccmect; + logic csr_mfdht; + logic csr_mfdhs; logic csr_dicawics; logic csr_dicad0h; logic csr_dicad0; logic csr_dicad1; logic csr_dicago; + logic valid_only; logic presync; logic postsync; logic legal; @@ -494,6 +510,9 @@ import el2_pkg::*; el2_dec_timer_ctl #(.pt(pt)) int_timers(.*); // end of internal timers + el2_dec_pmp_ctl #(.pt(pt)) pmp(.*); + // end of pmp + assign clk_override = dec_tlu_dec_clk_override; // Async inputs to the core have to be sync'd to the core clock. @@ -2373,14 +2392,14 @@ else // 1) coredecode -in csrdecode > corecsrdecode.e -// 2) espresso -Dso -oeqntott corecsrdecode.e | addassign > csrequations +// 2) espresso -Dso -oeqntott < corecsrdecode.e | addassign > csrequations // to generate the legal CSR equation below: // 1) coredecode -in csrdecode -legal > csrlegal.e -// 2) espresso -Dso -oeqntott csrlegal.e | addassign > csrlegal_equation -// coredecode -in csrdecode > corecsrdecode.e; espresso -Dso -oeqntott corecsrdecode.e | addassign > csrequations; coredecode -in csrdecode -legal > csrlegal.e; espresso -Dso -oeqntott csrlegal.e | addassign > csrlegal_equation +// 2) espresso -Dso -oeqntott < csrlegal.e | addassign > csrlegal_equation +// coredecode -in csrdecode > corecsrdecode.e; espresso -Dso -oeqntott < corecsrdecode.e | addassign > csrequations; coredecode -in csrdecode -legal > csrlegal.e; espresso -Dso -oeqntott csrlegal.e | addassign > csrlegal_equation assign csr_misa = (!dec_csr_rdaddr_d[11]&!dec_csr_rdaddr_d[6] &!dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[2]&dec_csr_rdaddr_d[0]); @@ -2420,7 +2439,7 @@ assign csr_minstretl = (!dec_csr_rdaddr_d[7]&!dec_csr_rdaddr_d[6] &!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[2] &dec_csr_rdaddr_d[1]&!dec_csr_rdaddr_d[0]); -assign csr_minstreth = (!dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[7] +assign csr_minstreth = (dec_csr_rdaddr_d[11]&dec_csr_rdaddr_d[7] &!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[2] &dec_csr_rdaddr_d[1]&!dec_csr_rdaddr_d[0]); @@ -2433,14 +2452,15 @@ assign csr_mepc = (!dec_csr_rdaddr_d[7]&dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[1] assign csr_mcause = (!dec_csr_rdaddr_d[7]&dec_csr_rdaddr_d[6] &dec_csr_rdaddr_d[1]&!dec_csr_rdaddr_d[0]); -assign csr_mscause = (dec_csr_rdaddr_d[6]&dec_csr_rdaddr_d[5] +assign csr_mscause = (dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[5] &dec_csr_rdaddr_d[2]); assign csr_mtval = (!dec_csr_rdaddr_d[7]&dec_csr_rdaddr_d[6]&dec_csr_rdaddr_d[1] &dec_csr_rdaddr_d[0]); -assign csr_mrac = (!dec_csr_rdaddr_d[11]&dec_csr_rdaddr_d[7]&!dec_csr_rdaddr_d[5] - &!dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[2]&!dec_csr_rdaddr_d[1]); +assign csr_mrac = (!dec_csr_rdaddr_d[11]&dec_csr_rdaddr_d[10] + &!dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[2] + &!dec_csr_rdaddr_d[1]); assign csr_dmst = (dec_csr_rdaddr_d[10]&!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3] &dec_csr_rdaddr_d[2]&!dec_csr_rdaddr_d[1]); @@ -2451,9 +2471,9 @@ assign csr_mdseac = (dec_csr_rdaddr_d[11]&dec_csr_rdaddr_d[10] assign csr_meihap = (dec_csr_rdaddr_d[11]&dec_csr_rdaddr_d[10] &dec_csr_rdaddr_d[3]); -assign csr_meivt = (!dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[6] - &dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[2]&!dec_csr_rdaddr_d[1] - &!dec_csr_rdaddr_d[0]); +assign csr_meivt = (dec_csr_rdaddr_d[11]&!dec_csr_rdaddr_d[10] + &dec_csr_rdaddr_d[6]&dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[2] + &!dec_csr_rdaddr_d[1]&!dec_csr_rdaddr_d[0]); assign csr_meipt = (dec_csr_rdaddr_d[11]&dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[1] &dec_csr_rdaddr_d[0]); @@ -2501,19 +2521,19 @@ assign csr_mhpmc6 = (!dec_csr_rdaddr_d[7]&!dec_csr_rdaddr_d[5] &!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3]&dec_csr_rdaddr_d[2] &dec_csr_rdaddr_d[1]&!dec_csr_rdaddr_d[0]); -assign csr_mhpmc3h = (dec_csr_rdaddr_d[7]&!dec_csr_rdaddr_d[4] - &!dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[2]&dec_csr_rdaddr_d[1] +assign csr_mhpmc3h = (dec_csr_rdaddr_d[11]&dec_csr_rdaddr_d[7] + &!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[2] &dec_csr_rdaddr_d[0]); -assign csr_mhpmc4h = (dec_csr_rdaddr_d[7]&!dec_csr_rdaddr_d[6] +assign csr_mhpmc4h = (dec_csr_rdaddr_d[11]&dec_csr_rdaddr_d[7] &!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3]&dec_csr_rdaddr_d[2] &!dec_csr_rdaddr_d[1]&!dec_csr_rdaddr_d[0]); -assign csr_mhpmc5h = (dec_csr_rdaddr_d[7]&!dec_csr_rdaddr_d[4] - &!dec_csr_rdaddr_d[3]&dec_csr_rdaddr_d[2]&!dec_csr_rdaddr_d[1] +assign csr_mhpmc5h = (dec_csr_rdaddr_d[11]&dec_csr_rdaddr_d[7] + &!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[1] &dec_csr_rdaddr_d[0]); -assign csr_mhpmc6h = (dec_csr_rdaddr_d[7]&!dec_csr_rdaddr_d[6] +assign csr_mhpmc6h = (dec_csr_rdaddr_d[11]&dec_csr_rdaddr_d[7] &!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3]&dec_csr_rdaddr_d[2] &dec_csr_rdaddr_d[1]&!dec_csr_rdaddr_d[0]); @@ -2521,64 +2541,72 @@ assign csr_mhpme3 = (!dec_csr_rdaddr_d[7]&dec_csr_rdaddr_d[5] &!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[2] &dec_csr_rdaddr_d[0]); -assign csr_mhpme4 = (dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[4] - &!dec_csr_rdaddr_d[3]&dec_csr_rdaddr_d[2]&!dec_csr_rdaddr_d[1] - &!dec_csr_rdaddr_d[0]); +assign csr_mhpme4 = (!dec_csr_rdaddr_d[7]&dec_csr_rdaddr_d[5] + &!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3]&dec_csr_rdaddr_d[2] + &!dec_csr_rdaddr_d[1]&!dec_csr_rdaddr_d[0]); -assign csr_mhpme5 = (dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[4] - &!dec_csr_rdaddr_d[3]&dec_csr_rdaddr_d[2]&!dec_csr_rdaddr_d[1] +assign csr_mhpme5 = (!dec_csr_rdaddr_d[7]&dec_csr_rdaddr_d[5] + &!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[1] &dec_csr_rdaddr_d[0]); -assign csr_mhpme6 = (dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[4] - &!dec_csr_rdaddr_d[3]&dec_csr_rdaddr_d[2]&dec_csr_rdaddr_d[1] +assign csr_mhpme6 = (!dec_csr_rdaddr_d[7]&dec_csr_rdaddr_d[5] + &!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3]&dec_csr_rdaddr_d[1] &!dec_csr_rdaddr_d[0]); assign csr_mcountinhibit = (!dec_csr_rdaddr_d[7]&dec_csr_rdaddr_d[5] &!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[2] &!dec_csr_rdaddr_d[0]); -assign csr_mitctl0 = (dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[5] - &dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[1]&!dec_csr_rdaddr_d[0]); +assign csr_mitctl0 = (dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[6] + &dec_csr_rdaddr_d[4]&dec_csr_rdaddr_d[2]&!dec_csr_rdaddr_d[1] + &!dec_csr_rdaddr_d[0]); -assign csr_mitctl1 = (dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[3] +assign csr_mitctl1 = (dec_csr_rdaddr_d[10]&!dec_csr_rdaddr_d[3] &dec_csr_rdaddr_d[2]&dec_csr_rdaddr_d[1]&dec_csr_rdaddr_d[0]); -assign csr_mitb0 = (dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[5]&dec_csr_rdaddr_d[4] - &!dec_csr_rdaddr_d[2]&dec_csr_rdaddr_d[0]); +assign csr_mitb0 = (dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[3] + &!dec_csr_rdaddr_d[2]&dec_csr_rdaddr_d[1]&dec_csr_rdaddr_d[0]); -assign csr_mitb1 = (dec_csr_rdaddr_d[6]&dec_csr_rdaddr_d[4]&dec_csr_rdaddr_d[2] +assign csr_mitb1 = (dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[4]&dec_csr_rdaddr_d[2] &dec_csr_rdaddr_d[1]&!dec_csr_rdaddr_d[0]); -assign csr_mitcnt0 = (dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[5] - &dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[2]&!dec_csr_rdaddr_d[0]); +assign csr_mitcnt0 = (dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[6] + &!dec_csr_rdaddr_d[5]&dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[2] + &!dec_csr_rdaddr_d[0]); -assign csr_mitcnt1 = (dec_csr_rdaddr_d[6]&dec_csr_rdaddr_d[2] +assign csr_mitcnt1 = (dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[2] &!dec_csr_rdaddr_d[1]&dec_csr_rdaddr_d[0]); -assign csr_mpmc = (dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3] +assign csr_mpmc = (dec_csr_rdaddr_d[10]&!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3] &dec_csr_rdaddr_d[2]&dec_csr_rdaddr_d[1]); +assign csr_mcpc = (dec_csr_rdaddr_d[10]&!dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[4] + &!dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[2]&dec_csr_rdaddr_d[1]); + assign csr_meicpct = (dec_csr_rdaddr_d[11]&dec_csr_rdaddr_d[6] &dec_csr_rdaddr_d[1]&!dec_csr_rdaddr_d[0]); -assign csr_micect = (dec_csr_rdaddr_d[6]&dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[3] - &!dec_csr_rdaddr_d[1]&!dec_csr_rdaddr_d[0]); +assign csr_mdeau = (dec_csr_rdaddr_d[11]&!dec_csr_rdaddr_d[10] + &dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[3]); -assign csr_miccmect = (dec_csr_rdaddr_d[6]&dec_csr_rdaddr_d[5] - &!dec_csr_rdaddr_d[3]&dec_csr_rdaddr_d[0]); +assign csr_micect = (dec_csr_rdaddr_d[6]&dec_csr_rdaddr_d[5]&dec_csr_rdaddr_d[4] + &!dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[1]&!dec_csr_rdaddr_d[0]); -assign csr_mdccmect = (dec_csr_rdaddr_d[6]&dec_csr_rdaddr_d[5] - &dec_csr_rdaddr_d[1]&!dec_csr_rdaddr_d[0]); +assign csr_miccmect = (dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[6] + &dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[3]&dec_csr_rdaddr_d[0]); + +assign csr_mdccmect = (dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[5] + &dec_csr_rdaddr_d[4]&dec_csr_rdaddr_d[1]&!dec_csr_rdaddr_d[0]); -assign csr_mfdht = (dec_csr_rdaddr_d[6]&dec_csr_rdaddr_d[3]&dec_csr_rdaddr_d[2] +assign csr_mfdht = (dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[3]&dec_csr_rdaddr_d[2] &dec_csr_rdaddr_d[1]&!dec_csr_rdaddr_d[0]); -assign csr_mfdhs = (dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[4]&dec_csr_rdaddr_d[2] - &dec_csr_rdaddr_d[0]); +assign csr_mfdhs = (dec_csr_rdaddr_d[10]&!dec_csr_rdaddr_d[4] + &dec_csr_rdaddr_d[2]&dec_csr_rdaddr_d[0]); -assign csr_dicawics = (!dec_csr_rdaddr_d[11]&!dec_csr_rdaddr_d[5] - &dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[2]&!dec_csr_rdaddr_d[1] - &!dec_csr_rdaddr_d[0]); +assign csr_dicawics = (!dec_csr_rdaddr_d[11]&dec_csr_rdaddr_d[10] + &!dec_csr_rdaddr_d[4]&dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[2] + &!dec_csr_rdaddr_d[1]&!dec_csr_rdaddr_d[0]); assign csr_dicad0h = (dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[3] &dec_csr_rdaddr_d[2]&!dec_csr_rdaddr_d[1]); @@ -2592,49 +2620,80 @@ assign csr_dicad1 = (dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[3] assign csr_dicago = (dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[3] &!dec_csr_rdaddr_d[2]&dec_csr_rdaddr_d[1]&dec_csr_rdaddr_d[0]); +assign csr_pmpcfg = (!dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[7] + &!dec_csr_rdaddr_d[6]&dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[4]); + +assign csr_pmpaddr0 = (!dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[7] + &dec_csr_rdaddr_d[5]&dec_csr_rdaddr_d[4]); + +assign csr_pmpaddr16 = (!dec_csr_rdaddr_d[11]&!dec_csr_rdaddr_d[10] + &dec_csr_rdaddr_d[7]&!dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[4]); + +assign csr_pmpaddr32 = (!dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[6] + &dec_csr_rdaddr_d[4]); + +assign csr_pmpaddr48 = (dec_csr_rdaddr_d[6]&dec_csr_rdaddr_d[5] + &!dec_csr_rdaddr_d[4]); + +assign valid_only = (dec_csr_rdaddr_d[11]&dec_csr_rdaddr_d[2] + &dec_csr_rdaddr_d[1]&dec_csr_rdaddr_d[0]) | (dec_csr_rdaddr_d[7] + &!dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[5]&dec_csr_rdaddr_d[4]) | ( + !dec_csr_rdaddr_d[10]&!dec_csr_rdaddr_d[7]&dec_csr_rdaddr_d[4]) | ( + !dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[5]&dec_csr_rdaddr_d[3]) | ( + !dec_csr_rdaddr_d[7]&dec_csr_rdaddr_d[2]&dec_csr_rdaddr_d[1] + &dec_csr_rdaddr_d[0]) | (!dec_csr_rdaddr_d[7]&dec_csr_rdaddr_d[3]); + assign presync = (dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[4]&dec_csr_rdaddr_d[3] - &!dec_csr_rdaddr_d[1]&dec_csr_rdaddr_d[0]) | (!dec_csr_rdaddr_d[7] - &dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3] - &!dec_csr_rdaddr_d[2]&!dec_csr_rdaddr_d[0]) | (dec_csr_rdaddr_d[5] - &!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[2] - &!dec_csr_rdaddr_d[1]&dec_csr_rdaddr_d[0]) | (!dec_csr_rdaddr_d[6] + &!dec_csr_rdaddr_d[1]&dec_csr_rdaddr_d[0]) | (dec_csr_rdaddr_d[10] &!dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3] - &!dec_csr_rdaddr_d[2]&dec_csr_rdaddr_d[1]) | (dec_csr_rdaddr_d[11] - &!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3]&dec_csr_rdaddr_d[1] - &!dec_csr_rdaddr_d[0]) | (dec_csr_rdaddr_d[11]&!dec_csr_rdaddr_d[4] - &!dec_csr_rdaddr_d[3]&dec_csr_rdaddr_d[2]&!dec_csr_rdaddr_d[1]) | ( - dec_csr_rdaddr_d[7]&!dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[4] - &!dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[2]&dec_csr_rdaddr_d[1]); + &!dec_csr_rdaddr_d[2]&dec_csr_rdaddr_d[1]) | (!dec_csr_rdaddr_d[7] + &dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3] + &!dec_csr_rdaddr_d[2]&!dec_csr_rdaddr_d[0]) | (dec_csr_rdaddr_d[10] + &!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3]&dec_csr_rdaddr_d[0]) | ( + dec_csr_rdaddr_d[11]&!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3] + &!dec_csr_rdaddr_d[2]&dec_csr_rdaddr_d[0]) | (dec_csr_rdaddr_d[11] + &!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3]&dec_csr_rdaddr_d[2] + &!dec_csr_rdaddr_d[1]) | (dec_csr_rdaddr_d[11]&!dec_csr_rdaddr_d[4] + &!dec_csr_rdaddr_d[3]&dec_csr_rdaddr_d[1]&!dec_csr_rdaddr_d[0]); assign postsync = (dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[4]&dec_csr_rdaddr_d[3] &!dec_csr_rdaddr_d[1]&dec_csr_rdaddr_d[0]) | (!dec_csr_rdaddr_d[11] &!dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[5]&dec_csr_rdaddr_d[2] - &dec_csr_rdaddr_d[0]) | (!dec_csr_rdaddr_d[7]&dec_csr_rdaddr_d[6] - &!dec_csr_rdaddr_d[1]&dec_csr_rdaddr_d[0]) | (dec_csr_rdaddr_d[10] + &dec_csr_rdaddr_d[0]) | (!dec_csr_rdaddr_d[11]&!dec_csr_rdaddr_d[7] + &!dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3] + &!dec_csr_rdaddr_d[2]&!dec_csr_rdaddr_d[0]) | (dec_csr_rdaddr_d[10] &!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3]&dec_csr_rdaddr_d[0]) | ( - !dec_csr_rdaddr_d[11]&!dec_csr_rdaddr_d[7]&!dec_csr_rdaddr_d[6] - &!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[2] - &!dec_csr_rdaddr_d[0]) | (!dec_csr_rdaddr_d[11]&dec_csr_rdaddr_d[7] - &dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3] - &!dec_csr_rdaddr_d[1]) | (dec_csr_rdaddr_d[10]&!dec_csr_rdaddr_d[4] - &!dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[2]&dec_csr_rdaddr_d[1]); + !dec_csr_rdaddr_d[11]&dec_csr_rdaddr_d[10]&!dec_csr_rdaddr_d[5] + &!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[1]) | ( + dec_csr_rdaddr_d[10]&!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3] + &!dec_csr_rdaddr_d[2]&dec_csr_rdaddr_d[1]) | (!dec_csr_rdaddr_d[7] + &dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[1]&dec_csr_rdaddr_d[0]); assign legal = (!dec_csr_rdaddr_d[11]&dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[9] &dec_csr_rdaddr_d[8]&dec_csr_rdaddr_d[7]&dec_csr_rdaddr_d[6] &dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[2] - &dec_csr_rdaddr_d[1]&!dec_csr_rdaddr_d[0]) | (!dec_csr_rdaddr_d[11] + &dec_csr_rdaddr_d[1]&!dec_csr_rdaddr_d[0]) | (!dec_csr_rdaddr_d[10] + &dec_csr_rdaddr_d[9]&dec_csr_rdaddr_d[8]&dec_csr_rdaddr_d[7] + &dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[4] + &dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[1]&!dec_csr_rdaddr_d[0]) | ( + !dec_csr_rdaddr_d[11]&!dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[9] + &dec_csr_rdaddr_d[8]&dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[5] + &!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[1] + &!dec_csr_rdaddr_d[0]) | (!dec_csr_rdaddr_d[11]&!dec_csr_rdaddr_d[10] + &dec_csr_rdaddr_d[9]&dec_csr_rdaddr_d[8]&!dec_csr_rdaddr_d[7] + &!dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[4] + &!dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[1]) | (dec_csr_rdaddr_d[11] &!dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[9]&dec_csr_rdaddr_d[8] - &!dec_csr_rdaddr_d[7]&!dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[5] - &!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[1]) | ( + &!dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[0]) | ( + !dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[9]&dec_csr_rdaddr_d[8] + &dec_csr_rdaddr_d[7]&dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[5] + &!dec_csr_rdaddr_d[4]&dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[2]) | ( !dec_csr_rdaddr_d[11]&!dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[9] &dec_csr_rdaddr_d[8]&!dec_csr_rdaddr_d[7]&!dec_csr_rdaddr_d[6] - &dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[1]&!dec_csr_rdaddr_d[0]) | ( - dec_csr_rdaddr_d[11]&dec_csr_rdaddr_d[9]&dec_csr_rdaddr_d[8] - &dec_csr_rdaddr_d[7]&dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[5] - &!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[2]&!dec_csr_rdaddr_d[1] - &!dec_csr_rdaddr_d[0]) | (dec_csr_rdaddr_d[11]&!dec_csr_rdaddr_d[10] - &dec_csr_rdaddr_d[9]&dec_csr_rdaddr_d[8]&!dec_csr_rdaddr_d[6] - &!dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[0]) | (!dec_csr_rdaddr_d[11] + &!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[1] + &!dec_csr_rdaddr_d[0]) | (!dec_csr_rdaddr_d[11]&!dec_csr_rdaddr_d[10] + &dec_csr_rdaddr_d[9]&dec_csr_rdaddr_d[8]&dec_csr_rdaddr_d[7] + &dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[4]) | (!dec_csr_rdaddr_d[11] &dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[9]&dec_csr_rdaddr_d[8] &dec_csr_rdaddr_d[7]&dec_csr_rdaddr_d[6]&dec_csr_rdaddr_d[5] &dec_csr_rdaddr_d[4]&dec_csr_rdaddr_d[3]&dec_csr_rdaddr_d[2] @@ -2645,71 +2704,62 @@ assign legal = (!dec_csr_rdaddr_d[11]&dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[9] dec_csr_rdaddr_d[11]&dec_csr_rdaddr_d[9]&dec_csr_rdaddr_d[8] &!dec_csr_rdaddr_d[7]&!dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[5] &dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[2] - &dec_csr_rdaddr_d[0]) | (!dec_csr_rdaddr_d[11]&dec_csr_rdaddr_d[10] - &dec_csr_rdaddr_d[9]&dec_csr_rdaddr_d[8]&dec_csr_rdaddr_d[7] - &!dec_csr_rdaddr_d[6]&dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[3] - &!dec_csr_rdaddr_d[2]&!dec_csr_rdaddr_d[1]) | (!dec_csr_rdaddr_d[11] - &!dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[9]&dec_csr_rdaddr_d[8] - &!dec_csr_rdaddr_d[7]&!dec_csr_rdaddr_d[6]&dec_csr_rdaddr_d[5] - &dec_csr_rdaddr_d[2]) | (dec_csr_rdaddr_d[11]&dec_csr_rdaddr_d[9] + &dec_csr_rdaddr_d[0]) | (dec_csr_rdaddr_d[11]&dec_csr_rdaddr_d[9] &dec_csr_rdaddr_d[8]&!dec_csr_rdaddr_d[7]&!dec_csr_rdaddr_d[6] &!dec_csr_rdaddr_d[5]&dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3] &dec_csr_rdaddr_d[2]&!dec_csr_rdaddr_d[1]&!dec_csr_rdaddr_d[0]) | ( - !dec_csr_rdaddr_d[11]&dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[9] - &dec_csr_rdaddr_d[8]&dec_csr_rdaddr_d[7]&dec_csr_rdaddr_d[6] - &!dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[4]&dec_csr_rdaddr_d[3] - &dec_csr_rdaddr_d[1]) | (!dec_csr_rdaddr_d[11]&dec_csr_rdaddr_d[10] + dec_csr_rdaddr_d[11]&dec_csr_rdaddr_d[9]&dec_csr_rdaddr_d[8] + &!dec_csr_rdaddr_d[7]&!dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[5] + &dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[2] + &dec_csr_rdaddr_d[1]) | (!dec_csr_rdaddr_d[11]&dec_csr_rdaddr_d[9] + &dec_csr_rdaddr_d[8]&dec_csr_rdaddr_d[7]&!dec_csr_rdaddr_d[6] + &dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[2] + &!dec_csr_rdaddr_d[1]) | (!dec_csr_rdaddr_d[11]&!dec_csr_rdaddr_d[10] + &dec_csr_rdaddr_d[9]&dec_csr_rdaddr_d[8]&!dec_csr_rdaddr_d[6] + &dec_csr_rdaddr_d[5]&dec_csr_rdaddr_d[2]) | (!dec_csr_rdaddr_d[11] &dec_csr_rdaddr_d[9]&dec_csr_rdaddr_d[8]&dec_csr_rdaddr_d[7] &dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[5]&dec_csr_rdaddr_d[4] - &!dec_csr_rdaddr_d[3]&dec_csr_rdaddr_d[2]) | (dec_csr_rdaddr_d[11] - &dec_csr_rdaddr_d[9]&dec_csr_rdaddr_d[8]&!dec_csr_rdaddr_d[7] - &!dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[5]&dec_csr_rdaddr_d[4] - &!dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[2]&dec_csr_rdaddr_d[1]) | ( - !dec_csr_rdaddr_d[11]&!dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[9] - &dec_csr_rdaddr_d[8]&!dec_csr_rdaddr_d[7]&!dec_csr_rdaddr_d[6] - &dec_csr_rdaddr_d[5]&dec_csr_rdaddr_d[1]&dec_csr_rdaddr_d[0]) | ( - dec_csr_rdaddr_d[11]&!dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[9] - &dec_csr_rdaddr_d[8]&dec_csr_rdaddr_d[7]&!dec_csr_rdaddr_d[5] - &!dec_csr_rdaddr_d[4]&dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[2]) | ( - dec_csr_rdaddr_d[11]&!dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[9] - &dec_csr_rdaddr_d[8]&dec_csr_rdaddr_d[7]&!dec_csr_rdaddr_d[5] - &!dec_csr_rdaddr_d[4]&dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[1] - &!dec_csr_rdaddr_d[0]) | (dec_csr_rdaddr_d[11]&!dec_csr_rdaddr_d[10] + &!dec_csr_rdaddr_d[3]&dec_csr_rdaddr_d[2]) | (!dec_csr_rdaddr_d[11] + &dec_csr_rdaddr_d[9]&dec_csr_rdaddr_d[8]&dec_csr_rdaddr_d[7] + &dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[4] + &dec_csr_rdaddr_d[3]&dec_csr_rdaddr_d[1]) | (!dec_csr_rdaddr_d[11] + &!dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[9]&dec_csr_rdaddr_d[8] + &!dec_csr_rdaddr_d[6]&dec_csr_rdaddr_d[5]&dec_csr_rdaddr_d[1] + &dec_csr_rdaddr_d[0]) | (dec_csr_rdaddr_d[11]&!dec_csr_rdaddr_d[10] &dec_csr_rdaddr_d[9]&dec_csr_rdaddr_d[8]&!dec_csr_rdaddr_d[6] &!dec_csr_rdaddr_d[5]&dec_csr_rdaddr_d[2]) | (!dec_csr_rdaddr_d[11] - &dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[9]&dec_csr_rdaddr_d[8] - &dec_csr_rdaddr_d[7]&dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[5] - &dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3]&dec_csr_rdaddr_d[1]) | ( - !dec_csr_rdaddr_d[11]&dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[9] - &dec_csr_rdaddr_d[8]&dec_csr_rdaddr_d[7]&dec_csr_rdaddr_d[6] - &!dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[0]) | ( - !dec_csr_rdaddr_d[11]&dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[9] + &dec_csr_rdaddr_d[9]&dec_csr_rdaddr_d[8]&dec_csr_rdaddr_d[7] + &dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[5]&dec_csr_rdaddr_d[4] + &!dec_csr_rdaddr_d[3]&dec_csr_rdaddr_d[1]) | (dec_csr_rdaddr_d[9] &dec_csr_rdaddr_d[8]&dec_csr_rdaddr_d[7]&dec_csr_rdaddr_d[6] - &!dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[4]&dec_csr_rdaddr_d[3] - &!dec_csr_rdaddr_d[2]) | (!dec_csr_rdaddr_d[11]&dec_csr_rdaddr_d[10] + &!dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[2] + &!dec_csr_rdaddr_d[1]&!dec_csr_rdaddr_d[0]) | (!dec_csr_rdaddr_d[11] + &dec_csr_rdaddr_d[9]&dec_csr_rdaddr_d[8]&dec_csr_rdaddr_d[7] + &dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[4] + &!dec_csr_rdaddr_d[0]) | (dec_csr_rdaddr_d[11]&!dec_csr_rdaddr_d[10] + &dec_csr_rdaddr_d[9]&dec_csr_rdaddr_d[8]&!dec_csr_rdaddr_d[6] + &!dec_csr_rdaddr_d[5]&dec_csr_rdaddr_d[1]) | (!dec_csr_rdaddr_d[11] + &dec_csr_rdaddr_d[9]&dec_csr_rdaddr_d[8]&dec_csr_rdaddr_d[7] + &dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[4] + &dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[2]) | (!dec_csr_rdaddr_d[11] &dec_csr_rdaddr_d[9]&dec_csr_rdaddr_d[8]&dec_csr_rdaddr_d[7] &!dec_csr_rdaddr_d[6]&dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[4] &!dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[2]&!dec_csr_rdaddr_d[0]) | ( - dec_csr_rdaddr_d[11]&!dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[9] - &dec_csr_rdaddr_d[8]&!dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[5] - &dec_csr_rdaddr_d[1]) | (!dec_csr_rdaddr_d[11]&!dec_csr_rdaddr_d[10] - &dec_csr_rdaddr_d[9]&dec_csr_rdaddr_d[8]&!dec_csr_rdaddr_d[7] - &dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[4] - &!dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[2]) | (!dec_csr_rdaddr_d[11] - &!dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[9]&dec_csr_rdaddr_d[8] - &!dec_csr_rdaddr_d[7]&!dec_csr_rdaddr_d[5]&!dec_csr_rdaddr_d[4] - &!dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[1]&!dec_csr_rdaddr_d[0]) | ( !dec_csr_rdaddr_d[11]&!dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[9] - &dec_csr_rdaddr_d[8]&!dec_csr_rdaddr_d[7]&!dec_csr_rdaddr_d[6] - &dec_csr_rdaddr_d[5]&dec_csr_rdaddr_d[3]) | (dec_csr_rdaddr_d[11] - &!dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[9]&dec_csr_rdaddr_d[8] - &!dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[5]&dec_csr_rdaddr_d[3]) | ( + &dec_csr_rdaddr_d[8]&dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[5] + &!dec_csr_rdaddr_d[4]&!dec_csr_rdaddr_d[3]&!dec_csr_rdaddr_d[2]) | ( !dec_csr_rdaddr_d[11]&!dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[9] - &dec_csr_rdaddr_d[8]&!dec_csr_rdaddr_d[7]&!dec_csr_rdaddr_d[6] - &dec_csr_rdaddr_d[5]&dec_csr_rdaddr_d[4]) | (dec_csr_rdaddr_d[11] + &dec_csr_rdaddr_d[8]&!dec_csr_rdaddr_d[6]&dec_csr_rdaddr_d[5] + &dec_csr_rdaddr_d[3]) | (dec_csr_rdaddr_d[11]&!dec_csr_rdaddr_d[10] + &dec_csr_rdaddr_d[9]&dec_csr_rdaddr_d[8]&!dec_csr_rdaddr_d[6] + &!dec_csr_rdaddr_d[5]&dec_csr_rdaddr_d[3]) | (!dec_csr_rdaddr_d[11] &!dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[9]&dec_csr_rdaddr_d[8] - &!dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[5]&dec_csr_rdaddr_d[4]); - + &!dec_csr_rdaddr_d[6]&dec_csr_rdaddr_d[5]&dec_csr_rdaddr_d[4]) | ( + dec_csr_rdaddr_d[11]&!dec_csr_rdaddr_d[10]&dec_csr_rdaddr_d[9] + &dec_csr_rdaddr_d[8]&!dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[5] + &dec_csr_rdaddr_d[4]) | (!dec_csr_rdaddr_d[11]&!dec_csr_rdaddr_d[10] + &dec_csr_rdaddr_d[9]&dec_csr_rdaddr_d[8]&dec_csr_rdaddr_d[7] + &dec_csr_rdaddr_d[6]&!dec_csr_rdaddr_d[5]); assign dec_tlu_presync_d = presync & dec_csr_any_unq_d & ~dec_csr_wen_unq_d; @@ -2781,7 +2831,8 @@ assign dec_csr_rddata_d[31:0] = ( ({32{csr_misa}} & 32'h40001104) | ({32{csr_mhpme6}} & {22'b0,mhpme6[9:0]}) | ({32{csr_mcountinhibit}} & {25'b0, mcountinhibit[6:0]}) | ({32{csr_mpmc}} & {30'b0, mpmc[1], 1'b0}) | - ({32{dec_timer_read_d}} & dec_timer_rddata_d[31:0]) + ({32{dec_timer_read_d}} & dec_timer_rddata_d[31:0]) | + ({32{dec_pmp_read_d}} & dec_pmp_rddata_d[31:0]) ); diff --git a/design/el2_pmp.sv b/design/el2_pmp.sv new file mode 100644 index 00000000000..9334d6747ec --- /dev/null +++ b/design/el2_pmp.sv @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright lowRISC contributors. +// Copyright 2023 Antmicro, Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +module el2_pmp + import el2_pkg::*; +#( + parameter PMP_CHANNELS = 3, + // Granularity of NAPOT access, + // 0 = No restriction, 1 = 8 byte, 2 = 16 byte, 3 = 32 byte, etc. + parameter PMP_GRANULARITY = 0, // TODO: Move to veer.config + `include "el2_param.vh" +) ( + input logic clk, // Top level clock + input logic rst_l, // Reset + input logic scan_mode, // Scan mode + + input el2_pmp_cfg_pkt_t pmp_pmpcfg [pt.PMP_ENTRIES], + input logic [31:0] pmp_pmpaddr[pt.PMP_ENTRIES], + + input logic [31:0] pmp_chan_addr[PMP_CHANNELS], + input el2_pmp_type_pkt_t pmp_chan_type[PMP_CHANNELS], + output logic pmp_chan_err [PMP_CHANNELS] +); + + logic [ 33:0] csr_pmp_addr_i [pt.PMP_ENTRIES]; + logic [ 33:0] pmp_req_addr_i [ PMP_CHANNELS]; + + logic [ 33:0] region_start_addr [pt.PMP_ENTRIES]; + logic [33:PMP_GRANULARITY+2] region_addr_mask [pt.PMP_ENTRIES]; + logic [ PMP_CHANNELS-1:0][pt.PMP_ENTRIES-1:0] region_match_gt; + logic [ PMP_CHANNELS-1:0][pt.PMP_ENTRIES-1:0] region_match_lt; + logic [ PMP_CHANNELS-1:0][pt.PMP_ENTRIES-1:0] region_match_eq; + logic [ PMP_CHANNELS-1:0][pt.PMP_ENTRIES-1:0] region_match_all; + logic [ PMP_CHANNELS-1:0][pt.PMP_ENTRIES-1:0] region_basic_perm_check; + logic [ PMP_CHANNELS-1:0][pt.PMP_ENTRIES-1:0] region_perm_check; + + /////////////////////// + // Functions for PMP // + /////////////////////// + + // Flow of the PMP checking operation follows as below + // + // basic_perm_check ---> perm_check_wrapper ---> orig_perm_check ---/ + // | + // region_match_all -----------------> access_fault_check <---------- + // | + // \--> pmp_chan_err + + // A wrapper function in which it is decided which form of permission check function gets called + function automatic logic perm_check_wrapper(el2_pmp_cfg_pkt_t csr_pmp_cfg, + logic permission_check); + return orig_perm_check(csr_pmp_cfg.lock, permission_check); + endfunction + + // Compute permissions checks that apply when MSECCFG.MML is unset. This is the original PMP + // behaviour before Smepmp was added. + function automatic logic orig_perm_check(logic pmp_cfg_lock, logic permission_check); + return (~pmp_cfg_lock | permission_check); + // For M-mode, any region which matches with the L-bit clear, or with sufficient + // access permissions will be allowed + endfunction + + // Access fault determination / prioritization + function automatic logic access_fault_check(logic [pt.PMP_ENTRIES-1:0] match_all, + logic [pt.PMP_ENTRIES-1:0] final_perm_check); + + + // When MSECCFG.MMWP is set default deny always, otherwise allow for M-mode, deny for other + // modes. Also deny unmatched for M-mode whe MSECCFG.MML is set and request type is EXEC. + logic access_fail = 1'b0; + logic matched = 1'b0; + + // PMP entries are statically prioritized, from 0 to N-1 + // The lowest-numbered PMP entry which matches an address determines accessibility + for (int r = 0; r < pt.PMP_ENTRIES; r++) begin + if (!matched && match_all[r]) begin + access_fail = ~final_perm_check[r]; + matched = 1'b1; + end + end + return access_fail; + endfunction + + // --------------- + // Access checking + // --------------- + + for (genvar r = 0; r < pt.PMP_ENTRIES; r++) begin : g_addr_exp + assign csr_pmp_addr_i[r] = { + pmp_pmpaddr[r], 2'b00 + }; // addr conv.: word @ 32-bit -> byte @ 34-bit + // Start address for TOR matching + if (r == 0) begin : g_entry0 + assign region_start_addr[r] = (pmp_pmpcfg[r].mode == TOR) ? 34'h000000000 : csr_pmp_addr_i[r]; + end else begin : g_oth + assign region_start_addr[r] = (pmp_pmpcfg[r].mode == TOR) ? csr_pmp_addr_i[r-1] : + csr_pmp_addr_i[r]; + end + // Address mask for NA matching + for (genvar b = PMP_GRANULARITY + 2; b < 34; b++) begin : g_bitmask + if (b == 2) begin : g_bit0 + // Always mask bit 2 for NAPOT + assign region_addr_mask[r][b] = (pmp_pmpcfg[r].mode != NAPOT); + end else begin : g_others + // We will mask this bit if it is within the programmed granule + // i.e. addr = yyyy 0111 + // ^ + // | This bit pos is the top of the mask, all lower bits set + // thus mask = 1111 0000 + if (PMP_GRANULARITY == 0) begin : g_region_addr_mask_zero_granularity + assign region_addr_mask[r][b] = (pmp_pmpcfg[r].mode != NAPOT) | + ~&csr_pmp_addr_i[r][b-1:2]; + end else begin : g_region_addr_mask_other_granularity + assign region_addr_mask[r][b] = (pmp_pmpcfg[r].mode != NAPOT) | + ~&csr_pmp_addr_i[r][b-1:PMP_GRANULARITY+1]; + end + end + end + end + + for (genvar c = 0; c < PMP_CHANNELS; c++) begin : g_access_check + assign pmp_req_addr_i[c] = {2'b00, pmp_chan_addr[c]}; // addr. widening: 32-bit -> 34-bit + for (genvar r = 0; r < pt.PMP_ENTRIES; r++) begin : g_regions + // Comparators are sized according to granularity + assign region_match_eq[c][r] = (pmp_req_addr_i[c][33:PMP_GRANULARITY+2] & + region_addr_mask[r]) == + (region_start_addr[r][33:PMP_GRANULARITY+2] & + region_addr_mask[r]); + assign region_match_gt[c][r] = pmp_req_addr_i[c][33:PMP_GRANULARITY+2] > + region_start_addr[r][33:PMP_GRANULARITY+2]; + assign region_match_lt[c][r] = pmp_req_addr_i[c][33:PMP_GRANULARITY+2] < + csr_pmp_addr_i[r][33:PMP_GRANULARITY+2]; + + always_comb begin + region_match_all[c][r] = 1'b0; + unique case (pmp_pmpcfg[r].mode) + OFF: region_match_all[c][r] = 1'b0; + NA4: region_match_all[c][r] = region_match_eq[c][r]; + NAPOT: region_match_all[c][r] = region_match_eq[c][r]; + TOR: begin + region_match_all[c][r] = (region_match_eq[c][r] | region_match_gt[c][r]) & + region_match_lt[c][r]; + end + default: region_match_all[c][r] = 1'b0; + endcase + end + + // Basic permission check compares cfg register only. + assign region_basic_perm_check[c][r] = + ((pmp_chan_type[c] == EXEC) & pmp_pmpcfg[r].execute) | + ((pmp_chan_type[c] == WRITE) & pmp_pmpcfg[r].write) | + ((pmp_chan_type[c] == READ) & pmp_pmpcfg[r].read); + + // Check specific required permissions since the behaviour is different + // between Smepmp implementation and original PMP. + assign region_perm_check[c][r] = perm_check_wrapper( + pmp_pmpcfg[r], region_basic_perm_check[c][r] + ); + + // Address bits below PMP granularity (which starts at 4 byte) are deliberately unused. + logic unused_sigs; + assign unused_sigs = ^{region_start_addr[r][PMP_GRANULARITY+2-1:0], + pmp_req_addr_i[c][PMP_GRANULARITY+2-1:0]}; + end + + // Once the permission checks of the regions are done, decide if the access is + // denied by figuring out the matching region and its permission check. + assign pmp_chan_err[c] = access_fault_check(region_match_all[c], region_perm_check[c]); + end + +endmodule // el2_pmp diff --git a/design/el2_veer.sv b/design/el2_veer.sv index 880aab38c98..315d14b4591 100644 --- a/design/el2_veer.sv +++ b/design/el2_veer.sv @@ -726,6 +726,22 @@ import el2_pkg::*; assign dccm_clk_override = dec_tlu_dccm_clk_override; // dccm memory assign icm_clk_override = dec_tlu_icm_clk_override; // icache/iccm memory + // PMP Signals + el2_pmp_cfg_pkt_t pmp_pmpcfg [pt.PMP_ENTRIES]; + logic [31:0] pmp_pmpaddr [pt.PMP_ENTRIES]; + logic [31:0] pmp_chan_addr [3]; + el2_pmp_type_pkt_t pmp_chan_type [3]; + logic pmp_chan_err [3]; + + logic [31:1] ifu_pmp_addr; + logic ifu_pmp_error; + logic [31:0] lsu_pmp_addr_start; + logic lsu_pmp_error_start; + logic [31:0] lsu_pmp_addr_end; + logic lsu_pmp_error_end; + logic lsu_pmp_we; + logic lsu_pmp_re; + // -----------------------DEBUG START ------------------------------- logic [31:0] dbg_cmd_addr; // the address of the debug command to used by the core @@ -954,6 +970,25 @@ import el2_pkg::*; .* ); + assign pmp_chan_addr[0] = {ifu_pmp_addr, 1'b0}; + assign pmp_chan_type[0] = EXEC; + assign ifu_pmp_error = pmp_chan_err[0]; + assign pmp_chan_addr[1] = lsu_pmp_addr_start; + assign pmp_chan_type[1] = lsu_pmp_we ? WRITE : (lsu_pmp_re ? READ : NONE); + assign lsu_pmp_error_start = pmp_chan_err[1]; + assign pmp_chan_addr[2] = lsu_pmp_addr_end; + assign pmp_chan_type[2] = lsu_pmp_we ? WRITE : (lsu_pmp_re ? READ : NONE); + assign lsu_pmp_error_end = pmp_chan_err[2]; + + el2_pmp #( + .PMP_CHANNELS(3), + .pt(pt) + ) pmp ( + .clk (active_l2clk), + .rst_l(core_rst_l), + .* + ); + if (pt.BUILD_AHB_LITE == 1) begin: Gen_AXI_To_AHB // AXI4 -> AHB Gasket for LSU diff --git a/design/flist b/design/flist index 60f6a40fb60..2ab5ee50346 100644 --- a/design/flist +++ b/design/flist @@ -3,6 +3,7 @@ $RV_ROOT/design/el2_mem.sv $RV_ROOT/design/el2_pic_ctrl.sv $RV_ROOT/design/el2_veer.sv $RV_ROOT/design/el2_dma_ctrl.sv +$RV_ROOT/design/el2_pmp.sv $RV_ROOT/design/ifu/el2_ifu_aln_ctl.sv $RV_ROOT/design/ifu/el2_ifu_compress_ctl.sv $RV_ROOT/design/ifu/el2_ifu_ifc_ctl.sv @@ -14,6 +15,7 @@ $RV_ROOT/design/ifu/el2_ifu.sv $RV_ROOT/design/dec/el2_dec_decode_ctl.sv $RV_ROOT/design/dec/el2_dec_gpr_ctl.sv $RV_ROOT/design/dec/el2_dec_ib_ctl.sv +$RV_ROOT/design/dec/el2_dec_pmp_ctl.sv $RV_ROOT/design/dec/el2_dec_tlu_ctl.sv $RV_ROOT/design/dec/el2_dec_trigger.sv $RV_ROOT/design/dec/el2_dec.sv diff --git a/design/flist.formal b/design/flist.formal index d1f9f160c10..b9dadbef4c1 100644 --- a/design/flist.formal +++ b/design/flist.formal @@ -23,6 +23,7 @@ $RV_ROOT/design/el2_mem.sv $RV_ROOT/design/el2_pic_ctrl.sv $RV_ROOT/design/el2_veer.sv $RV_ROOT/design/el2_dma_ctrl.sv +$RV_ROOT/design/el2_pmp.sv $RV_ROOT/design/ifu/el2_ifu_aln_ctl.sv $RV_ROOT/design/ifu/el2_ifu_compress_ctl.sv $RV_ROOT/design/ifu/el2_ifu_ifc_ctl.sv @@ -34,6 +35,7 @@ $RV_ROOT/design/ifu/el2_ifu.sv $RV_ROOT/design/dec/el2_dec_decode_ctl.sv $RV_ROOT/design/dec/el2_dec_gpr_ctl.sv $RV_ROOT/design/dec/el2_dec_ib_ctl.sv +$RV_ROOT/design/dec/el2_dec_pmp_ctl.sv $RV_ROOT/design/dec/el2_dec_tlu_ctl.sv $RV_ROOT/design/dec/el2_dec_trigger.sv $RV_ROOT/design/dec/el2_dec.sv diff --git a/design/flist.questa b/design/flist.questa index 7a8c9c77322..8a25ebcbdf6 100644 --- a/design/flist.questa +++ b/design/flist.questa @@ -21,6 +21,7 @@ $RV_ROOT/design/el2_mem.sv $RV_ROOT/design/el2_pic_ctrl.sv $RV_ROOT/design/el2_veer.sv $RV_ROOT/design/el2_dma_ctrl.sv +$RV_ROOT/design/el2_pmp.sv $RV_ROOT/design/ifu/el2_ifu_aln_ctl.sv $RV_ROOT/design/ifu/el2_ifu_compress_ctl.sv $RV_ROOT/design/ifu/el2_ifu_ifc_ctl.sv @@ -32,6 +33,7 @@ $RV_ROOT/design/ifu/el2_ifu.sv $RV_ROOT/design/dec/el2_dec_decode_ctl.sv $RV_ROOT/design/dec/el2_dec_gpr_ctl.sv $RV_ROOT/design/dec/el2_dec_ib_ctl.sv +$RV_ROOT/design/dec/el2_dec_pmp_ctl.sv $RV_ROOT/design/dec/el2_dec_tlu_ctl.sv $RV_ROOT/design/dec/el2_dec_trigger.sv $RV_ROOT/design/dec/el2_dec.sv diff --git a/design/ifu/el2_ifu.sv b/design/ifu/el2_ifu.sv index d36cd68bd7f..601a9869fd6 100644 --- a/design/ifu/el2_ifu.sv +++ b/design/ifu/el2_ifu.sv @@ -193,6 +193,8 @@ import el2_pkg::*; output logic [15:0] ifu_i0_cinst, + output logic [31:1] ifu_pmp_addr, + input logic ifu_pmp_error, /// Icache debug input el2_cache_debug_pkt_t dec_tlu_ic_diag_pkt , @@ -209,6 +211,7 @@ import el2_pkg::*; logic ifu_fb_consume1, ifu_fb_consume2; logic [31:1] ifc_fetch_addr_f; logic [31:1] ifc_fetch_addr_bf; + assign ifu_pmp_addr = ifc_fetch_addr_bf; logic [1:0] ifu_fetch_val; // valids on a 2B boundary, left justified [7] implies valid fetch logic [31:1] ifu_fetch_pc; // starting pc of fetch diff --git a/design/ifu/el2_ifu_mem_ctl.sv b/design/ifu/el2_ifu_mem_ctl.sv index 0d267f27e5d..09f67f571d5 100644 --- a/design/ifu/el2_ifu_mem_ctl.sv +++ b/design/ifu/el2_ifu_mem_ctl.sv @@ -180,6 +180,8 @@ import el2_pkg::*; output logic iccm_buf_correct_ecc, output logic iccm_correction_state, + input logic ifu_pmp_error, + input logic scan_mode ); @@ -1652,17 +1654,22 @@ assign debug_data_clken = ic_debug_rd_en_ff; // memory protection - equation to look identical to the LSU equation - assign ifc_region_acc_okay = (~(|{pt.INST_ACCESS_ENABLE0,pt.INST_ACCESS_ENABLE1,pt.INST_ACCESS_ENABLE2,pt.INST_ACCESS_ENABLE3,pt.INST_ACCESS_ENABLE4,pt.INST_ACCESS_ENABLE5,pt.INST_ACCESS_ENABLE6,pt.INST_ACCESS_ENABLE7})) | - (pt.INST_ACCESS_ENABLE0 & (({ifc_fetch_addr_bf[31:1],1'b0} | pt.INST_ACCESS_MASK0)) == (pt.INST_ACCESS_ADDR0 | pt.INST_ACCESS_MASK0)) | - (pt.INST_ACCESS_ENABLE1 & (({ifc_fetch_addr_bf[31:1],1'b0} | pt.INST_ACCESS_MASK1)) == (pt.INST_ACCESS_ADDR1 | pt.INST_ACCESS_MASK1)) | - (pt.INST_ACCESS_ENABLE2 & (({ifc_fetch_addr_bf[31:1],1'b0} | pt.INST_ACCESS_MASK2)) == (pt.INST_ACCESS_ADDR2 | pt.INST_ACCESS_MASK2)) | - (pt.INST_ACCESS_ENABLE3 & (({ifc_fetch_addr_bf[31:1],1'b0} | pt.INST_ACCESS_MASK3)) == (pt.INST_ACCESS_ADDR3 | pt.INST_ACCESS_MASK3)) | - (pt.INST_ACCESS_ENABLE4 & (({ifc_fetch_addr_bf[31:1],1'b0} | pt.INST_ACCESS_MASK4)) == (pt.INST_ACCESS_ADDR4 | pt.INST_ACCESS_MASK4)) | - (pt.INST_ACCESS_ENABLE5 & (({ifc_fetch_addr_bf[31:1],1'b0} | pt.INST_ACCESS_MASK5)) == (pt.INST_ACCESS_ADDR5 | pt.INST_ACCESS_MASK5)) | - (pt.INST_ACCESS_ENABLE6 & (({ifc_fetch_addr_bf[31:1],1'b0} | pt.INST_ACCESS_MASK6)) == (pt.INST_ACCESS_ADDR6 | pt.INST_ACCESS_MASK6)) | - (pt.INST_ACCESS_ENABLE7 & (({ifc_fetch_addr_bf[31:1],1'b0} | pt.INST_ACCESS_MASK7)) == (pt.INST_ACCESS_ADDR7 | pt.INST_ACCESS_MASK7)); - - assign ifc_region_acc_fault_memory_bf = ~ifc_iccm_access_bf & ~ifc_region_acc_okay & ifc_fetch_req_bf; + if (pt.PMP_ENTRIES != 0) begin : g_ifc_access_check_pmp + assign ifc_region_acc_okay = ~ifu_pmp_error; + assign ifc_region_acc_fault_memory_bf = ~ifc_region_acc_okay & ifc_fetch_req_bf; + end + else begin : g_ifc_access_check + assign ifc_region_acc_okay = (~(|{pt.INST_ACCESS_ENABLE0,pt.INST_ACCESS_ENABLE1,pt.INST_ACCESS_ENABLE2,pt.INST_ACCESS_ENABLE3,pt.INST_ACCESS_ENABLE4,pt.INST_ACCESS_ENABLE5,pt.INST_ACCESS_ENABLE6,pt.INST_ACCESS_ENABLE7})) | + (pt.INST_ACCESS_ENABLE0 & (({ifc_fetch_addr_bf[31:1],1'b0} | pt.INST_ACCESS_MASK0)) == (pt.INST_ACCESS_ADDR0 | pt.INST_ACCESS_MASK0)) | + (pt.INST_ACCESS_ENABLE1 & (({ifc_fetch_addr_bf[31:1],1'b0} | pt.INST_ACCESS_MASK1)) == (pt.INST_ACCESS_ADDR1 | pt.INST_ACCESS_MASK1)) | + (pt.INST_ACCESS_ENABLE2 & (({ifc_fetch_addr_bf[31:1],1'b0} | pt.INST_ACCESS_MASK2)) == (pt.INST_ACCESS_ADDR2 | pt.INST_ACCESS_MASK2)) | + (pt.INST_ACCESS_ENABLE3 & (({ifc_fetch_addr_bf[31:1],1'b0} | pt.INST_ACCESS_MASK3)) == (pt.INST_ACCESS_ADDR3 | pt.INST_ACCESS_MASK3)) | + (pt.INST_ACCESS_ENABLE4 & (({ifc_fetch_addr_bf[31:1],1'b0} | pt.INST_ACCESS_MASK4)) == (pt.INST_ACCESS_ADDR4 | pt.INST_ACCESS_MASK4)) | + (pt.INST_ACCESS_ENABLE5 & (({ifc_fetch_addr_bf[31:1],1'b0} | pt.INST_ACCESS_MASK5)) == (pt.INST_ACCESS_ADDR5 | pt.INST_ACCESS_MASK5)) | + (pt.INST_ACCESS_ENABLE6 & (({ifc_fetch_addr_bf[31:1],1'b0} | pt.INST_ACCESS_MASK6)) == (pt.INST_ACCESS_ADDR6 | pt.INST_ACCESS_MASK6)) | + (pt.INST_ACCESS_ENABLE7 & (({ifc_fetch_addr_bf[31:1],1'b0} | pt.INST_ACCESS_MASK7)) == (pt.INST_ACCESS_ADDR7 | pt.INST_ACCESS_MASK7)); + assign ifc_region_acc_fault_memory_bf = ~ifc_iccm_access_bf & ~ifc_region_acc_okay & ifc_fetch_req_bf; + end assign ifc_region_acc_fault_final_bf = ifc_region_acc_fault_bf | ifc_region_acc_fault_memory_bf; diff --git a/design/include/el2_def.sv b/design/include/el2_def.sv index f4de36e2b5e..7f11e798901 100644 --- a/design/include/el2_def.sv +++ b/design/include/el2_def.sv @@ -404,6 +404,32 @@ typedef struct packed { logic icache_rd_valid; logic icache_wr_valid; } el2_cache_debug_pkt_t; + + + typedef enum logic [2:0] { + NONE = 3'b000, + READ = 3'b001, + WRITE = 3'b010, + EXEC = 3'b100 + } el2_pmp_type_pkt_t; + + + typedef enum logic [1:0] { + OFF = 2'b00, + TOR = 2'b01, + NA4 = 2'b10, + NAPOT = 2'b11 + } el2_pmp_mode_pkt_t; + + + typedef struct packed { + logic lock; + logic [1:0] reserved; + el2_pmp_mode_pkt_t mode; + logic execute; + logic write; + logic read; + } el2_pmp_cfg_pkt_t; //`endif endpackage // el2_pkg diff --git a/design/lsu/el2_lsu.sv b/design/lsu/el2_lsu.sv index 06bd86907c4..5e2f76f3753 100644 --- a/design/lsu/el2_lsu.sv +++ b/design/lsu/el2_lsu.sv @@ -177,7 +177,14 @@ import el2_pkg::*; input logic scan_mode, // scan mode input logic clk, // Clock only while core active. Through one clock header. For flops with second clock header built in. Connected to ACTIVE_L2CLK. input logic active_clk, // Clock only while core active. Through two clock headers. For flops without second clock header built in. - input logic rst_l // reset, active low + input logic rst_l, // reset, active low + + output logic [31:0] lsu_pmp_addr_start, + output logic [31:0] lsu_pmp_addr_end, + input logic lsu_pmp_error_start, + input logic lsu_pmp_error_end, + output logic lsu_pmp_we, + output logic lsu_pmp_re ); @@ -209,9 +216,13 @@ import el2_pkg::*; logic [31:0] lsu_addr_d, lsu_addr_m, lsu_addr_r; logic [31:0] end_addr_d, end_addr_m, end_addr_r; + assign lsu_pmp_addr_start = lsu_addr_d; + assign lsu_pmp_addr_end = end_addr_d; el2_lsu_pkt_t lsu_pkt_d, lsu_pkt_m, lsu_pkt_r; logic lsu_i0_valid_d, lsu_i0_valid_m, lsu_i0_valid_r; + assign lsu_pmp_we = lsu_pkt_d.store & lsu_pkt_d.valid; + assign lsu_pmp_re = lsu_pkt_d.load & lsu_pkt_d.valid; // Store Buffer signals logic store_stbuf_reqvld_r; @@ -306,8 +317,8 @@ import el2_pkg::*; // Store buffer now have only non-dma dccm stores // stbuf_empty not needed since it has only dccm stores assign lsu_idle_any = ~((lsu_pkt_m.valid & ~lsu_pkt_m.dma) | - (lsu_pkt_r.valid & ~lsu_pkt_r.dma)) & - lsu_bus_buffer_empty_any; + (lsu_pkt_r.valid & ~lsu_pkt_r.dma)) & + lsu_bus_buffer_empty_any; assign lsu_active = (lsu_pkt_m.valid | lsu_pkt_r.valid | ld_single_ecc_error_r_ff) | ~lsu_bus_buffer_empty_any; // This includes DMA. Used for gating top clock diff --git a/design/lsu/el2_lsu_addrcheck.sv b/design/lsu/el2_lsu_addrcheck.sv index 2abe8e183cc..d6b0666ab83 100644 --- a/design/lsu/el2_lsu_addrcheck.sv +++ b/design/lsu/el2_lsu_addrcheck.sv @@ -50,6 +50,9 @@ import el2_pkg::*; output logic fir_dccm_access_error_d, // Fast interrupt dccm access error output logic fir_nondccm_access_error_d,// Fast interrupt dccm access error + input logic lsu_pmp_error_start, + input logic lsu_pmp_error_end, + input logic scan_mode // Scan mode ); @@ -124,9 +127,10 @@ import el2_pkg::*; assign csr_idx[4:0] = {start_addr_d[31:28], 1'b1}; assign is_sideeffects_d = dec_tlu_mrac_ff[csr_idx] & ~(start_addr_in_dccm_region_d | start_addr_in_pic_region_d | addr_in_iccm) & lsu_pkt_d.valid & (lsu_pkt_d.store | lsu_pkt_d.load); //every region has the 2 LSB indicating ( 1: sideeffects/no_side effects, and 0: cacheable ). Ignored in internal regions assign is_aligned_d = (lsu_pkt_d.word & (start_addr_d[1:0] == 2'b0)) | - (lsu_pkt_d.half & (start_addr_d[0] == 1'b0)) | - lsu_pkt_d.by; + (lsu_pkt_d.half & (start_addr_d[0] == 1'b0)) | + lsu_pkt_d.by; + if (pt.PMP_ENTRIES == 0) begin assign non_dccm_access_ok = (~(|{pt.DATA_ACCESS_ENABLE0,pt.DATA_ACCESS_ENABLE1,pt.DATA_ACCESS_ENABLE2,pt.DATA_ACCESS_ENABLE3,pt.DATA_ACCESS_ENABLE4,pt.DATA_ACCESS_ENABLE5,pt.DATA_ACCESS_ENABLE6,pt.DATA_ACCESS_ENABLE7})) | (((pt.DATA_ACCESS_ENABLE0 & ((start_addr_d[31:0] | pt.DATA_ACCESS_MASK0)) == (pt.DATA_ACCESS_ADDR0 | pt.DATA_ACCESS_MASK0)) | (pt.DATA_ACCESS_ENABLE1 & ((start_addr_d[31:0] | pt.DATA_ACCESS_MASK1)) == (pt.DATA_ACCESS_ADDR1 | pt.DATA_ACCESS_MASK1)) | @@ -144,6 +148,7 @@ import el2_pkg::*; (pt.DATA_ACCESS_ENABLE5 & ((end_addr_d[31:0] | pt.DATA_ACCESS_MASK5)) == (pt.DATA_ACCESS_ADDR5 | pt.DATA_ACCESS_MASK5)) | (pt.DATA_ACCESS_ENABLE6 & ((end_addr_d[31:0] | pt.DATA_ACCESS_MASK6)) == (pt.DATA_ACCESS_ADDR6 | pt.DATA_ACCESS_MASK6)) | (pt.DATA_ACCESS_ENABLE7 & ((end_addr_d[31:0] | pt.DATA_ACCESS_MASK7)) == (pt.DATA_ACCESS_ADDR7 | pt.DATA_ACCESS_MASK7)))); + end // Access fault logic // 0. Unmapped local memory : Addr in dccm region but not in dccm offset OR Addr in picm region but not in picm offset OR DCCM -> PIC cross when DCCM/PIC in same region @@ -159,13 +164,21 @@ import el2_pkg::*; (end_addr_in_dccm_region_d & ~(end_addr_in_dccm_d | end_addr_in_pic_d)) | // 0. Addr in dccm/pic region but not in dccm/pic offset (start_addr_in_dccm_d & end_addr_in_pic_d) | // 0. DCCM -> PIC cross when DCCM/PIC in same region (start_addr_in_pic_d & end_addr_in_dccm_d)); // 0. DCCM -> PIC cross when DCCM/PIC in same region - assign mpu_access_fault_d = (~start_addr_in_dccm_region_d & ~non_dccm_access_ok); // 3. Address is not in a populated non-dccm region + if (pt.PMP_ENTRIES > 0) begin + assign mpu_access_fault_d = (lsu_pmp_error_start | lsu_pmp_error_end); // X. Address is in blocked region + end else begin + assign mpu_access_fault_d = (~start_addr_in_dccm_region_d & ~non_dccm_access_ok); // 3. Address is not in a populated non-dccm region + end end else begin assign unmapped_access_fault_d = ((start_addr_in_dccm_region_d & ~start_addr_in_dccm_d) | // 0. Addr in dccm region but not in dccm offset (end_addr_in_dccm_region_d & ~end_addr_in_dccm_d) | // 0. Addr in dccm region but not in dccm offset (start_addr_in_pic_region_d & ~start_addr_in_pic_d) | // 0. Addr in picm region but not in picm offset (end_addr_in_pic_region_d & ~end_addr_in_pic_d)); // 0. Addr in picm region but not in picm offset - assign mpu_access_fault_d = (~start_addr_in_pic_region_d & ~start_addr_in_dccm_region_d & ~non_dccm_access_ok); // 3. Address is not in a populated non-dccm region + if (pt.PMP_ENTRIES > 0) begin + assign mpu_access_fault_d = (lsu_pmp_error_start | lsu_pmp_error_end); // X. Address is in blocked region + end else begin + assign mpu_access_fault_d = (~start_addr_in_pic_region_d & ~start_addr_in_dccm_region_d & ~non_dccm_access_ok); // 3. Address is not in a populated non-dccm region + end end assign access_fault_d = (unmapped_access_fault_d | mpu_access_fault_d | picm_access_fault_d | regpred_access_fault_d) & lsu_pkt_d.valid & ~lsu_pkt_d.dma; @@ -183,7 +196,7 @@ import el2_pkg::*; // Fast interrupt error logic assign fir_dccm_access_error_d = ((start_addr_in_dccm_region_d & ~start_addr_in_dccm_d) | - (end_addr_in_dccm_region_d & ~end_addr_in_dccm_d)) & lsu_pkt_d.valid & lsu_pkt_d.fast_int; + (end_addr_in_dccm_region_d & ~end_addr_in_dccm_d)) & lsu_pkt_d.valid & lsu_pkt_d.fast_int; assign fir_nondccm_access_error_d = ~(start_addr_in_dccm_region_d & end_addr_in_dccm_region_d) & lsu_pkt_d.valid & lsu_pkt_d.fast_int; rvdff #(.WIDTH(1)) is_sideeffects_mff (.din(is_sideeffects_d), .dout(is_sideeffects_m), .clk(lsu_c2_m_clk), .*); diff --git a/design/lsu/el2_lsu_lsc_ctl.sv b/design/lsu/el2_lsu_lsc_ctl.sv index d9aeb1fb178..46bb468d7c2 100644 --- a/design/lsu/el2_lsu_lsc_ctl.sv +++ b/design/lsu/el2_lsu_lsc_ctl.sv @@ -111,6 +111,9 @@ import el2_pkg::*; output el2_lsu_pkt_t lsu_pkt_m, output el2_lsu_pkt_t lsu_pkt_r, + input logic lsu_pmp_error_start, + input logic lsu_pmp_error_end, + input logic scan_mode // Scan mode ); @@ -256,17 +259,17 @@ import el2_pkg::*; // this is really R stage signal assign lsu_result_m[31:0] = ({32{ lsu_pkt_r.unsign & lsu_pkt_r.by }} & {24'b0,lsu_ld_datafn_r[7:0]}) | - ({32{ lsu_pkt_r.unsign & lsu_pkt_r.half}} & {16'b0,lsu_ld_datafn_r[15:0]}) | - ({32{~lsu_pkt_r.unsign & lsu_pkt_r.by }} & {{24{ lsu_ld_datafn_r[7]}}, lsu_ld_datafn_r[7:0]}) | - ({32{~lsu_pkt_r.unsign & lsu_pkt_r.half}} & {{16{ lsu_ld_datafn_r[15]}},lsu_ld_datafn_r[15:0]}) | - ({32{lsu_pkt_r.word}} & lsu_ld_datafn_r[31:0]); + ({32{ lsu_pkt_r.unsign & lsu_pkt_r.half}} & {16'b0,lsu_ld_datafn_r[15:0]}) | + ({32{~lsu_pkt_r.unsign & lsu_pkt_r.by }} & {{24{ lsu_ld_datafn_r[7]}}, lsu_ld_datafn_r[7:0]}) | + ({32{~lsu_pkt_r.unsign & lsu_pkt_r.half}} & {{16{ lsu_ld_datafn_r[15]}},lsu_ld_datafn_r[15:0]}) | + ({32{lsu_pkt_r.word}} & lsu_ld_datafn_r[31:0]); // this signal is used for gpr update assign lsu_result_corr_r[31:0] = ({32{ lsu_pkt_r.unsign & lsu_pkt_r.by }} & {24'b0,lsu_ld_datafn_corr_r[7:0]}) | - ({32{ lsu_pkt_r.unsign & lsu_pkt_r.half}} & {16'b0,lsu_ld_datafn_corr_r[15:0]}) | - ({32{~lsu_pkt_r.unsign & lsu_pkt_r.by }} & {{24{ lsu_ld_datafn_corr_r[7]}}, lsu_ld_datafn_corr_r[7:0]}) | - ({32{~lsu_pkt_r.unsign & lsu_pkt_r.half}} & {{16{ lsu_ld_datafn_corr_r[15]}},lsu_ld_datafn_corr_r[15:0]}) | - ({32{lsu_pkt_r.word}} & lsu_ld_datafn_corr_r[31:0]); + ({32{ lsu_pkt_r.unsign & lsu_pkt_r.half}} & {16'b0,lsu_ld_datafn_corr_r[15:0]}) | + ({32{~lsu_pkt_r.unsign & lsu_pkt_r.by }} & {{24{ lsu_ld_datafn_corr_r[7]}}, lsu_ld_datafn_corr_r[7:0]}) | + ({32{~lsu_pkt_r.unsign & lsu_pkt_r.half}} & {{16{ lsu_ld_datafn_corr_r[15]}},lsu_ld_datafn_corr_r[15:0]}) | + ({32{lsu_pkt_r.word}} & lsu_ld_datafn_corr_r[31:0]); end else begin: L2U1_Plus1_0 // block: L2U1_Plus1_1 logic [31:0] lsu_ld_datafn_m, lsu_ld_datafn_corr_r; @@ -276,17 +279,17 @@ import el2_pkg::*; // this result must look at prior stores and merge them in assign lsu_result_m[31:0] = ({32{ lsu_pkt_m.unsign & lsu_pkt_m.by }} & {24'b0,lsu_ld_datafn_m[7:0]}) | - ({32{ lsu_pkt_m.unsign & lsu_pkt_m.half}} & {16'b0,lsu_ld_datafn_m[15:0]}) | - ({32{~lsu_pkt_m.unsign & lsu_pkt_m.by }} & {{24{ lsu_ld_datafn_m[7]}}, lsu_ld_datafn_m[7:0]}) | - ({32{~lsu_pkt_m.unsign & lsu_pkt_m.half}} & {{16{ lsu_ld_datafn_m[15]}},lsu_ld_datafn_m[15:0]}) | - ({32{lsu_pkt_m.word}} & lsu_ld_datafn_m[31:0]); + ({32{ lsu_pkt_m.unsign & lsu_pkt_m.half}} & {16'b0,lsu_ld_datafn_m[15:0]}) | + ({32{~lsu_pkt_m.unsign & lsu_pkt_m.by }} & {{24{ lsu_ld_datafn_m[7]}}, lsu_ld_datafn_m[7:0]}) | + ({32{~lsu_pkt_m.unsign & lsu_pkt_m.half}} & {{16{ lsu_ld_datafn_m[15]}},lsu_ld_datafn_m[15:0]}) | + ({32{lsu_pkt_m.word}} & lsu_ld_datafn_m[31:0]); // this signal is used for gpr update assign lsu_result_corr_r[31:0] = ({32{ lsu_pkt_r.unsign & lsu_pkt_r.by }} & {24'b0,lsu_ld_datafn_corr_r[7:0]}) | - ({32{ lsu_pkt_r.unsign & lsu_pkt_r.half}} & {16'b0,lsu_ld_datafn_corr_r[15:0]}) | - ({32{~lsu_pkt_r.unsign & lsu_pkt_r.by }} & {{24{ lsu_ld_datafn_corr_r[7]}}, lsu_ld_datafn_corr_r[7:0]}) | - ({32{~lsu_pkt_r.unsign & lsu_pkt_r.half}} & {{16{ lsu_ld_datafn_corr_r[15]}},lsu_ld_datafn_corr_r[15:0]}) | - ({32{lsu_pkt_r.word}} & lsu_ld_datafn_corr_r[31:0]); + ({32{ lsu_pkt_r.unsign & lsu_pkt_r.half}} & {16'b0,lsu_ld_datafn_corr_r[15:0]}) | + ({32{~lsu_pkt_r.unsign & lsu_pkt_r.by }} & {{24{ lsu_ld_datafn_corr_r[7]}}, lsu_ld_datafn_corr_r[7:0]}) | + ({32{~lsu_pkt_r.unsign & lsu_pkt_r.half}} & {{16{ lsu_ld_datafn_corr_r[15]}},lsu_ld_datafn_corr_r[15:0]}) | + ({32{lsu_pkt_r.word}} & lsu_ld_datafn_corr_r[31:0]); end // Fast interrupt address diff --git a/testbench/flist b/testbench/flist index 402cd6f9676..c11a9e8f62c 100644 --- a/testbench/flist +++ b/testbench/flist @@ -7,6 +7,7 @@ $RV_ROOT/design/el2_mem.sv $RV_ROOT/design/el2_pic_ctrl.sv $RV_ROOT/design/el2_veer.sv $RV_ROOT/design/el2_dma_ctrl.sv +$RV_ROOT/design/el2_pmp.sv $RV_ROOT/design/ifu/el2_ifu_aln_ctl.sv $RV_ROOT/design/ifu/el2_ifu_compress_ctl.sv $RV_ROOT/design/ifu/el2_ifu_ifc_ctl.sv @@ -18,6 +19,7 @@ $RV_ROOT/design/ifu/el2_ifu.sv $RV_ROOT/design/dec/el2_dec_decode_ctl.sv $RV_ROOT/design/dec/el2_dec_gpr_ctl.sv $RV_ROOT/design/dec/el2_dec_ib_ctl.sv +$RV_ROOT/design/dec/el2_dec_pmp_ctl.sv $RV_ROOT/design/dec/el2_dec_tlu_ctl.sv $RV_ROOT/design/dec/el2_dec_trigger.sv $RV_ROOT/design/dec/el2_dec.sv diff --git a/testbench/tests/pmp/crt0.s b/testbench/tests/pmp/crt0.s new file mode 120000 index 00000000000..d09de58fb6a --- /dev/null +++ b/testbench/tests/pmp/crt0.s @@ -0,0 +1 @@ +../../asm/crt0.s \ No newline at end of file diff --git a/testbench/tests/pmp/fault.c b/testbench/tests/pmp/fault.c new file mode 100644 index 00000000000..41e71f19f40 --- /dev/null +++ b/testbench/tests/pmp/fault.c @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2023 Antmicro, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include "trap.h" +#include "fault.h" + +int fault_jmp_env_set = 0; +jmp_buf fault_jmp_env; +struct fault fault_last; + +void fault_install(void) +{ + __asm__("la t0, _trap"); + __asm__("csrw mtvec, t0"); +} + +void fault_setjmp(jmp_buf env) +{ + memcpy(fault_jmp_env, env, sizeof(fault_jmp_env)); + fault_jmp_env_set = 1; +} + +struct fault fault_last_get(void) +{ + return fault_last; +} + +void fault_return(struct fault *fault) +{ + // Save register state for later usage + memcpy(&fault_last, fault, sizeof(fault_last)); + + // Return to program if setjmp-based try-catch was used + if (fault_jmp_env_set) { + fault_jmp_env_set = 0; + longjmp(fault_jmp_env, 1); + } + else { + exit(1); + } +} diff --git a/testbench/tests/pmp/fault.h b/testbench/tests/pmp/fault.h new file mode 100644 index 00000000000..92538a0cdd2 --- /dev/null +++ b/testbench/tests/pmp/fault.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2023 Antmicro, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _FAULT_H +#define _FAULT_H + +#include +#include "trap.h" + +#define EXC_INSTRUCTION_ACC_FAULT 1 +#define EXC_LOAD_ACC_FAULT 5 +#define EXC_STORE_ACC_FAULT 7 + +void fault_install(void); +void fault_setjmp(jmp_buf env); +struct fault fault_last_get(void); +void fault_return(struct fault *fault); + +#endif diff --git a/testbench/tests/pmp/main.c b/testbench/tests/pmp/main.c new file mode 100644 index 00000000000..8ca3674ed3f --- /dev/null +++ b/testbench/tests/pmp/main.c @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2023 Antmicro, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include "veer.h" +#include "trap.h" +#include "fault.h" +#include "pmp.h" + +#define TEST_NUMBER 3 + +int test_load(int); +int test_store(int); +int test_exec(int); + +int (*tests[TEST_NUMBER]) (int) = {test_load, test_store, test_exec}; +const char *const test_names[TEST_NUMBER] = {"test_load", "test_store", "test_exec"}; + +volatile int temp_load; +volatile int temp_store; + +#define PATTERN_A 0xaaaaaaaa +#define PATTERN_B 0x55555555 + +int test_load(int id) +{ + int temp = PATTERN_B; + struct fault ret; + + TRY { + struct pmp_entry_s entry = { + .addr = ((uintptr_t)(&temp_load)) >> 2, + .cfg = PMP_LOCK | PMP_NA4 | PMP_X | PMP_W + }; + pmp_entry_write(id, &entry); + temp_load = PATTERN_A; + temp = temp_load; + } + CATCH { + if ((temp != PATTERN_B) && (temp == PATTERN_A)) return 3; + ret = fault_last_get(); + return (ret.mcause == EXC_LOAD_ACC_FAULT) ? 0 : 1; + } + END_TRY; + return 2; +} + +int test_store(int id) +{ + struct fault ret; + + TRY { + temp_store = PATTERN_A; + struct pmp_entry_s entry = { + .addr = ((uintptr_t)(&temp_store)) >> 2, + .cfg = PMP_LOCK | PMP_NA4 | PMP_X | PMP_R + }; + pmp_entry_write(id, &entry); + temp_store = PATTERN_B; + } + CATCH { + if (temp_store == PATTERN_B) return 3; + ret = fault_last_get(); + return (ret.mcause == EXC_STORE_ACC_FAULT) ? 0 : 1; + } + END_TRY; + return 2; +} + +void __attribute__ ((noinline)) test_exec_1(void) +{ + puts(__func__); + return; +} + +int test_exec(int id) +{ + struct fault ret; + + TRY { + struct pmp_entry_s entry = { + .addr = ((uintptr_t)(test_exec_1)) >> 2, + .cfg = PMP_LOCK | PMP_NA4 | PMP_W | PMP_R + }; + pmp_entry_write(id, &entry); + test_exec_1(); + } + CATCH { + ret = fault_last_get(); + return (ret.mcause == EXC_INSTRUCTION_ACC_FAULT) ? 0 : 1; + } + END_TRY; + return 2; +} + +void main(void) +{ + int results[TEST_NUMBER]; + int sum = 0; + + puts("PMP/exception test program"); + fault_install(); + + for (int i = 0; i < TEST_NUMBER; i++) { + printf(":: %s\n", test_names[i]); + results[i] = tests[i](i); + printf(":: %s: %s (%d)\n", test_names[i], (results[i] ? "failed" : "passed"), results[i]); + sum += results[i]; + } + + exit(sum); +} diff --git a/testbench/tests/pmp/pmp.c b/testbench/tests/pmp/pmp.c new file mode 100644 index 00000000000..b76a48acee2 --- /dev/null +++ b/testbench/tests/pmp/pmp.c @@ -0,0 +1,285 @@ +#include +#include "pmp.h" + +int pmp_read_pmpcfg(unsigned int offset, uintptr_t * dest) +{ + uintptr_t csr_value; + + if (!dest) return 1; + + switch (offset) { + case 0x0: CSRR_READ(csr_value, CSR_PMPCFG_BASE + 0x0); break; + case 0x1: CSRR_READ(csr_value, CSR_PMPCFG_BASE + 0x1); break; + case 0x2: CSRR_READ(csr_value, CSR_PMPCFG_BASE + 0x2); break; + case 0x3: CSRR_READ(csr_value, CSR_PMPCFG_BASE + 0x3); break; + case 0x4: CSRR_READ(csr_value, CSR_PMPCFG_BASE + 0x4); break; + case 0x5: CSRR_READ(csr_value, CSR_PMPCFG_BASE + 0x5); break; + case 0x6: CSRR_READ(csr_value, CSR_PMPCFG_BASE + 0x6); break; + case 0x7: CSRR_READ(csr_value, CSR_PMPCFG_BASE + 0x7); break; + case 0x8: CSRR_READ(csr_value, CSR_PMPCFG_BASE + 0x8); break; + case 0x9: CSRR_READ(csr_value, CSR_PMPCFG_BASE + 0x9); break; + case 0xA: CSRR_READ(csr_value, CSR_PMPCFG_BASE + 0xA); break; + case 0xB: CSRR_READ(csr_value, CSR_PMPCFG_BASE + 0xB); break; + case 0xC: CSRR_READ(csr_value, CSR_PMPCFG_BASE + 0xC); break; + case 0xD: CSRR_READ(csr_value, CSR_PMPCFG_BASE + 0xD); break; + case 0xE: CSRR_READ(csr_value, CSR_PMPCFG_BASE + 0xE); break; + case 0xF: CSRR_READ(csr_value, CSR_PMPCFG_BASE + 0xF); break; + default: return 2; break; + } + + (*dest) = csr_value; + + return 0; +} + +int pmp_read_pmpaddr(unsigned int offset, uintptr_t * dest) +{ + uintptr_t csr_value; + + if (!dest) return 1; + + switch (offset) { + case 0x00: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x00); break; + case 0x01: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x01); break; + case 0x02: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x02); break; + case 0x03: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x03); break; + case 0x04: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x04); break; + case 0x05: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x05); break; + case 0x06: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x06); break; + case 0x07: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x07); break; + case 0x08: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x08); break; + case 0x09: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x09); break; + case 0x0A: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x0A); break; + case 0x0B: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x0B); break; + case 0x0C: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x0C); break; + case 0x0D: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x0D); break; + case 0x0E: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x0E); break; + case 0x0F: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x0F); break; + + case 0x10: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x10); break; + case 0x11: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x11); break; + case 0x12: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x12); break; + case 0x13: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x13); break; + case 0x14: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x14); break; + case 0x15: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x15); break; + case 0x16: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x16); break; + case 0x17: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x17); break; + case 0x18: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x18); break; + case 0x19: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x19); break; + case 0x1A: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x1A); break; + case 0x1B: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x1B); break; + case 0x1C: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x1C); break; + case 0x1D: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x1D); break; + case 0x1E: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x1E); break; + case 0x1F: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x1F); break; + + case 0x20: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x20); break; + case 0x21: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x21); break; + case 0x22: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x22); break; + case 0x23: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x23); break; + case 0x24: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x24); break; + case 0x25: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x25); break; + case 0x26: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x26); break; + case 0x27: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x27); break; + case 0x28: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x28); break; + case 0x29: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x29); break; + case 0x2A: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x2A); break; + case 0x2B: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x2B); break; + case 0x2C: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x2C); break; + case 0x2D: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x2D); break; + case 0x2E: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x2E); break; + case 0x2F: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x2F); break; + + case 0x30: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x30); break; + case 0x31: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x31); break; + case 0x32: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x32); break; + case 0x33: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x33); break; + case 0x34: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x34); break; + case 0x35: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x35); break; + case 0x36: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x36); break; + case 0x37: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x37); break; + case 0x38: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x38); break; + case 0x39: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x39); break; + case 0x3A: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x3A); break; + case 0x3B: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x3B); break; + case 0x3C: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x3C); break; + case 0x3D: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x3D); break; + case 0x3E: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x3E); break; + case 0x3F: CSRR_READ(csr_value, CSR_PMPADDR_BASE + 0x3F); break; + + default: return 2; break; + } + + (*dest) = csr_value; + + return 0; +} + +int pmp_write_pmpcfg(unsigned int offset, uintptr_t * src) +{ + uintptr_t csr_value; + + if (!src) return 1; + + csr_value = (*src); + + switch (offset) { + case 0x0: CSRR_WRITE(csr_value, CSR_PMPCFG_BASE + 0x0); break; + case 0x1: CSRR_WRITE(csr_value, CSR_PMPCFG_BASE + 0x1); break; + case 0x2: CSRR_WRITE(csr_value, CSR_PMPCFG_BASE + 0x2); break; + case 0x3: CSRR_WRITE(csr_value, CSR_PMPCFG_BASE + 0x3); break; + case 0x4: CSRR_WRITE(csr_value, CSR_PMPCFG_BASE + 0x4); break; + case 0x5: CSRR_WRITE(csr_value, CSR_PMPCFG_BASE + 0x5); break; + case 0x6: CSRR_WRITE(csr_value, CSR_PMPCFG_BASE + 0x6); break; + case 0x7: CSRR_WRITE(csr_value, CSR_PMPCFG_BASE + 0x7); break; + case 0x8: CSRR_WRITE(csr_value, CSR_PMPCFG_BASE + 0x8); break; + case 0x9: CSRR_WRITE(csr_value, CSR_PMPCFG_BASE + 0x9); break; + case 0xA: CSRR_WRITE(csr_value, CSR_PMPCFG_BASE + 0xA); break; + case 0xB: CSRR_WRITE(csr_value, CSR_PMPCFG_BASE + 0xB); break; + case 0xC: CSRR_WRITE(csr_value, CSR_PMPCFG_BASE + 0xC); break; + case 0xD: CSRR_WRITE(csr_value, CSR_PMPCFG_BASE + 0xD); break; + case 0xE: CSRR_WRITE(csr_value, CSR_PMPCFG_BASE + 0xE); break; + case 0xF: CSRR_WRITE(csr_value, CSR_PMPCFG_BASE + 0xF); break; + + default: return 2; break; + } + + return 0; +} + +int pmp_write_pmpaddr(unsigned int offset, uintptr_t * src) +{ + uintptr_t csr_value; + + if (!src) return 1; + + csr_value = (*src); + + switch (offset) { + case 0x00: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x00); break; + case 0x01: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x01); break; + case 0x02: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x02); break; + case 0x03: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x03); break; + case 0x04: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x04); break; + case 0x05: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x05); break; + case 0x06: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x06); break; + case 0x07: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x07); break; + case 0x08: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x08); break; + case 0x09: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x09); break; + case 0x0A: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x0A); break; + case 0x0B: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x0B); break; + case 0x0C: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x0C); break; + case 0x0D: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x0D); break; + case 0x0E: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x0E); break; + case 0x0F: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x0F); break; + + case 0x10: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x10); break; + case 0x11: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x11); break; + case 0x12: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x12); break; + case 0x13: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x13); break; + case 0x14: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x14); break; + case 0x15: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x15); break; + case 0x16: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x16); break; + case 0x17: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x17); break; + case 0x18: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x18); break; + case 0x19: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x19); break; + case 0x1A: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x1A); break; + case 0x1B: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x1B); break; + case 0x1C: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x1C); break; + case 0x1D: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x1D); break; + case 0x1E: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x1E); break; + case 0x1F: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x1F); break; + + case 0x20: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x20); break; + case 0x21: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x21); break; + case 0x22: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x22); break; + case 0x23: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x23); break; + case 0x24: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x24); break; + case 0x25: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x25); break; + case 0x26: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x26); break; + case 0x27: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x27); break; + case 0x28: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x28); break; + case 0x29: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x29); break; + case 0x2A: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x2A); break; + case 0x2B: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x2B); break; + case 0x2C: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x2C); break; + case 0x2D: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x2D); break; + case 0x2E: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x2E); break; + case 0x2F: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x2F); break; + + case 0x30: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x30); break; + case 0x31: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x31); break; + case 0x32: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x32); break; + case 0x33: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x33); break; + case 0x34: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x34); break; + case 0x35: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x35); break; + case 0x36: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x36); break; + case 0x37: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x37); break; + case 0x38: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x38); break; + case 0x39: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x39); break; + case 0x3A: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x3A); break; + case 0x3B: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x3B); break; + case 0x3C: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x3C); break; + case 0x3D: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x3D); break; + case 0x3E: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x3E); break; + case 0x3F: CSRR_WRITE(csr_value, CSR_PMPADDR_BASE + 0x3F); break; + + default: return 2; break; + } + + return 0; +} + +int pmp_entry_read(unsigned int id, struct pmp_entry_s * entry) +{ + unsigned int pmpcfg_id_coarse = (id >> 2) & ((__riscv_xlen == 32) ? (~0) : (~1)); + unsigned int pmpcfg_id_fine = id & ((__riscv_xlen == 32) ? 3 : 7); + + uintptr_t pmpcfg_csr; + uintptr_t pmpaddr_csr; + + /* One PMPCFGx register contains configuration + * for 4 entries (RV32) or 8 entries (RV64). + */ + + if (!entry) return 1; + if (id > 64) return 2; + + /* Read PMPADDRx CSR */ + if (pmp_read_pmpaddr(id, &pmpaddr_csr) != 0) return 3; + + /* Read PMPCFGx CSR */ + if (pmp_read_pmpcfg(pmpcfg_id_coarse, &pmpcfg_csr) != 0) return 4; + + entry->addr = pmpaddr_csr; + entry->cfg = (pmpcfg_csr >> (8 * pmpcfg_id_fine)) & 0xff; + + return 0; +} + +int pmp_entry_write(unsigned int id, struct pmp_entry_s * entry) +{ + unsigned int pmpcfg_id_coarse = (id >> 2) & ((__riscv_xlen == 32) ? (~0) : (~1)); + unsigned int pmpcfg_id_fine = id & ((__riscv_xlen == 32) ? 3 : 7); + + unsigned int pmpcfg_csr; + unsigned int pmpaddr_csr; + + /* One PMPCFGx register contains configuration + * for 4 entries (RV32) or 8 entries (RV64). + */ + + if (!entry) return 1; + if (id > 64) return 2; + + /* Write entry to PMPADDRx CSR */ + pmpaddr_csr = entry->addr; + if (pmp_write_pmpaddr(id, &pmpaddr_csr) != 0) return 3; + + /* Write entry to PMPCFGx CSR. Other entries in the same CSR are left intact */ + if (pmp_read_pmpcfg(pmpcfg_id_coarse, &pmpcfg_csr) != 0) return 4; + pmpcfg_csr = pmpcfg_csr & (~(0xff << (pmpcfg_id_fine * 8))) | (entry->cfg << (pmpcfg_id_fine * 8)); + if (pmp_write_pmpcfg(pmpcfg_id_coarse, &pmpcfg_csr) != 0) return 5; + + return 0; +} diff --git a/testbench/tests/pmp/pmp.h b/testbench/tests/pmp/pmp.h new file mode 100644 index 00000000000..35f846a3f24 --- /dev/null +++ b/testbench/tests/pmp/pmp.h @@ -0,0 +1,43 @@ +#include + +#define PMP_LOCK (1<<7) +#define PMP_OFF (0<<3) +#define PMP_TOR (1<<3) +#define PMP_NA4 (2<<3) +#define PMP_NAPOT (3<<3) +#define PMP_X (1<<2) +#define PMP_W (1<<1) +#define PMP_R (1<<0) + +#define CSR_PMPCFG_BASE 0x3A0 +#define CSR_PMPADDR_BASE 0x3B0 + +#define CSRR_READ(v, csr) \ +/* CSRR_READ(v, csr): \ + * csr: MUST be a compile time integer 12-bit constant (0-4095) \ + */ \ +__asm__ __volatile__ ("csrr %0, %1" \ + : "=r" (v) \ + : "n" (csr) \ + : /* clobbers: none */ ) + +#define CSRR_WRITE(v, csr) \ +/* CSRR_WRITE(v, csr): \ + * csr: MUST be a compile time integer 12-bit constant (0-4095) \ + */ \ +__asm__ __volatile__ ("csrw %0, %1" \ + : \ + : "n" (csr), "rK" (v) \ + : /* clobbers: none */ ) + +struct pmp_entry_s { + uintptr_t addr; + uint8_t cfg; +}; + +int pmp_read_pmpcfg(unsigned int offset, uintptr_t * dest); +int pmp_read_pmpaddr(unsigned int offset, uintptr_t * dest); +int pmp_write_pmpcfg(unsigned int offset, uintptr_t * src); +int pmp_write_pmpaddr(unsigned int offset, uintptr_t * src); +int pmp_entry_read(unsigned int id, struct pmp_entry_s * entry); +int pmp_entry_write(unsigned int id, struct pmp_entry_s * entry); diff --git a/testbench/tests/pmp/pmp.ld b/testbench/tests/pmp/pmp.ld new file mode 100644 index 00000000000..86617e23df6 --- /dev/null +++ b/testbench/tests/pmp/pmp.ld @@ -0,0 +1,12 @@ +OUTPUT_ARCH( "riscv" ) +ENTRY(_start) + +SECTIONS { + . = 0x80000000; + .text : { *(.text*) } + _end = .; + .data : { *(.*data) *(.rodata*) *(.sbss) STACK = ALIGN(16) + 0x1000; FAULTSTACK = ALIGN(16) + 0x200;} + .bss : { *(.bss) } + . = 0xd0580000; + .data.io . : { *(.data.io) } +} diff --git a/testbench/tests/pmp/pmp.mki b/testbench/tests/pmp/pmp.mki new file mode 100644 index 00000000000..9d2eeff6ac6 --- /dev/null +++ b/testbench/tests/pmp/pmp.mki @@ -0,0 +1,2 @@ +OFILES = crt0.o main.o pmp.o veer.o trap.o fault.o +TEST_CFLAGS = -g -O3 -falign-functions=16 diff --git a/testbench/tests/pmp/trap.c b/testbench/tests/pmp/trap.c new file mode 100644 index 00000000000..c1a7bff27a9 --- /dev/null +++ b/testbench/tests/pmp/trap.c @@ -0,0 +1,127 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright © 2020 Sebastian Meyer + * Copyright 2023 Antmicro, Ltd. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Derived from picolibc: picocrt/machine/riscv/crt0.c + */ + +#include +#include +#include +#include +#include "veer.h" +#include "fault.h" +#include "trap.h" + + +static const char *const names[NUM_REG] = { + "zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2", + "s0/fp","s1", "a0", "a1", "a2", "a3", "a4", "a5", +#if NUM_REG > 16 + "a6", "a7", "s2", "s3", "s4", "s5", "s6", "s7", + "s8", "s9", "s10", "s11", "t3", "t4", "t5", "t6", +#endif +}; + +static void __attribute__((used)) +_ctrap(struct fault *fault) +{ + int r; + printf("RISC-V fault\n"); + for (r = 0; r < NUM_REG; r++) + printf("\tx%d %-5.5s%s 0x" FMT "\n", r, names[r], r < 10 ? " " : "", fault->r[r]); + printf("\tmepc: 0x" FMT "\n", fault->mepc); + printf("\tmcause: 0x" FMT "\n", fault->mcause); + printf("\tmtval: 0x" FMT "\n", fault->mtval); + + fault_return(fault); + exit(1); +} + +void __attribute__((naked)) __section(".text.init") __attribute__((used)) __attribute((aligned(4))) +_trap(void) +{ +#ifndef __clang__ + __asm__(".option nopic"); +#endif + + /* Build a known-working C environment */ + __asm__(".option push\n" + ".option norelax\n" + "la sp, FAULTSTACK\n" + ".option pop"); + + /* Make space for saved registers */ + __asm__("addi sp,sp,%0" :: "i" (-sizeof(struct fault))); + + /* Save registers on stack */ +#define SAVE_REG(num) \ + __asm__(SD" x%0, %1(sp)" :: "i" (num), \ + "i" ((num) * sizeof(unsigned long) + offsetof(struct fault, r))) + +#define SAVE_REGS_8(base) \ + SAVE_REG(base+0); SAVE_REG(base+1); SAVE_REG(base+2); SAVE_REG(base+3); \ + SAVE_REG(base+4); SAVE_REG(base+5); SAVE_REG(base+6); SAVE_REG(base+7) + + SAVE_REGS_8(0); + SAVE_REGS_8(8); +#ifndef __riscv_32e + SAVE_REGS_8(16); + SAVE_REGS_8(24); +#endif + +#define SAVE_CSR(name) \ + __asm__("csrr t0, "PASTE(name));\ + __asm__(SD" t0, %0(sp)" :: "i" (offsetof(struct fault, name))) + + SAVE_CSR(mepc); + SAVE_CSR(mcause); + SAVE_CSR(mtval); + + /* + * Pass pointer to saved registers in first parameter register + */ + __asm__("mv a0, sp"); + + /* Enable FPU (just in case) */ +#ifdef __riscv_flen + __asm__("csrr t0, mstatus\n" + "li t1, 8192\n" // 1 << 13 = 8192 + "or t0, t1, t0\n" + "csrw mstatus, t0\n" + "csrwi fcsr, 0"); +#endif + __asm__("j _ctrap"); +} diff --git a/testbench/tests/pmp/trap.h b/testbench/tests/pmp/trap.h new file mode 100644 index 00000000000..1b1006f290d --- /dev/null +++ b/testbench/tests/pmp/trap.h @@ -0,0 +1,67 @@ +/* + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright © 2020 Sebastian Meyer + * Copyright 2023 Antmicro, Ltd. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* Derived from picolibc: picocrt/machine/riscv/crt0.c + */ + +#ifndef _TRAP_H +#define _TRAP_H + +#ifdef __riscv_32e +#define NUM_REG 16 +#else +#define NUM_REG 32 +#endif + +#if __riscv_xlen == 32 +#define FMT "%08lx" +#define SD "sw" +#else +#define FMT "%016lx" +#define SD "sd" +#endif + +#define _PASTE(r) #r +#define PASTE(r) _PASTE(r) + +struct fault { + unsigned long r[NUM_REG]; + unsigned long mepc; + unsigned long mcause; + unsigned long mtval; +}; + +#endif diff --git a/testbench/tests/pmp/veer.c b/testbench/tests/pmp/veer.c new file mode 100644 index 00000000000..0ea7e530136 --- /dev/null +++ b/testbench/tests/pmp/veer.c @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2023 Antmicro, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +extern volatile char tohost; + + +__attribute__((__noreturn__)) void _exit (int status) +{ + if (!status) tohost = 0xff; + else tohost = 0x01; + while (1) {}; +} + +int veer_tb_putc(char c, FILE *stream) +{ + (void) stream; + tohost = c; + return c; +} + +static FILE __stdio = FDEV_SETUP_STREAM(veer_tb_putc, NULL, NULL, _FDEV_SETUP_WRITE); +FILE *const stdin = &__stdio; +__strong_reference(stdin, stdout); +__strong_reference(stdin, stderr); diff --git a/testbench/tests/pmp/veer.h b/testbench/tests/pmp/veer.h new file mode 100644 index 00000000000..830f8f0e737 --- /dev/null +++ b/testbench/tests/pmp/veer.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2023 Antmicro, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __VEER_H +#define __VEER_H + +#include + +#define TRY do { jmp_buf try_buf; if(!setjmp(try_buf)){ fault_setjmp(try_buf); +#define CATCH } else { +#define END_TRY } } while(0) +#define THROW longjmp(try_buf, 1) + +#endif diff --git a/tools/Makefile b/tools/Makefile index a3d70852170..55726813a45 100755 --- a/tools/Makefile +++ b/tools/Makefile @@ -108,8 +108,8 @@ all: clean verilator clean: rm -rf *.log *.s *.hex *.dis *.tbl irun* vcs* simv* *.map snapshots veer* \ - verilator* *.exe obj* *.o ucli.key vc_hdrs.h csrc *.csv work\ - dataset.asdb library.cfg vsimsa.cfg riviera-build wave.asdb + verilator* *.exe obj* *.o *.sym ucli.key vc_hdrs.h csrc *.csv work \ + dataset.asdb library.cfg vsimsa.cfg riviera-build wave.asdb diff --git a/verification/block/noxfile.py b/verification/block/noxfile.py index c1e24726cab..0ef06a08a7f 100644 --- a/verification/block/noxfile.py +++ b/verification/block/noxfile.py @@ -266,6 +266,21 @@ def lib_axi4_to_ahb_verify(session, blockName, testName, coverage): verify_block(session, blockName, testName, coverage) +@nox.session(tags=["tests"]) +@nox.parametrize("blockName", ["pmp"]) +@nox.parametrize( + "testName", + [ + "test_xwr_access", + "test_address_matching", + "test_multiple_configs", + ], +) +@nox.parametrize("coverage", coverageTypes) +def pmp_verify(session, blockName, testName, coverage): + verify_block(session, blockName, testName, coverage) + + @nox.session() def isort(session: nox.Session) -> None: """Options are defined in pyproject.toml file""" diff --git a/verification/block/pmp/Makefile b/verification/block/pmp/Makefile new file mode 100644 index 00000000000..d54a4be3e07 --- /dev/null +++ b/verification/block/pmp/Makefile @@ -0,0 +1,17 @@ +null := +space := $(null) # +comma := , + +CURDIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) +SRCDIR := $(abspath $(CURDIR)../../../../design) + +TEST_FILES = $(sort $(wildcard test_*.py)) + +MODULE ?= $(subst $(space),$(comma),$(subst .py,,$(TEST_FILES))) +TOPLEVEL = el2_pmp_wrapper + +VERILOG_SOURCES = \ + $(CURDIR)/pmp/el2_pmp_wrapper.sv \ + $(SRCDIR)/el2_pmp.sv + +include $(CURDIR)/../common.mk diff --git a/verification/block/pmp/common.py b/verification/block/pmp/common.py new file mode 100644 index 00000000000..e6e8365ae32 --- /dev/null +++ b/verification/block/pmp/common.py @@ -0,0 +1,41 @@ +from random import randrange + +from pyuvm import ConfigDB, uvm_sequence +from testbench import PMPCheckItem + + +class BaseSequence(uvm_sequence): + MAX_ADDR = 2**32 - 4 + + def __init__(self, name): + super().__init__(name) + + self.pmp_regs = ConfigDB().get(None, "", "PMP_CSRS") + self.pmp_seqr = ConfigDB().get(None, "", "PMP_SEQR") + self.pmp_channels = ConfigDB().get(None, "", "PMP_CHANNELS") + + # Access (R, W, X) memory at a given address on all channels + async def accessAtAddr(self, addr): + for t in range(3): + type = 1 << t + for c in range(self.pmp_channels): + item = PMPCheckItem(channel=c, addr=addr, type=type) + await self.pmp_seqr.start_item(item) + await self.pmp_seqr.finish_item(item) + + # Try to access memory at random locations in a given address range + async def randomAccessInAddrRange(self, start_addr, end_addr): + addr = randrange(start_addr, end_addr, 4) + await self.accessAtAddr(addr) + + # Access memory at a given address and at adjacent addresses + async def checkRangeBoundary(self, addr): + # Ensure access address is always aligned and doesn't extend 32 bits, + # address is assumed to be inclusive so increment it by 1 intially. + addr = min(self.MAX_ADDR, (addr + 1) & 0xFFFFFFFC) + + if addr >= 4: + await self.accessAtAddr(addr - 4) + await self.accessAtAddr(addr) + if addr < self.MAX_ADDR: + await self.accessAtAddr(addr + 4) diff --git a/verification/block/pmp/el2_pmp_wrapper.sv b/verification/block/pmp/el2_pmp_wrapper.sv new file mode 100644 index 00000000000..7f624bf37be --- /dev/null +++ b/verification/block/pmp/el2_pmp_wrapper.sv @@ -0,0 +1,33 @@ +// Copyright (c) 2023 Antmicro +// SPDX-License-Identifier: Apache-2.0 + +module el2_pmp_wrapper + import el2_pkg::*; +#( + parameter PMP_CHANNELS = 3, + `include "el2_param.vh" +) ( + input logic clk, // Top level clock + input logic rst_l, // Reset + input logic scan_mode, // Scan mode + + input el2_pmp_cfg_pkt_t pmp_pmpcfg [pt.PMP_ENTRIES], + input logic [31:0] pmp_pmpaddr[pt.PMP_ENTRIES], + + input logic [ 31:0] pmp_chan_addr[PMP_CHANNELS], + input el2_pmp_type_pkt_t pmp_chan_type[PMP_CHANNELS], + output logic [0:PMP_CHANNELS-1] pmp_chan_err +); + logic pmp_chan_err_unpacked[PMP_CHANNELS]; + + for (genvar c = 0; c < PMP_CHANNELS; c++) begin + assign pmp_chan_err[c] = pmp_chan_err_unpacked[c]; + end + + // The PMP module + el2_pmp pmp ( + .pmp_chan_err(pmp_chan_err_unpacked), + .* + ); + +endmodule diff --git a/verification/block/pmp/test_address_matching.py b/verification/block/pmp/test_address_matching.py new file mode 100644 index 00000000000..b4b0ece3ce6 --- /dev/null +++ b/verification/block/pmp/test_address_matching.py @@ -0,0 +1,142 @@ +# Copyright (c) 2023 Antmicro +# SPDX-License-Identifier: Apache-2.0 + + +from common import BaseSequence +from pyuvm import ConfigDB, test +from testbench import BaseEnv, BaseTest, PMPWriteCSRItem, getDecodedEntryCfg + +pmp_configurations = [ + { + # 0 - Entry locked but disabled, address 0x1000 + "pmpcfg": 0b10000000, + "pmpaddr": (0x1000 >> 2), + }, + { + # 1 - Entry locked, allow RWX, TOR, address range 0x1000-0x1FFF + "pmpcfg": 0b10001111, + "pmpaddr": (0x2000 >> 2), + }, + { + # 2 - Entry locked, allow R, TOR, address range 0x2000-0x3FFF + "pmpcfg": 0b10001001, + "pmpaddr": (0x4000 >> 2), + }, + { + # 3 - Entry locked, allow X, TOR, address range 0x4000-0xFFFF + "pmpcfg": 0b10001100, + "pmpaddr": (0x10000 >> 2), + }, + { + # 4 - Entry locked, allow W, TOR, address range 0x10000-0x1FFFF + "pmpcfg": 0b10001010, + "pmpaddr": (0x20000 >> 2), + }, + { + # 5 - Entry unlocked, allow none, TOR, address range 0x20000-0xFFFFFFFF + "pmpcfg": 0b00001000, + "pmpaddr": (0x100000000 >> 2), + }, + { + # 6 - Entry locked, allow none, TOR, address range 0xFFFFFFFF-0x10000 + "pmpcfg": 0b10001000, + "pmpaddr": (0x20000 >> 2), + }, + { + # 7 - Entry locked, allow RWX, NA4, address range 0x20000-0x20003 + "pmpcfg": 0b10010111, + "pmpaddr": (0x20000 >> 2), + }, + { + # 8 - Entry locked, allow none, NA4, address range 0x30000-0x30003 + "pmpcfg": 0b10010000, + "pmpaddr": (0x30000 >> 2), + }, + { + # 9 - Entry locked, allow none, NAPOT, address range 0x40000-0x5FFFF + "pmpcfg": 0b10011000, + "pmpaddr": (0x57FFF >> 2), + }, + { + # 10 - Entry locked, allow RW, NAPOT, address range 0x24000-0x25FFF + "pmpcfg": 0b10011011, + "pmpaddr": (0x257FF >> 2), + }, + { + # 11 - Entry locked, allow X, NAPOT, address range 0x26000-0x26FFF + "pmpcfg": 0b10011100, + "pmpaddr": (0x26BFF >> 2), + }, + { + # 12 - Entry locked, allow RW, NAPOT, address range 0x26000-0x26FFF + "pmpcfg": 0b10011011, + "pmpaddr": (0x26BFF >> 2), + }, +] + + +# ============================================================================= + + +class TestSequence(BaseSequence): + def __init__(self, name): + super().__init__(name) + + async def body(self): + pmp_entries = ConfigDB().get(None, "", "PMP_ENTRIES") + + # Configure PMP entries + possible_configs = min(pmp_entries, len(pmp_configurations)) + for i, cfg in enumerate(pmp_configurations): + # Ensure to not use more configurations than PMP entries + if i == possible_configs: + break + + item = PMPWriteCSRItem(index=i, pmpcfg=cfg["pmpcfg"], pmpaddr=cfg["pmpaddr"]) + await self.pmp_seqr.start_item(item) + await self.pmp_seqr.finish_item(item) + + # Check boundaries and few random addresses of each PMP entry + for i in range(len(pmp_configurations)): + start_addr, end_addr = getDecodedEntryCfg(self.pmp_regs, i, range_only=True) + + await self.checkRangeBoundary(start_addr) + + # Access up to 10 random memory cells + accesses = min((end_addr - start_addr) // 4, 10) + if start_addr != end_addr: + for _ in range(accesses): + await self.randomAccessInAddrRange(start_addr, end_addr) + + await self.checkRangeBoundary(end_addr) + + # In the end check 1000 accesses at random memory locations + for _ in range(1000): + await self.randomAccessInAddrRange(0x00000000, 0xFFFFFFFF) + + +# ============================================================================== + + +@test() +class TestAddressMatching(BaseTest): + """ + This test provides a sequence that checks behaviour for different address + matching schemes like: + - Disabled entries + - Top of range with lower boundary than previous entry + - Top of range with higher boundary than previous entry + - Unlocked entry that "forbids" access + - Different permissions restrictions + - Different configurations with interlacing address ranges + """ + + def __init__(self, name, parent): + super().__init__(name, parent, BaseEnv) + + def end_of_elaboration_phase(self): + super().end_of_elaboration_phase() + self.seq = TestSequence.create("stimulus") + + async def run(self): + await self.seq.start() diff --git a/verification/block/pmp/test_multiple_configs.py b/verification/block/pmp/test_multiple_configs.py new file mode 100644 index 00000000000..f3d89bb3562 --- /dev/null +++ b/verification/block/pmp/test_multiple_configs.py @@ -0,0 +1,105 @@ +# Copyright (c) 2023 Antmicro +# SPDX-License-Identifier: Apache-2.0 + +from common import BaseSequence +from pyuvm import ConfigDB, test +from testbench import BaseEnv, BaseTest, PMPWriteCSRItem + +LOWER_BOUNDARY = 0x00000 +UPPER_BOUNDARY = 0x20000 + +pmp_configurations = [ + { + # 0 - Entry locked, allow none, TOR, address range 0x00000-0x0FFFF + "pmpcfg": 0b10001000, + "pmpaddr": (0x10000 >> 2), + }, + { + # 1 - Entry locked, but disabled, address 0x01000 + "pmpcfg": 0b10000000, + "pmpaddr": (0x01000 >> 2), + }, + { + # 2 - Entry locked, allow RWX, TOR, address range 0x01000-0x0FFFF + "pmpcfg": 0b10001111, + "pmpaddr": (0x10000 >> 2), + }, + { + # 3 - Entry unlocked, address 0x01000 + "pmpcfg": 0b00000000, + "pmpaddr": (0x01000 >> 2), + }, + { + # 4 - Entry locked, allow R, TOR, address range 0x01000-0x1FFFF + "pmpcfg": 0b10001001, + "pmpaddr": (0x20000 >> 2), + }, + { + # 5 - Entry unlocked, address 0x01000 + "pmpcfg": 0b00000000, + "pmpaddr": (0x01000 >> 2), + }, + { + # 6 - Entry locked, allow W, TOR, address range 0x01000-0x1FFFF + "pmpcfg": 0b10001010, + "pmpaddr": (0x20000 >> 2), + }, + { + # 7 - Entry unlocked, address 0x01000 + "pmpcfg": 0b00000000, + "pmpaddr": (0x01000 >> 2), + }, + { + # 8 - Entry locked, allow X, TOR, address range 0x01000-0x1FFFF + "pmpcfg": 0b10001100, + "pmpaddr": (0x20000 >> 2), + }, +] + + +# ============================================================================= + + +class TestSequence(BaseSequence): + def __init__(self, name): + super().__init__(name) + + async def body(self): + pmp_entries = ConfigDB().get(None, "", "PMP_ENTRIES") + + # Configure PMP entries + possible_configs = min(pmp_entries, len(pmp_configurations)) + for i, cfg in enumerate(pmp_configurations): + # Ensure to not use more configurations than PMP entries + if i == possible_configs: + break + + item = PMPWriteCSRItem(index=i, pmpcfg=cfg["pmpcfg"], pmpaddr=cfg["pmpaddr"]) + await self.pmp_seqr.start_item(item) + await self.pmp_seqr.finish_item(item) + + self.checkRangeBoundary(LOWER_BOUNDARY) + for _ in range(1000): + await self.randomAccessInAddrRange(LOWER_BOUNDARY, UPPER_BOUNDARY) + self.checkRangeBoundary(UPPER_BOUNDARY) + + +# ============================================================================== + + +@test() +class TestMultipleConfigs(BaseTest): + """ + This test provides a sequence that checks behaviour for multiple PMP configurations + appplying to the same address ranges. + """ + + def __init__(self, name, parent): + super().__init__(name, parent, BaseEnv) + + def end_of_elaboration_phase(self): + super().end_of_elaboration_phase() + self.seq = TestSequence.create("stimulus") + + async def run(self): + await self.seq.start() diff --git a/verification/block/pmp/test_xwr_access.py b/verification/block/pmp/test_xwr_access.py new file mode 100644 index 00000000000..cc95d95d399 --- /dev/null +++ b/verification/block/pmp/test_xwr_access.py @@ -0,0 +1,69 @@ +# Copyright (c) 2023 Antmicro +# SPDX-License-Identifier: Apache-2.0 + +from pyuvm import ConfigDB, test, uvm_sequence +from testbench import BaseEnv, BaseTest, PMPCheckItem, PMPWriteCSRItem + +# ============================================================================= + + +class TestSequence(uvm_sequence): + def __init__(self, name): + super().__init__(name) + + self.pmp_seqr = ConfigDB().get(None, "", "PMP_SEQR") + + async def body(self): + pmp_entries = ConfigDB().get(None, "", "PMP_ENTRIES") + pmp_channels = ConfigDB().get(None, "", "PMP_CHANNELS") + + # Configure first 8 entries to all possible XWR configurations + # Use TOR address matching for simplicity + # 0b10001000 + # - bit 7 - Locked status (1 is locked) + # - bits 6-5 - Reserved (always 0) + # - bits 4-3 - Address Matching configuration (01 is TOR) + # - bit 2 - Execute permission + # - bit 1 - Write permission + # - bit 0 - Read permission + for i in range(8): + cfg = 0b10001000 + i + addr = ((i + 1) * 0x1000) >> 2 + item = PMPWriteCSRItem(index=i, pmpcfg=cfg, pmpaddr=addr) + await self.pmp_seqr.start_item(item) + await self.pmp_seqr.finish_item(item) + + # Check all possible access variants on configured entries + for i in range(pmp_channels): + # Set type to each of 3 available (R or W or X) + for j in range(3): + type = 1 << j + for k in range(pmp_entries): + # Set address somewhere in the 0x1000 wide entry + addr = (0x200 + (k * 0x1000)) >> 2 + channel = i + + item = PMPCheckItem(channel, addr, type) + await self.pmp_seqr.start_item(item) + await self.pmp_seqr.finish_item(item) + + +# ============================================================================== + + +@test() +class TestXWRAccess(BaseTest): + """ + This test configures few registers to covers or possible variants of RWX + access permissions and then checks if they are properly checked. + """ + + def __init__(self, name, parent): + super().__init__(name, parent, BaseEnv) + + def end_of_elaboration_phase(self): + super().end_of_elaboration_phase() + self.seq = TestSequence.create("stimulus") + + async def run(self): + await self.seq.start() diff --git a/verification/block/pmp/testbench.py b/verification/block/pmp/testbench.py new file mode 100644 index 00000000000..9fa21794feb --- /dev/null +++ b/verification/block/pmp/testbench.py @@ -0,0 +1,382 @@ +# +# Copyright (c) 2023 Antmicro +# SPDX-License-Identifier: Apache-2.0 + +import os + +from cocotb.binary import BinaryValue +from cocotb.clock import Clock +from cocotb.triggers import ClockCycles, RisingEdge +from pyuvm import * + +# ============================================================================== + + +ACCESS_TYPE = { + 0b001: "R", + 0b010: "W", + 0b100: "X", +} + + +class RegisterMap: + def __init__(self, pmp_entries): + self.reg = dict() + for i in range(pmp_entries): + name = "pmpcfg{}".format(i) + self.reg[name] = BinaryValue(value=0, bigEndian=False, n_bits=8) + + name = "pmpaddr{}".format(i) + self.reg[name] = BinaryValue(value=0, bigEndian=False, n_bits=32) + + +def getDecodedEntryCfg(regs, index, range_only=False): + """ """ + pmpcfg = regs.reg["pmpcfg{}".format(index)] + pmpaddr = regs.reg["pmpaddr{}".format(index)] + + # bits 0-2, (R, W, X) + permissions = {"R": pmpcfg[0].integer, "W": pmpcfg[1].integer, "X": pmpcfg[2].integer} + address_matching = pmpcfg[4:3].integer + locked = pmpcfg[7].integer + + if index != 0: + start_address = regs.reg["pmpaddr{}".format(index - 1)].integer << 2 + else: + start_address = 0 + + match address_matching: + case 0: # Entry disabled + if range_only: + end_address = pmpaddr.integer << 2 + return start_address, end_address + else: + return None + case 1: # Top of range + end_address = pmpaddr.integer << 2 + if start_address > end_address: + if range_only: + return start_address, end_address + else: + return None + case 2: # Naturally aligned four-byte region + end_address = (pmpaddr.integer << 2) + 4 + case 3: # Naturally aligned power-of-two region, >=8 bytes + napot = 3 + start_address = pmpaddr + for i in range(len(pmpaddr)): + if pmpaddr[i].integer == 1: + start_address[i].value = 0 + napot += 1 + else: + continue + + start_address = start_address.integer << 2 + end_address = start_address + 2**napot + + # PMP upper address bundary is non-inclusive + end_address = end_address - 1 + + if range_only: + return start_address, end_address + else: + return start_address, end_address, permissions, locked + + +# ============================================================================== + + +class PMPWriteCSRItem(uvm_sequence_item): + def __init__(self, index, pmpcfg=None, pmpaddr=None): + super().__init__("PMPWriteCSRItem") + self.index = index + + if pmpcfg is not None: + self.pmpcfg = pmpcfg + if pmpaddr is not None: + self.pmpaddr = pmpaddr + + +class PMPCheckItem(uvm_sequence_item): + def __init__(self, channel, addr, type, err=None): + super().__init__("PMPCheckItem") + self.channel = channel + self.addr = addr + self.type = type + self.err = err + + +# ============================================================================== + + +def collect_signals(signals, uut, obj): + """ + Collects signal objects from UUT and attaches them to the given object + """ + + for sig in signals: + if hasattr(uut, sig): + s = getattr(uut, sig) + + else: + s = None + logging.error("Module {} does not have a signal '{}'".format(str(uut), sig)) + + setattr(obj, sig, s) + + +# ============================================================================== + + +class PMPDriver(uvm_driver): + SIGNALS = [ + "clk", + # CSRs + "pmp_pmpcfg", + "pmp_pmpaddr", + # PMP logic + "pmp_chan_addr", + "pmp_chan_type", + ] + + def __init__(self, *args, **kwargs): + uut = kwargs["uut"] + del kwargs["uut"] + super().__init__(*args, **kwargs) + self.regs = ConfigDB().get(None, "", "PMP_CSRS") + + # Collect signals + collect_signals(self.SIGNALS, uut, self) + + async def run_phase(self): + while True: + it = await self.seq_item_port.get_next_item() + + if isinstance(it, PMPWriteCSRItem): + self.pmp_pmpcfg[it.index].value = it.pmpcfg + self.pmp_pmpaddr[it.index].value = it.pmpaddr + self.regs.reg["pmpcfg{}".format(it.index)].integer = it.pmpcfg + self.regs.reg["pmpaddr{}".format(it.index)].integer = it.pmpaddr + elif isinstance(it, PMPCheckItem): + self.pmp_chan_addr[it.channel].value = it.addr + self.pmp_chan_type[it.channel].value = it.type + else: + raise RuntimeError("Unknown item '{}'".format(type(it))) + + await ClockCycles(self.clk, 1) + self.seq_item_port.item_done() + + +class PMPMonitor(uvm_component): + SIGNALS = [ + "clk", + # CSRs + "pmp_pmpcfg", + "pmp_pmpaddr", + # PMP logic + "pmp_chan_addr", + "pmp_chan_type", + "pmp_chan_err", + ] + + def __init__(self, *args, **kwargs): + uut = kwargs["uut"] + del kwargs["uut"] + + super().__init__(*args, **kwargs) + + collect_signals(self.SIGNALS, uut, self) + + self.pmp_channels = ConfigDB().get(None, "", "PMP_CHANNELS") + self.pmp_entries = ConfigDB().get(None, "", "PMP_ENTRIES") + self.prev_addr = [None for _ in range(self.pmp_channels)] + self.prev_type = [None for _ in range(self.pmp_channels)] + self.prev_err = [None for _ in range(self.pmp_channels)] + self.prev_pmpcfg = [None for _ in range(self.pmp_entries)] + self.prev_pmpaddr = [None for _ in range(self.pmp_entries)] + + def build_phase(self): + self.ap = uvm_analysis_port("ap", self) + + async def run_phase(self): + while True: + # Even though the signals are not sequential sample them on + # rising clock edge + await RisingEdge(self.clk) + + # Sample signals + for i in range(self.pmp_channels): + curr_addr = int(self.pmp_chan_addr[i].value) + curr_type = int(self.pmp_chan_type[i].value) + curr_err = int(self.pmp_chan_err.value[i]) + + # Send an item in case of a change + if ( + self.prev_err[i] != curr_err + or self.prev_addr[i] != curr_addr + or self.prev_type[i] != curr_type + ): + self.ap.write(PMPCheckItem(i, curr_addr, curr_type, curr_err)) + + self.prev_err[i] = curr_err + self.prev_addr[i] = curr_addr + self.prev_type[i] = curr_type + + # If any PMP entry has changed, check all PMP channels + for i in range(self.pmp_entries): + curr_pmpcfg = int(self.pmp_pmpcfg[i].value) + curr_pmpaddr = int(self.pmp_pmpaddr[i].value) + + if (curr_pmpcfg != self.prev_pmpcfg[i]) or (curr_pmpaddr != self.prev_pmpaddr[i]): + for j in range(self.pmp_channels): + self.ap.write(PMPCheckItem(j, curr_addr, curr_type, curr_err)) + + self.prev_pmpcfg[i] = curr_pmpcfg + self.prev_pmpaddr[i] = curr_pmpaddr + + +# ============================================================================== + + +class Scoreboard(uvm_component): + def build_phase(self): + self.passed = True + self.fifo = uvm_tlm_analysis_fifo("fifo", self) + self.port = uvm_get_port("port", self) + self.regs = ConfigDB().get(None, "", "PMP_CSRS") + + def connect_phase(self): + self.port.connect(self.fifo.get_export) + + def check_phase(self): + while self.port.can_get(): + _, item = self.port.try_get() + + if isinstance(item, PMPCheckItem): + addr = item.addr + type = item.type + chan = item.channel + err = item.err + type_str = ACCESS_TYPE.get(type, "UNKNOWN ({})".format(type)) + + if type_str not in ACCESS_TYPE.values(): + self.logger.debug( + "Unknown access type ({}), probably checking channel that doesn't request access.".format( + type + ) + ) + continue + + # Check if address range can be matched to any PMP entry + entry_permissions = None + for i in range(len(self.regs.reg) // 2): + entry = getDecodedEntryCfg(self.regs, i) + if entry is not None: + pmp_start_addr, pmp_end_addr, permissions, locked = entry + else: + continue + + # Check if entry address range matches channel address + if addr in range(pmp_start_addr, pmp_end_addr): + if locked: # If entry is locked, save it for permission checks + entry_permissions = permissions + break + + log_msg = "PMPCheckItem: Validating access 0x{:08x}, type={} ({}), channel={}, error={}".format( + addr, type, type_str, chan, err + ) + if entry_permissions is None: + # If address range was not matched, ensure that error is not raised + if err: + self.logger.error("Error asserted when no entry was matched!") + self.logger.debug(log_msg) + self.passed = False + else: + # If address range was matched, compare permissions to the command type + for op in ACCESS_TYPE.values(): + if type_str == op and not (entry_permissions[op] ^ err): + self.logger.error("Unexpected error state on access request!") + self.logger.debug(log_msg) + self.passed = False + + def final_phase(self): + if not self.passed: + self.logger.critical("{} reports a failure".format(type(self))) + assert False + + +# ============================================================================== + + +class BaseEnv(uvm_env): + """ + Base PyUVM test environment + """ + + def build_phase(self): + # Config + pmp_entries = 16 + ConfigDB().set(None, "*", "PMP_ENTRIES", pmp_entries) + ConfigDB().set(None, "*", "PMP_CHANNELS", 3) + ConfigDB().set(None, "*", "PMP_GRANULARITY", 0) + + ConfigDB().set(None, "*", "TEST_CLK_PERIOD", 1) + ConfigDB().set(None, "*", "TEST_ITERATIONS", 50) + + # PMP Registers + self.regs = RegisterMap(pmp_entries) + ConfigDB().set(None, "*", "PMP_CSRS", self.regs) + + # Sequencers + self.pmp_seqr = uvm_sequencer("pmp_seqr", self) + + # PMP interface + self.pmp_drv = PMPDriver("pmp_drv", self, uut=cocotb.top) + self.pmp_mon = PMPMonitor("pmp_mon", self, uut=cocotb.top) + + # Add scoreboard + self.scoreboard = Scoreboard("scoreboard", self) + + ConfigDB().set(None, "*", "PMP_SEQR", self.pmp_seqr) + + def connect_phase(self): + self.pmp_drv.seq_item_port.connect(self.pmp_seqr.seq_item_export) + self.pmp_mon.ap.connect(self.scoreboard.fifo.analysis_export) + + +# ============================================================================== + + +class BaseTest(uvm_test): + """ + Base test for the module + """ + + def __init__(self, name, parent, env_class=BaseEnv): + super().__init__(name, parent) + self.env_class = env_class + + # Syncrhonize pyuvm logging level with cocotb logging level. Unclear + # why it does not happen automatically. + level = logging.getLevelName(os.environ.get("COCOTB_LOG_LEVEL", "DEBUG")) + uvm_report_object.set_default_logging_level(level) + + def build_phase(self): + self.env = self.env_class("env", self) + + def start_clock(self, name): + period = ConfigDB().get(None, "", "TEST_CLK_PERIOD") + sig = getattr(cocotb.top, name) + clock = Clock(sig, period, units="ns") + cocotb.start_soon(clock.start(start_high=False)) + + async def run_phase(self): + self.raise_objection() + self.start_clock("clk") + await ClockCycles(cocotb.top.clk, 2) + await self.run() + await ClockCycles(cocotb.top.clk, 2) + self.drop_objection() + + async def run(self): + raise NotImplementedError() diff --git a/violations.waiver b/violations.waiver index 5844b2e4556..a0dceaaf71f 100644 --- a/violations.waiver +++ b/violations.waiver @@ -1,6 +1,11 @@ waive --rule=module-filename --location="design/lib/.*_lib.sv" waive --rule=line-length --location="design/ifu/.*.sv" +waive --rule=line-length --location="design/dec/.*.sv" +waive --rule=line-length --location="design/lsu/.*.sv" waive --rule=line-length --location="design/el2_.*_ctrl.sv" waive --rule=no-trailing-spaces --location="design/ifu/.*.sv" waive --rule=no-trailing-spaces --location="design/el2_.*_ctrl.sv" +waive --rule=generate-label --location="design/lsu/el2_.*.sv" +waive --rule=explicit-parameter-storage-type --location="design/el2_pmp.sv" +waive --rule=explicit-parameter-storage-type --location="design/dec/el2_dec_pmp_ctl.sv"