exception updates

This commit is contained in:
Eric Matthews 2022-01-15 21:41:26 -08:00
parent 279efe70a9
commit 56c5b2bbf3
8 changed files with 141 additions and 131 deletions

View file

@ -46,6 +46,7 @@ module csr_unit
//GC
input logic interrupt_taken,
output logic interrupt_pending,
output logic processing_csr,
//TLB and MMU
output logic tlb_on,
@ -102,6 +103,8 @@ module csr_unit
endfunction
////////////////////////////////////////////////////
//Implementation
assign processing_csr = busy | issue.new_request;
assign issue.ready = ~busy;
always_ff @(posedge clk) begin

View file

@ -25,6 +25,7 @@ module decode_and_issue
import taiga_config::*;
import riscv_types::*;
import taiga_types::*;
import csr_types::*;
# (
parameter cpu_config_t CONFIG = EXAMPLE_CONFIG,
@ -40,6 +41,7 @@ module decode_and_issue
input logic pc_id_available,
input decode_packet_t decode,
output logic decode_advance,
output exception_sources_t decode_exception_unit,
//Renamer
renamer_interface.decode renamer,
@ -68,9 +70,9 @@ module decode_and_issue
unit_issue_interface.decode unit_issue [NUM_UNITS-1:0],
input gc_outputs_t gc,
output logic gc_flush_required,
input logic [1:0] current_privilege,
output logic illegal_instruction,
exception_interface.unit exception,
//Trace signals
output logic tr_operand_stall,
@ -124,6 +126,7 @@ module decode_and_issue
logic [NUM_UNITS-1:0] issue_ready;
logic [NUM_UNITS-1:0] issue_to;
logic pre_issue_exception_pending;
logic illegal_instruction_pattern;
logic issue_stage_ready;
@ -197,16 +200,16 @@ module decode_and_issue
assign decode_rs_wb_group[RS1] = renamer.rs_wb_group[RS1];
assign decode_rs_wb_group[RS2] = renamer.rs_wb_group[RS2];
//TODO: Consider ways of parameterizing so that any exception generating unit
//can be automatically added to this expression
exception_sources_t decode_exception_unit;
always_comb begin
unique case (1'b1)
unit_needed[UNIT_IDS.LS] : decode_exception_unit = LS_EXCEPTION;
unit_needed[UNIT_IDS.BR] : decode_exception_unit = BR_EXCEPTION;
default : decode_exception_unit = IEC_EXCEPTION;
default : decode_exception_unit = PRE_ISSUE_EXCEPTION;
endcase
if (illegal_instruction_pattern)
decode_exception_unit = PRE_ISSUE_EXCEPTION;
end
////////////////////////////////////////////////////
//Issue
@ -261,7 +264,7 @@ module decode_and_issue
assign operands_ready = (~rs1_conflict) & (~rs2_conflict);
assign issue_ready = unit_needed_issue_stage & unit_ready;
assign issue_valid = issue.stage_valid & operands_ready & ~gc.issue_hold;
assign issue_valid = issue.stage_valid & operands_ready & ~gc.issue_hold & ~pre_issue_exception_pending;
assign issue_to = {NUM_UNITS{issue_valid & ~gc.fetch_flush}} & issue_ready;
@ -467,10 +470,10 @@ module decode_and_issue
////////////////////////////////////////////////////
//Global Control unit inputs
logic potential_flush;
logic is_ecall;
logic is_ebreak;
logic is_ret;
logic is_ecall_r;
logic is_ebreak_r;
logic is_mret_r;
logic is_sret_r;
logic is_ifence_r;
logic [7:0] sys_op_match;
@ -488,7 +491,6 @@ module decode_and_issue
case (decode.instruction[31:20]) inside
ECALL_imm : sys_op_match[ECALL_i] = CONFIG.INCLUDE_M_MODE;
EBREAK_imm : sys_op_match[EBREAK_i] = CONFIG.INCLUDE_M_MODE;
URET_imm : sys_op_match[URET_i] = CONFIG.INCLUDE_U_MODE;
SRET_imm : sys_op_match[SRET_i] = CONFIG.INCLUDE_S_MODE;
MRET_imm : sys_op_match[MRET_i] = CONFIG.INCLUDE_M_MODE;
SFENCE_imm : sys_op_match[SFENCE_i] = CONFIG.INCLUDE_S_MODE;
@ -498,22 +500,18 @@ module decode_and_issue
always_ff @(posedge clk) begin
if (issue_stage_ready) begin
is_ecall <= environment_op & sys_op_match[ECALL_i];
is_ebreak <= environment_op & sys_op_match[EBREAK_i];
is_ret <= environment_op & (sys_op_match[URET_i] | sys_op_match[SRET_i] | sys_op_match[MRET_i]);
is_ecall_r <= sys_op_match[ECALL_i];
is_ebreak_r <= sys_op_match[EBREAK_i];
is_mret_r <= sys_op_match[MRET_i];
is_sret_r <= sys_op_match[SRET_i];
is_ifence_r <= is_ifence;
potential_flush <= (environment_op | is_ifence);
end
end
assign gc_inputs.is_ecall = is_ecall;
assign gc_inputs.is_ebreak = is_ebreak;
assign gc_inputs.is_ret = is_ret;
assign gc_inputs.pc_p4 = constant_alu;
assign gc_inputs.instruction = issue.instruction;
assign gc_inputs.is_i_fence = CONFIG.INCLUDE_M_MODE & issue_to[UNIT_IDS.IEC] & is_ifence_r;
assign gc_flush_required = CONFIG.INCLUDE_M_MODE && issue_to[UNIT_IDS.IEC] && potential_flush;
assign gc_inputs.is_ifence = is_ifence_r;
assign gc_inputs.is_mret = is_mret_r;
assign gc_inputs.is_mret = is_sret_r;
////////////////////////////////////////////////////
//CSR unit inputs
@ -580,20 +578,65 @@ module decode_and_issue
//Illegal Instruction check
logic illegal_instruction_pattern_r;
generate if (CONFIG.INCLUDE_M_MODE) begin
illegal_instruction_checker # (.CONFIG(CONFIG))
illegal_op_check (
.instruction(decode.instruction), .illegal_instruction(illegal_instruction_pattern)
);
always_ff @(posedge clk) begin
if (rst)
illegal_instruction_pattern_r <= 0;
else if (issue_stage_ready)
illegal_instruction_pattern_r <= illegal_instruction_pattern;
illegal_instruction_checker # (.CONFIG(CONFIG))
illegal_op_check (
.instruction(decode.instruction), .illegal_instruction(illegal_instruction_pattern)
);
always_ff @(posedge clk) begin
if (rst)
illegal_instruction_pattern_r <= 0;
else if (issue_stage_ready)
illegal_instruction_pattern_r <= illegal_instruction_pattern;
end
////////////////////////////////////////////////////
//ECALL/EBREAK
//The type of call instruction is depedent on the current privilege level
exception_code_t ecall_code;
always_comb begin
case (current_privilege)
USER_PRIVILEGE : ecall_code = ECALL_U;
SUPERVISOR_PRIVILEGE : ecall_code = ECALL_S;
MACHINE_PRIVILEGE : ecall_code = ECALL_M;
default : ecall_code = ECALL_U;
endcase
end
////////////////////////////////////////////////////
//Exception generation (ecall/ebreak/illegal instruction/propagated fetch error)
logic new_exception;
exception_code_t ecode;
always_ff @(posedge clk) begin
if (rst)
pre_issue_exception_pending <= 0;
else if (issue_stage_ready)
pre_issue_exception_pending <= illegal_instruction_pattern | (opcode_trim inside {SYSTEM_T} & ~is_csr & (sys_op_match[ECALL_i] | sys_op_match[EBREAK_i])) | ~decode.fetch_metadata.ok;
end
assign new_exception = issue.stage_valid & pre_issue_exception_pending & ~(gc.issue_hold | gc.fetch_flush);
always_ff @(posedge clk) begin
if (rst)
exception.valid <= 0;
else
exception.valid <= (exception.valid | new_exception) & ~exception.ack;
end
assign ecode =
illegal_instruction_pattern_r ? ILLEGAL_INST :
is_ecall_r ? ecall_code :
~issue.fetch_metadata.ok ? issue.fetch_metadata.error_code :
BREAK;
always_ff @(posedge clk) begin
if (new_exception) begin
exception.code <= ecode;
exception.tval <= issue.instruction;
exception.id <= issue.id;
end
end
//Illegal instruction if the instruction is invalid, but could otherwise be issued
assign illegal_instruction = illegal_instruction_pattern_r & issue.stage_valid & ~gc.issue_hold & ~gc.fetch_flush;
end endgenerate
////////////////////////////////////////////////////
//End of Implementation
@ -601,11 +644,6 @@ module decode_and_issue
////////////////////////////////////////////////////
//Assertions
//TODO: convert into exception and expand support into all fetch stage exceptions
//If an invalid fetch address has reached the issue stage and has not been flushed as a branch, processor state is corrupted
invalid_fetch_address_assertion:
assert property (@(posedge clk) disable iff (rst) (issue.stage_valid & (~issue.fetch_metadata.ok & issue.fetch_metadata.error_code == FETCH_ACCESS_FAULT)) |-> (gc.fetch_flush))
else $error("invalid fetch address");
////////////////////////////////////////////////////
//Trace Interface

View file

@ -260,7 +260,7 @@ module fetch
assign if_pc = pc;
assign fetch_metadata.ok = valid_fetch_result;
assign fetch_metadata.error_code = fetch_attr.mmu_fault ? FETCH_PAGE_FAULT : FETCH_ACCESS_FAULT;
assign fetch_metadata.error_code = INST_ACCESS_FAULT;
assign fetch_instruction = unit_data_array[fetch_attr.subunit_id];
assign fetch_complete = (fetch_attr_fifo.valid & ~valid_fetch_result) | (|unit_data_valid);//allow instruction to propagate to decode if address is invalid

View file

@ -35,14 +35,15 @@ module gc_unit
input logic clk,
input logic rst,
//Decode
unit_issue_interface.unit issue,
input gc_inputs_t gc_inputs,
input logic gc_flush_required,
//Branch miss predict
input logic branch_flush,
//exception_interface.unit pre_issue_exception,
//Exception
exception_interface.econtrol exception [NUM_EXCEPTION_SOURCES],
input logic [31:0] exception_target_pc,
@ -61,6 +62,8 @@ module gc_unit
input logic interrupt_pending,
output logic interrupt_taken,
input logic processing_csr,
//Output controls
output gc_outputs_t gc,
@ -75,18 +78,19 @@ module gc_unit
localparam int INIT_CLEAR_DEPTH = CONFIG.INCLUDE_S_MODE ? (TLB_CLEAR_DEPTH > 64 ? TLB_CLEAR_DEPTH : 64) : 64;
////////////////////////////////////////////////////
//Instructions
//All instructions are processed only if in IDLE state, meaning there can be no exceptions caused by instructions already further in the pipeline.
//FENCE:
// Drain Load/Store FIFO
//Overview
//All CSR instructions hold the issue stage until they are the oldest instruction and complete.
// As such, their values are known at issue time for any call/ret instruction.
//FENCE.I:
// flush and hold fetch until L/S unit empty
// Local mem (nothing extra required for coherency)
// Caches, currently not supported. Need snooping for Icache and draining of data FIFO to L2 and after FIFO drained, poping at least the current number of entries in the invalidation FIFO
// Caches, currently not supported. Requires L2 monitoring. Need to know that all previous stores from this processor
// have been sent out as potential invalidations and ack-ed before fetch can resume
//SFENCE
// flush and hold fetch, wait until L/S input FIFO empty, hold fetch until TLB update complete
//ECALL, EBREAK, SRET, MRET:
// flush fetch, update to CSRs is pipelined
// flush fetch, hold until oldest instruction
//Interrupt
//wait until issue/execute exceptions are no longer possible, flush fetch, take exception
@ -106,27 +110,16 @@ module gc_unit
//LS exceptions (miss-aligned, TLB and MMU) (issue stage)
//fetch flush, take exception. If execute or later exception occurs first, exception is overridden
typedef enum {RST_STATE, PRE_CLEAR_STATE, INIT_CLEAR_STATE, IDLE_STATE, HOLD_STATE, TLB_CLEAR_STATE, IQ_DRAIN} gc_state;
typedef enum {RST_STATE, PRE_CLEAR_STATE, INIT_CLEAR_STATE, IDLE_STATE, TLB_CLEAR_STATE, ISSUE_DRAIN, ISSUE_DISCARD} gc_state;
gc_state state;
gc_state next_state;
logic init_clear_done;
logic tlb_clear_done;
logic i_fence_flush;
exception_code_t ecall_code;
logic second_cycle_flush;
logic system_op_or_exception_complete;
logic exception_with_rd_complete;
logic [1:0] current_privilege;
gc_inputs_t stage1;
gc_inputs_t gc_inputs_r;
logic post_issue_idle;
//CSR
logic processing_csr;
//GC registered global outputs
logic gc_init_clear;
@ -143,21 +136,19 @@ module gc_unit
//Implementation
//Input registering
always_ff @(posedge clk) begin
if (issue.possible_issue & ~gc.issue_hold) begin
stage1 <= gc_inputs;
end
if (issue.possible_issue & ~gc.issue_hold)
gc_inputs_r <= gc_inputs;
end
////////////////////////////////////////////////////
//GC Operation
assign post_issue_idle = (post_issue_count == 0) & sq_empty;
assign gc.fetch_flush = branch_flush | gc_pc_override;
always_ff @ (posedge clk) begin
gc_fetch_hold <= next_state inside {PRE_CLEAR_STATE, INIT_CLEAR_STATE};
gc_issue_hold <= issue.new_request | processing_csr | (next_state inside {PRE_CLEAR_STATE, INIT_CLEAR_STATE, HOLD_STATE, TLB_CLEAR_STATE, IQ_DRAIN});
gc_supress_writeback <= next_state inside {PRE_CLEAR_STATE, INIT_CLEAR_STATE, TLB_CLEAR_STATE};
gc_fetch_hold <= next_state inside {PRE_CLEAR_STATE, INIT_CLEAR_STATE, ISSUE_DRAIN};
gc_issue_hold <= processing_csr | (next_state inside {PRE_CLEAR_STATE, INIT_CLEAR_STATE, TLB_CLEAR_STATE, ISSUE_DRAIN});
gc_supress_writeback <= next_state inside {PRE_CLEAR_STATE, INIT_CLEAR_STATE};
gc_init_clear <= next_state inside {INIT_CLEAR_STATE};
gc_tlb_flush <= next_state inside {INIT_CLEAR_STATE, TLB_CLEAR_STATE};
end
@ -183,16 +174,11 @@ module gc_unit
PRE_CLEAR_STATE : next_state = INIT_CLEAR_STATE;
INIT_CLEAR_STATE : if (init_clear_done) next_state = IDLE_STATE;
IDLE_STATE : begin
if (issue.new_request)
next_state = HOLD_STATE;
//IF exception and post-issue > 1 OR FLUSHING interrupts
//if (ls_exception.valid | potential_branch_exception | system_op_or_exception_complete) begin
// next_state = IQ_DRAIN;
//end
if (issue.new_request | interrupt_pending | gc.exception_pending)
next_state = ISSUE_DRAIN;
end
HOLD_STATE : if (post_issue_idle) next_state = IDLE_STATE;
TLB_CLEAR_STATE : if (tlb_clear_done) next_state = IDLE_STATE;
IQ_DRAIN : if (post_issue_idle) next_state = IDLE_STATE;
ISSUE_DRAIN : if (post_issue_idle) next_state = IDLE_STATE;
default : next_state = RST_STATE;
endcase
end
@ -209,65 +195,49 @@ module gc_unit
assign init_clear_done = state_counter[$clog2(INIT_CLEAR_DEPTH)];
assign tlb_clear_done = state_counter[$clog2(TLB_CLEAR_DEPTH)];
////////////////////////////////////////////////////
//mret/sret
always_ff @ (posedge clk) begin
mret = issue.new_request & gc_inputs.is_ret & (gc_inputs.instruction[31:25] == 7'b0011000);
sret = issue.new_request & gc_inputs.is_ret & (gc_inputs.instruction[31:25] == 7'b0001000);
end
////////////////////////////////////////////////////
//Exception handling
//The type of call instruction is depedent on the current privilege level
always_comb begin
case (current_privilege)
USER_PRIVILEGE : ecall_code = ECALL_U;
SUPERVISOR_PRIVILEGE : ecall_code = ECALL_S;
MACHINE_PRIVILEGE : ecall_code = ECALL_M;
default : ecall_code = ECALL_U;
endcase
end
//Re-assigning interface inputs to array types so that they can be dynamically indexed
logic [NUM_EXCEPTION_SOURCES-1:0] ex_pending;
exception_code_t [NUM_EXCEPTION_SOURCES-1:0] ex_code;
id_t [NUM_EXCEPTION_SOURCES-1:0] ex_id;
logic [NUM_EXCEPTION_SOURCES-1:0][31:0] ex_tval;
logic ex_ack;
logic [NUM_EXCEPTION_SOURCES-1:0] exception_pending;
exception_code_t [NUM_EXCEPTION_SOURCES-1:0] exception_code;
id_t [NUM_EXCEPTION_SOURCES-1:0] exception_id;
logic [NUM_EXCEPTION_SOURCES-1:0][31:0] exception_tval;
logic exception_ack;
generate
for (genvar i = 0; i < NUM_EXCEPTION_SOURCES; i++) begin
assign ex_pending[i] = exception[i].valid;
assign ex_code[i] = exception[i].code;
assign ex_id[i] = exception[i].id;
assign ex_tval[i] = exception[i].tval;
assign exception[i].ack = ex_ack;
assign exception_pending[i] = exception[i].valid;
assign exception_code[i] = exception[i].code;
assign exception_id[i] = exception[i].id;
assign exception_tval[i] = exception[i].tval;
assign exception[i].ack = exception_ack;
end
endgenerate
//Exception valid when the oldest instruction is a valid ID. This is done with a level of indirection (through the exception unit table)
//for better scalability, avoiding the need to compare against all exception sources.
always_comb begin
gc.exception_pending = |ex_pending;
gc.exception.valid = (retire_ids[0] == ex_id[current_exception_unit]) & ex_pending[current_exception_unit];
gc.exception_pending = |exception_pending;
gc.exception.valid = (retire_ids[0] == exception_id[current_exception_unit]) & exception_pending[current_exception_unit];
gc.exception.pc = oldest_pc;
gc.exception.code = ex_code[current_exception_unit];
gc.exception.tval = ex_tval[current_exception_unit];
gc.exception.code = exception_code[current_exception_unit];
gc.exception.tval = exception_tval[current_exception_unit];
end
always_ff @ (posedge clk) begin
ex_ack <= gc.exception.valid;
exception_ack <= gc.exception.valid;
end
//PC determination (trap, flush or return)
//Two cycles: on first cycle the processor front end is flushed,
//on the second cycle the new PC is fetched
always_ff @ (posedge clk) begin
second_cycle_flush <= gc_flush_required;
gc_pc_override <= gc_flush_required | second_cycle_flush | gc.exception.valid | (next_state == INIT_CLEAR_STATE);
gc_pc_override <= issue.new_request | gc.exception.valid | (next_state == INIT_CLEAR_STATE);
gc_pc <=
gc.exception.valid ? exception_target_pc :
gc_inputs.is_ret ? epc :
stage1.pc_p4; //ifence
(gc_inputs.is_mret | gc_inputs.is_sret) ? epc :
gc_inputs.pc_p4; //ifence
end
//work-around for verilator BLKANDNBLK signal optimizations
assign gc.pc_override = gc_pc_override;

View file

@ -52,6 +52,7 @@ module instruction_metadata_and_id_management
input logic decode_advance,
input logic decode_uses_rd,
input rs_addr_t decode_rd_addr,
input exception_sources_t decode_exception_unit,
//renamer
input phys_addr_t decode_phys_rd_addr,
@ -149,8 +150,8 @@ module instruction_metadata_and_id_management
////////////////////////////////////////////////////
//Exception unit table
always_ff @ (posedge clk) begin
if (instruction_issued)
exception_unit_table[issue.id] <= issue.exception_unit;
if (decode_advance)
exception_unit_table[decode_id] <= decode_exception_unit;
end
////////////////////////////////////////////////////

View file

@ -148,6 +148,7 @@ module taiga
decode_packet_t decode;
logic decode_uses_rd;
rs_addr_t decode_rd_addr;
exception_sources_t decode_exception_unit;
phys_addr_t decode_phys_rd_addr;
phys_addr_t decode_phys_rs_addr [REGFILE_READ_PORTS];
logic [$clog2(CONFIG.NUM_WB_GROUPS)-1:0] decode_rs_wb_group [REGFILE_READ_PORTS];
@ -181,11 +182,12 @@ module taiga
logic interrupt_taken;
logic interrupt_pending;
logic processing_csr;
//Decode Unit and Fetch Unit
logic illegal_instruction;
logic instruction_issued;
logic instruction_issued_with_rd;
logic gc_flush_required;
//LS
wb_packet_t wb_snoop;
@ -267,6 +269,7 @@ module taiga
.decode_uses_rd (decode_uses_rd),
.decode_rd_addr (decode_rd_addr),
.decode_phys_rd_addr (decode_phys_rd_addr),
.decode_exception_unit (decode_exception_unit),
.issue (issue),
.instruction_issued (instruction_issued),
.instruction_issued_with_rd (instruction_issued_with_rd),
@ -386,6 +389,7 @@ module taiga
.renamer (decode_rename_interface),
.decode_uses_rd (decode_uses_rd),
.decode_rd_addr (decode_rd_addr),
.decode_exception_unit (decode_exception_unit),
.decode_phys_rd_addr (decode_phys_rd_addr),
.decode_phys_rs_addr (decode_phys_rs_addr),
.decode_rs_wb_group (decode_rs_wb_group),
@ -402,8 +406,8 @@ module taiga
.div_inputs (div_inputs),
.unit_issue (unit_issue),
.gc (gc),
.gc_flush_required (gc_flush_required),
.illegal_instruction (illegal_instruction),
.current_privilege (current_privilege),
.exception (exception[PRE_ISSUE_EXCEPTION]),
.tr_operand_stall (tr_operand_stall),
.tr_unit_stall (tr_unit_stall),
.tr_no_id_stall (tr_no_id_stall),
@ -534,6 +538,7 @@ module taiga
.current_privilege(current_privilege),
.interrupt_taken(interrupt_taken),
.interrupt_pending(interrupt_pending),
.processing_csr(processing_csr),
.tlb_on(tlb_on),
.asid(asid),
.immu(immu),
@ -555,7 +560,6 @@ module taiga
.rst (rst),
.issue (unit_issue[UNIT_IDS.IEC]),
.gc_inputs (gc_inputs),
.gc_flush_required (gc_flush_required),
.branch_flush (branch_flush),
.exception (exception),
.exception_target_pc (exception_target_pc),
@ -569,6 +573,7 @@ module taiga
.retire_ids (retire_ids),
.interrupt_taken(interrupt_taken),
.interrupt_pending(interrupt_pending),
.processing_csr(processing_csr),
.sq_empty (sq_empty),
.post_issue_count (post_issue_count)
);

View file

@ -255,7 +255,7 @@ package taiga_config;
typedef enum bit [1:0] {
LS_EXCEPTION = 0,
BR_EXCEPTION = 1,
IEC_EXCEPTION = 2
PRE_ISSUE_EXCEPTION = 2
} exception_sources_t;
////////////////////////////////////////////////////

View file

@ -56,14 +56,9 @@ package taiga_types;
id_t id;
} exception_packet_t;
typedef enum logic {
FETCH_ACCESS_FAULT = 1'b0,
FETCH_PAGE_FAULT = 1'b1
} fetch_error_codes_t;
typedef struct packed{
logic ok;
fetch_error_codes_t error_code;
exception_code_t error_code;
} fetch_metadata_t;
typedef struct packed{
@ -185,13 +180,11 @@ package taiga_types;
typedef struct packed{
logic [31:0] pc_p4;
logic [31:0] instruction;
logic is_csr;
logic is_fence;
logic is_i_fence;
logic is_ecall;
logic is_ebreak;
logic is_ret;
logic is_ifence;
logic is_mret;
logic is_sret;
logic is_exception;
exception_code_t code;
} gc_inputs_t;
typedef struct packed{