Significant overhaul of trap handling. Exceptions now taken from stage 3 instead of stage 2
This commit is contained in:
		
							parent
							
								
									5e61c9f9ac
								
							
						
					
					
						commit
						1b252d4bda
					
				
							
								
								
									
										25
									
								
								Readme.md
								
								
								
								
							
							
						
						
									
										25
									
								
								Readme.md
								
								
								
								
							| 
						 | 
					@ -36,3 +36,28 @@ On Hazard3 the expectation is for all jumps and taken branches to take 2 cycles,
 | 
				
			||||||
- Don't half-ass exceptions -- particularly things like instruction fetch memory fault
 | 
					- Don't half-ass exceptions -- particularly things like instruction fetch memory fault
 | 
				
			||||||
- Debug
 | 
					- Debug
 | 
				
			||||||
- Don't half-ass CSRs
 | 
					- Don't half-ass CSRs
 | 
				
			||||||
 | 
					- WFI instruction
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Exceptions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Exceptions have a number of sources:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Instruction fetch (hresp captured and piped through to decode, flushed if fetch speculation was incorrect)
 | 
				
			||||||
 | 
					- Instruction decode (invalid instructions, or exception-causing instructions like ecall)
 | 
				
			||||||
 | 
					- CSR address decode
 | 
				
			||||||
 | 
					- Load/store address alignment (address phase)
 | 
				
			||||||
 | 
					- Load/store bus error (data phase)
 | 
				
			||||||
 | 
					- External interrupts
 | 
				
			||||||
 | 
					- Internal interrupts (timers etc)
 | 
				
			||||||
 | 
					- Debugger breakpoints
 | 
				
			||||||
 | 
					- Debugger single-step
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Out of these the most troublesome is probably load/store bus error, as it *must* be associated with stage 3 of the pipeline, not with stage 2.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Therefore it may be best to take the exception branch from stage 3, kind of like a branch mispredict. This flush signal would inhibit side-effecting instructions in stage 2. In particular, the case of a load/store in stage 2 with a faulting load/store in stage 3. The sequence of events there is probably:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- Cycles m through n (maybe): data phase stall of instruction in stage 3
 | 
				
			||||||
 | 
					- Cycle n + 1: first cycle of error response. Error is registered locally.
 | 
				
			||||||
 | 
					- Cycle n + 2: Second cycle of error response. Exception branch is generated, and load/store in stage 2 is suppressed based on the registered flag.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -57,21 +57,6 @@ module hazard3_core #(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
`include "hazard3_ops.vh"
 | 
					`include "hazard3_ops.vh"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
`ifdef FORMAL
 | 
					 | 
				
			||||||
// Only yosys-smtbmc seems to support immediate assertions
 | 
					 | 
				
			||||||
`ifdef RISCV_FORMAL
 | 
					 | 
				
			||||||
`define ASSERT(x)
 | 
					 | 
				
			||||||
`else
 | 
					 | 
				
			||||||
`define ASSERT(x) assert(x)
 | 
					 | 
				
			||||||
`endif
 | 
					 | 
				
			||||||
`else
 | 
					 | 
				
			||||||
`define ASSERT(x)
 | 
					 | 
				
			||||||
//synthesis translate_off
 | 
					 | 
				
			||||||
`undef ASSERT
 | 
					 | 
				
			||||||
`define ASSERT(x) if (!x) begin $display("Assertion failed!"); $finish(1); end
 | 
					 | 
				
			||||||
//synthesis translate_on
 | 
					 | 
				
			||||||
`endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
wire d_stall;
 | 
					wire d_stall;
 | 
				
			||||||
wire x_stall;
 | 
					wire x_stall;
 | 
				
			||||||
wire m_stall;
 | 
					wire m_stall;
 | 
				
			||||||
| 
						 | 
					@ -235,10 +220,9 @@ wire  [W_DATA-1:0]   x_alu_result;
 | 
				
			||||||
wire  [W_DATA-1:0]   x_alu_add;
 | 
					wire  [W_DATA-1:0]   x_alu_add;
 | 
				
			||||||
wire                 x_alu_cmp;
 | 
					wire                 x_alu_cmp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
wire [W_DATA-1:0]    x_trap_addr;
 | 
					wire [W_DATA-1:0]    m_trap_addr;
 | 
				
			||||||
wire [W_DATA-1:0]    x_mepc;
 | 
					wire                 m_trap_enter_vld;
 | 
				
			||||||
wire                 x_trap_enter;
 | 
					wire                 m_trap_enter_rdy = f_jump_rdy;
 | 
				
			||||||
wire                 x_trap_exit;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
reg  [W_REGADDR-1:0] xm_rs1;
 | 
					reg  [W_REGADDR-1:0] xm_rs1;
 | 
				
			||||||
reg  [W_REGADDR-1:0] xm_rs2;
 | 
					reg  [W_REGADDR-1:0] xm_rs2;
 | 
				
			||||||
| 
						 | 
					@ -246,15 +230,17 @@ reg  [W_REGADDR-1:0] xm_rd;
 | 
				
			||||||
reg  [W_DATA-1:0]    xm_result;
 | 
					reg  [W_DATA-1:0]    xm_result;
 | 
				
			||||||
reg  [W_DATA-1:0]    xm_store_data;
 | 
					reg  [W_DATA-1:0]    xm_store_data;
 | 
				
			||||||
reg  [W_MEMOP-1:0]   xm_memop;
 | 
					reg  [W_MEMOP-1:0]   xm_memop;
 | 
				
			||||||
 | 
					reg  [W_EXCEPT-1:0]  xm_except;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
reg x_stall_raw;
 | 
					reg x_stall_raw;
 | 
				
			||||||
wire x_stall_muldiv;
 | 
					wire x_stall_muldiv;
 | 
				
			||||||
 | 
					wire x_jump_req;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
assign x_stall =
 | 
					assign x_stall =
 | 
				
			||||||
	m_stall ||
 | 
						m_stall ||
 | 
				
			||||||
	x_stall_raw || x_stall_muldiv ||
 | 
						x_stall_raw || x_stall_muldiv ||
 | 
				
			||||||
	bus_aph_req_d && !bus_aph_ready_d ||
 | 
						bus_aph_req_d && !bus_aph_ready_d ||
 | 
				
			||||||
	f_jump_req && !f_jump_rdy;
 | 
						x_jump_req && !f_jump_rdy;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
wire m_fast_mul_result_vld;
 | 
					wire m_fast_mul_result_vld;
 | 
				
			||||||
wire m_generating_result = xm_memop < MEMOP_SW || m_fast_mul_result_vld;
 | 
					wire m_generating_result = xm_memop < MEMOP_SW || m_fast_mul_result_vld;
 | 
				
			||||||
| 
						 | 
					@ -328,9 +314,6 @@ wire x_unaligned_addr =
 | 
				
			||||||
	bus_hsize_d == HSIZE_WORD && |bus_haddr_d[1:0] ||
 | 
						bus_hsize_d == HSIZE_WORD && |bus_haddr_d[1:0] ||
 | 
				
			||||||
	bus_hsize_d == HSIZE_HWORD && bus_haddr_d[0];
 | 
						bus_hsize_d == HSIZE_HWORD && bus_haddr_d[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
wire x_except_load_misaligned = x_memop_vld && x_unaligned_addr && !x_memop_write;
 | 
					 | 
				
			||||||
wire x_except_store_misaligned = x_memop_vld && x_unaligned_addr && x_memop_write;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
always @ (*) begin
 | 
					always @ (*) begin
 | 
				
			||||||
	// Need to be careful not to use anything hready-sourced to gate htrans!
 | 
						// Need to be careful not to use anything hready-sourced to gate htrans!
 | 
				
			||||||
	bus_haddr_d = x_alu_add;
 | 
						bus_haddr_d = x_alu_add;
 | 
				
			||||||
| 
						 | 
					@ -343,70 +326,9 @@ always @ (*) begin
 | 
				
			||||||
		MEMOP_SH:  bus_hsize_d = HSIZE_HWORD;
 | 
							MEMOP_SH:  bus_hsize_d = HSIZE_HWORD;
 | 
				
			||||||
		default:   bus_hsize_d = HSIZE_BYTE;
 | 
							default:   bus_hsize_d = HSIZE_BYTE;
 | 
				
			||||||
	endcase
 | 
						endcase
 | 
				
			||||||
	bus_aph_req_d = x_memop_vld && !(x_stall_raw || x_trap_enter);
 | 
						bus_aph_req_d = x_memop_vld && !(x_stall_raw || m_trap_enter_vld);
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CSRs and Trap Handling
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
wire   x_except_ecall         = d_except == EXCEPT_ECALL;
 | 
					 | 
				
			||||||
wire   x_except_breakpoint    = d_except == EXCEPT_EBREAK;
 | 
					 | 
				
			||||||
wire   x_except_invalid_instr = d_except == EXCEPT_INSTR_ILLEGAL;
 | 
					 | 
				
			||||||
assign x_trap_exit            = d_except == EXCEPT_MRET;
 | 
					 | 
				
			||||||
wire   x_trap_enter_rdy       = !(x_stall || x_trap_exit);
 | 
					 | 
				
			||||||
wire   x_trap_is_exception; // diagnostic
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
`ifdef FORMAL
 | 
					 | 
				
			||||||
always @ (posedge clk) begin
 | 
					 | 
				
			||||||
	if (x_trap_exit)
 | 
					 | 
				
			||||||
		assert(!bus_aph_req_d);
 | 
					 | 
				
			||||||
end
 | 
					 | 
				
			||||||
`endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
wire [W_DATA-1:0] x_csr_wdata = d_csr_w_imm ?
 | 
					 | 
				
			||||||
	{{W_DATA-5{1'b0}}, d_rs1} : x_rs1_bypass;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
wire [W_DATA-1:0] x_csr_rdata;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
hazard3_csr #(
 | 
					 | 
				
			||||||
	.XLEN            (W_DATA),
 | 
					 | 
				
			||||||
`include "hazard3_config_inst.vh"
 | 
					 | 
				
			||||||
) inst_hazard3_csr (
 | 
					 | 
				
			||||||
	.clk                     (clk),
 | 
					 | 
				
			||||||
	.rst_n                   (rst_n),
 | 
					 | 
				
			||||||
	// CSR access port
 | 
					 | 
				
			||||||
	// *en_soon are early access strobes which are not a function of bus stall.
 | 
					 | 
				
			||||||
	// Can generate access faults (hence traps), but do not actually perform access.
 | 
					 | 
				
			||||||
	.addr                    (d_imm[11:0]), // todo could just connect this to the instruction bits
 | 
					 | 
				
			||||||
	.wdata                   (x_csr_wdata),
 | 
					 | 
				
			||||||
	.wen_soon                (d_csr_wen),
 | 
					 | 
				
			||||||
	.wen                     (d_csr_wen && !x_stall),
 | 
					 | 
				
			||||||
	.wtype                   (d_csr_wtype),
 | 
					 | 
				
			||||||
	.rdata                   (x_csr_rdata),
 | 
					 | 
				
			||||||
	.ren_soon                (d_csr_ren),
 | 
					 | 
				
			||||||
	.ren                     (d_csr_ren && !x_stall),
 | 
					 | 
				
			||||||
	// Trap signalling
 | 
					 | 
				
			||||||
	.trap_addr               (x_trap_addr),
 | 
					 | 
				
			||||||
	.trap_enter_vld          (x_trap_enter),
 | 
					 | 
				
			||||||
	.trap_enter_rdy          (x_trap_enter_rdy),
 | 
					 | 
				
			||||||
	.trap_exit               (x_trap_exit && !x_stall),
 | 
					 | 
				
			||||||
	.trap_is_exception       (x_trap_is_exception),
 | 
					 | 
				
			||||||
	.mepc_in                 (d_pc),
 | 
					 | 
				
			||||||
	.mepc_out                (x_mepc),
 | 
					 | 
				
			||||||
	// IRQ and exception requests
 | 
					 | 
				
			||||||
	.irq                     (irq),
 | 
					 | 
				
			||||||
	.except_instr_misaligned (1'b0), // TODO
 | 
					 | 
				
			||||||
	.except_instr_fault      (1'b0), // TODO
 | 
					 | 
				
			||||||
	.except_instr_invalid    (x_except_invalid_instr),
 | 
					 | 
				
			||||||
	.except_breakpoint       (x_except_breakpoint),
 | 
					 | 
				
			||||||
	.except_load_misaligned  (x_except_load_misaligned),
 | 
					 | 
				
			||||||
	.except_load_fault       (1'b0), // TODO
 | 
					 | 
				
			||||||
	.except_store_misaligned (x_except_store_misaligned),
 | 
					 | 
				
			||||||
	.except_store_fault      (1'b0), // TODO
 | 
					 | 
				
			||||||
	.except_ecall            (x_except_ecall),
 | 
					 | 
				
			||||||
	// Other CSR-specific signalling
 | 
					 | 
				
			||||||
	.instr_ret               (1'b0)  // TODO
 | 
					 | 
				
			||||||
);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Multiply/divide
 | 
					// Multiply/divide
 | 
				
			||||||
 | 
					
 | 
				
			||||||
wire [W_DATA-1:0] x_muldiv_result;
 | 
					wire [W_DATA-1:0] x_muldiv_result;
 | 
				
			||||||
| 
						 | 
					@ -427,7 +349,7 @@ if (EXTENSION_M) begin: has_muldiv
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
			x_muldiv_posted <= (x_muldiv_posted || (x_muldiv_op_vld && x_muldiv_op_rdy)) && x_stall;
 | 
								x_muldiv_posted <= (x_muldiv_posted || (x_muldiv_op_vld && x_muldiv_op_rdy)) && x_stall;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wire x_muldiv_kill = x_trap_enter; // TODO this takes an extra cycle to kill muldiv before trap entry
 | 
						wire x_muldiv_kill = m_trap_enter_vld;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	wire x_use_fast_mul = MUL_FAST && d_aluop == ALUOP_MULDIV && d_mulop == M_OP_MUL;
 | 
						wire x_use_fast_mul = MUL_FAST && d_aluop == ALUOP_MULDIV && d_mulop == M_OP_MUL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -501,21 +423,86 @@ end else begin: no_muldiv
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
endgenerate
 | 
					endgenerate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// State machine
 | 
					// CSRs and Trap Handling
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					wire [W_DATA-1:0] x_csr_wdata = d_csr_w_imm ?
 | 
				
			||||||
 | 
						{{W_DATA-5{1'b0}}, d_rs1} : x_rs1_bypass;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					wire [W_DATA-1:0] x_csr_rdata;
 | 
				
			||||||
 | 
					wire              x_csr_illegal_access;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					reg prev_instr_was_32_bit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					always @ (posedge clk or negedge rst_n) begin
 | 
				
			||||||
 | 
						if (!rst_n) begin
 | 
				
			||||||
 | 
							prev_instr_was_32_bit <= 1'b0;
 | 
				
			||||||
 | 
						end else if (!x_stall) begin
 | 
				
			||||||
 | 
							prev_instr_was_32_bit <= df_cir_use == 2'd2;
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					hazard3_csr #(
 | 
				
			||||||
 | 
						.XLEN            (W_DATA),
 | 
				
			||||||
 | 
					`include "hazard3_config_inst.vh"
 | 
				
			||||||
 | 
					) inst_hazard3_csr (
 | 
				
			||||||
 | 
						.clk                     (clk),
 | 
				
			||||||
 | 
						.rst_n                   (rst_n),
 | 
				
			||||||
 | 
						// CSR access port
 | 
				
			||||||
 | 
						// *en_soon are early access strobes which are not a function of bus stall.
 | 
				
			||||||
 | 
						// Can generate access faults (hence traps), but do not actually perform access.
 | 
				
			||||||
 | 
						.addr                    (d_imm[11:0]), // todo could just connect this to the instruction bits
 | 
				
			||||||
 | 
						.wdata                   (x_csr_wdata),
 | 
				
			||||||
 | 
						.wen_soon                (d_csr_wen && !m_trap_enter_vld),
 | 
				
			||||||
 | 
						.wen                     (d_csr_wen && !m_trap_enter_vld && !x_stall),
 | 
				
			||||||
 | 
						.wtype                   (d_csr_wtype),
 | 
				
			||||||
 | 
						.rdata                   (x_csr_rdata),
 | 
				
			||||||
 | 
						.ren_soon                (d_csr_ren && !m_trap_enter_vld),
 | 
				
			||||||
 | 
						.ren                     (d_csr_ren && !m_trap_enter_vld && !x_stall),
 | 
				
			||||||
 | 
						.illegal                 (x_csr_illegal_access),
 | 
				
			||||||
 | 
						// Trap signalling
 | 
				
			||||||
 | 
						.trap_addr               (m_trap_addr),
 | 
				
			||||||
 | 
						.trap_enter_vld          (m_trap_enter_vld),
 | 
				
			||||||
 | 
						.trap_enter_rdy          (m_trap_enter_rdy),
 | 
				
			||||||
 | 
						.mepc_in                 (d_pc - (prev_instr_was_32_bit ? 32'h4 : 32'h2)),
 | 
				
			||||||
 | 
						// IRQ and exception requests
 | 
				
			||||||
 | 
						.irq                     (irq),
 | 
				
			||||||
 | 
						.except                  (xm_except),
 | 
				
			||||||
 | 
						// Other CSR-specific signalling
 | 
				
			||||||
 | 
						.instr_ret               (|df_cir_use)
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					wire [W_EXCEPT-1:0] x_except =
 | 
				
			||||||
 | 
						x_csr_illegal_access               ? EXCEPT_INSTR_ILLEGAL :
 | 
				
			||||||
 | 
						x_unaligned_addr &&  x_memop_write ? EXCEPT_STORE_ALIGN   :
 | 
				
			||||||
 | 
						x_unaligned_addr && !x_memop_write ? EXCEPT_LOAD_ALIGN    : d_except;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Pipe register
 | 
				
			||||||
 | 
					
 | 
				
			||||||
always @ (posedge clk or negedge rst_n) begin
 | 
					always @ (posedge clk or negedge rst_n) begin
 | 
				
			||||||
	if (!rst_n) begin
 | 
						if (!rst_n) begin
 | 
				
			||||||
		xm_memop <= MEMOP_NONE;
 | 
							xm_memop <= MEMOP_NONE;
 | 
				
			||||||
 | 
							xm_except <= EXCEPT_NONE;
 | 
				
			||||||
		{xm_rs1, xm_rs2, xm_rd} <= {3 * W_REGADDR{1'b0}};
 | 
							{xm_rs1, xm_rs2, xm_rd} <= {3 * W_REGADDR{1'b0}};
 | 
				
			||||||
	end else begin
 | 
						end else begin
 | 
				
			||||||
		if (!m_stall) begin
 | 
							if (!m_stall) begin
 | 
				
			||||||
			{xm_rs1, xm_rs2, xm_rd} <= {d_rs1, d_rs2, d_rd};
 | 
								{xm_rs1, xm_rs2, xm_rd} <= {d_rs1, d_rs2, d_rd};
 | 
				
			||||||
			// If the transfer is unaligned, make sure it is completely NOP'd on the bus
 | 
								// If the transfer is unaligned, make sure it is completely NOP'd on the bus
 | 
				
			||||||
			xm_memop <= d_memop | {x_unaligned_addr, 3'h0};
 | 
								xm_memop <= d_memop | {x_unaligned_addr, 3'h0};
 | 
				
			||||||
			if (x_stall || x_trap_enter) begin
 | 
								if (x_stall || m_trap_enter_vld) begin
 | 
				
			||||||
				// Insert bubble
 | 
									// Insert bubble
 | 
				
			||||||
				xm_rd <= {W_REGADDR{1'b0}};
 | 
									xm_rd <= {W_REGADDR{1'b0}};
 | 
				
			||||||
				xm_memop <= MEMOP_NONE;
 | 
									xm_memop <= MEMOP_NONE;
 | 
				
			||||||
 | 
									xm_except <= EXCEPT_NONE;
 | 
				
			||||||
			end
 | 
								end
 | 
				
			||||||
 | 
								xm_except <= x_except;
 | 
				
			||||||
 | 
							end else if (bus_dph_err_d) begin
 | 
				
			||||||
 | 
								// First phase of 2-phase AHBL error response. Pass the exception along on
 | 
				
			||||||
 | 
								// this cycle, and on the next cycle the trap entry will be asserted,
 | 
				
			||||||
 | 
								// suppressing any load/store that may currently be in stage X.
 | 
				
			||||||
 | 
					`ifdef FORMAL
 | 
				
			||||||
 | 
								assert(!xm_memop[3]); // Not NONE
 | 
				
			||||||
 | 
					`endif
 | 
				
			||||||
 | 
								xm_except <= xm_memop <= MEMOP_LBU ? EXCEPT_LOAD_FAULT : EXCEPT_STORE_FAULT;
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
	end
 | 
						end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
| 
						 | 
					@ -533,25 +520,15 @@ always @ (posedge clk)
 | 
				
			||||||
// Branch handling
 | 
					// Branch handling
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// For JALR, the LSB of the result must be cleared by hardware
 | 
					// For JALR, the LSB of the result must be cleared by hardware
 | 
				
			||||||
wire [W_ADDR-1:0] x_taken_jump_target = ((d_jump_is_regoffs ? x_rs1_bypass : d_pc) + d_jump_offs) & ~32'h1;
 | 
					wire [W_ADDR-1:0] x_jump_target = ((d_jump_is_regoffs ? x_rs1_bypass : d_pc) + d_jump_offs) & ~32'h1;
 | 
				
			||||||
 | 
					 | 
				
			||||||
wire [W_ADDR-1:0] x_jump_target =
 | 
					 | 
				
			||||||
	x_trap_exit                                 ? x_mepc             : // Note precedence -- it's possible to have enter && exit, but in this case enter_rdy is false.
 | 
					 | 
				
			||||||
	x_trap_enter                                ? x_trap_addr        :
 | 
					 | 
				
			||||||
	                                              x_taken_jump_target;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Be careful not to take branches whose comparisons depend on a load result
 | 
					// Be careful not to take branches whose comparisons depend on a load result
 | 
				
			||||||
wire x_jump_req = x_trap_enter || x_trap_exit || !x_stall_raw && (
 | 
					assign x_jump_req = !x_stall_raw && (
 | 
				
			||||||
	d_branchcond == BCOND_ALWAYS ||
 | 
						d_branchcond == BCOND_ALWAYS ||
 | 
				
			||||||
	d_branchcond == BCOND_ZERO && !x_alu_cmp ||
 | 
						d_branchcond == BCOND_ZERO && !x_alu_cmp ||
 | 
				
			||||||
	d_branchcond == BCOND_NZERO && x_alu_cmp
 | 
						d_branchcond == BCOND_NZERO && x_alu_cmp
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
assign f_jump_req = x_jump_req;
 | 
					 | 
				
			||||||
assign f_jump_target = x_jump_target;
 | 
					 | 
				
			||||||
assign x_jump_not_except = !(x_trap_enter || x_trap_exit);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ----------------------------------------------------------------------------
 | 
					// ----------------------------------------------------------------------------
 | 
				
			||||||
//                               Pipe Stage M
 | 
					//                               Pipe Stage M
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -559,9 +536,11 @@ reg [W_DATA-1:0] m_rdata_shift;
 | 
				
			||||||
reg [W_DATA-1:0] m_wdata;
 | 
					reg [W_DATA-1:0] m_wdata;
 | 
				
			||||||
reg [W_DATA-1:0] m_result;
 | 
					reg [W_DATA-1:0] m_result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
assign m_stall = !xm_memop[3] && !bus_dph_ready_d;
 | 
					assign f_jump_req = x_jump_req || m_trap_enter_vld;
 | 
				
			||||||
 | 
					assign f_jump_target = m_trap_enter_vld	? m_trap_addr : x_jump_target;
 | 
				
			||||||
 | 
					assign x_jump_not_except = !m_trap_enter_vld;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
wire m_except_bus_fault = bus_dph_err_d; // TODO: handle differently for LSU/ifetch?
 | 
					assign m_stall = (!xm_memop[3] && !bus_dph_ready_d) || (m_trap_enter_vld && !m_trap_enter_rdy);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
always @ (*) begin
 | 
					always @ (*) begin
 | 
				
			||||||
	// Local forwarding of store data
 | 
						// Local forwarding of store data
 | 
				
			||||||
| 
						 | 
					@ -607,11 +586,6 @@ always @ (posedge clk or negedge rst_n) begin
 | 
				
			||||||
		mw_rd <= {W_REGADDR{1'b0}};
 | 
							mw_rd <= {W_REGADDR{1'b0}};
 | 
				
			||||||
	end else if (!m_stall) begin
 | 
						end else if (!m_stall) begin
 | 
				
			||||||
		//synthesis translate_off
 | 
							//synthesis translate_off
 | 
				
			||||||
		// TODO: proper exception support
 | 
					 | 
				
			||||||
		if (m_except_bus_fault) begin
 | 
					 | 
				
			||||||
			$display("Bus fault!");
 | 
					 | 
				
			||||||
			$finish;
 | 
					 | 
				
			||||||
		end
 | 
					 | 
				
			||||||
		if (^bus_wdata_d === 1'bX) begin
 | 
							if (^bus_wdata_d === 1'bX) begin
 | 
				
			||||||
			$display("Writing Xs to memory!");
 | 
								$display("Writing Xs to memory!");
 | 
				
			||||||
			$finish;
 | 
								$finish;
 | 
				
			||||||
| 
						 | 
					@ -632,7 +606,7 @@ always @ (posedge clk)
 | 
				
			||||||
// mw_result and mw_rd register the most recent write to the register file,
 | 
					// mw_result and mw_rd register the most recent write to the register file,
 | 
				
			||||||
// so that X can bypass them in.
 | 
					// so that X can bypass them in.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
wire w_reg_wen = |xm_rd && !m_stall;
 | 
					wire w_reg_wen = |xm_rd && !m_stall && xm_except == EXCEPT_NONE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//synthesis translate_off
 | 
					//synthesis translate_off
 | 
				
			||||||
always @ (posedge clk) begin
 | 
					always @ (posedge clk) begin
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,6 +25,8 @@ module hazard3_csr #(
 | 
				
			||||||
	                                  // The full 64 bits is writeable, so high-word increment can
 | 
						                                  // The full 64 bits is writeable, so high-word increment can
 | 
				
			||||||
	                                  // be implemented in software, and a narrower hw counter used
 | 
						                                  // be implemented in software, and a narrower hw counter used
 | 
				
			||||||
`include "hazard3_config.vh"
 | 
					`include "hazard3_config.vh"
 | 
				
			||||||
 | 
					,
 | 
				
			||||||
 | 
					`include "hazard3_width_const.vh"
 | 
				
			||||||
) (
 | 
					) (
 | 
				
			||||||
	input  wire            clk,
 | 
						input  wire            clk,
 | 
				
			||||||
	input  wire            rst_n,
 | 
						input  wire            rst_n,
 | 
				
			||||||
| 
						 | 
					@ -45,6 +47,7 @@ module hazard3_csr #(
 | 
				
			||||||
	output reg  [XLEN-1:0]    rdata,
 | 
						output reg  [XLEN-1:0]    rdata,
 | 
				
			||||||
	input  wire               ren,
 | 
						input  wire               ren,
 | 
				
			||||||
	input  wire               ren_soon, // ren will be asserted once some stall condition clears
 | 
						input  wire               ren_soon, // ren will be asserted once some stall condition clears
 | 
				
			||||||
 | 
						output wire               illegal,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Trap signalling
 | 
						// Trap signalling
 | 
				
			||||||
	// *We* tell the core that we are taking a trap, and where to, based on:
 | 
						// *We* tell the core that we are taking a trap, and where to, based on:
 | 
				
			||||||
| 
						 | 
					@ -58,29 +61,14 @@ module hazard3_csr #(
 | 
				
			||||||
	//
 | 
						//
 | 
				
			||||||
	// Note that an exception input can go away, e.g. if the pipe gets flushed. In this
 | 
						// Note that an exception input can go away, e.g. if the pipe gets flushed. In this
 | 
				
			||||||
	// case we lower trap_enter_vld.
 | 
						// case we lower trap_enter_vld.
 | 
				
			||||||
	//
 | 
					 | 
				
			||||||
	// The core tells *us* that we are leaving the trap, by putting a 1-clock pulse on
 | 
					 | 
				
			||||||
	// trap_exit. The core will simultaneously produce a jump (specifically a mispredict)
 | 
					 | 
				
			||||||
	// to mepc_out.
 | 
					 | 
				
			||||||
	output wire [XLEN-1:0]     trap_addr,
 | 
						output wire [XLEN-1:0]     trap_addr,
 | 
				
			||||||
	output wire                trap_enter_vld,
 | 
						output wire                trap_enter_vld,
 | 
				
			||||||
	input  wire                trap_enter_rdy,
 | 
						input  wire                trap_enter_rdy,
 | 
				
			||||||
	input  wire            trap_exit,
 | 
					 | 
				
			||||||
	output wire            trap_is_exception, // diagnostic
 | 
					 | 
				
			||||||
	input  wire [XLEN-1:0]     mepc_in,
 | 
						input  wire [XLEN-1:0]     mepc_in,
 | 
				
			||||||
	output wire [XLEN-1:0] mepc_out,
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Exceptions must *not* be a function of bus stall.
 | 
						// Exceptions must *not* be a function of bus stall.
 | 
				
			||||||
	input  wire [15:0]         irq,
 | 
						input  wire [15:0]         irq,
 | 
				
			||||||
	input wire             except_instr_misaligned,
 | 
						input  wire [W_EXCEPT-1:0] except,
 | 
				
			||||||
	input wire             except_instr_fault,
 | 
					 | 
				
			||||||
	input wire             except_instr_invalid,
 | 
					 | 
				
			||||||
	input wire             except_breakpoint,
 | 
					 | 
				
			||||||
	input wire             except_load_misaligned,
 | 
					 | 
				
			||||||
	input wire             except_load_fault,
 | 
					 | 
				
			||||||
	input wire             except_store_misaligned,
 | 
					 | 
				
			||||||
	input wire             except_store_fault,
 | 
					 | 
				
			||||||
	input wire             except_ecall,
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Other CSR-specific signalling
 | 
						// Other CSR-specific signalling
 | 
				
			||||||
	input  wire                instr_ret
 | 
						input  wire                instr_ret
 | 
				
			||||||
| 
						 | 
					@ -262,11 +250,13 @@ always @ (posedge clk or negedge rst_n) begin
 | 
				
			||||||
		mstatus_mie <= 1'b0;
 | 
							mstatus_mie <= 1'b0;
 | 
				
			||||||
	end else if (CSR_M_TRAP) begin
 | 
						end else if (CSR_M_TRAP) begin
 | 
				
			||||||
		if (trap_enter_vld && trap_enter_rdy) begin
 | 
							if (trap_enter_vld && trap_enter_rdy) begin
 | 
				
			||||||
			mstatus_mpie <= mstatus_mie;
 | 
								if (except == EXCEPT_MRET) begin
 | 
				
			||||||
			mstatus_mie <= 1'b0;
 | 
					 | 
				
			||||||
		end else if (trap_exit) begin
 | 
					 | 
				
			||||||
				mstatus_mpie <= 1'b1;
 | 
									mstatus_mpie <= 1'b1;
 | 
				
			||||||
				mstatus_mie <= mstatus_mpie;
 | 
									mstatus_mie <= mstatus_mpie;
 | 
				
			||||||
 | 
								end else begin
 | 
				
			||||||
 | 
									mstatus_mpie <= mstatus_mie;
 | 
				
			||||||
 | 
									mstatus_mie <= 1'b0;
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
		end else if (wen && addr == MSTATUS) begin
 | 
							end else if (wen && addr == MSTATUS) begin
 | 
				
			||||||
			{mstatus_mpie, mstatus_mie} <=
 | 
								{mstatus_mpie, mstatus_mie} <=
 | 
				
			||||||
				wtype == CSR_WTYPE_C ? {mstatus_mpie, mstatus_mie} & ~{wdata[7], wdata[3]} :
 | 
									wtype == CSR_WTYPE_C ? {mstatus_mpie, mstatus_mie} & ~{wdata[7], wdata[3]} :
 | 
				
			||||||
| 
						 | 
					@ -310,7 +300,7 @@ always @ (posedge clk or negedge rst_n) begin
 | 
				
			||||||
	if (!rst_n) begin
 | 
						if (!rst_n) begin
 | 
				
			||||||
		mepc <= X0;
 | 
							mepc <= X0;
 | 
				
			||||||
	end else if (CSR_M_TRAP) begin
 | 
						end else if (CSR_M_TRAP) begin
 | 
				
			||||||
		if (trap_enter_vld && trap_enter_rdy) begin
 | 
							if (trap_enter_vld && trap_enter_rdy && except != EXCEPT_MRET) begin
 | 
				
			||||||
			mepc <= mepc_in & MEPC_MASK;
 | 
								mepc <= mepc_in & MEPC_MASK;
 | 
				
			||||||
		end else if (wen && addr == MEPC) begin
 | 
							end else if (wen && addr == MEPC) begin
 | 
				
			||||||
			mepc <= update(mepc) & MEPC_MASK;
 | 
								mepc <= update(mepc) & MEPC_MASK;
 | 
				
			||||||
| 
						 | 
					@ -354,7 +344,7 @@ always @ (posedge clk or negedge rst_n) begin
 | 
				
			||||||
		mcause_irq <= 1'b0;
 | 
							mcause_irq <= 1'b0;
 | 
				
			||||||
		mcause_code <= 5'h0;
 | 
							mcause_code <= 5'h0;
 | 
				
			||||||
	end else if (CSR_M_TRAP) begin
 | 
						end else if (CSR_M_TRAP) begin
 | 
				
			||||||
		if (trap_enter_vld && trap_enter_rdy) begin
 | 
							if (trap_enter_vld && trap_enter_rdy && except != EXCEPT_MRET) begin
 | 
				
			||||||
			mcause_irq <= mcause_irq_next;
 | 
								mcause_irq <= mcause_irq_next;
 | 
				
			||||||
			mcause_code <= mcause_code_next;
 | 
								mcause_code <= mcause_code_next;
 | 
				
			||||||
		end else if (wen && addr == MCAUSE) begin
 | 
							end else if (wen && addr == MCAUSE) begin
 | 
				
			||||||
| 
						 | 
					@ -657,49 +647,13 @@ always @ (*) begin
 | 
				
			||||||
	endcase
 | 
						endcase
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
wire csr_access_error = (wen_soon || ren_soon) && !decode_match;
 | 
					assign illegal = (wen_soon || ren_soon) && !decode_match;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ----------------------------------------------------------------------------
 | 
					// ----------------------------------------------------------------------------
 | 
				
			||||||
// Trap request generation
 | 
					// Trap request generation
 | 
				
			||||||
// ----------------------------------------------------------------------------
 | 
					// ----------------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Keep track of whether we are in a trap; we do not permit exception nesting.
 | 
					wire exception_req_any = except != EXCEPT_NONE;
 | 
				
			||||||
// TODO lockup condition?
 | 
					 | 
				
			||||||
reg in_trap;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
always @ (posedge clk or negedge rst_n)
 | 
					 | 
				
			||||||
	if (!rst_n)
 | 
					 | 
				
			||||||
		in_trap <= 1'b0;
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		in_trap <= (in_trap || (trap_enter_vld && trap_enter_rdy)) && !trap_exit;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Exception selection
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Most-significant is lowest priority
 | 
					 | 
				
			||||||
// FIXME: this is different from the priority order given in the spec, but will get us off the ground
 | 
					 | 
				
			||||||
wire [15:0] exception_req = {
 | 
					 | 
				
			||||||
	4'h0, // reserved by spec
 | 
					 | 
				
			||||||
	except_ecall,
 | 
					 | 
				
			||||||
	3'h0, // nonimplemented privileges
 | 
					 | 
				
			||||||
	except_store_fault,
 | 
					 | 
				
			||||||
	except_store_misaligned,
 | 
					 | 
				
			||||||
	except_load_fault,
 | 
					 | 
				
			||||||
	except_load_misaligned,
 | 
					 | 
				
			||||||
	except_breakpoint,
 | 
					 | 
				
			||||||
	except_instr_invalid || csr_access_error,
 | 
					 | 
				
			||||||
	except_instr_fault,
 | 
					 | 
				
			||||||
	except_instr_misaligned
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
wire exception_req_any = |exception_req && !in_trap;
 | 
					 | 
				
			||||||
wire [3:0] exception_req_num;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
hazard3_priority_encode #(
 | 
					 | 
				
			||||||
	.W_REQ(16)
 | 
					 | 
				
			||||||
) except_priority (
 | 
					 | 
				
			||||||
	.req (exception_req),
 | 
					 | 
				
			||||||
	.gnt (exception_req_num)
 | 
					 | 
				
			||||||
);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Interrupt masking and selection
 | 
					// Interrupt masking and selection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -735,13 +689,12 @@ hazard3_priority_encode #(
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
wire [11:0] mtvec_offs = (exception_req_any ?
 | 
					wire [11:0] mtvec_offs = (exception_req_any ?
 | 
				
			||||||
	{8'h0, exception_req_num} :
 | 
						{8'h0, except} :
 | 
				
			||||||
	12'h10 + {7'h0, irq_num}
 | 
						12'h10 + {7'h0, irq_num}
 | 
				
			||||||
) << 2;
 | 
					) << 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
assign trap_addr = mtvec | {20'h0, mtvec_offs};
 | 
					assign trap_addr = except == EXCEPT_MRET ? mepc : mtvec | {20'h0, mtvec_offs};
 | 
				
			||||||
assign trap_enter_vld = CSR_M_TRAP && (exception_req_any || irq_any);
 | 
					assign trap_enter_vld = CSR_M_TRAP && (exception_req_any || irq_any);
 | 
				
			||||||
assign trap_is_exception = exception_req_any;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
assign mcause_irq_next = !exception_req_any;
 | 
					assign mcause_irq_next = !exception_req_any;
 | 
				
			||||||
assign mcause_code_next = exception_req_any ? exception_req_num : {1'b0, irq_num};
 | 
					assign mcause_code_next = exception_req_any ? exception_req_num : {1'b0, irq_num};
 | 
				
			||||||
| 
						 | 
					@ -749,22 +702,33 @@ assign mcause_code_next = exception_req_any ? exception_req_num : {1'b0, irq_num
 | 
				
			||||||
// ----------------------------------------------------------------------------
 | 
					// ----------------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
`ifdef RISCV_FORMAL
 | 
					`ifdef RISCV_FORMAL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Keep track of whether we are in a trap (only for formal property purposes)
 | 
				
			||||||
 | 
					reg in_trap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					always @ (posedge clk or negedge rst_n)
 | 
				
			||||||
 | 
						if (!rst_n)
 | 
				
			||||||
 | 
							in_trap <= 1'b0;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							in_trap <= (in_trap || (trap_enter_vld && trap_enter_rdy))
 | 
				
			||||||
 | 
								&& !(trap_enter_vld && trap_enter_rdy && except == EXCEPT_MRET);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
always @ (posedge clk) begin
 | 
					always @ (posedge clk) begin
 | 
				
			||||||
	// We disallow double exceptions -- this causes riscv-formal to complain that
 | 
						// Assume there are no nested exceptions, to stop risc-formal from doing
 | 
				
			||||||
	// loads/stores don't trap inside of traps. Therefore assume this doesn't happen
 | 
						// annoying things like stopping instructions from retiring by repeatedly
 | 
				
			||||||
 | 
						// feeding in invalid instructions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (in_trap)
 | 
						if (in_trap)
 | 
				
			||||||
		assume(!(except_load_misaligned || except_store_misaligned));
 | 
							assume(except == EXCEPT_NONE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Something is screwed up if this happens
 | 
						// Something is screwed up if this happens
 | 
				
			||||||
	if ($past(trap_enter_vld && trap_enter_rdy))
 | 
						if ($past(trap_enter_vld && trap_enter_rdy))
 | 
				
			||||||
		assert(!wen);
 | 
							assert(!wen);
 | 
				
			||||||
	// Don't do this
 | 
					 | 
				
			||||||
	assert(!(trap_enter_vld && trap_enter_rdy && trap_exit));
 | 
					 | 
				
			||||||
	// Should be impossible to get into the trap and exit it so quickly:
 | 
						// Should be impossible to get into the trap and exit it so quickly:
 | 
				
			||||||
	if (in_trap && !$past(in_trap))
 | 
						if (in_trap && !$past(in_trap))
 | 
				
			||||||
		assert(!trap_exit);
 | 
							assert(except != EXCEPT_MRET);
 | 
				
			||||||
	// Should be impossible to get to another mret so soon after exiting:
 | 
						// Should be impossible to get to another mret so soon after exiting:
 | 
				
			||||||
	assert(!(trap_exit && $past(trap_exit)));
 | 
						assert(!(except == EXCEPT_MRET && $past(except == EXCEPT_MRET)));
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
`endif
 | 
					`endif
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -52,7 +52,7 @@ module hazard3_decode #(
 | 
				
			||||||
	output reg  [W_BCOND-1:0]   d_branchcond,
 | 
						output reg  [W_BCOND-1:0]   d_branchcond,
 | 
				
			||||||
	output reg  [W_ADDR-1:0]    d_jump_offs,
 | 
						output reg  [W_ADDR-1:0]    d_jump_offs,
 | 
				
			||||||
	output reg                  d_jump_is_regoffs,
 | 
						output reg                  d_jump_is_regoffs,
 | 
				
			||||||
	output reg  [2:0]           d_except
 | 
						output reg  [W_EXCEPT-1:0]  d_except
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
`include "rv_opcodes.vh"
 | 
					`include "rv_opcodes.vh"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -49,13 +49,6 @@ module hazard3_frontend #(
 | 
				
			||||||
	output wire              next_regs_vld
 | 
						output wire              next_regs_vld
 | 
				
			||||||
);
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
`undef ASSERT
 | 
					 | 
				
			||||||
`ifdef HAZARD3_FRONTEND_ASSERTIONS
 | 
					 | 
				
			||||||
`define ASSERT(x) assert(x);
 | 
					 | 
				
			||||||
`else
 | 
					 | 
				
			||||||
`define ASSERT(x)
 | 
					 | 
				
			||||||
`endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
localparam W_BUNDLE = W_DATA / 2;
 | 
					localparam W_BUNDLE = W_DATA / 2;
 | 
				
			||||||
parameter W_FIFO_LEVEL = $clog2(FIFO_DEPTH + 1);
 | 
					parameter W_FIFO_LEVEL = $clog2(FIFO_DEPTH + 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -124,10 +117,12 @@ always @ (posedge clk or negedge rst_n) begin
 | 
				
			||||||
		pending_fetches <= 2'h0;
 | 
							pending_fetches <= 2'h0;
 | 
				
			||||||
		ctr_flush_pending <= 2'h0;
 | 
							ctr_flush_pending <= 2'h0;
 | 
				
			||||||
	end else begin
 | 
						end else begin
 | 
				
			||||||
		`ASSERT(ctr_flush_pending <= pending_fetches)
 | 
					`ifdef FORMAL
 | 
				
			||||||
		`ASSERT(pending_fetches < 2'd3)
 | 
							assert(ctr_flush_pending <= pending_fetches);
 | 
				
			||||||
		`ASSERT(!(mem_data_vld && !pending_fetches))
 | 
							assert(pending_fetches < 2'd3);
 | 
				
			||||||
		// `ASSERT(!($past(mem_addr_hold) && $past(mem_addr_vld) && !$stable(mem_addr)))
 | 
							assert(!(mem_data_vld && !pending_fetches));
 | 
				
			||||||
 | 
							// assert(!($past(mem_addr_hold) && $past(mem_addr_vld) && !$stable(mem_addr)));
 | 
				
			||||||
 | 
					`endif
 | 
				
			||||||
		mem_addr_hold <= mem_addr_vld && !mem_addr_rdy;
 | 
							mem_addr_hold <= mem_addr_vld && !mem_addr_rdy;
 | 
				
			||||||
		pending_fetches <= pending_fetches_next;
 | 
							pending_fetches <= pending_fetches_next;
 | 
				
			||||||
		if (jump_now) begin
 | 
							if (jump_now) begin
 | 
				
			||||||
| 
						 | 
					@ -175,9 +170,11 @@ always @ (posedge clk or negedge rst_n) begin
 | 
				
			||||||
		unaligned_jump_aph <= 1'b0;
 | 
							unaligned_jump_aph <= 1'b0;
 | 
				
			||||||
		unaligned_jump_dph <= 1'b0;
 | 
							unaligned_jump_dph <= 1'b0;
 | 
				
			||||||
	end else if (EXTENSION_C) begin
 | 
						end else if (EXTENSION_C) begin
 | 
				
			||||||
		`ASSERT(!(unaligned_jump_aph && !unaligned_jump_dph))
 | 
					`ifdef FORMAL
 | 
				
			||||||
		`ASSERT(!($past(jump_now && !jump_target[1]) && unaligned_jump_aph))
 | 
							assert(!(unaligned_jump_aph && !unaligned_jump_dph));
 | 
				
			||||||
		`ASSERT(!($past(jump_now && !jump_target[1]) && unaligned_jump_dph))
 | 
							assert(!($past(jump_now && !jump_target[1]) && unaligned_jump_aph));
 | 
				
			||||||
 | 
							assert(!($past(jump_now && !jump_target[1]) && unaligned_jump_dph));
 | 
				
			||||||
 | 
					`endif
 | 
				
			||||||
		if (mem_addr_rdy || (jump_now && !unaligned_jump_now)) begin
 | 
							if (mem_addr_rdy || (jump_now && !unaligned_jump_now)) begin
 | 
				
			||||||
			unaligned_jump_aph <= 1'b0;
 | 
								unaligned_jump_aph <= 1'b0;
 | 
				
			||||||
		end
 | 
							end
 | 
				
			||||||
| 
						 | 
					@ -277,10 +274,12 @@ always @ (posedge clk or negedge rst_n) begin
 | 
				
			||||||
		hwbuf_vld <= 1'b0;
 | 
							hwbuf_vld <= 1'b0;
 | 
				
			||||||
		cir_vld <= 2'h0;
 | 
							cir_vld <= 2'h0;
 | 
				
			||||||
	end else begin
 | 
						end else begin
 | 
				
			||||||
		`ASSERT(cir_vld <= 2)
 | 
					`ifdef FORMAL
 | 
				
			||||||
		`ASSERT(cir_use <= 2)
 | 
							assert(cir_vld <= 2);
 | 
				
			||||||
		`ASSERT(cir_use <= cir_vld)
 | 
							assert(cir_use <= 2);
 | 
				
			||||||
		`ASSERT(cir_vld <= buf_level || $past(cir_lock))
 | 
							assert(cir_use <= cir_vld);
 | 
				
			||||||
 | 
							assert(cir_vld <= buf_level || $past(cir_lock));
 | 
				
			||||||
 | 
					`endif
 | 
				
			||||||
		// Update CIR flags
 | 
							// Update CIR flags
 | 
				
			||||||
		buf_level <= buf_level_next;
 | 
							buf_level <= buf_level_next;
 | 
				
			||||||
		hwbuf_vld <= &buf_level_next;
 | 
							hwbuf_vld <= &buf_level_next;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -46,14 +46,20 @@ localparam CSR_WTYPE_C    = 2'h2;
 | 
				
			||||||
// Exceptional condition signals which travel alongside (or instead of)
 | 
					// Exceptional condition signals which travel alongside (or instead of)
 | 
				
			||||||
// instructions in the pipeline. These are speculative and can be flushed
 | 
					// instructions in the pipeline. These are speculative and can be flushed
 | 
				
			||||||
// on e.g. branch mispredict
 | 
					// on e.g. branch mispredict
 | 
				
			||||||
 | 
					// These mostly align with mcause values.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
localparam EXCEPT_NONE           = 3'h0;
 | 
					localparam EXCEPT_NONE           = 4'hf;
 | 
				
			||||||
localparam EXCEPT_ECALL          = 3'h1;
 | 
					
 | 
				
			||||||
localparam EXCEPT_EBREAK         = 3'h2;
 | 
					localparam EXCEPT_INSTR_MISALIGN = 4'h0;
 | 
				
			||||||
localparam EXCEPT_MRET           = 3'h3; // separate, but handled similarly
 | 
					localparam EXCEPT_INSTR_FAULT    = 4'h1;
 | 
				
			||||||
localparam EXCEPT_INSTR_ILLEGAL  = 3'h4;
 | 
					localparam EXCEPT_INSTR_ILLEGAL  = 4'h2;
 | 
				
			||||||
localparam EXCEPT_INSTR_MISALIGN = 3'h5;
 | 
					localparam EXCEPT_EBREAK         = 4'h3;
 | 
				
			||||||
localparam EXCEPT_INSTR_FAULT    = 3'h6;
 | 
					localparam EXCEPT_LOAD_ALIGN     = 4'h4;
 | 
				
			||||||
 | 
					localparam EXCEPT_LOAD_FAULT     = 4'h5;
 | 
				
			||||||
 | 
					localparam EXCEPT_STORE_ALIGN    = 4'h6;
 | 
				
			||||||
 | 
					localparam EXCEPT_STORE_FAULT    = 4'h7;
 | 
				
			||||||
 | 
					localparam EXCEPT_MRET           = 4'ha; // Not really an exception, but handled like one
 | 
				
			||||||
 | 
					localparam EXCEPT_ECALL          = 4'hb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Operations for M extension (these are just instr[14:12])
 | 
					// Operations for M extension (these are just instr[14:12])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,5 +10,5 @@ parameter W_ALUSRC  = 2,
 | 
				
			||||||
parameter W_MEMOP   = 4,
 | 
					parameter W_MEMOP   = 4,
 | 
				
			||||||
parameter W_BCOND   = 2,
 | 
					parameter W_BCOND   = 2,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
parameter W_EXCEPT  = 3,
 | 
					parameter W_EXCEPT  = 4,
 | 
				
			||||||
parameter W_MULOP   = 3
 | 
					parameter W_MULOP   = 3
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					SRCS := ../common/init.S main.c
 | 
				
			||||||
 | 
					APP  := ecall_simple
 | 
				
			||||||
 | 
					CCFLAGS = -march=rv32ic
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					include ../common/src_only_app.mk
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,89 @@
 | 
				
			||||||
 | 
					[*]
 | 
				
			||||||
 | 
					[*] GTKWave Analyzer v3.3.103 (w)1999-2019 BSI
 | 
				
			||||||
 | 
					[*] Sat May 22 07:37:18 2021
 | 
				
			||||||
 | 
					[*]
 | 
				
			||||||
 | 
					[dumpfile] "/home/luke/proj/hazard3/test/ecall_simple/ecall_simple_run.vcd"
 | 
				
			||||||
 | 
					[dumpfile_mtime] "Sat May 22 07:33:26 2021"
 | 
				
			||||||
 | 
					[dumpfile_size] 1269546
 | 
				
			||||||
 | 
					[savefile] "/home/luke/proj/hazard3/test/ecall_simple/ecall_simple_run.gtkw"
 | 
				
			||||||
 | 
					[timestart] 314
 | 
				
			||||||
 | 
					[size] 2560 1403
 | 
				
			||||||
 | 
					[pos] -1 -1
 | 
				
			||||||
 | 
					*-2.000000 330 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
 | 
				
			||||||
 | 
					[treeopen] core.
 | 
				
			||||||
 | 
					[sst_width] 593
 | 
				
			||||||
 | 
					[signals_width] 214
 | 
				
			||||||
 | 
					[sst_expanded] 1
 | 
				
			||||||
 | 
					[sst_vpaned_height] 423
 | 
				
			||||||
 | 
					@200
 | 
				
			||||||
 | 
					-I Bus
 | 
				
			||||||
 | 
					@22
 | 
				
			||||||
 | 
					i_haddr[31:0]
 | 
				
			||||||
 | 
					@28
 | 
				
			||||||
 | 
					i_htrans[1:0]
 | 
				
			||||||
 | 
					i_hsize[2:0]
 | 
				
			||||||
 | 
					i_hready
 | 
				
			||||||
 | 
					@22
 | 
				
			||||||
 | 
					i_hrdata[31:0]
 | 
				
			||||||
 | 
					@200
 | 
				
			||||||
 | 
					-
 | 
				
			||||||
 | 
					-Frontend
 | 
				
			||||||
 | 
					@28
 | 
				
			||||||
 | 
					core.f_jump_req
 | 
				
			||||||
 | 
					core.f_jump_rdy
 | 
				
			||||||
 | 
					@22
 | 
				
			||||||
 | 
					core.frontend.jump_target[31:0]
 | 
				
			||||||
 | 
					@200
 | 
				
			||||||
 | 
					-
 | 
				
			||||||
 | 
					-PC/CIR
 | 
				
			||||||
 | 
					@28
 | 
				
			||||||
 | 
					core.inst_hazard3_decode.d_starved
 | 
				
			||||||
 | 
					@22
 | 
				
			||||||
 | 
					core.inst_hazard3_decode.pc[31:0]
 | 
				
			||||||
 | 
					core.frontend.cir[31:0]
 | 
				
			||||||
 | 
					@200
 | 
				
			||||||
 | 
					-
 | 
				
			||||||
 | 
					-Exceptions
 | 
				
			||||||
 | 
					@28
 | 
				
			||||||
 | 
					core.inst_hazard3_csr.except_breakpoint
 | 
				
			||||||
 | 
					core.inst_hazard3_csr.except_ecall
 | 
				
			||||||
 | 
					core.inst_hazard3_csr.except_instr_invalid
 | 
				
			||||||
 | 
					core.inst_hazard3_csr.except_load_misaligned
 | 
				
			||||||
 | 
					core.inst_hazard3_csr.except_store_misaligned
 | 
				
			||||||
 | 
					core.inst_hazard3_csr.exception_req_any
 | 
				
			||||||
 | 
					@22
 | 
				
			||||||
 | 
					core.inst_hazard3_csr.exception_req_num[3:0]
 | 
				
			||||||
 | 
					@29
 | 
				
			||||||
 | 
					core.inst_hazard3_csr.trap_enter_rdy
 | 
				
			||||||
 | 
					core.inst_hazard3_csr.trap_enter_vld
 | 
				
			||||||
 | 
					@200
 | 
				
			||||||
 | 
					-
 | 
				
			||||||
 | 
					-Reg Read
 | 
				
			||||||
 | 
					@22
 | 
				
			||||||
 | 
					core.f_rs1[4:0]
 | 
				
			||||||
 | 
					core.f_rs2[4:0]
 | 
				
			||||||
 | 
					core.d_rs1[4:0]
 | 
				
			||||||
 | 
					core.d_rs2[4:0]
 | 
				
			||||||
 | 
					core.inst_regfile_1w2r.rdata1[31:0]
 | 
				
			||||||
 | 
					core.inst_regfile_1w2r.rdata2[31:0]
 | 
				
			||||||
 | 
					@200
 | 
				
			||||||
 | 
					-
 | 
				
			||||||
 | 
					-Reg Write
 | 
				
			||||||
 | 
					@22
 | 
				
			||||||
 | 
					core.xm_rd[4:0]
 | 
				
			||||||
 | 
					core.xm_result[31:0]
 | 
				
			||||||
 | 
					@200
 | 
				
			||||||
 | 
					-
 | 
				
			||||||
 | 
					-D Bus
 | 
				
			||||||
 | 
					@22
 | 
				
			||||||
 | 
					d_haddr[31:0]
 | 
				
			||||||
 | 
					@28
 | 
				
			||||||
 | 
					d_htrans[1:0]
 | 
				
			||||||
 | 
					d_hsize[2:0]
 | 
				
			||||||
 | 
					d_hwrite
 | 
				
			||||||
 | 
					d_hready
 | 
				
			||||||
 | 
					@22
 | 
				
			||||||
 | 
					d_hwdata[31:0]
 | 
				
			||||||
 | 
					d_hrdata[31:0]
 | 
				
			||||||
 | 
					[pattern_trace] 1
 | 
				
			||||||
 | 
					[pattern_trace] 0
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,37 @@
 | 
				
			||||||
 | 
					#include "tb_cxxrtl_io.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define read_csr(csrname) ({ \
 | 
				
			||||||
 | 
					  uint32_t __csr_tmp_u32; \
 | 
				
			||||||
 | 
					  __asm__ ("csrr %0, " #csrname : "=r" (__csr_tmp_u32)); \
 | 
				
			||||||
 | 
					  __csr_tmp_u32; \
 | 
				
			||||||
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define write_csr(csrname, val) __asm__ ("csrw " #csrname ", %0" : : "r" (val))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void __attribute__((interrupt)) handle_ecall() {
 | 
				
			||||||
 | 
						uint32_t call_num;
 | 
				
			||||||
 | 
						asm volatile ("mv %0, a7" : "=r" (call_num));
 | 
				
			||||||
 | 
						tb_puts("Handling ecall. Call number:\n");
 | 
				
			||||||
 | 
						tb_put_u32(call_num);
 | 
				
			||||||
 | 
						write_csr(mepc, read_csr(mepc) + 4);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline void make_ecall(uint32_t call) {
 | 
				
			||||||
 | 
						asm volatile ("mv a7, %0 \n ecall" : : "r" (call));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const uint32_t call_nums[] = {
 | 
				
			||||||
 | 
						0x123,
 | 
				
			||||||
 | 
						0x456,
 | 
				
			||||||
 | 
						0xdeadbeef
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void main() {
 | 
				
			||||||
 | 
						for (int i = 0; i < sizeof(call_nums) / sizeof(*call_nums); ++i)
 | 
				
			||||||
 | 
							make_ecall(call_nums[i]);
 | 
				
			||||||
 | 
						tb_puts("Finished making calls.\n");
 | 
				
			||||||
 | 
						tb_exit(0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -12,7 +12,7 @@ REDUCED_BYPASS   := 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
all: tb
 | 
					all: tb
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SYNTH_CMD += read_verilog -I ../../hdl $(shell listfiles ../../hdl/hazard3.f);
 | 
					SYNTH_CMD += read_verilog -I ../../../hdl $(shell listfiles ../../../hdl/hazard3.f);
 | 
				
			||||||
SYNTH_CMD += chparam -set EXTENSION_C $(EXTENSION_C) $(TOP);
 | 
					SYNTH_CMD += chparam -set EXTENSION_C $(EXTENSION_C) $(TOP);
 | 
				
			||||||
SYNTH_CMD += chparam -set EXTENSION_M $(EXTENSION_M) $(TOP);
 | 
					SYNTH_CMD += chparam -set EXTENSION_M $(EXTENSION_M) $(TOP);
 | 
				
			||||||
SYNTH_CMD += chparam -set CSR_COUNTER 1 $(TOP);
 | 
					SYNTH_CMD += chparam -set CSR_COUNTER 1 $(TOP);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue