First attempt at hacking in triggers, at least seems to have not broken other exception logic. Not yet tested.
This commit is contained in:
		
							parent
							
								
									5d6b5a80b0
								
							
						
					
					
						commit
						6e3799eed0
					
				|  | @ -16,4 +16,5 @@ file hazard3_decode.v | |||
| file hazard3_csr.v | ||||
| file hazard3_regfile_1w2r.v | ||||
| file hazard3_pmp.v | ||||
| file hazard3_triggers.v | ||||
| include . | ||||
|  |  | |||
|  | @ -120,6 +120,10 @@ parameter PMP_HARDWIRED_CFG   = PMP_REGIONS > 0 ? {PMP_REGIONS{8'h00}} : 1'b0, | |||
| // Requires: CSR_M_MANDATORY, CSR_M_TRAP. | ||||
| parameter DEBUG_SUPPORT       = 0, | ||||
| 
 | ||||
| // BREAKPOINT_TRIGGERS: Number of triggers which support type=2 execute=1 | ||||
| // (but not store/load=1, i.e. not a watchpoint). Requires: DEBUG_SUPPORT | ||||
| parameter BREAKPOINT_TRIGGERS = 0, | ||||
| 
 | ||||
| // ---------------------------------------------------------------------------- | ||||
| // External interrupt support | ||||
| 
 | ||||
|  |  | |||
|  | @ -12,47 +12,48 @@ | |||
| // except for MHARTID_VAL. It must be defined once before each include of | ||||
| // this file. | ||||
| 
 | ||||
| .RESET_VECTOR       (RESET_VECTOR), | ||||
| .MTVEC_INIT         (MTVEC_INIT), | ||||
| .EXTENSION_A        (EXTENSION_A), | ||||
| .EXTENSION_C        (EXTENSION_C), | ||||
| .EXTENSION_M        (EXTENSION_M), | ||||
| .EXTENSION_ZBA      (EXTENSION_ZBA), | ||||
| .EXTENSION_ZBB      (EXTENSION_ZBB), | ||||
| .EXTENSION_ZBC      (EXTENSION_ZBC), | ||||
| .EXTENSION_ZBS      (EXTENSION_ZBS), | ||||
| .EXTENSION_ZBKB     (EXTENSION_ZBKB), | ||||
| .EXTENSION_XH3B     (EXTENSION_XH3B), | ||||
| .EXTENSION_ZIFENCEI (EXTENSION_ZIFENCEI), | ||||
| .CSR_M_MANDATORY    (CSR_M_MANDATORY), | ||||
| .CSR_M_TRAP         (CSR_M_TRAP), | ||||
| .CSR_COUNTER        (CSR_COUNTER), | ||||
| .U_MODE             (U_MODE), | ||||
| .PMP_REGIONS        (PMP_REGIONS), | ||||
| .PMP_GRAIN          (PMP_GRAIN), | ||||
| .PMP_HARDWIRED      (PMP_HARDWIRED), | ||||
| .PMP_HARDWIRED_ADDR (PMP_HARDWIRED_ADDR), | ||||
| .PMP_HARDWIRED_CFG  (PMP_HARDWIRED_CFG), | ||||
| .DEBUG_SUPPORT      (DEBUG_SUPPORT), | ||||
| .NUM_IRQS           (NUM_IRQS), | ||||
| .IRQ_PRIORITY_BITS  (IRQ_PRIORITY_BITS), | ||||
| .MVENDORID_VAL      (MVENDORID_VAL), | ||||
| .MIMPID_VAL         (MIMPID_VAL), | ||||
| .RESET_VECTOR        (RESET_VECTOR), | ||||
| .MTVEC_INIT          (MTVEC_INIT), | ||||
| .EXTENSION_A         (EXTENSION_A), | ||||
| .EXTENSION_C         (EXTENSION_C), | ||||
| .EXTENSION_M         (EXTENSION_M), | ||||
| .EXTENSION_ZBA       (EXTENSION_ZBA), | ||||
| .EXTENSION_ZBB       (EXTENSION_ZBB), | ||||
| .EXTENSION_ZBC       (EXTENSION_ZBC), | ||||
| .EXTENSION_ZBS       (EXTENSION_ZBS), | ||||
| .EXTENSION_ZBKB      (EXTENSION_ZBKB), | ||||
| .EXTENSION_XH3B      (EXTENSION_XH3B), | ||||
| .EXTENSION_ZIFENCEI  (EXTENSION_ZIFENCEI), | ||||
| .CSR_M_MANDATORY     (CSR_M_MANDATORY), | ||||
| .CSR_M_TRAP          (CSR_M_TRAP), | ||||
| .CSR_COUNTER         (CSR_COUNTER), | ||||
| .U_MODE              (U_MODE), | ||||
| .PMP_REGIONS         (PMP_REGIONS), | ||||
| .PMP_GRAIN           (PMP_GRAIN), | ||||
| .PMP_HARDWIRED       (PMP_HARDWIRED), | ||||
| .PMP_HARDWIRED_ADDR  (PMP_HARDWIRED_ADDR), | ||||
| .PMP_HARDWIRED_CFG   (PMP_HARDWIRED_CFG), | ||||
| .DEBUG_SUPPORT       (DEBUG_SUPPORT), | ||||
| .BREAKPOINT_TRIGGERS (BREAKPOINT_TRIGGERS), | ||||
| .NUM_IRQS            (NUM_IRQS), | ||||
| .IRQ_PRIORITY_BITS   (IRQ_PRIORITY_BITS), | ||||
| .MVENDORID_VAL       (MVENDORID_VAL), | ||||
| .MIMPID_VAL          (MIMPID_VAL), | ||||
| `ifndef HAZARD3_CONFIG_INST_NO_MHARTID | ||||
| .MHARTID_VAL        (MHARTID_VAL), | ||||
| .MHARTID_VAL         (MHARTID_VAL), | ||||
| `endif | ||||
| .MCONFIGPTR_VAL     (MCONFIGPTR_VAL), | ||||
| .REDUCED_BYPASS     (REDUCED_BYPASS), | ||||
| .MULDIV_UNROLL      (MULDIV_UNROLL), | ||||
| .MUL_FAST           (MUL_FAST), | ||||
| .MUL_FASTER         (MUL_FASTER), | ||||
| .MULH_FAST          (MULH_FAST), | ||||
| .FAST_BRANCHCMP     (FAST_BRANCHCMP), | ||||
| .BRANCH_PREDICTOR   (BRANCH_PREDICTOR), | ||||
| .MTVEC_WMASK        (MTVEC_WMASK), | ||||
| .RESET_REGFILE      (RESET_REGFILE), | ||||
| .W_ADDR             (W_ADDR), | ||||
| .W_DATA             (W_DATA) | ||||
| .MCONFIGPTR_VAL      (MCONFIGPTR_VAL), | ||||
| .REDUCED_BYPASS      (REDUCED_BYPASS), | ||||
| .MULDIV_UNROLL       (MULDIV_UNROLL), | ||||
| .MUL_FAST            (MUL_FAST), | ||||
| .MUL_FASTER          (MUL_FASTER), | ||||
| .MULH_FAST           (MULH_FAST), | ||||
| .FAST_BRANCHCMP      (FAST_BRANCHCMP), | ||||
| .BRANCH_PREDICTOR    (BRANCH_PREDICTOR), | ||||
| .MTVEC_WMASK         (MTVEC_WMASK), | ||||
| .RESET_REGFILE       (RESET_REGFILE), | ||||
| .W_ADDR              (W_ADDR), | ||||
| .W_DATA              (W_DATA) | ||||
| 
 | ||||
| `ifdef HAZARD3_CONFIG_INST_NO_MHARTID | ||||
| `undef HAZARD3_CONFIG_INST_NO_MHARTID | ||||
|  |  | |||
|  | @ -296,6 +296,7 @@ reg  [W_DATA-1:0]    xm_result; | |||
| reg  [1:0]           xm_addr_align; | ||||
| reg  [W_MEMOP-1:0]   xm_memop; | ||||
| reg  [W_EXCEPT-1:0]  xm_except; | ||||
| reg                  xm_except_to_d_mode; | ||||
| reg                  xm_wfi; | ||||
| reg                  xm_delay_irq_entry; | ||||
| 
 | ||||
|  | @ -377,6 +378,11 @@ assign x_stall = | |||
| 
 | ||||
| wire m_wfi_stall_clear; | ||||
| 
 | ||||
| wire x_loadstore_pmp_fail; | ||||
| wire x_exec_pmp_fail; | ||||
| wire x_trig_break; | ||||
| wire x_trig_break_d_mode; | ||||
| 
 | ||||
| // ---------------------------------------------------------------------------- | ||||
| // Execution logic | ||||
| 
 | ||||
|  | @ -465,9 +471,6 @@ hazard3_alu #( | |||
| 
 | ||||
| // Load/store bus request | ||||
| 
 | ||||
| wire x_loadstore_pmp_fail; | ||||
| wire x_exec_pmp_fail; | ||||
| 
 | ||||
| wire x_unaligned_addr = d_memop != MEMOP_NONE && ( | ||||
| 	bus_hsize_d == HSIZE_WORD && |bus_haddr_d[1:0] || | ||||
| 	bus_hsize_d == HSIZE_HWORD && bus_haddr_d[0] | ||||
|  | @ -608,6 +611,7 @@ always @ (*) begin | |||
| 		x_stall_on_exclusive_overlap || | ||||
| 		x_loadstore_pmp_fail || | ||||
| 		x_exec_pmp_fail || | ||||
| 		x_trig_break || | ||||
| 		x_unaligned_addr || | ||||
| 		m_trap_enter_soon || | ||||
| 		(xm_wfi && !m_wfi_stall_clear) // FIXME will cause a timing issue, better to stall til *after* clear | ||||
|  | @ -750,7 +754,7 @@ wire x_jump_req_unchecked = !x_stall_on_raw && ( | |||
| 	d_branchcond == BCOND_NZERO && x_branch_cmp | ||||
| ); | ||||
| 
 | ||||
| assign x_jump_req = x_jump_req_unchecked && !x_jump_misaligned && !x_exec_pmp_fail; | ||||
| assign x_jump_req = x_jump_req_unchecked && !x_jump_misaligned && !x_exec_pmp_fail || x_trig_break; | ||||
| 
 | ||||
| assign x_btb_set = |BRANCH_PREDICTOR && ( | ||||
| 	x_jump_req_unchecked && d_addr_offs[W_ADDR - 1] && !x_branch_was_predicted && | ||||
|  | @ -809,6 +813,46 @@ end else begin: no_pmp | |||
| end | ||||
| endgenerate | ||||
| 
 | ||||
| // Triggers | ||||
| 
 | ||||
| wire [11:0]       x_trig_cfg_addr; | ||||
| wire              x_trig_cfg_wen; | ||||
| wire [W_DATA-1:0] x_trig_cfg_wdata; | ||||
| wire [W_DATA-1:0] x_trig_cfg_rdata; | ||||
| wire              x_trig_m_en; | ||||
| 
 | ||||
| generate | ||||
| if (BREAKPOINT_TRIGGERS > 0) begin: have_triggers | ||||
| 
 | ||||
| 	hazard3_triggers #( | ||||
| 	`include "hazard3_config_inst.vh" | ||||
| 	) triggers ( | ||||
| 		.clk              (clk), | ||||
| 		.rst_n            (rst_n), | ||||
| 
 | ||||
| 		.cfg_addr         (x_trig_cfg_addr), | ||||
| 		.cfg_wen          (x_trig_cfg_wen), | ||||
| 		.cfg_wdata        (x_trig_cfg_wdata), | ||||
| 		.cfg_rdata        (x_trig_cfg_rdata), | ||||
| 		.trig_m_en        (x_trig_m_en), | ||||
| 
 | ||||
| 		.pc               (d_pc), | ||||
| 		.m_mode           (x_mmode_execution), | ||||
| 		.d_mode           (debug_mode), | ||||
| 
 | ||||
| 		.break            (x_trig_break), | ||||
| 		.break_d_mode     (x_trig_break_d_mode) | ||||
| 	); | ||||
| 
 | ||||
| end else begin: no_triggers | ||||
| 
 | ||||
| 	assign x_trig_cfg_rdata = {W_DATA{1'b0}}; | ||||
| 	assign x_trig_break = 1'b0; | ||||
| 	assign x_trig_break_d_mode = 1'b0; | ||||
| 
 | ||||
| end | ||||
| endgenerate | ||||
| 
 | ||||
| // CSRs and Trap Handling | ||||
| 
 | ||||
| wire [W_DATA-1:0] x_csr_wdata = d_csr_w_imm ? | ||||
|  | @ -850,6 +894,7 @@ end | |||
| wire [W_ADDR-1:0] m_exception_return_addr; | ||||
| 
 | ||||
| wire [W_EXCEPT-1:0] x_except = | ||||
| 	x_trig_break                                             ? EXCEPT_EBREAK         : | ||||
| 	x_exec_pmp_fail                                          ? EXCEPT_INSTR_FAULT    : | ||||
| 	x_jump_req_unchecked && x_jump_misaligned                ? EXCEPT_INSTR_MISALIGN : | ||||
| 	x_csr_illegal_access                                     ? EXCEPT_INSTR_ILLEGAL  : | ||||
|  | @ -925,12 +970,19 @@ hazard3_csr #( | |||
| 	.irq_software               (soft_irq), | ||||
| 	.irq_timer                  (timer_irq), | ||||
| 	.except                     (xm_except), | ||||
| 	.except_to_d_mode           (xm_except_to_d_mode), | ||||
| 
 | ||||
| 	.pmp_cfg_addr               (x_pmp_cfg_addr), | ||||
| 	.pmp_cfg_wen                (x_pmp_cfg_wen), | ||||
| 	.pmp_cfg_wdata              (x_pmp_cfg_wdata), | ||||
| 	.pmp_cfg_rdata              (x_pmp_cfg_rdata), | ||||
| 
 | ||||
| 	.trig_cfg_addr              (x_trig_cfg_addr), | ||||
| 	.trig_cfg_wen               (x_trig_cfg_wen), | ||||
| 	.trig_cfg_wdata             (x_trig_cfg_wdata), | ||||
| 	.trig_cfg_rdata             (x_trig_cfg_rdata), | ||||
| 	.trig_m_en                  (x_trig_m_en), | ||||
| 
 | ||||
| 	// Other CSR-specific signalling | ||||
| 	.permit_wfi                 (x_permit_wfi), | ||||
| 	.instr_ret                  (x_instr_ret) | ||||
|  | @ -942,6 +994,7 @@ always @ (posedge clk or negedge rst_n) begin | |||
| 	if (!rst_n) begin | ||||
| 		xm_memop <= MEMOP_NONE; | ||||
| 		xm_except <= EXCEPT_NONE; | ||||
| 		xm_except_to_d_mode <= 1'b0; | ||||
| 		xm_wfi <= 1'b0; | ||||
| 		{xm_rs1, xm_rs2, xm_rd} <= {3 * W_REGADDR{1'b0}}; | ||||
| 	end else begin | ||||
|  | @ -950,6 +1003,7 @@ always @ (posedge clk or negedge rst_n) begin | |||
| 			// If some X-sourced exception has squashed the address phase, need to squash the data phase too. | ||||
| 			xm_memop <= x_unaligned_addr || x_exec_pmp_fail || x_loadstore_pmp_fail ? MEMOP_NONE : d_memop; | ||||
| 			xm_except <= x_except; | ||||
| 			xm_except_to_d_mode <= x_trig_break_d_mode; | ||||
| 			xm_wfi <= d_wfi && !x_exec_pmp_fail; | ||||
| 			// Note the d_starved term is required because it is possible | ||||
| 			// (e.g. PMP X permission fail) to except when the frontend is | ||||
|  | @ -959,6 +1013,7 @@ always @ (posedge clk or negedge rst_n) begin | |||
| 				xm_rd <= {W_REGADDR{1'b0}}; | ||||
| 				xm_memop <= MEMOP_NONE; | ||||
| 				xm_except <= EXCEPT_NONE; | ||||
| 				xm_except_to_d_mode <= 1'b0; | ||||
| 				xm_wfi <= 1'b0; | ||||
| 			end | ||||
| 		end else if (bus_dph_err_d) begin | ||||
|  |  | |||
|  | @ -89,6 +89,7 @@ module hazard3_csr #( | |||
| 
 | ||||
| 	// Exceptions must *not* be a function of bus stall. | ||||
| 	input  wire [W_EXCEPT-1:0] except, | ||||
| 	input  wire                except_to_d_mode, | ||||
| 
 | ||||
| 	// Level-sensitive interrupt sources | ||||
| 	input  wire                delay_irq_entry, | ||||
|  | @ -102,6 +103,13 @@ module hazard3_csr #( | |||
| 	output wire [W_DATA-1:0]   pmp_cfg_wdata, | ||||
| 	input  wire [W_DATA-1:0]   pmp_cfg_rdata, | ||||
| 
 | ||||
| 	// Trigger config interface | ||||
| 	output wire [11:0]         trig_cfg_addr, | ||||
| 	output reg                 trig_cfg_wen, | ||||
| 	output wire [W_DATA-1:0]   trig_cfg_wdata, | ||||
| 	input  wire [W_DATA-1:0]   trig_cfg_rdata, | ||||
| 	output wire                trig_m_en, | ||||
| 
 | ||||
| 	// Other CSR-specific signalling | ||||
| 	output wire                permit_wfi, | ||||
| 	input  wire                instr_ret | ||||
|  | @ -619,6 +627,25 @@ end | |||
| assign dbg_data0_wdata = wdata; | ||||
| assign dbg_data0_wen = debug_mode && wen && addr == DMDATA0; | ||||
| 
 | ||||
| reg tcontrol_mte; | ||||
| reg tcontrol_mpte; | ||||
| 
 | ||||
| always @ (posedge clk or negedge rst_n) begin | ||||
| 	if (!rst_n) begin | ||||
| 		tcontrol_mpte <= 1'b0; | ||||
| 		tcontrol_mte <= 1'b0; | ||||
| 	end else if (wen_m_mode && addr == TCONTROL) begin | ||||
| 		tcontrol_mpte <= wdata_update[7] && DEBUG_SUPPORT; | ||||
| 		tcontrol_mte <= wdata_update[3] && DEBUG_SUPPORT; | ||||
| 	end else if (DEBUG_SUPPORT && trapreg_update_enter) begin | ||||
| 		tcontrol_mte <= 1'b0; | ||||
| 		tcontrol_mpte <= tcontrol_mte; | ||||
| 	end else if (DEBUG_SUPPORT && trapreg_update_exit) begin | ||||
| 		tcontrol_mte <= tcontrol_mpte; | ||||
| 		tcontrol_mpte <= 1'b1; | ||||
| 	end | ||||
| end | ||||
| 
 | ||||
| // ---------------------------------------------------------------------------- | ||||
| // Read port + detect addressing of unmapped CSRs | ||||
| // ---------------------------------------------------------------------------- | ||||
|  | @ -637,6 +664,7 @@ always @ (*) begin | |||
| 	decode_match = 1'b0; | ||||
| 	rdata = {XLEN{1'b0}}; | ||||
| 	pmp_cfg_wen = 1'b0; | ||||
| 	trig_cfg_wen = 1'b0; | ||||
| 	case (addr) | ||||
| 
 | ||||
| 	// ------------------------------------------------------------------------ | ||||
|  | @ -1047,12 +1075,61 @@ always @ (*) begin | |||
| 	// ------------------------------------------------------------------------ | ||||
| 	// Trigger Module CSRs | ||||
| 
 | ||||
| 	// If triggers aren't supported, OpenOCD expects the following: | ||||
| 	// - tselect must be present | ||||
| 	// - tselect must raise an exception when written to | ||||
| 	// Otherwise it returns an error instead of 0 count when enumerating triggers | ||||
| 	// If triggers aren't supported, we implement tselect as hardwired 0, | ||||
| 	// tinfo as hardwired 1 (meaning type=0 always) and tdata as hardwired 0 | ||||
| 	// (meaning type=0). The debugger will see the first trigger as | ||||
| 	// unimplemented, and immediately halt discovery. | ||||
| 	TSELECT: if (DEBUG_SUPPORT) begin | ||||
| 		decode_match = match_mro; | ||||
| 		decode_match = match_mrw; | ||||
| 		if (BREAKPOINT_TRIGGERS > 0) begin | ||||
| 			trig_cfg_wen = match_mrw && wen; | ||||
| 			rdata = trig_cfg_rdata; | ||||
| 		end else begin | ||||
| 			rdata = 32'h00000000; | ||||
| 		end | ||||
| 	end | ||||
| 
 | ||||
| 	TDATA1: if (DEBUG_SUPPORT) begin | ||||
| 		decode_match = match_mrw; | ||||
| 		if (BREAKPOINT_TRIGGERS > 0) begin | ||||
| 			trig_cfg_wen = match_mrw && wen; | ||||
| 			rdata = trig_cfg_rdata; | ||||
| 		end else begin | ||||
| 			rdata = 32'h00000000; | ||||
| 		end | ||||
| 	end | ||||
| 
 | ||||
| 	TDATA2: if (DEBUG_SUPPORT) begin | ||||
| 		decode_match = match_mrw; | ||||
| 		if (BREAKPOINT_TRIGGERS > 0) begin | ||||
| 			trig_cfg_wen = match_mrw && wen; | ||||
| 			rdata = trig_cfg_rdata; | ||||
| 		end else begin | ||||
| 			rdata = 32'h00000000; | ||||
| 		end | ||||
| 	end | ||||
| 
 | ||||
| 	TINFO: if (DEBUG_SUPPORT) begin | ||||
| 		// Note tinfo is a read-write CSR (writes don't trap) even though it | ||||
| 		// is entirely read-only. | ||||
| 		decode_match = match_mrw; | ||||
| 		if (BREAKPOINT_TRIGGERS > 0) begin | ||||
| 			trig_cfg_wen = match_mrw && wen; | ||||
| 			rdata = trig_cfg_rdata; | ||||
| 		end else begin | ||||
| 			rdata = 32'h00000001; | ||||
| 		end | ||||
| 	end | ||||
| 
 | ||||
| 	TCONTROL: if (DEBUG_SUPPORT && BREAKPOINT_TRIGGERS > 0) begin | ||||
| 		decode_match = match_mrw; | ||||
| 		rdata = { | ||||
| 			24'h0, | ||||
| 			tcontrol_mpte, | ||||
| 			3'h0, | ||||
| 			tcontrol_mte, | ||||
| 			3'h0 | ||||
| 		}; | ||||
| 	end | ||||
| 
 | ||||
| 	// ------------------------------------------------------------------------ | ||||
|  | @ -1218,10 +1295,10 @@ end | |||
| wire exception_req_any; | ||||
| wire halt_delayed_by_exception = exception_req_any || loadstore_dphase_pending; | ||||
| 
 | ||||
| // This would also include triggers, if/when those are implemented: | ||||
| wire want_halt_except = DEBUG_SUPPORT && !debug_mode && ( | ||||
| 	dcsr_ebreakm &&  m_mode && except == EXCEPT_EBREAK || | ||||
| 	dcsr_ebreaku && !m_mode && except == EXCEPT_EBREAK | ||||
| 	dcsr_ebreaku && !m_mode && except == EXCEPT_EBREAK || | ||||
| 	except_to_d_mode        && except == EXCEPT_EBREAK | ||||
| ); | ||||
| 
 | ||||
| // Note exception-like causes (trigger, ebreak) are higher priority than | ||||
|  | @ -1243,7 +1320,7 @@ wire want_halt_irq_if_no_exception = DEBUG_SUPPORT && !debug_mode && !want_halt_ | |||
| wire want_halt_irq = want_halt_irq_if_no_exception && !halt_delayed_by_exception; | ||||
| 
 | ||||
| assign dcause_next = | ||||
| 	// Trigger would be highest priority if implemented | ||||
| 	except == EXCEPT_EBREAK && except_to_d_mode                     ? 3'h2 : // trigger (priority 4) | ||||
| 	except == EXCEPT_EBREAK                                         ? 3'h1 : // ebreak (priority 3) | ||||
| 	dbg_req_halt_prev || (dbg_req_halt_on_reset && have_just_reset) ? 3'h3 : // halt or reset-halt (priority 1, 2) | ||||
| 	                                                                  3'h4;  // single-step (priority 0) | ||||
|  | @ -1346,6 +1423,10 @@ assign mcause_code_next = exception_req_any ? except : mcause_irq_num; | |||
| assign pmp_cfg_addr = addr; | ||||
| assign pmp_cfg_wdata = wdata_update; | ||||
| 
 | ||||
| assign trig_cfg_addr = addr; | ||||
| assign trig_cfg_wdata = wdata_update; | ||||
| assign trig_m_en = tcontrol_mte; | ||||
| 
 | ||||
| // Effective privilege for execution. Used for: | ||||
| // - Privilege level of branch target fetches (frontend keeps fetch privilege | ||||
| //   constant during sequential fetch) | ||||
|  | @ -1441,6 +1522,11 @@ always @ (posedge clk) begin | |||
| 		assert(!trap_is_irq); | ||||
| 	end | ||||
| 
 | ||||
| 	// This must be a breakpoint exception (presumably from the trigger unit). | ||||
| 	if (except_to_d_mode) begin | ||||
| 		assert(except == EXCEPT_EBREAK); | ||||
| 	end | ||||
| 
 | ||||
| end | ||||
| 
 | ||||
| `endif | ||||
|  |  | |||
|  | @ -188,6 +188,11 @@ localparam SLEEP          = 12'h8f0; // U-mode subset of M-mode sleep control | |||
| // Trigger Module | ||||
| 
 | ||||
| localparam TSELECT       = 12'h7a0; | ||||
| localparam TDATA1        = 12'h7a1; | ||||
| localparam TDATA2        = 12'h7a2; | ||||
| localparam TINFO         = 12'h7a4; | ||||
| localparam TCONTROL      = 12'h7a5; | ||||
| localparam MCONTEXT      = 12'h7a8; | ||||
| 
 | ||||
| // ---------------------------------------------------------------------------- | ||||
| // D-mode CSRs | ||||
|  |  | |||
|  | @ -0,0 +1,158 @@ | |||
| /*****************************************************************************\ | ||||
| |                        Copyright (C) 2022 Luke Wren                         | | ||||
| |                     SPDX-License-Identifier: Apache-2.0                     | | ||||
| \*****************************************************************************/ | ||||
| 
 | ||||
| `default_nettype none | ||||
| 
 | ||||
| // Trigger unit. Currently only breakpoint (type=2 execute=1 select=0) | ||||
| // triggers are supported. Only exact address matches are supported, and the | ||||
| // timing is always "early". | ||||
| 
 | ||||
| module hazard3_triggers #( | ||||
| `include "hazard3_config.vh" | ||||
| ) ( | ||||
| 	input  wire              clk, | ||||
| 	input  wire              rst_n, | ||||
| 
 | ||||
| 	// Config interface passed through CSR block | ||||
| 	input  wire [11:0]       cfg_addr, | ||||
| 	input  wire              cfg_wen, | ||||
| 	input  wire [W_DATA-1:0] cfg_wdata, | ||||
| 	output reg  [W_DATA-1:0] cfg_rdata, | ||||
| 
 | ||||
| 	// Global trigger-to-M-mode enable (e.g. from tcontrol or mstatus.mie) | ||||
| 	input  wire              trig_m_en, | ||||
| 
 | ||||
| 	// PC query | ||||
| 	input  wire [W_ADDR-1:0] pc, | ||||
| 	input  wire              m_mode, | ||||
| 	input  wire              d_mode, | ||||
| 
 | ||||
| 	// Break request | ||||
| 	output wire              break, | ||||
| 	output wire              break_d_mode | ||||
| ); | ||||
| 
 | ||||
| `include "hazard3_csr_addr.vh" | ||||
| 
 | ||||
| // ---------------------------------------------------------------------------- | ||||
| // Configuration state | ||||
| 
 | ||||
| parameter W_TSELECT = $clog2(BREAKPOINT_TRIGGERS); | ||||
| 
 | ||||
| reg [W_TSELECT-1:0] tselect; | ||||
| 
 | ||||
| // Note tdata1 and mcontrol are the same CSR. tdata1 refers to the universal | ||||
| // fields (type/dmode) and mcontrol refers to those fields specific to | ||||
| // type=2 (address/data match), the only trigger type we implement. | ||||
| 
 | ||||
| reg              tdata1_dmode    [0:BREAKPOINT_TRIGGERS-1]; | ||||
| reg              mcontrol_action [0:BREAKPOINT_TRIGGERS-1]; | ||||
| reg              mcontrol_m      [0:BREAKPOINT_TRIGGERS-1]; | ||||
| reg              mcontrol_u      [0:BREAKPOINT_TRIGGERS-1]; | ||||
| reg [W_DATA-1:0] tdata2          [0:BREAKPOINT_TRIGGERS-1]; | ||||
| 
 | ||||
| // ---------------------------------------------------------------------------- | ||||
| // Configuration write port | ||||
| 
 | ||||
| always @ (posedge clk or negedge rst_n) begin: cfg_update | ||||
| 	integer i; | ||||
| 	if (!rst_n) begin | ||||
| 		tselect <= {W_TSELECT{1'b0}}; | ||||
| 		for (i = 0; i < BREAKPOINT_TRIGGERS; i = i + 1) begin | ||||
| 			tdata1_dmode[i] <= 1'b0; | ||||
| 			mcontrol_action[i] <= 1'b0; | ||||
| 			mcontrol_m[i] <= 1'b0; | ||||
| 			mcontrol_u[i] <= 1'b0; | ||||
| 			tdata2[i] <= {W_DATA{1'b0}}; | ||||
| 		end | ||||
| 	end else if (cfg_wen && cfg_addr == TSELECT) begin | ||||
| 		tselect <= cfg_wdata[W_TSELECT-1:0]; | ||||
| 	end else if (cfg_wen && tselect < BREAKPOINT_TRIGGERS && !(tdata1_dmode[i] && !d_mode)) begin | ||||
| 		// Handle writes to tselect-indexed registers (note writes to D-mode | ||||
| 		// triggers in non-D-mode are ignored rather than raising an exception) | ||||
| 		if (cfg_addr == TDATA1) begin | ||||
| 			tdata1_dmode[tselect] <= cfg_wdata[27]; | ||||
| 			mcontrol_action[tselect] <= cfg_wdata[12]; | ||||
| 			mcontrol_m[tselect] <= cfg_wdata[6]; | ||||
| 			mcontrol_u[tselect] <= cfg_wdata[3]; | ||||
| 		end else if (cfg_addr == TDATA2) begin | ||||
| 			tdata2[tselect] <= cfg_wdata; | ||||
| 		end | ||||
| 	end | ||||
| end | ||||
| 
 | ||||
| // ---------------------------------------------------------------------------- | ||||
| // Configuration read port | ||||
| 
 | ||||
| always @ (*) begin | ||||
| 	cfg_rdata = {W_DATA{1'b0}}; | ||||
| 	if (cfg_addr == TSELECT) begin | ||||
| 		cfg_rdata = {{W_DATA-W_TSELECT{1'b0}}, tselect}; | ||||
| 	end else if (cfg_addr == TDATA1) begin | ||||
| 		if (tselect >= BREAKPOINT_TRIGGERS) begin | ||||
| 			// Nonexistent -> type=0 | ||||
| 			cfg_rdata = {W_DATA{1'b0}}; | ||||
| 		end else begin | ||||
| 			cfg_rdata = { | ||||
| 				4'h2,                              // type = address/data match | ||||
| 				tdata1_dmode[tselect], | ||||
| 				6'h00,                             // maskmax = 0, exact match only | ||||
| 				1'b0,                              // hit = 0, not implemented | ||||
| 				1'b0,                              // select = 0, address match only | ||||
| 				1'b0,                              // timing = 0, trigger before execution | ||||
| 				2'h0,                              // sizelo = 0, unsized | ||||
| 				{3'h0, mcontrol_action[tselect]},  // action = 0/1, break to M-mode/D-mode | ||||
| 				1'b0,                              // chain = 0, chaining is useless for exact matches | ||||
| 				3'h0,                              // match = 0, exact match only | ||||
| 				mcontrol_m[tselect], | ||||
| 				1'b0, | ||||
| 				1'b0,                              // s = 0, no S-mode | ||||
| 				mcontrol_u[tselect], | ||||
| 				1'b1,                              // execute = 1, this is a breakpoint | ||||
| 				1'b0,                              // store = 0, this is not a watchpoint | ||||
| 				1'b0                               // load = 0, this is not a watchpoint | ||||
| 			}; | ||||
| 		end | ||||
| 	end else if (cfg_rdata == TDATA2) begin | ||||
| 		if (tselect >= BREAKPOINT_TRIGGERS) begin | ||||
| 			cfg_rdata = {W_DATA{1'b0}}; | ||||
| 		end else begin | ||||
| 			cfg_rdata = tdata2[tselect]; | ||||
| 		end | ||||
| 	end else if (cfg_rdata == TINFO) begin | ||||
| 		if (tselect >= BREAKPOINT_TRIGGERS) begin | ||||
| 			cfg_rdata = 32'h00000001; // type = 0, no trigger | ||||
| 		end else begin | ||||
| 			cfg_rdata = 32'h00000004; // type = 2, address/data match | ||||
| 		end | ||||
| 	end | ||||
| end | ||||
| 
 | ||||
| // ---------------------------------------------------------------------------- | ||||
| // Trigger match logic | ||||
| 
 | ||||
| reg [BREAKPOINT_TRIGGERS-1:0] want_d_mode_break; | ||||
| reg [BREAKPOINT_TRIGGERS-1:0] want_m_mode_break; | ||||
| 
 | ||||
| always @ (*) begin: match_pc | ||||
| 	integer i; | ||||
| 	want_m_mode_break = {BREAKPOINT_TRIGGERS{1'b0}}; | ||||
| 	want_d_mode_break = {BREAKPOINT_TRIGGERS{1'b0}}; | ||||
| 	for (i = 0; i < BREAKPOINT_TRIGGERS; i = i + 1) begin | ||||
| 		if (tdata2[i] == pc && !d_mode && (m_mode ? mcontrol_m[tselect] : mcontrol_u[tselect])) begin | ||||
| 			want_d_mode_break[i] = mcontrol_action[i] && tdata1_dmode[i]; | ||||
| 			want_m_mode_break[i] = !mcontrol_action[i] && trig_m_en; | ||||
| 		end | ||||
| 	end | ||||
| end | ||||
| 
 | ||||
| assign break        = |want_m_mode_break || |want_d_mode_break; | ||||
| assign break_d_mode = |want_d_mode_break; | ||||
| 
 | ||||
| endmodule | ||||
| 
 | ||||
| `ifndef YOSYS | ||||
| `default_nettype wire | ||||
| `endif | ||||
|  | @ -24,8 +24,6 @@ CSR was 302 | |||
| -> exception, mcause = 2 | ||||
| CSR was 303 | ||||
| -> exception, mcause = 2 | ||||
| CSR was 7a1 | ||||
| -> exception, mcause = 2 | ||||
| CSR was 7b0 | ||||
| -> exception, mcause = 2 | ||||
| CSR was 7b1 | ||||
|  | @ -132,6 +130,9 @@ int main() { | |||
| 	(void)read_csr(mhpmevent3); | ||||
| 	(void)read_csr(tselect); | ||||
| 	(void)read_csr(tdata1); | ||||
| 	(void)read_csr(tdata2); | ||||
| 	(void)read_csr(tinfo); | ||||
| 	(void)read_csr(tcontrol); | ||||
| 	(void)read_csr(dcsr); | ||||
| 	(void)read_csr(dpc); | ||||
| 	(void)read_csr(dscratch0); | ||||
|  |  | |||
|  | @ -12,6 +12,7 @@ int main() { | |||
| 	tb_assert(NUM_IRQS <= 32, "Test invalid for >32 IRQs"); | ||||
| 	global_irq_enable(true); | ||||
| 	external_irq_enable(true); | ||||
| 
 | ||||
| 	// Dry run: Check that IRQ force array can be written/read and that it
 | ||||
| 	// sets the pending array appropriately
 | ||||
| 	for (int i = 0; i < NUM_IRQS; ++i) { | ||||
|  |  | |||
|  | @ -6,7 +6,7 @@ | |||
| //   the next descending priority
 | ||||
| // - Each bottom half hander re-pends the top half handler (which immediately
 | ||||
| //   preempts it) to set the next lower-numbered bottom handler
 | ||||
| // - When top returns to bottom, and that bottom returns to the *next* bottom,
 | ||||
| // - Then top returns to bottom, and that bottom returns to the *next* bottom,
 | ||||
| //   which re-pends the top
 | ||||
| // - So on until all handlers have fired
 | ||||
| // - This should stack within two exception frames and enter the EIRQ vector
 | ||||
|  |  | |||
|  | @ -1,32 +1,33 @@ | |||
| TOP               := tb | ||||
| DOTF              := tb.f | ||||
| TOP                 := tb | ||||
| DOTF                := tb.f | ||||
| 
 | ||||
| CPU_RESET_VECTOR  := 32'h40 | ||||
| CPU_RESET_VECTOR    := 32'h40 | ||||
| 
 | ||||
| EXTENSION_C       := 1 | ||||
| EXTENSION_M       := 1 | ||||
| EXTENSION_ZBA     := 1 | ||||
| EXTENSION_ZBB     := 1 | ||||
| EXTENSION_ZBC     := 1 | ||||
| EXTENSION_ZBS     := 1 | ||||
| EXTENSION_C         := 1 | ||||
| EXTENSION_M         := 1 | ||||
| EXTENSION_ZBA       := 1 | ||||
| EXTENSION_ZBB       := 1 | ||||
| EXTENSION_ZBC       := 1 | ||||
| EXTENSION_ZBS       := 1 | ||||
| 
 | ||||
| DEBUG_SUPPORT     := 1 | ||||
| U_MODE            := 1 | ||||
| PMP_REGIONS       := 4 | ||||
| DEBUG_SUPPORT       := 1 | ||||
| BREAKPOINT_TRIGGERS := 4 | ||||
| U_MODE              := 1 | ||||
| PMP_REGIONS         := 4 | ||||
| 
 | ||||
| NUM_IRQS          := 32 | ||||
| IRQ_PRIORITY_BITS := 4 | ||||
| NUM_IRQS            := 32 | ||||
| IRQ_PRIORITY_BITS   := 4 | ||||
| 
 | ||||
| MULDIV_UNROLL     := 2 | ||||
| MUL_FAST          := 1 | ||||
| MUL_FASTER        := 1 | ||||
| MULH_FAST         := 1 | ||||
| FAST_BRANCHCMP    := 1 | ||||
| REDUCED_BYPASS    := 0 | ||||
| MULDIV_UNROLL       := 2 | ||||
| MUL_FAST            := 1 | ||||
| MUL_FASTER          := 1 | ||||
| MULH_FAST           := 1 | ||||
| FAST_BRANCHCMP      := 1 | ||||
| REDUCED_BYPASS      := 0 | ||||
| 
 | ||||
| MVENDORID_VAL     := 32'hdeadbeef | ||||
| MIMPID_VAL        := 32'h12345678 | ||||
| MCONFIGPTR_VAL    := 32'h9abcdef0 | ||||
| MVENDORID_VAL       := 32'hdeadbeef | ||||
| MIMPID_VAL          := 32'h12345678 | ||||
| MCONFIGPTR_VAL      := 32'h9abcdef0 | ||||
| 
 | ||||
| .PHONY: clean all | ||||
| 
 | ||||
|  | @ -40,6 +41,7 @@ SYNTH_CMD += chparam -set EXTENSION_ZBB $(EXTENSION_ZBB) $(TOP); | |||
| SYNTH_CMD += chparam -set EXTENSION_ZBC $(EXTENSION_ZBC) $(TOP); | ||||
| SYNTH_CMD += chparam -set EXTENSION_ZBS $(EXTENSION_ZBS) $(TOP); | ||||
| SYNTH_CMD += chparam -set DEBUG_SUPPORT $(DEBUG_SUPPORT) $(TOP); | ||||
| SYNTH_CMD += chparam -set BREAKPOINT_TRIGGERS $(BREAKPOINT_TRIGGERS) $(TOP); | ||||
| SYNTH_CMD += chparam -set U_MODE $(U_MODE) $(TOP); | ||||
| SYNTH_CMD += chparam -set PMP_REGIONS $(PMP_REGIONS) $(TOP); | ||||
| SYNTH_CMD += chparam -set NUM_IRQS $(NUM_IRQS) $(TOP); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue