diff --git a/doc/exception_interrupts.rst b/doc/exception_interrupts.rst index a0e12127..99be67e4 100644 --- a/doc/exception_interrupts.rst +++ b/doc/exception_interrupts.rst @@ -70,6 +70,8 @@ Ibex can trigger an exception due to the following exception causes: +----------------+---------------------------------------------------------------+ | Exception Code | Description | +----------------+---------------------------------------------------------------+ +| 1 | Instruction access fault | ++----------------+---------------------------------------------------------------+ | 2 | Illegal instruction | +----------------+---------------------------------------------------------------+ | 3 | Breakpoint | @@ -81,7 +83,7 @@ Ibex can trigger an exception due to the following exception causes: | 11 | Environment call from M-mode (ECALL) | +----------------+---------------------------------------------------------------+ -The illegal instruction exception, LSU error exceptions and ECALL instruction exceptions cannot be disabled and are always active. +The illegal instruction exception, instruction access fault, LSU error exceptions and ECALL instruction exceptions cannot be disabled and are always active. Handling diff --git a/doc/instruction_fetch.rst b/doc/instruction_fetch.rst index f55bc4d7..3c62b853 100644 --- a/doc/instruction_fetch.rst +++ b/doc/instruction_fetch.rst @@ -30,6 +30,8 @@ The main difference is that the instruction interface does not allow for write t +-------------------------+-----------+-----------------------------------------------+ | ``instr_rdata_i[31:0]`` | input | Data read from memory | +-------------------------+-----------+-----------------------------------------------+ +| ``instr_err_i`` | input | Memory access error | ++-------------------------+-----------+-----------------------------------------------+ Misaligned Accesses diff --git a/doc/integration.rst b/doc/integration.rst index fb39b59e..b470a7a0 100644 --- a/doc/integration.rst +++ b/doc/integration.rst @@ -35,6 +35,7 @@ Instantiation Template .instr_rvalid_i (), .instr_addr_o (), .instr_rdata_i (), + .instr_err_i (), // Data memory interface .data_req_o (), diff --git a/dv/riscv_compliance/rtl/ibex_riscv_compliance.sv b/dv/riscv_compliance/rtl/ibex_riscv_compliance.sv index d8e88a2b..939b887f 100644 --- a/dv/riscv_compliance/rtl/ibex_riscv_compliance.sv +++ b/dv/riscv_compliance/rtl/ibex_riscv_compliance.sv @@ -120,6 +120,7 @@ module ibex_riscv_compliance ( .instr_rvalid_i (host_rvalid[CoreI]), .instr_addr_o (host_addr[CoreI]), .instr_rdata_i (host_rdata[CoreI]), + .instr_err_i (host_err[CoreI]), .data_req_o (host_req[CoreD]), .data_gnt_i (host_gnt[CoreD]), diff --git a/dv/uvm/tb/core_ibex_tb_top.sv b/dv/uvm/tb/core_ibex_tb_top.sv index 1eaab876..65861510 100644 --- a/dv/uvm/tb/core_ibex_tb_top.sv +++ b/dv/uvm/tb/core_ibex_tb_top.sv @@ -64,6 +64,7 @@ module core_ibex_tb_top; force dut.instr_rvalid_i = instr_mem_vif.rvalid; force instr_mem_vif.addr = dut.instr_addr_o; force dut.instr_rdata_i = instr_mem_vif.rdata; + force dut.instr_err_i = 0; // TODO(taliu) Support interface error // IRQ interface force irq_vif.clock = clk; force irq_vif.reset = ~rst_n; diff --git a/examples/fpga/artya7-100/rtl/top_artya7_100.sv b/examples/fpga/artya7-100/rtl/top_artya7_100.sv index e212bf70..512606f7 100644 --- a/examples/fpga/artya7-100/rtl/top_artya7_100.sv +++ b/examples/fpga/artya7-100/rtl/top_artya7_100.sv @@ -58,6 +58,7 @@ module top_artya7_100 ( .instr_rvalid_i (instr_rvalid), .instr_addr_o (instr_addr), .instr_rdata_i (instr_rdata), + .instr_err_i ('b0), .data_req_o (data_req), .data_gnt_i (data_gnt), diff --git a/examples/sim/tb/ibex_tracing_tb.sv b/examples/sim/tb/ibex_tracing_tb.sv index 48345a0f..cbaa7410 100644 --- a/examples/sim/tb/ibex_tracing_tb.sv +++ b/examples/sim/tb/ibex_tracing_tb.sv @@ -87,6 +87,7 @@ module ibex_tracing_tb; .instr_rvalid_i (instr_rvalid), .instr_addr_o (), .instr_rdata_i (instr_rdata), + .instr_err_i (1'b0), // Data memory interface .data_req_o (), diff --git a/rtl/ibex_controller.sv b/rtl/ibex_controller.sv index 464d6a1e..1e03adf7 100644 --- a/rtl/ibex_controller.sv +++ b/rtl/ibex_controller.sv @@ -45,6 +45,8 @@ module ibex_controller ( input logic [31:0] instr_i, // instr from IF-ID reg, for mtval input logic [15:0] instr_compressed_i, // instr from IF-ID reg, for mtval input logic instr_is_compressed_i, // instr from IF-ID reg is compressed + input logic instr_fetch_err_i, // instr from IF-ID reg has error + input logic [31:0] pc_id_i, // instr from IF-ID reg address // to IF-ID pipeline stage output logic instr_valid_clear_o, // kill instr in IF-ID reg @@ -150,7 +152,7 @@ module ibex_controller ( assign store_err_d = store_err_i; // exception requests - assign exc_req = ecall_insn_i | ebrk_insn_i | illegal_insn_i; + assign exc_req = ecall_insn_i | ebrk_insn_i | illegal_insn_i | instr_fetch_err_i; // LSU exception requests assign exc_req_lsu = store_err_i | load_err_i; @@ -453,7 +455,11 @@ module ibex_controller ( csr_save_cause_o = 1'b1; // set exception registers, priorities according to Table 3.7 of Privileged Spec v1.11 - if (illegal_insn_i) begin + if (instr_fetch_err_i) begin + exc_cause_o = EXC_CAUSE_INSTR_ACCESS_FAULT; + csr_mtval_o = pc_id_i; + + end else if (illegal_insn_i) begin exc_cause_o = EXC_CAUSE_ILLEGAL_INSN; csr_mtval_o = instr_is_compressed_i ? {16'b0, instr_compressed_i} : instr_i; diff --git a/rtl/ibex_core.sv b/rtl/ibex_core.sv index 98da120c..966968b2 100644 --- a/rtl/ibex_core.sv +++ b/rtl/ibex_core.sv @@ -53,6 +53,7 @@ module ibex_core #( input logic instr_rvalid_i, output logic [31:0] instr_addr_o, input logic [31:0] instr_rdata_i, + input logic instr_err_i, // Data memory interface output logic data_req_o, @@ -115,6 +116,7 @@ module ibex_core #( logic [31:0] instr_rdata_id; // Instruction sampled inside IF stage logic [15:0] instr_rdata_c_id; // Compressed instruction sampled inside IF stage logic instr_is_compressed_id; + logic instr_fetch_err; // Bus error on instr fetch logic illegal_c_insn_id; // Illegal compressed instruction sent to ID stage logic [31:0] pc_if; // Program counter in IF stage logic [31:0] pc_id; // Program counter in ID stage @@ -309,6 +311,7 @@ module ibex_core #( .instr_gnt_i ( instr_gnt_i ), .instr_rvalid_i ( instr_rvalid_i ), .instr_rdata_i ( instr_rdata_i ), + .instr_err_i ( instr_err_i ), // outputs to ID stage .instr_valid_id_o ( instr_valid_id ), @@ -316,6 +319,7 @@ module ibex_core #( .instr_rdata_id_o ( instr_rdata_id ), .instr_rdata_c_id_o ( instr_rdata_c_id ), .instr_is_compressed_id_o ( instr_is_compressed_id ), + .instr_fetch_err_o ( instr_fetch_err ), .illegal_c_insn_id_o ( illegal_c_insn_id ), .pc_if_o ( pc_if ), .pc_id_o ( pc_id ), @@ -382,6 +386,7 @@ module ibex_core #( .exc_pc_mux_o ( exc_pc_mux_id ), .exc_cause_o ( exc_cause ), + .instr_fetch_err_i ( instr_fetch_err ), .illegal_c_insn_i ( illegal_c_insn_id ), .pc_id_i ( pc_id ), diff --git a/rtl/ibex_core_tracing.sv b/rtl/ibex_core_tracing.sv index 48f3d3b0..2fa588d4 100644 --- a/rtl/ibex_core_tracing.sv +++ b/rtl/ibex_core_tracing.sv @@ -31,6 +31,7 @@ module ibex_core_tracing #( input logic instr_rvalid_i, output logic [31:0] instr_addr_o, input logic [31:0] instr_rdata_i, + input logic instr_err_i, // Data memory interface output logic data_req_o, @@ -109,6 +110,7 @@ module ibex_core_tracing #( .instr_rvalid_i, .instr_addr_o, .instr_rdata_i, + .instr_err_i, .data_req_o, .data_gnt_i, diff --git a/rtl/ibex_fetch_fifo.sv b/rtl/ibex_fetch_fifo.sv index ba327672..a3c3a12e 100644 --- a/rtl/ibex_fetch_fifo.sv +++ b/rtl/ibex_fetch_fifo.sv @@ -30,6 +30,7 @@ module ibex_fetch_fifo ( // input port input logic [31:0] in_addr_i, input logic [31:0] in_rdata_i, + input logic in_err_i, input logic in_valid_i, output logic in_ready_o, @@ -39,6 +40,7 @@ module ibex_fetch_fifo ( input logic out_ready_i, output logic [31:0] out_rdata_o, output logic [31:0] out_addr_o, + output logic out_err_o, output logic out_valid_stored_o // same as out_valid_o, except that if something is // incoming now it is not included. This signal is @@ -50,10 +52,12 @@ module ibex_fetch_fifo ( // index 0 is used for output logic [DEPTH-1:0] [31:0] addr_n, addr_int, addr_q; logic [DEPTH-1:0] [31:0] rdata_n, rdata_int, rdata_q; + logic [DEPTH-1:0] err_n, err_int, err_q; logic [DEPTH-1:0] valid_n, valid_int, valid_q; logic [31:2] addr_next; logic [31:0] rdata, rdata_unaligned; + logic err, err_unaligned; logic valid, valid_unaligned; logic aligned_is_compressed, unaligned_is_compressed; @@ -65,12 +69,22 @@ module ibex_fetch_fifo ( assign rdata = valid_q[0] ? rdata_q[0] : in_rdata_i; + assign err = valid_q[0] ? err_q[0] : in_err_i; assign valid = valid_q[0] | in_valid_i; assign rdata_unaligned = valid_q[1] ? {rdata_q[1][15:0], rdata[31:16]} : {in_rdata_i[15:0], rdata[31:16]}; - // it is implied that rdata_valid_q[0] is set - assign valid_unaligned = valid_q[1] | (valid_q[0] & in_valid_i); + // If entry[1] is valid, an error can come from entry[0] or entry[1], unless the + // instruction in entry[0] is compressed (entry[1] is a new instruction) + // If entry[1] is not valid, and entry[0] is, an error can come from entry[0] or the incoming + // data, unless the instruction in entry[0] is compressed + // If entry[0] is not valid, the error must come from the incoming data + assign err_unaligned = valid_q[1] ? ((err_q[1] & ~unaligned_is_compressed) | err_q[0]) : + ((valid_q[0] & err_q[0]) | + (in_err_i & (~valid_q[0] | ~unaligned_is_compressed))); + // An uncompressed unaligned instruction is only valid if both parts are available + assign valid_unaligned = valid_q[1] ? 1'b1 : + (valid_q[0] & in_valid_i); assign unaligned_is_compressed = rdata[17:16] != 2'b11; assign aligned_is_compressed = rdata[ 1: 0] != 2'b11; @@ -87,6 +101,7 @@ module ibex_fetch_fifo ( if (out_addr_o[1]) begin // unaligned case out_rdata_o = rdata_unaligned; + out_err_o = err_unaligned; if (unaligned_is_compressed) begin out_valid_o = valid; @@ -96,6 +111,7 @@ module ibex_fetch_fifo ( end else begin // aligned case out_rdata_o = rdata; + out_err_o = err; out_valid_o = valid; end end @@ -134,12 +150,14 @@ module ibex_fetch_fifo ( always_comb begin addr_int = addr_q; rdata_int = rdata_q; + err_int = err_q; valid_int = valid_q; if (in_valid_i) begin for (int j = 0; j < DEPTH; j++) begin if (!valid_q[j]) begin addr_int[j] = in_addr_i; rdata_int[j] = in_rdata_i; + err_int[j] = in_err_i; valid_int[j] = 1'b1; break; end @@ -153,6 +171,7 @@ module ibex_fetch_fifo ( always_comb begin addr_n = addr_int; rdata_n = rdata_int; + err_n = err_int; valid_n = valid_int; if (out_ready_i && out_valid_o) begin @@ -165,6 +184,7 @@ module ibex_fetch_fifo ( end rdata_n = {32'b0, rdata_int[DEPTH-1:1]}; + err_n = {1'b0, err_int[DEPTH-1:1]}; valid_n = {1'b0, valid_int[DEPTH-1:1]}; end else if (aligned_is_compressed) begin // just increase address, do not move to next entry in FIFO @@ -173,6 +193,7 @@ module ibex_fetch_fifo ( // move to next entry in FIFO addr_n[0] = {addr_next[31:2], 2'b00}; rdata_n = {32'b0, rdata_int[DEPTH-1:1]}; + err_n = {1'b0, err_int[DEPTH-1:1]}; valid_n = {1'b0, valid_int[DEPTH-1:1]}; end end @@ -186,6 +207,7 @@ module ibex_fetch_fifo ( if (!rst_ni) begin addr_q <= '{default: '0}; rdata_q <= '{default: '0}; + err_q <= '0; valid_q <= '0; end else begin // on a clear signal from outside we invalidate the content of the FIFO @@ -195,6 +217,7 @@ module ibex_fetch_fifo ( end else begin addr_q <= addr_n; rdata_q <= rdata_n; + err_q <= err_n; valid_q <= valid_n; end end diff --git a/rtl/ibex_id_stage.sv b/rtl/ibex_id_stage.sv index e6e36c48..e27df8f1 100644 --- a/rtl/ibex_id_stage.sv +++ b/rtl/ibex_id_stage.sv @@ -65,6 +65,7 @@ module ibex_id_stage #( output ibex_pkg::exc_cause_e exc_cause_o, input logic illegal_c_insn_i, + input logic instr_fetch_err_i, input logic [31:0] pc_id_i, @@ -419,6 +420,8 @@ module ibex_id_stage #( .instr_i ( instr_rdata_i ), .instr_compressed_i ( instr_rdata_c_i ), .instr_is_compressed_i ( instr_is_compressed_i ), + .instr_fetch_err_i ( instr_fetch_err_i ), + .pc_id_i ( pc_id_i ), // to IF-ID pipeline .instr_valid_clear_o ( instr_valid_clear_o ), @@ -630,7 +633,8 @@ module ibex_id_stage #( // the instruction delivered to the ID stage should always be valid assert property ( - @(posedge clk_i) (instr_valid_i & (~illegal_c_insn_i)) |-> (!$isunknown(instr_rdata_i)) ) else + @(posedge clk_i) (instr_valid_i & ~(illegal_c_insn_i | instr_fetch_err_i)) |-> + (!$isunknown(instr_rdata_i)) ) else $display("Instruction is valid, but has at least one X"); // make sure multicycles enable signals are unique diff --git a/rtl/ibex_if_stage.sv b/rtl/ibex_if_stage.sv index 62cf845a..1499a260 100644 --- a/rtl/ibex_if_stage.sv +++ b/rtl/ibex_if_stage.sv @@ -42,6 +42,7 @@ module ibex_if_stage #( input logic instr_gnt_i, input logic instr_rvalid_i, input logic [31:0] instr_rdata_i, + input logic instr_err_i, // output of ID stage output logic instr_valid_id_o, // instr in IF-ID is valid @@ -52,6 +53,7 @@ module ibex_if_stage #( // instr_is_compressed_id_o = 1'b1 output logic instr_is_compressed_id_o, // compressed decoder thinks this // is a compressed instr + output logic instr_fetch_err_o, // bus error on fetch output logic illegal_c_insn_id_o, // compressed decoder thinks this // is an invalid instr output logic [31:0] pc_if_o, @@ -97,6 +99,7 @@ module ibex_if_stage #( logic fetch_ready; logic [31:0] fetch_rdata; logic [31:0] fetch_addr; + logic fetch_err; logic [31:0] exc_pc; @@ -155,6 +158,7 @@ module ibex_if_stage #( .valid_o ( fetch_valid ), .rdata_o ( fetch_rdata ), .addr_o ( fetch_addr ), + .err_o ( fetch_err ), // goes to instruction memory / instruction cache .instr_req_o ( instr_req_o ), @@ -162,6 +166,7 @@ module ibex_if_stage #( .instr_gnt_i ( instr_gnt_i ), .instr_rvalid_i ( instr_rvalid_i ), .instr_rdata_i ( instr_rdata_i ), + .instr_err_i ( instr_err_i ), // Prefetch Buffer Status .busy_o ( prefetch_busy ) @@ -241,6 +246,7 @@ module ibex_if_stage #( instr_new_id_o <= 1'b0; instr_valid_id_o <= 1'b0; instr_rdata_id_o <= '0; + instr_fetch_err_o <= '0; instr_rdata_c_id_o <= '0; instr_is_compressed_id_o <= 1'b0; illegal_c_insn_id_o <= 1'b0; @@ -250,6 +256,7 @@ module ibex_if_stage #( if (if_id_pipe_reg_we) begin instr_valid_id_o <= 1'b1; instr_rdata_id_o <= instr_decompressed; + instr_fetch_err_o <= fetch_err; instr_rdata_c_id_o <= fetch_rdata[15:0]; instr_is_compressed_id_o <= instr_is_compressed_int; illegal_c_insn_id_o <= illegal_c_insn; diff --git a/rtl/ibex_pkg.sv b/rtl/ibex_pkg.sv index cf92a977..55886091 100644 --- a/rtl/ibex_pkg.sv +++ b/rtl/ibex_pkg.sv @@ -190,6 +190,7 @@ typedef enum logic [5:0] { // EXC_CAUSE_IRQ_FAST_14 = {1'b1, 5'd30}, EXC_CAUSE_IRQ_NM = {1'b1, 5'd31}, // == EXC_CAUSE_IRQ_FAST_15 EXC_CAUSE_INSN_ADDR_MISA = {1'b0, 5'd00}, + EXC_CAUSE_INSTR_ACCESS_FAULT = {1'b0, 5'd01}, EXC_CAUSE_ILLEGAL_INSN = {1'b0, 5'd02}, EXC_CAUSE_BREAKPOINT = {1'b0, 5'd03}, EXC_CAUSE_LOAD_ACCESS_FAULT = {1'b0, 5'd05}, diff --git a/rtl/ibex_prefetch_buffer.sv b/rtl/ibex_prefetch_buffer.sv index bef2d17b..9b75557f 100644 --- a/rtl/ibex_prefetch_buffer.sv +++ b/rtl/ibex_prefetch_buffer.sv @@ -34,6 +34,7 @@ module ibex_prefetch_buffer ( output logic valid_o, output logic [31:0] rdata_o, output logic [31:0] addr_o, + output logic err_o, // goes to instruction memory / instruction cache @@ -41,6 +42,7 @@ module ibex_prefetch_buffer ( input logic instr_gnt_i, output logic [31:0] instr_addr_o, input logic [31:0] instr_rdata_i, + input logic instr_err_i, input logic instr_rvalid_i, // Prefetch Buffer Status @@ -79,6 +81,7 @@ module ibex_prefetch_buffer ( .in_addr_i ( instr_addr_q ), .in_rdata_i ( instr_rdata_i ), + .in_err_i ( instr_err_i ), .in_valid_i ( fifo_valid ), .in_ready_o ( fifo_ready ), @@ -87,6 +90,7 @@ module ibex_prefetch_buffer ( .out_ready_i ( ready_i ), .out_rdata_o ( rdata_o ), .out_addr_o ( addr_o ), + .out_err_o ( err_o ), .out_valid_stored_o ( ) );