Start hacking together a DM
This commit is contained in:
		
							parent
							
								
									5cc483898d
								
							
						
					
					
						commit
						0dce59daaf
					
				| 
						 | 
					@ -0,0 +1,489 @@
 | 
				
			||||||
 | 
					/**********************************************************************
 | 
				
			||||||
 | 
					 * DO WHAT THE FUCK YOU WANT TO AND DON'T BLAME US PUBLIC LICENSE     *
 | 
				
			||||||
 | 
					 *                    Version 3, April 2008                           *
 | 
				
			||||||
 | 
					 *                                                                    *
 | 
				
			||||||
 | 
					 * Copyright (C) 2021 Luke Wren                                       *
 | 
				
			||||||
 | 
					 *                                                                    *
 | 
				
			||||||
 | 
					 * Everyone is permitted to copy and distribute verbatim or modified  *
 | 
				
			||||||
 | 
					 * copies of this license document and accompanying software, and     *
 | 
				
			||||||
 | 
					 * changing either is allowed.                                        *
 | 
				
			||||||
 | 
					 *                                                                    *
 | 
				
			||||||
 | 
					 *   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION  *
 | 
				
			||||||
 | 
					 *                                                                    *
 | 
				
			||||||
 | 
					 * 0. You just DO WHAT THE FUCK YOU WANT TO.                          *
 | 
				
			||||||
 | 
					 * 1. We're NOT RESPONSIBLE WHEN IT DOESN'T FUCKING WORK.             *
 | 
				
			||||||
 | 
					 *                                                                    *
 | 
				
			||||||
 | 
					 *********************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RISC-V Debug Module for Hazard3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					`default_nettype none
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module hazard3_dm #(
 | 
				
			||||||
 | 
						// Where there are multiple harts per DM, the least-indexed hart is the
 | 
				
			||||||
 | 
						// least-significant on each concatenated hart access bus.
 | 
				
			||||||
 | 
						parameter N_HARTS = 1,
 | 
				
			||||||
 | 
						// Where there are multiple DMs, the address of each DM should be a
 | 
				
			||||||
 | 
						// multiple of 'h100, so that the lower 8 bits decode correctly.
 | 
				
			||||||
 | 
						parameter NEXT_DM_ADDR = 32'h0000_0000,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						parameter XLEN = 32, // Do not modify
 | 
				
			||||||
 | 
						parameter W_HARTSEL = N_HARTS > 1 ? $clog2(N_HARTS) : 1 // Do not modify
 | 
				
			||||||
 | 
					) (
 | 
				
			||||||
 | 
						// DM is assumed to be in same clock domain as core; clock crossing
 | 
				
			||||||
 | 
						// (if any) is inside DTM, or between DTM and DM.
 | 
				
			||||||
 | 
						input  wire                      clk,
 | 
				
			||||||
 | 
						input  wire                      rst_n,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// APB access from Debug Transport Module
 | 
				
			||||||
 | 
						input  wire                      dmi_psel,
 | 
				
			||||||
 | 
						input  wire                      dmi_penable,
 | 
				
			||||||
 | 
						input  wire                      dmi_pwrite,
 | 
				
			||||||
 | 
						input  wire [7:0]                dmi_paddr,
 | 
				
			||||||
 | 
						input  wire [31:0]               dmi_pwdata,
 | 
				
			||||||
 | 
						output reg  [31:0]               dmi_prdata,
 | 
				
			||||||
 | 
						output wire                      dmi_pready,
 | 
				
			||||||
 | 
						output wire                      dmi_pslverr,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Reset request/acknowledge. "req" is a pulse >= 1 cycle wide. "done" is
 | 
				
			||||||
 | 
						// level-sensitive, goes high once component is out of reset.
 | 
				
			||||||
 | 
						//
 | 
				
			||||||
 | 
						// The "sys" reset (ndmreset) is conventionally everything apart from DM +
 | 
				
			||||||
 | 
						// DTM, but, as per section 3.2 in 0.13.2 debug spec: "Exactly what is
 | 
				
			||||||
 | 
						// affected by this reset is implementation dependent, as long as it is
 | 
				
			||||||
 | 
						// possible to debug programs from the first instruction executed." So
 | 
				
			||||||
 | 
						// this could simply be an all-hart reset.
 | 
				
			||||||
 | 
						output wire                      sys_reset_req,
 | 
				
			||||||
 | 
						input  wire                      sys_reset_done,
 | 
				
			||||||
 | 
						output wire [N_HARTS-1:0]        hart_reset_req,
 | 
				
			||||||
 | 
						input  wire [N_HARTS-1:0]        hart_reset_done,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Hart run/halt control
 | 
				
			||||||
 | 
						output wire [N_HARTS-1:0]        hart_req_halt,
 | 
				
			||||||
 | 
						output wire [N_HARTS-1:0]        hart_req_halt_on_reset,
 | 
				
			||||||
 | 
						output wire [N_HARTS-1:0]        hart_req_resume,
 | 
				
			||||||
 | 
						input  wire [N_HARTS-1:0]        hart_halted,
 | 
				
			||||||
 | 
						input  wire [N_HARTS-1:0]        hart_running,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Hart access to data0 CSR (assumed to be core-internal but per-hart)
 | 
				
			||||||
 | 
						input  wire [N_HARTS*XLEN-1:0]   hart_data0_rdata,
 | 
				
			||||||
 | 
						output wire [N_HARTS*XLEN-1:0]   hart_data0_wdata,
 | 
				
			||||||
 | 
						output wire [N_HARTS-1:0]        hart_data0_wen,
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						// Hart instruction injection
 | 
				
			||||||
 | 
						output wire [N_HARTS*XLEN-1:0]   hart_instr_data,
 | 
				
			||||||
 | 
						output wire [N_HARTS-1:0]        hart_instr_data_vld,
 | 
				
			||||||
 | 
						input  wire [N_HARTS-1:0]        hart_instr_data_rdy,
 | 
				
			||||||
 | 
						input  wire [N_HARTS-1:0]        hart_instr_caught_exception,
 | 
				
			||||||
 | 
						input  wire [N_HARTS-1:0]        hart_instr_caught_ebreak
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					wire dmi_write = dmi_psel && dmi_penable && dmi_pready && dmi_pwrite;
 | 
				
			||||||
 | 
					wire dmi_read = dmi_psel && dmi_penable && dmi_pready && !dmi_pwrite;
 | 
				
			||||||
 | 
					assign dmi_pready = 1'b1;
 | 
				
			||||||
 | 
					assign dmi_pslverr = 1'b0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Program buffer is fixed at 2 words plus impebreak. The main thing we care
 | 
				
			||||||
 | 
					// about is support for efficient memory block transfers using abstractauto;
 | 
				
			||||||
 | 
					// in this case 2 words + impebreak is sufficient for RV32I, and 1 word +
 | 
				
			||||||
 | 
					// impebreak is sufficient for RV32IC.
 | 
				
			||||||
 | 
					localparam PROGBUF_SIZE = 2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ----------------------------------------------------------------------------
 | 
				
			||||||
 | 
					// Address constants
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					localparam ADDR_DATA0        = 8'h04;
 | 
				
			||||||
 | 
					// Other data registers not present.
 | 
				
			||||||
 | 
					localparam ADDR_DMCONTROL    = 8'h10;
 | 
				
			||||||
 | 
					localparam ADDR_DMSTATUS     = 8'h11;
 | 
				
			||||||
 | 
					localparam ADDR_HARTINFO     = 8'h12;
 | 
				
			||||||
 | 
					// No halt summary registers (assume no more than 32 harts)
 | 
				
			||||||
 | 
					// No array mask select registers
 | 
				
			||||||
 | 
					localparam ADDR_ABSTRACTCS   = 8'h16;
 | 
				
			||||||
 | 
					localparam ADDR_COMMAND      = 8'h17;
 | 
				
			||||||
 | 
					localparam ADDR_ABSTRACTAUTO = 8'h18;
 | 
				
			||||||
 | 
					localparam ADDR_CONFSTRPTR0  = 8'h19;
 | 
				
			||||||
 | 
					localparam ADDR_CONFSTRPTR1  = 8'h1a;
 | 
				
			||||||
 | 
					localparam ADDR_CONFSTRPTR2  = 8'h1b;
 | 
				
			||||||
 | 
					localparam ADDR_CONFSTRPTR3  = 8'h1c;
 | 
				
			||||||
 | 
					localparam ADDR_NEXTDM       = 8'h1d;
 | 
				
			||||||
 | 
					localparam ADDR_PROGBUF0     = 8'h20;
 | 
				
			||||||
 | 
					localparam ADDR_PROGBUF1     = 8'h21;
 | 
				
			||||||
 | 
					// No authentication, no system bus access
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ----------------------------------------------------------------------------
 | 
				
			||||||
 | 
					// Hart selection
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					reg dmactive;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Some fiddliness to make sure we get a single-wide zero-valued signal when
 | 
				
			||||||
 | 
					// N_HARTS == 1 (so we can use this for indexing of per-hart signals)
 | 
				
			||||||
 | 
					reg  [W_HARTSEL-1:0] hartsel;
 | 
				
			||||||
 | 
					wire [W_HARTSEL-1:0] hartsel_next;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					generate
 | 
				
			||||||
 | 
					if (N_HARTS > 1) begin: has_hartsel
 | 
				
			||||||
 | 
						// only the lower 10 bits of hartsel are supported
 | 
				
			||||||
 | 
						assign hartsel_next = dmi_write && dmi_paddr == ADDR_DMCONTROL ?
 | 
				
			||||||
 | 
							dmi_pwdata[16 +: W_HARTSEL] : hartsel;
 | 
				
			||||||
 | 
					end else begin: has_no_hartsel
 | 
				
			||||||
 | 
						assign hartsel_next = 1'b0;
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					always @ (posedge clk or negedge rst_n) begin
 | 
				
			||||||
 | 
						if (!rst_n) begin
 | 
				
			||||||
 | 
							hartsel <= {W_HARTSEL{1'b0}};
 | 
				
			||||||
 | 
						end else if (!dmactive) begin
 | 
				
			||||||
 | 
							hartsel <= {W_HARTSEL{1'b0}};
 | 
				
			||||||
 | 
						end else begin
 | 
				
			||||||
 | 
							hartsel <= hartsel_next;
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ----------------------------------------------------------------------------
 | 
				
			||||||
 | 
					// Run/halt/reset control
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Normal read/write fields for dmcontrol (note some of these are per-hart
 | 
				
			||||||
 | 
					// fields that get rotated into dmcontrol based on the current/next hartsel).
 | 
				
			||||||
 | 
					reg [N_HARTS-1:0] dmcontrol_haltreq;
 | 
				
			||||||
 | 
					reg [N_HARTS-1:0] dmcontrol_hartreset;
 | 
				
			||||||
 | 
					reg [N_HARTS-1:0] dmcontrol_resethaltreq;
 | 
				
			||||||
 | 
					reg               dmcontrol_ndmreset;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					always @ (posedge clk or negedge rst_n) begin
 | 
				
			||||||
 | 
						if (!rst_n) begin
 | 
				
			||||||
 | 
							dmactive <= 1'b0;
 | 
				
			||||||
 | 
							dmcontrol_ndmreset <= 1'b0;
 | 
				
			||||||
 | 
							dmcontrol_haltreq <= {N_HARTS{1'b0}};
 | 
				
			||||||
 | 
							dmcontrol_hartreset <= {N_HARTS{1'b0}};
 | 
				
			||||||
 | 
							dmcontrol_resethaltreq <= {N_HARTS{1'b0}};
 | 
				
			||||||
 | 
						end else if (!dmactive) begin
 | 
				
			||||||
 | 
							// Only dmactive is writable when !dmactive
 | 
				
			||||||
 | 
							if (dmi_write && dmi_paddr == ADDR_DMCONTROL)
 | 
				
			||||||
 | 
								dmactive <= dmi_pwdata[0];
 | 
				
			||||||
 | 
							dmcontrol_ndmreset <= 1'b0;
 | 
				
			||||||
 | 
							dmcontrol_haltreq <= {N_HARTS{1'b0}};
 | 
				
			||||||
 | 
							dmcontrol_hartreset <= {N_HARTS{1'b0}};
 | 
				
			||||||
 | 
							dmcontrol_resethaltreq <= {N_HARTS{1'b0}};
 | 
				
			||||||
 | 
						end else if dmi_write && dmi_paddr == ADDR_DMCONTROL) begin
 | 
				
			||||||
 | 
							dmactive <= dmi_pwdata[0];
 | 
				
			||||||
 | 
							dmcontrol_ndmreset <= dmi_pwdata[1];
 | 
				
			||||||
 | 
							dmcontrol_haltreq[hartsel_next] <= dmi_pwdata[31];
 | 
				
			||||||
 | 
							dmcontrol_hartreset[hartsel_next] <= dmi_pwdata[29];
 | 
				
			||||||
 | 
							// set/clear fields on this one for some reason
 | 
				
			||||||
 | 
							dmcontrol_resethaltreq[hartsel_next] <= dmcontrol_resethaltreq[hartsel_next]
 | 
				
			||||||
 | 
								&& !dmi_pwdata[2] || dmi_pwdata[3];
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					assign sys_reset_req = dmcontrol_ndmreset;
 | 
				
			||||||
 | 
					assign hart_reset_req = dmcontrol_hartreset;
 | 
				
			||||||
 | 
					assign hart_req_halt = dmcontrol_haltreq;
 | 
				
			||||||
 | 
					assign hart_req_halt_on_reset = dmcontrol_resethaltreq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					reg  [N_HARTS-1:0] hart_reset_done_prev;
 | 
				
			||||||
 | 
					reg  [N_HARTS-1:0] dmstatus_havereset;
 | 
				
			||||||
 | 
					wire [N_HARTS-1:0] hart_available = hart_reset_done & {N_HARTS{sys_reset_done}};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					always @ (posedge clk or negedge rst_n) begin
 | 
				
			||||||
 | 
						if (!rst_n) begin
 | 
				
			||||||
 | 
							hart_reset_done_prev <= {N_HARTS{1'b0}};
 | 
				
			||||||
 | 
						end else begin
 | 
				
			||||||
 | 
							hart_reset_done_prev <= hart_reset_done;
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					always @ (posedge clk or negedge rst_n) begin
 | 
				
			||||||
 | 
						if (!rst_n) begin
 | 
				
			||||||
 | 
							dmstatus_havereset <= {N_HARTS{1'b0}};
 | 
				
			||||||
 | 
						end else if (!dmactive) begin
 | 
				
			||||||
 | 
							dmstatus_havereset <= {N_HARTS{1'b0}};
 | 
				
			||||||
 | 
						end else begin
 | 
				
			||||||
 | 
							dmstatus_havereset <= dmstatus_havereset | (hart_reset_done & ~hart_reset_done_prev);
 | 
				
			||||||
 | 
							// dmcontrol.ackhavereset:
 | 
				
			||||||
 | 
							if (dmi_write && dmi_paddr == ADDR_DMCONTROL && dmi_pwdata[28])
 | 
				
			||||||
 | 
								dmstatus_havereset[hartsel_next] <= 1'b0;
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					reg [N_HARTS-1:0] dmstatus_resumeack;
 | 
				
			||||||
 | 
					reg [N_HARTS-1:0] dmcontrol_resumereq_sticky;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					always @ (posedge clk or negedge rst_n) begin
 | 
				
			||||||
 | 
						if (!rst_n) begin
 | 
				
			||||||
 | 
							dmstatus_resumeack <= {N_HARTS{1'b0}};
 | 
				
			||||||
 | 
							dmcontrol_resumereq_sticky <= {N_HARTS{1'b0}};
 | 
				
			||||||
 | 
						end else if (!dmactive) begin
 | 
				
			||||||
 | 
							dmstatus_resumeack <= {N_HARTS{1'b0}};
 | 
				
			||||||
 | 
							dmcontrol_resumereq_sticky <= {N_HARTS{1'b0}};
 | 
				
			||||||
 | 
						end else begin
 | 
				
			||||||
 | 
							dmstatus_resumeack <= dmstatus_resumeack | (dmcontrol_resumereq_sticky & hart_running);
 | 
				
			||||||
 | 
							dmcontrol_resumereq_sticky <= dmcontrol_resumereq_sticky & ~hart_running;
 | 
				
			||||||
 | 
							// dmcontrol.resumereq:
 | 
				
			||||||
 | 
							if (dmi_write && dmi_paddr == ADDR_DMCONTROL && dmi_pwdata[30]) begin
 | 
				
			||||||
 | 
								dmcontrol_resumereq_sticky[hartsel_next] <= 1'b1;
 | 
				
			||||||
 | 
								dmstatus_resumeack[hartsel_next] <= 1'b0;
 | 
				
			||||||
 | 
							end
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					assign hart_req_resume = ~dmstatus_resumeack;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ----------------------------------------------------------------------------
 | 
				
			||||||
 | 
					// Abstract command data registers
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					wire abstractcs_busy;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					assign hart_data0_wdata = {N_HARTS{dmi_pwdata}};
 | 
				
			||||||
 | 
					assign hart_data0_wen = {{N_HARTS-1{1'b0}}, dmi_write && dmi_paddr == ADDR_DATA0 && !abstractcs_busy} << hartsel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					reg [XLEN-1:0] progbuf0;
 | 
				
			||||||
 | 
					reg [XLEN-1:0] progbuf1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					always @ (posedge clk or negedge rst_n) begin
 | 
				
			||||||
 | 
						if (!rst_n) begin
 | 
				
			||||||
 | 
							progbuf0 <= {XLEN{1'b0}};
 | 
				
			||||||
 | 
							progbuf1 <= {XLEN{1'b0}};
 | 
				
			||||||
 | 
						end else if (!dmactive) begin
 | 
				
			||||||
 | 
							progbuf0 <= {XLEN{1'b0}};
 | 
				
			||||||
 | 
							progbuf1 <= {XLEN{1'b0}};
 | 
				
			||||||
 | 
						end else if (dmi_write && !abstractcs_busy) begin
 | 
				
			||||||
 | 
							if (dmi_paddr == ADDR_PROGBUF0)
 | 
				
			||||||
 | 
								progbuf0 <= dmi_pwdata;
 | 
				
			||||||
 | 
							if (dmi_paddr == ADDR_PROGBUF1)
 | 
				
			||||||
 | 
								progbuf1 <= dmi_pwdata;
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// We only support abstractauto on data0 update (use case is bulk memory read/write)
 | 
				
			||||||
 | 
					reg abstractauto_autoexecdata;
 | 
				
			||||||
 | 
					always @ (posedge clk or negedge rst_n) begin
 | 
				
			||||||
 | 
						if (!rst_n) begin
 | 
				
			||||||
 | 
							abstractauto_autoexecdata <= 1'b0;
 | 
				
			||||||
 | 
						end else if (!dmactive) begin
 | 
				
			||||||
 | 
							abstractauto_autoexecdata <= 1'b0;
 | 
				
			||||||
 | 
						end else if (dmi_write && dmi_paddr == ADDR_ABSTRACTAUTO) begin
 | 
				
			||||||
 | 
							abstractauto_autoexecdata <= pwdata[1];
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ----------------------------------------------------------------------------
 | 
				
			||||||
 | 
					// Abstract command state machine
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					localparam W_STATE = 4;
 | 
				
			||||||
 | 
					localparam S_IDLE            = 4'd0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					localparam S_ISSUE_REGREAD   = 4'd1;
 | 
				
			||||||
 | 
					localparam S_ISSUE_REGWRITE  = 4'd2;
 | 
				
			||||||
 | 
					localparam S_ISSUE_REGEBREAK = 4'd3;
 | 
				
			||||||
 | 
					localparam S_WAIT_REGEBREAK  = 4'd4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					localparam S_ISSUE_PROGBUF0  = 4'd5;
 | 
				
			||||||
 | 
					localparam S_ISSUE_PROGBUF1  = 4'd6;
 | 
				
			||||||
 | 
					localparam S_ISSUE_IMPEBREAK = 4'd7;
 | 
				
			||||||
 | 
					localparam S_WAIT_IMPEBREAK  = 4'd8;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					localparam CMDERR_OK = 3'h0;
 | 
				
			||||||
 | 
					localparam CMDERR_BUSY = 3'h1;
 | 
				
			||||||
 | 
					localparam CMDERR_UNSUPPORTED = 3'h2;
 | 
				
			||||||
 | 
					localparam CMDERR_EXCEPTION = 3'h3;
 | 
				
			||||||
 | 
					localparam CMDERR_HALTRESUME = 3'h4;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					reg [2:0]         abstractcs_cmderr;
 | 
				
			||||||
 | 
					reg [W_STATE-1:0] acmd_state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					assign abstracts_busy = acmd_state != S_IDLE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					wire start_abstract_cmd = abstractcs_cmderr == CMDERR_OK && !abstractcs_busy && (
 | 
				
			||||||
 | 
						(dmi_write && dmi_paddr == ADDR_COMMAND) ||
 | 
				
			||||||
 | 
						((dmi_write || dmi_read) && abstractauto_autoexecdata && dmi_paddr == ADDR_DATA0
 | 
				
			||||||
 | 
					);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					wire dmi_access_illegal_when_busy =
 | 
				
			||||||
 | 
						(dmi_write && (
 | 
				
			||||||
 | 
							dmi_paddr == ADDR_ABSTRACTCS || dmi_paddr == ADDR_COMMAND || dmi_paddr == ADDR_ABSTRACTAUTO ||
 | 
				
			||||||
 | 
							dmi_paddr == ADDR_DATA0 || dmi_paddr == ADDR_PROGBUF0 || dmi_paddr == ADDR_PROGBUF0)) ||
 | 
				
			||||||
 | 
						(dmi_read && (
 | 
				
			||||||
 | 
							dmi_paddr == ADDR_DATA0 || dmi_paddr == ADDR_PROGBUF0 || dmi_paddr == ADDR_PROGBUF0));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					wire acmd_unsupported =
 | 
				
			||||||
 | 
						dmi_pwdata[31:24] != 8'h00 || // Only Access Register command supported
 | 
				
			||||||
 | 
						dmi_pwdata[22:20] != 3'h2  || // Must be 32 bits in size
 | 
				
			||||||
 | 
						dmi_pwdata[19]             || // aarpostincrement not supported
 | 
				
			||||||
 | 
						dmi_pwdata[15:12] != 4'h1  || // Only core register access supported
 | 
				
			||||||
 | 
						dmi_pwdata[11:5]  != 7'h0;    // Only GPRs supported
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					wire       acmd_postexec = dmi_pwdata[18];
 | 
				
			||||||
 | 
					wire       acmd_transfer = dmi_pwdata[17];
 | 
				
			||||||
 | 
					wire       acmd_write    = dmi_pwdata[16];
 | 
				
			||||||
 | 
					wire [4:0] acmd_regno    = dmi_pwdata[4:0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					reg        acmd_postexec_r;
 | 
				
			||||||
 | 
					reg  [4:0] acmd_regno_r;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					always @ (posedge clk or negedge rst_n) begin
 | 
				
			||||||
 | 
						if (!rst_n) begin
 | 
				
			||||||
 | 
							abstractcs_cmderr <= CMDERR_OK;
 | 
				
			||||||
 | 
							abstractcs_state <= S_IDLE;
 | 
				
			||||||
 | 
							acmd_postexec_r <= 1'b0;
 | 
				
			||||||
 | 
							acmd_regno_r <= 5'h0;
 | 
				
			||||||
 | 
						end else if (!dmactive) begin
 | 
				
			||||||
 | 
							abstractcs_cmderr <= CMDERR_OK;
 | 
				
			||||||
 | 
							abstractcs_state <= S_IDLE;
 | 
				
			||||||
 | 
						end else begin
 | 
				
			||||||
 | 
							if (abstractcs_cmderr == CMDERR_OK && abstracts_busy && dmi_access_illegal_when_busy)
 | 
				
			||||||
 | 
								abstractcs_cmderr <= CMDERR_BUSY;
 | 
				
			||||||
 | 
							if (state != S_IDLE && hart_instr_caught_exception)
 | 
				
			||||||
 | 
								abstractcs_cmderr <= CMDERR_EXCEPTION;
 | 
				
			||||||
 | 
							case (acmd_state)
 | 
				
			||||||
 | 
								S_IDLE: begin
 | 
				
			||||||
 | 
									if (start_abstract_cmd) begin
 | 
				
			||||||
 | 
										if (!hart_halted[hartsel] || !hart_available[hartsel]) begin
 | 
				
			||||||
 | 
											abstracts_cmderr <= CMDERR_HALTRESUME;
 | 
				
			||||||
 | 
										end else if (acmd_unsupported)
 | 
				
			||||||
 | 
											abstractcs_cmderr <= CMDERR_UNSUPPORTED;
 | 
				
			||||||
 | 
										end else begin
 | 
				
			||||||
 | 
											acmd_postexec_r <= acmd_postexec;
 | 
				
			||||||
 | 
											acmd_regno_r <= acmd_regno;
 | 
				
			||||||
 | 
											if (acmd_transfer && acmd_write)
 | 
				
			||||||
 | 
												state <= S_ISSUE_REGWRITE;
 | 
				
			||||||
 | 
											else if (acmd_transfer && !acmd_write)
 | 
				
			||||||
 | 
												state <= S_ISSUE_REGREAD;
 | 
				
			||||||
 | 
											else if (acmd_postexec)
 | 
				
			||||||
 | 
												state <= S_ISSUE_PROGBUF0;
 | 
				
			||||||
 | 
											else
 | 
				
			||||||
 | 
												state <= S_IDLE;					
 | 
				
			||||||
 | 
										end
 | 
				
			||||||
 | 
									end
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								S_ISSUE_REGREAD: begin
 | 
				
			||||||
 | 
									if (hart_instr_data_rdy[hartsel])
 | 
				
			||||||
 | 
										state <= S_ISSUE_REGEBREAK;
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
 | 
								S_ISSUE_REGWRITE: begin
 | 
				
			||||||
 | 
									if (hart_instr_data_rdy[hartsel])
 | 
				
			||||||
 | 
										state <= S_ISSUE_REGEBREAK;
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
 | 
								S_ISSUE_REGEBREAK: begin
 | 
				
			||||||
 | 
									if (hart_instr_data_rdy[hartsel])
 | 
				
			||||||
 | 
										state <= S_WAIT_REGEBREAK
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
 | 
								S_WAIT_REGEBREAK: begin
 | 
				
			||||||
 | 
									if (hart_instr_caught_ebreak) begin
 | 
				
			||||||
 | 
										if (acmd_postexec_r)
 | 
				
			||||||
 | 
											state <= S_ISSUE_PROGBUF0;
 | 
				
			||||||
 | 
										else
 | 
				
			||||||
 | 
											state <= S_IDLE;
 | 
				
			||||||
 | 
									end
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								S_ISSUE_PROGBUF0: begin
 | 
				
			||||||
 | 
									if (hart_instr_data_rdy[hartsel])
 | 
				
			||||||
 | 
										state <= S_ISSUE_PROGBUF1;
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
 | 
								S_ISSUE_PROGBUF1: begin
 | 
				
			||||||
 | 
									if (hart_instr_caught_exception || hart_instr_caught_ebreak) begin
 | 
				
			||||||
 | 
										state <= S_IDLE;
 | 
				
			||||||
 | 
									end else if (hart_instr_data_rdy[hartsel]) begin
 | 
				
			||||||
 | 
										state <= S_ISSUE_IMPEBREAK;
 | 
				
			||||||
 | 
									end
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
 | 
								S_ISSUE_IMPEBREAK: begin
 | 
				
			||||||
 | 
									if (hart_instr_caught_exception || hart_instr_caught_ebreak) begin
 | 
				
			||||||
 | 
										state <= S_IDLE;
 | 
				
			||||||
 | 
									end else if (hart_instr_data_rdy[hartsel]) begin
 | 
				
			||||||
 | 
										state <= S_WAIT_IMPEBREAK;
 | 
				
			||||||
 | 
									end
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
 | 
								S_WAIT_IMPEBREAK: begin
 | 
				
			||||||
 | 
									if (hart_instr_caught_exception || hart_instr_caught_ebreak) begin
 | 
				
			||||||
 | 
										state <= S_IDLE;
 | 
				
			||||||
 | 
									end
 | 
				
			||||||
 | 
								end
 | 
				
			||||||
 | 
							endcase
 | 
				
			||||||
 | 
						end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					assign hart_instr_data_vld = {{N_HARTS{1'b0}},
 | 
				
			||||||
 | 
						state == S_ISSUE_REGREAD || state == S_ISSUE_REGWRITE || state == S_ISSUE_REGEBREAK ||
 | 
				
			||||||
 | 
						state == S_ISSUE_PROGBUF0 || state == S_ISSUE_PROGBUF1 || state == S_ISSUE_IMPEBREAK
 | 
				
			||||||
 | 
					} << hartsel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					assign hart_instr_data = {N_HARTS{
 | 
				
			||||||
 | 
						state == S_ISSUE_REGWRITE  ? 32'h7b202073 | {20'd0, acmd_regno_r,  7'd0} : // csrr xx, data0
 | 
				
			||||||
 | 
						state == S_ISSUE_REGREAD   ? 32'h7b201073 | {12'd0, acmd_regno_r, 15'd0} : // csrw data0, xx
 | 
				
			||||||
 | 
						state == S_ISSUE_PROGBUF0  ? progbuf0                                    :
 | 
				
			||||||
 | 
						state == S_ISSUE_PROGBUF1  ? progbuf1                                    :
 | 
				
			||||||
 | 
						                             32'h00100073                                  // ebreak
 | 
				
			||||||
 | 
					}};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ----------------------------------------------------------------------------
 | 
				
			||||||
 | 
					// DMI read data mux
 | 
				
			||||||
 | 
					always @ (*) begin
 | 
				
			||||||
 | 
						case (dmi_paddr)
 | 
				
			||||||
 | 
						ADDR_DATA0:        dmi_prdata = hart_data0_rdata[hartsel * XLEN +: XLEN];
 | 
				
			||||||
 | 
						ADDR_DMCONTROL:    dmi_prdata = {
 | 
				
			||||||
 | 
							dmcontrol_haltreq[hartsel],
 | 
				
			||||||
 | 
							1'b0,                             // resumereq is a W1 field
 | 
				
			||||||
 | 
							dmcontrol_hartreset[hartsel],
 | 
				
			||||||
 | 
							1'b0,                             // ackhavereset is a W1 field
 | 
				
			||||||
 | 
							1'b0,                             // reserved
 | 
				
			||||||
 | 
							1'b0,                             // hasel hardwired 0 (no array mask)
 | 
				
			||||||
 | 
							{{10-W_HARTSEL{1'b0}}, hartsel}   // hartsello
 | 
				
			||||||
 | 
							10'h0,                            // hartselhi
 | 
				
			||||||
 | 
							2'h0,                             // reserved
 | 
				
			||||||
 | 
							2'h0,                             // set/clrresethaltreq are W1 fields
 | 
				
			||||||
 | 
							dmcontrol_ndmreset,
 | 
				
			||||||
 | 
							dmactive
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						ADDR_DMSTATUS:     dmi_prdata = {
 | 
				
			||||||
 | 
							9'h0,                             // reserved
 | 
				
			||||||
 | 
							1'b1,                             // impebreak = 1
 | 
				
			||||||
 | 
							2'h0,                             // reserved
 | 
				
			||||||
 | 
							{2{dmstatus_havereset[hartsel]}}, // allhavereset, anyhavereset
 | 
				
			||||||
 | 
							{2{dmstatus_resumeack[hartsel]}}, // allresumeack, anyresumeack
 | 
				
			||||||
 | 
							{2{hartsel >= N_HARTS}},          // allnonexistent, anynonexistent
 | 
				
			||||||
 | 
							{2{!hart_available[hartsel}},     // allunavail, anyunavail
 | 
				
			||||||
 | 
							{2{hart_running[hartsel]}},       // allrunning, anyrunning
 | 
				
			||||||
 | 
							{2{hart_halted[hartsel]}},        // allhalted, anyhalted
 | 
				
			||||||
 | 
							1'b1,                             // authenticated
 | 
				
			||||||
 | 
							1'b0,                             // authbusy
 | 
				
			||||||
 | 
							1'b1,                             // hasresethaltreq = 1 (we do support it)
 | 
				
			||||||
 | 
							1'b0,                             // confstrptrvalid
 | 
				
			||||||
 | 
							4'd2                              // version = 2: RISC-V debug spec 0.13.2
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						ADDR_HARTINFO:     dmi_prdata = {
 | 
				
			||||||
 | 
							8'h0,                             // reserved
 | 
				
			||||||
 | 
							4'h0,                             // nscratch = 0
 | 
				
			||||||
 | 
							3'h0,                             // reserved
 | 
				
			||||||
 | 
							1'b0,                             // dataccess = 0, data0 is backed by a per-hart CSR
 | 
				
			||||||
 | 
							4'h1,                             // datasize = 1, a single data CSR (data0) is available
 | 
				
			||||||
 | 
							12'h7b2                           // dataaddr, same location where dscratch0 would be if implemented
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						ADDR_ABSTRACTCS:   dmi_prdata = {
 | 
				
			||||||
 | 
							3'h0,                             // reserved
 | 
				
			||||||
 | 
							5'd2,                             // progbufsize = 2
 | 
				
			||||||
 | 
							11'h0,                            // reserved
 | 
				
			||||||
 | 
							abstractcs_busy,
 | 
				
			||||||
 | 
							1'b0,
 | 
				
			||||||
 | 
							abstractcs_cmderr,
 | 
				
			||||||
 | 
							4'h0,
 | 
				
			||||||
 | 
							4'd1                              // datacount = 1
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						ADDR_ABSTRACTAUTO: dmi_prdata = {
 | 
				
			||||||
 | 
							31'h0,
 | 
				
			||||||
 | 
							abstractauto_autoexecdata         // only data0 supported
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						ADDR_CONFSTRPTR0:  dmi_prdata = 32'h4c296328;
 | 
				
			||||||
 | 
						ADDR_CONFSTRPTR1:  dmi_prdata = 32'h20656b75;
 | 
				
			||||||
 | 
						ADDR_CONFSTRPTR2:  dmi_prdata = 32'h6e657257;
 | 
				
			||||||
 | 
						ADDR_CONFSTRPTR3:  dmi_prdata = 32'h31322720;
 | 
				
			||||||
 | 
						ADDR_NEXTDM:       dmi_prdata = NEXT_DM_ADDR;
 | 
				
			||||||
 | 
						ADDR_PROGBUF0:     dmi_prdata = progbuf0;
 | 
				
			||||||
 | 
						ADDR_PROGBUF1:     dmi_prdata = progbuf1;
 | 
				
			||||||
 | 
						default:           dmi_prdata = {XLEN{1'b0}};
 | 
				
			||||||
 | 
						endcase
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					endmodule
 | 
				
			||||||
		Loading…
	
		Reference in New Issue