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
 | 
			
		||||
- Debug
 | 
			
		||||
- 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"
 | 
			
		||||
 | 
			
		||||
`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 x_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                 x_alu_cmp;
 | 
			
		||||
 | 
			
		||||
wire [W_DATA-1:0]    x_trap_addr;
 | 
			
		||||
wire [W_DATA-1:0]    x_mepc;
 | 
			
		||||
wire                 x_trap_enter;
 | 
			
		||||
wire                 x_trap_exit;
 | 
			
		||||
wire [W_DATA-1:0]    m_trap_addr;
 | 
			
		||||
wire                 m_trap_enter_vld;
 | 
			
		||||
wire                 m_trap_enter_rdy = f_jump_rdy;
 | 
			
		||||
 | 
			
		||||
reg  [W_REGADDR-1:0] xm_rs1;
 | 
			
		||||
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_store_data;
 | 
			
		||||
reg  [W_MEMOP-1:0]   xm_memop;
 | 
			
		||||
reg  [W_EXCEPT-1:0]  xm_except;
 | 
			
		||||
 | 
			
		||||
reg x_stall_raw;
 | 
			
		||||
wire x_stall_muldiv;
 | 
			
		||||
wire x_jump_req;
 | 
			
		||||
 | 
			
		||||
assign x_stall =
 | 
			
		||||
	m_stall ||
 | 
			
		||||
	x_stall_raw || x_stall_muldiv ||
 | 
			
		||||
	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_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_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
 | 
			
		||||
	// Need to be careful not to use anything hready-sourced to gate htrans!
 | 
			
		||||
	bus_haddr_d = x_alu_add;
 | 
			
		||||
| 
						 | 
				
			
			@ -343,70 +326,9 @@ always @ (*) begin
 | 
			
		|||
		MEMOP_SH:  bus_hsize_d = HSIZE_HWORD;
 | 
			
		||||
		default:   bus_hsize_d = HSIZE_BYTE;
 | 
			
		||||
	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
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
 | 
			
		||||
wire [W_DATA-1:0] x_muldiv_result;
 | 
			
		||||
| 
						 | 
				
			
			@ -427,7 +349,7 @@ if (EXTENSION_M) begin: has_muldiv
 | 
			
		|||
		else
 | 
			
		||||
			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;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -501,21 +423,86 @@ end else begin: no_muldiv
 | 
			
		|||
end
 | 
			
		||||
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
 | 
			
		||||
	if (!rst_n) begin
 | 
			
		||||
		xm_memop <= MEMOP_NONE;
 | 
			
		||||
		xm_except <= EXCEPT_NONE;
 | 
			
		||||
		{xm_rs1, xm_rs2, xm_rd} <= {3 * W_REGADDR{1'b0}};
 | 
			
		||||
	end else begin
 | 
			
		||||
		if (!m_stall) begin
 | 
			
		||||
			{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
 | 
			
		||||
			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
 | 
			
		||||
				xm_rd <= {W_REGADDR{1'b0}};
 | 
			
		||||
				xm_memop <= MEMOP_NONE;
 | 
			
		||||
				xm_except <= EXCEPT_NONE;
 | 
			
		||||
			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
 | 
			
		||||
| 
						 | 
				
			
			@ -533,25 +520,15 @@ always @ (posedge clk)
 | 
			
		|||
// Branch handling
 | 
			
		||||
 | 
			
		||||
// 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 =
 | 
			
		||||
	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;
 | 
			
		||||
wire [W_ADDR-1:0] x_jump_target = ((d_jump_is_regoffs ? x_rs1_bypass : d_pc) + d_jump_offs) & ~32'h1;
 | 
			
		||||
 | 
			
		||||
// 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_ZERO && !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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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_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
 | 
			
		||||
	// Local forwarding of store data
 | 
			
		||||
| 
						 | 
				
			
			@ -607,11 +586,6 @@ always @ (posedge clk or negedge rst_n) begin
 | 
			
		|||
		mw_rd <= {W_REGADDR{1'b0}};
 | 
			
		||||
	end else if (!m_stall) begin
 | 
			
		||||
		//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
 | 
			
		||||
			$display("Writing Xs to memory!");
 | 
			
		||||
			$finish;
 | 
			
		||||
| 
						 | 
				
			
			@ -632,7 +606,7 @@ always @ (posedge clk)
 | 
			
		|||
// mw_result and mw_rd register the most recent write to the register file,
 | 
			
		||||
// 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
 | 
			
		||||
always @ (posedge clk) begin
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,6 +25,8 @@ module hazard3_csr #(
 | 
			
		|||
	                                  // The full 64 bits is writeable, so high-word increment can
 | 
			
		||||
	                                  // be implemented in software, and a narrower hw counter used
 | 
			
		||||
`include "hazard3_config.vh"
 | 
			
		||||
,
 | 
			
		||||
`include "hazard3_width_const.vh"
 | 
			
		||||
) (
 | 
			
		||||
	input  wire            clk,
 | 
			
		||||
	input  wire            rst_n,
 | 
			
		||||
| 
						 | 
				
			
			@ -37,14 +39,15 @@ module hazard3_csr #(
 | 
			
		|||
	// - Illegal CSR accesses produce trap entry
 | 
			
		||||
	// - Trap entry (not necessarily caused by CSR access) gates outgoing bus accesses
 | 
			
		||||
	// - Through-paths from e.g. hready to htrans are problematic for timing/implementation
 | 
			
		||||
	input  wire [11:0]     addr,
 | 
			
		||||
	input  wire [XLEN-1:0] wdata,
 | 
			
		||||
	input  wire            wen,
 | 
			
		||||
	input  wire            wen_soon, // wen will be asserted once some stall condition clears
 | 
			
		||||
	input  wire [1:0]      wtype,
 | 
			
		||||
	output reg  [XLEN-1:0] rdata,
 | 
			
		||||
	input  wire            ren,
 | 
			
		||||
	input  wire            ren_soon, // ren will be asserted once some stall condition clears
 | 
			
		||||
	input  wire [11:0]        addr,
 | 
			
		||||
	input  wire [XLEN-1:0]    wdata,
 | 
			
		||||
	input  wire               wen,
 | 
			
		||||
	input  wire               wen_soon, // wen will be asserted once some stall condition clears
 | 
			
		||||
	input  wire [1:0]         wtype,
 | 
			
		||||
	output reg  [XLEN-1:0]    rdata,
 | 
			
		||||
	input  wire               ren,
 | 
			
		||||
	input  wire               ren_soon, // ren will be asserted once some stall condition clears
 | 
			
		||||
	output wire               illegal,
 | 
			
		||||
 | 
			
		||||
	// Trap signalling
 | 
			
		||||
	// *We* tell the core that we are taking a trap, and where to, based on:
 | 
			
		||||
| 
						 | 
				
			
			@ -58,32 +61,17 @@ module hazard3_csr #(
 | 
			
		|||
	//
 | 
			
		||||
	// Note that an exception input can go away, e.g. if the pipe gets flushed. In this
 | 
			
		||||
	// 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            trap_enter_vld,
 | 
			
		||||
	input  wire            trap_enter_rdy,
 | 
			
		||||
	input  wire            trap_exit,
 | 
			
		||||
	output wire            trap_is_exception, // diagnostic
 | 
			
		||||
	input  wire [XLEN-1:0] mepc_in,
 | 
			
		||||
	output wire [XLEN-1:0] mepc_out,
 | 
			
		||||
	output wire [XLEN-1:0]     trap_addr,
 | 
			
		||||
	output wire                trap_enter_vld,
 | 
			
		||||
	input  wire                trap_enter_rdy,
 | 
			
		||||
	input  wire [XLEN-1:0]     mepc_in,
 | 
			
		||||
 | 
			
		||||
	// Exceptions must *not* be a function of bus stall.
 | 
			
		||||
	input wire  [15:0]     irq,
 | 
			
		||||
	input wire             except_instr_misaligned,
 | 
			
		||||
	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,
 | 
			
		||||
	input  wire [15:0]         irq,
 | 
			
		||||
	input  wire [W_EXCEPT-1:0] except,
 | 
			
		||||
 | 
			
		||||
	// Other CSR-specific signalling
 | 
			
		||||
	input  wire            instr_ret
 | 
			
		||||
	input  wire                instr_ret
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
// TODO block CSR access when entering trap?
 | 
			
		||||
| 
						 | 
				
			
			@ -262,11 +250,13 @@ always @ (posedge clk or negedge rst_n) begin
 | 
			
		|||
		mstatus_mie <= 1'b0;
 | 
			
		||||
	end else if (CSR_M_TRAP) begin
 | 
			
		||||
		if (trap_enter_vld && trap_enter_rdy) begin
 | 
			
		||||
			mstatus_mpie <= mstatus_mie;
 | 
			
		||||
			mstatus_mie <= 1'b0;
 | 
			
		||||
		end else if (trap_exit) begin
 | 
			
		||||
			mstatus_mpie <= 1'b1;
 | 
			
		||||
			mstatus_mie <= mstatus_mpie;
 | 
			
		||||
			if (except == EXCEPT_MRET) begin
 | 
			
		||||
				mstatus_mpie <= 1'b1;
 | 
			
		||||
				mstatus_mie <= mstatus_mpie;
 | 
			
		||||
			end else begin
 | 
			
		||||
				mstatus_mpie <= mstatus_mie;
 | 
			
		||||
				mstatus_mie <= 1'b0;
 | 
			
		||||
			end
 | 
			
		||||
		end else if (wen && addr == MSTATUS) begin
 | 
			
		||||
			{mstatus_mpie, mstatus_mie} <=
 | 
			
		||||
				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
 | 
			
		||||
		mepc <= X0;
 | 
			
		||||
	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;
 | 
			
		||||
		end else if (wen && addr == MEPC) begin
 | 
			
		||||
			mepc <= update(mepc) & MEPC_MASK;
 | 
			
		||||
| 
						 | 
				
			
			@ -354,7 +344,7 @@ always @ (posedge clk or negedge rst_n) begin
 | 
			
		|||
		mcause_irq <= 1'b0;
 | 
			
		||||
		mcause_code <= 5'h0;
 | 
			
		||||
	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_code <= mcause_code_next;
 | 
			
		||||
		end else if (wen && addr == MCAUSE) begin
 | 
			
		||||
| 
						 | 
				
			
			@ -657,49 +647,13 @@ always @ (*) begin
 | 
			
		|||
	endcase
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
wire csr_access_error = (wen_soon || ren_soon) && !decode_match;
 | 
			
		||||
assign illegal = (wen_soon || ren_soon) && !decode_match;
 | 
			
		||||
 | 
			
		||||
// ----------------------------------------------------------------------------
 | 
			
		||||
// Trap request generation
 | 
			
		||||
// ----------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
// Keep track of whether we are in a trap; we do not permit exception nesting.
 | 
			
		||||
// 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)
 | 
			
		||||
);
 | 
			
		||||
wire exception_req_any = except != EXCEPT_NONE;
 | 
			
		||||
 | 
			
		||||
// Interrupt masking and selection
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -735,13 +689,12 @@ hazard3_priority_encode #(
 | 
			
		|||
);
 | 
			
		||||
 | 
			
		||||
wire [11:0] mtvec_offs = (exception_req_any ?
 | 
			
		||||
	{8'h0, exception_req_num} :
 | 
			
		||||
	{8'h0, except} :
 | 
			
		||||
	12'h10 + {7'h0, irq_num}
 | 
			
		||||
) << 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_is_exception = exception_req_any;
 | 
			
		||||
 | 
			
		||||
assign mcause_irq_next = !exception_req_any;
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
	// We disallow double exceptions -- this causes riscv-formal to complain that
 | 
			
		||||
	// loads/stores don't trap inside of traps. Therefore assume this doesn't happen
 | 
			
		||||
	// Assume there are no nested exceptions, to stop risc-formal from doing
 | 
			
		||||
	// annoying things like stopping instructions from retiring by repeatedly
 | 
			
		||||
	// feeding in invalid instructions
 | 
			
		||||
 | 
			
		||||
	if (in_trap)
 | 
			
		||||
		assume(!(except_load_misaligned || except_store_misaligned));
 | 
			
		||||
		assume(except == EXCEPT_NONE);
 | 
			
		||||
 | 
			
		||||
	// Something is screwed up if this happens
 | 
			
		||||
	if ($past(trap_enter_vld && trap_enter_rdy))
 | 
			
		||||
		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:
 | 
			
		||||
	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:
 | 
			
		||||
	assert(!(trap_exit && $past(trap_exit)));
 | 
			
		||||
	assert(!(except == EXCEPT_MRET && $past(except == EXCEPT_MRET)));
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
`endif
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,7 +52,7 @@ module hazard3_decode #(
 | 
			
		|||
	output reg  [W_BCOND-1:0]   d_branchcond,
 | 
			
		||||
	output reg  [W_ADDR-1:0]    d_jump_offs,
 | 
			
		||||
	output reg                  d_jump_is_regoffs,
 | 
			
		||||
	output reg  [2:0]           d_except
 | 
			
		||||
	output reg  [W_EXCEPT-1:0]  d_except
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
`include "rv_opcodes.vh"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -49,13 +49,6 @@ module hazard3_frontend #(
 | 
			
		|||
	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;
 | 
			
		||||
parameter W_FIFO_LEVEL = $clog2(FIFO_DEPTH + 1);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -124,10 +117,12 @@ always @ (posedge clk or negedge rst_n) begin
 | 
			
		|||
		pending_fetches <= 2'h0;
 | 
			
		||||
		ctr_flush_pending <= 2'h0;
 | 
			
		||||
	end else begin
 | 
			
		||||
		`ASSERT(ctr_flush_pending <= pending_fetches)
 | 
			
		||||
		`ASSERT(pending_fetches < 2'd3)
 | 
			
		||||
		`ASSERT(!(mem_data_vld && !pending_fetches))
 | 
			
		||||
		// `ASSERT(!($past(mem_addr_hold) && $past(mem_addr_vld) && !$stable(mem_addr)))
 | 
			
		||||
`ifdef FORMAL
 | 
			
		||||
		assert(ctr_flush_pending <= pending_fetches);
 | 
			
		||||
		assert(pending_fetches < 2'd3);
 | 
			
		||||
		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;
 | 
			
		||||
		pending_fetches <= pending_fetches_next;
 | 
			
		||||
		if (jump_now) begin
 | 
			
		||||
| 
						 | 
				
			
			@ -175,9 +170,11 @@ always @ (posedge clk or negedge rst_n) begin
 | 
			
		|||
		unaligned_jump_aph <= 1'b0;
 | 
			
		||||
		unaligned_jump_dph <= 1'b0;
 | 
			
		||||
	end else if (EXTENSION_C) begin
 | 
			
		||||
		`ASSERT(!(unaligned_jump_aph && !unaligned_jump_dph))
 | 
			
		||||
		`ASSERT(!($past(jump_now && !jump_target[1]) && unaligned_jump_aph))
 | 
			
		||||
		`ASSERT(!($past(jump_now && !jump_target[1]) && unaligned_jump_dph))
 | 
			
		||||
`ifdef FORMAL
 | 
			
		||||
		assert(!(unaligned_jump_aph && !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
 | 
			
		||||
			unaligned_jump_aph <= 1'b0;
 | 
			
		||||
		end
 | 
			
		||||
| 
						 | 
				
			
			@ -277,10 +274,12 @@ always @ (posedge clk or negedge rst_n) begin
 | 
			
		|||
		hwbuf_vld <= 1'b0;
 | 
			
		||||
		cir_vld <= 2'h0;
 | 
			
		||||
	end else begin
 | 
			
		||||
		`ASSERT(cir_vld <= 2)
 | 
			
		||||
		`ASSERT(cir_use <= 2)
 | 
			
		||||
		`ASSERT(cir_use <= cir_vld)
 | 
			
		||||
		`ASSERT(cir_vld <= buf_level || $past(cir_lock))
 | 
			
		||||
`ifdef FORMAL
 | 
			
		||||
		assert(cir_vld <= 2);
 | 
			
		||||
		assert(cir_use <= 2);
 | 
			
		||||
		assert(cir_use <= cir_vld);
 | 
			
		||||
		assert(cir_vld <= buf_level || $past(cir_lock));
 | 
			
		||||
`endif
 | 
			
		||||
		// Update CIR flags
 | 
			
		||||
		buf_level <= 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)
 | 
			
		||||
// instructions in the pipeline. These are speculative and can be flushed
 | 
			
		||||
// on e.g. branch mispredict
 | 
			
		||||
// These mostly align with mcause values.
 | 
			
		||||
 | 
			
		||||
localparam EXCEPT_NONE           = 3'h0;
 | 
			
		||||
localparam EXCEPT_ECALL          = 3'h1;
 | 
			
		||||
localparam EXCEPT_EBREAK         = 3'h2;
 | 
			
		||||
localparam EXCEPT_MRET           = 3'h3; // separate, but handled similarly
 | 
			
		||||
localparam EXCEPT_INSTR_ILLEGAL  = 3'h4;
 | 
			
		||||
localparam EXCEPT_INSTR_MISALIGN = 3'h5;
 | 
			
		||||
localparam EXCEPT_INSTR_FAULT    = 3'h6;
 | 
			
		||||
localparam EXCEPT_NONE           = 4'hf;
 | 
			
		||||
 | 
			
		||||
localparam EXCEPT_INSTR_MISALIGN = 4'h0;
 | 
			
		||||
localparam EXCEPT_INSTR_FAULT    = 4'h1;
 | 
			
		||||
localparam EXCEPT_INSTR_ILLEGAL  = 4'h2;
 | 
			
		||||
localparam EXCEPT_EBREAK         = 4'h3;
 | 
			
		||||
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])
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,5 +10,5 @@ parameter W_ALUSRC  = 2,
 | 
			
		|||
parameter W_MEMOP   = 4,
 | 
			
		||||
parameter W_BCOND   = 2,
 | 
			
		||||
 | 
			
		||||
parameter W_EXCEPT  = 3,
 | 
			
		||||
parameter W_EXCEPT  = 4,
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
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_M $(EXTENSION_M) $(TOP);
 | 
			
		||||
SYNTH_CMD += chparam -set CSR_COUNTER 1 $(TOP);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue