From c93228d13ede8ea6938ff203295fee60fa76a418 Mon Sep 17 00:00:00 2001 From: Luke Wren Date: Tue, 24 May 2022 19:57:45 +0100 Subject: [PATCH] Integrate PMP, and fix a couple of PMP bugs --- hdl/hazard3.f | 1 + hdl/hazard3_core.v | 74 ++++++++++++++++++++++--- hdl/hazard3_csr.v | 121 ++++++++++++++++++++++++++++++++++++++++- hdl/hazard3_frontend.v | 2 +- hdl/hazard3_pmp.v | 13 +++-- 5 files changed, 193 insertions(+), 18 deletions(-) diff --git a/hdl/hazard3.f b/hdl/hazard3.f index 3bbc2cf..3346461 100644 --- a/hdl/hazard3.f +++ b/hdl/hazard3.f @@ -12,4 +12,5 @@ file hazard3_instr_decompress.v file hazard3_decode.v file hazard3_csr.v file hazard3_regfile_1w2r.v +file hazard3_pmp.v include . diff --git a/hdl/hazard3_core.v b/hdl/hazard3_core.v index eb78b6e..108d64d 100644 --- a/hdl/hazard3_core.v +++ b/hdl/hazard3_core.v @@ -434,7 +434,9 @@ hazard3_alu #( .cmp (x_alu_cmp) ); -// AHB transaction request +// Load/store bus request + +wire x_loadstore_pmp_fail; wire x_unaligned_addr = d_memop != MEMOP_NONE && ( bus_hsize_d == HSIZE_WORD && |bus_haddr_d[1:0] || @@ -574,6 +576,7 @@ always @ (*) begin bus_aph_req_d = x_memop_vld && !( x_stall_on_raw || x_stall_on_exclusive_overlap || + x_loadstore_pmp_fail || x_unaligned_addr || m_trap_enter_soon || (xm_wfi && !m_wfi_stall_clear) // FIXME will cause a timing issue, better to stall til *after* clear @@ -713,6 +716,51 @@ wire x_jump_req_if_aligned = !x_stall_on_raw && ( assign x_jump_req = x_jump_req_if_aligned && !x_jump_misaligned; +// Memory protection + +wire [11:0] x_pmp_cfg_addr; +wire x_pmp_cfg_wen; +wire [W_DATA-1:0] x_pmp_cfg_wdata; +wire [W_DATA-1:0] x_pmp_cfg_rdata; + +wire x_exec_pmp_fail; + +generate +if (PMP_REGIONS > 0) begin: have_pmp + + hazard3_pmp #( + `include "hazard3_config_inst.vh" + ) pmp ( + .clk (clk), + .rst_n (rst_n), + + .mstatus_mxr (1'b0), // Only used when S mode present + + .cfg_addr (x_pmp_cfg_addr), + .cfg_wen (x_pmp_cfg_wen), + .cfg_wdata (x_pmp_cfg_wdata), + .cfg_rdata (x_pmp_cfg_rdata), + + .i_addr (d_pc), + .i_instr_is_32bit (&fd_cir[1:0]), + .i_m_mode (x_mmode_execution), + .i_kill (x_exec_pmp_fail), + + .d_addr (bus_haddr_d), + .d_m_mode (x_mmode_loadstore), + .d_write (bus_hwrite_d), + .d_kill (x_loadstore_pmp_fail) + ); + +end else begin: no_pmp + + assign x_pmp_cfg_rdata = 1'b0; + assign x_loadstore_pmp_fail = 1'b0; + assign x_exec_pmp_fail = 1'b0; + +end +endgenerate + // CSRs and Trap Handling wire [W_DATA-1:0] x_csr_wdata = d_csr_w_imm ? @@ -754,13 +802,16 @@ end wire [W_ADDR-1:0] m_exception_return_addr; wire [W_EXCEPT-1:0] x_except = - x_jump_req_if_aligned && x_jump_misaligned ? EXCEPT_INSTR_MISALIGN : - x_csr_illegal_access ? EXCEPT_INSTR_ILLEGAL : - |EXTENSION_A && x_unaligned_addr && d_memop_is_amo ? EXCEPT_STORE_ALIGN : - |EXTENSION_A && x_amo_phase == 3'h4 && x_unaligned_addr? EXCEPT_STORE_ALIGN : - |EXTENSION_A && x_amo_phase == 3'h4 ? EXCEPT_STORE_FAULT : - x_unaligned_addr && x_memop_write ? EXCEPT_STORE_ALIGN : - x_unaligned_addr && !x_memop_write ? EXCEPT_LOAD_ALIGN : d_except; + x_jump_req_if_aligned && x_jump_misaligned ? EXCEPT_INSTR_MISALIGN : + x_exec_pmp_fail ? EXCEPT_INSTR_FAULT : + x_csr_illegal_access ? EXCEPT_INSTR_ILLEGAL : + |EXTENSION_A && x_unaligned_addr && d_memop_is_amo ? EXCEPT_STORE_ALIGN : + |EXTENSION_A && x_amo_phase == 3'h4 && x_unaligned_addr ? EXCEPT_STORE_ALIGN : + |EXTENSION_A && x_amo_phase == 3'h4 ? EXCEPT_STORE_FAULT : + x_unaligned_addr && x_memop_write ? EXCEPT_STORE_ALIGN : + x_unaligned_addr && !x_memop_write ? EXCEPT_LOAD_ALIGN : + x_loadstore_pmp_fail && (d_memop_is_amo || bus_hwrite_d) ? EXCEPT_STORE_FAULT : + x_loadstore_pmp_fail ? EXCEPT_LOAD_FAULT : d_except; // If an instruction causes an exceptional condition we do not consider it to have retired. wire x_except_counts_as_retire = x_except == EXCEPT_EBREAK || x_except == EXCEPT_MRET || x_except == EXCEPT_ECALL; @@ -821,6 +872,11 @@ hazard3_csr #( .irq_timer (timer_irq), .except (xm_except), + .pmp_cfg_addr (x_pmp_cfg_addr), + .pmp_cfg_wen (x_pmp_cfg_wen), + .pmp_cfg_wdata (x_pmp_cfg_wdata), + .pmp_cfg_rdata (x_pmp_cfg_rdata), + // Other CSR-specific signalling .instr_ret (|x_instr_ret) ); @@ -1080,4 +1136,6 @@ hazard3_regfile_1w2r #( endmodule +`ifndef YOSYS `default_nettype wire +`endif diff --git a/hdl/hazard3_csr.v b/hdl/hazard3_csr.v index 9b1c190..e49a5d7 100644 --- a/hdl/hazard3_csr.v +++ b/hdl/hazard3_csr.v @@ -82,9 +82,9 @@ module hazard3_csr #( output wire wfi_stall_clear, // Each of these may be performed at a different privilege level from the others: - output wire m_mode_execution, - output wire m_mode_trap_entry, - output wire m_mode_loadstore, + output wire m_mode_execution, + output wire m_mode_trap_entry, + output wire m_mode_loadstore, // Exceptions must *not* be a function of bus stall. input wire [W_EXCEPT-1:0] except, @@ -95,6 +95,11 @@ module hazard3_csr #( input wire irq_software, input wire irq_timer, + // PMP config interface + output wire [11:0] pmp_cfg_addr, + output reg pmp_cfg_wen, + output wire [W_DATA-1:0] pmp_cfg_wdata, + input wire [W_DATA-1:0] pmp_cfg_rdata, // Other CSR-specific signalling input wire instr_ret @@ -451,6 +456,7 @@ wire match_uro = U_MODE && !wen_soon; always @ (*) begin decode_match = 1'b0; rdata = {XLEN{1'b0}}; + pmp_cfg_wen = 1'b0; case (addr) // ------------------------------------------------------------------------ @@ -712,6 +718,112 @@ always @ (*) begin }; end + // ------------------------------------------------------------------------ + // PMP CSRs (bridge to PMP config interface) + + // If PMP is present, all 16 registers are present, but some may be WARL'd + // to 0 depending on how many regions are actually implemented. + PMPCFG0: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPCFG1: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPCFG2: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPCFG3: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR0: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR1: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR2: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR3: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR4: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR5: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR6: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR7: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR8: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR9: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR10: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR11: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR12: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR13: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR14: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + PMPADDR15: if (PMP_REGIONS > 0) begin + decode_match = match_mrw; + pmp_cfg_wen = match_mrw && wen; + rdata = pmp_cfg_rdata; + end + // ------------------------------------------------------------------------ // U-mode CSRs @@ -1036,6 +1148,9 @@ assign mcause_code_next = exception_req_any ? {2'h0, except} : mcause_irq_num; // ---------------------------------------------------------------------------- // Privilege state outputs +assign pmp_cfg_addr = addr; +assign pmp_cfg_wdata = wdata_update; + // Effective privilege for execution. Used for: // - Privilege level of branch target fetches (frontend keeps fetch privilege // constant during sequential fetch) diff --git a/hdl/hazard3_frontend.v b/hdl/hazard3_frontend.v index ae47cbd..36b9851 100644 --- a/hdl/hazard3_frontend.v +++ b/hdl/hazard3_frontend.v @@ -274,7 +274,7 @@ reg mem_addr_vld_r; // Downstream accesses are always word-sized word-aligned. assign mem_addr = mem_addr_r; -assign mem_priv = mem_addr_r; +assign mem_priv = mem_priv_r; assign mem_addr_vld = mem_addr_vld_r && !reset_holdoff; assign mem_size = 1'b1; diff --git a/hdl/hazard3_pmp.v b/hdl/hazard3_pmp.v index 036564f..64aa55e 100644 --- a/hdl/hazard3_pmp.v +++ b/hdl/hazard3_pmp.v @@ -12,7 +12,6 @@ module hazard3_pmp #( ) ( input wire clk, input wire rst_n, - input wire m_mode, input wire mstatus_mxr, // make executable readable // Config interface passed through CSR block @@ -67,18 +66,18 @@ always @ (posedge clk or negedge rst_n) begin: cfg_update end end else if (cfg_wen) begin for (i = 0; i < PMP_REGIONS; i = i + 1) begin - if (cfg_addr == PMPCFG0 + i / 4) begin + if (cfg_addr == PMPCFG0 + i / 4 && !pmpcfg_l[i]) begin pmpcfg_l[i] <= cfg_wdata[i % 4 * 8 + 7]; // TOR is not supported, gets mapped to OFF: pmpcfg_a[i] <= { cfg_wdata[i % 4 * 8 + 4], - cfg_wdata[i % 4 * 8 + 3] && csr[i % 4 * 8 + 4] + cfg_wdata[i % 4 * 8 + 3] && cfg_wdata[i % 4 * 8 + 4] }; pmpcfg_r[i] <= cfg_wdata[i % 4 * 8 + 2]; pmpcfg_w[i] <= cfg_wdata[i % 4 * 8 + 1]; pmpcfg_x[i] <= cfg_wdata[i % 4 * 8 + 0]; end - if (cfg_addr == PMPADDR0 + i) begin + if (cfg_addr == PMPADDR0 + i && !pmpcfg_l[i]) begin pmpaddr[i] <= cfg_wdata[W_ADDR-3:0]; end end @@ -218,7 +217,7 @@ always @ (*) begin: check_i_match i_partial_match = 1'b0; i_l = 1'b0; i_x = 1'b0; - for (i = PMP_REGIONS - 1; i >= 0; i = i -q 1) begin + for (i = PMP_REGIONS - 1; i >= 0; i = i - 1) begin match_hw0 = |pmpcfg_a[i] && (i_addr & match_mask[i]) == match_addr[i]; match_hw1 = |pmpcfg_a[i] && (i_addr_hw1 & match_mask[i]) == match_addr[i]; if (match_hw0 || match_hw1) begin @@ -248,4 +247,6 @@ assign i_kill = i_partial_match || ( endmodule -`default_nettype wire \ No newline at end of file +`ifndef YOSYS +`default_nettype wire +`endif