[rtl] Add support for instruction fetch errors

- Add required signals to top-level
- Propagate error through fetch stages
- Add new exception type
- Update documentation for new exception type
- Resolves issue #109
This commit is contained in:
Tom Roberts 2019-08-07 17:18:08 +01:00 committed by tomroberts-lowrisc
parent ca97cfb58e
commit 44b033cf8b
15 changed files with 67 additions and 6 deletions

View file

@ -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

View file

@ -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

View file

@ -35,6 +35,7 @@ Instantiation Template
.instr_rvalid_i (),
.instr_addr_o (),
.instr_rdata_i (),
.instr_err_i (),
// Data memory interface
.data_req_o (),

View file

@ -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]),

View file

@ -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;

View file

@ -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),

View file

@ -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 (),

View file

@ -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;

View file

@ -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 ),

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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},

View file

@ -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 ( )
);