First pass at adding system bus access to DM. Currently only the 2-port processor supports SBA patchthrough.
This commit is contained in:
parent
36cee73d1f
commit
51bc26f8ac
|
@ -10,13 +10,16 @@
|
||||||
module hazard3_dm #(
|
module hazard3_dm #(
|
||||||
// Where there are multiple harts per DM, the least-indexed hart is the
|
// Where there are multiple harts per DM, the least-indexed hart is the
|
||||||
// least-significant on each concatenated hart access bus.
|
// least-significant on each concatenated hart access bus.
|
||||||
parameter N_HARTS = 1,
|
parameter N_HARTS = 1,
|
||||||
// Where there are multiple DMs, the address of each DM should be a
|
// Where there are multiple DMs, the address of each DM should be a
|
||||||
// multiple of 'h200, so that bits[8:2] decode correctly.
|
// multiple of 'h200, so that bits[8:2] decode correctly.
|
||||||
parameter NEXT_DM_ADDR = 32'h0000_0000,
|
parameter NEXT_DM_ADDR = 32'h0000_0000,
|
||||||
|
// Implement support for system bus access:
|
||||||
|
parameter HAVE_SBA = 1,
|
||||||
|
|
||||||
parameter XLEN = 32, // Do not modify
|
// Do not modify:
|
||||||
parameter W_HARTSEL = N_HARTS > 1 ? $clog2(N_HARTS) : 1 // Do not modify
|
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
|
// DM is assumed to be in same clock domain as core; clock crossing
|
||||||
// (if any) is inside DTM, or between DTM and DM.
|
// (if any) is inside DTM, or between DTM and DM.
|
||||||
|
@ -63,7 +66,21 @@ module hazard3_dm #(
|
||||||
output wire [N_HARTS-1:0] hart_instr_data_vld,
|
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_data_rdy,
|
||||||
input wire [N_HARTS-1:0] hart_instr_caught_exception,
|
input wire [N_HARTS-1:0] hart_instr_caught_exception,
|
||||||
input wire [N_HARTS-1:0] hart_instr_caught_ebreak
|
input wire [N_HARTS-1:0] hart_instr_caught_ebreak,
|
||||||
|
|
||||||
|
// System bus access (optional) -- can be hooked up to the standalone AHB
|
||||||
|
// shim (hazard3_sba_to_ahb.v) or the SBA input port on the processor
|
||||||
|
// wrapper, which muxes SBA into the processor's load/store bus access
|
||||||
|
// port. SBA does not increase debugger bus throughput, but supports
|
||||||
|
// minimally intrusive debug bus access for e.g. Segger RTT.
|
||||||
|
output wire [31:0] sbus_addr,
|
||||||
|
output wire sbus_write,
|
||||||
|
output wire [1:0] sbus_size,
|
||||||
|
output wire sbus_vld,
|
||||||
|
input wire sbus_rdy,
|
||||||
|
input wire sbus_err,
|
||||||
|
output wire [31:0] sbus_wdata,
|
||||||
|
input wire [31:0] sbus_rdata
|
||||||
);
|
);
|
||||||
|
|
||||||
wire dmi_write = dmi_psel && dmi_penable && dmi_pready && dmi_pwrite;
|
wire dmi_write = dmi_psel && dmi_penable && dmi_pready && dmi_pwrite;
|
||||||
|
@ -100,7 +117,10 @@ localparam ADDR_CONFSTRPTR3 = 7'h1c;
|
||||||
localparam ADDR_NEXTDM = 7'h1d;
|
localparam ADDR_NEXTDM = 7'h1d;
|
||||||
localparam ADDR_PROGBUF0 = 7'h20;
|
localparam ADDR_PROGBUF0 = 7'h20;
|
||||||
localparam ADDR_PROGBUF1 = 7'h21;
|
localparam ADDR_PROGBUF1 = 7'h21;
|
||||||
// No authentication, no system bus access
|
// No authentication
|
||||||
|
localparam ADDR_SBCS = 7'h38;
|
||||||
|
localparam ADDR_SBADDRESS0 = 7'h39;
|
||||||
|
localparam ADDR_SBDATA0 = 7'h3c;
|
||||||
|
|
||||||
// APB is byte-addressed, DM registers are word-addressed.
|
// APB is byte-addressed, DM registers are word-addressed.
|
||||||
wire [6:0] dmi_regaddr = dmi_paddr[8:2];
|
wire [6:0] dmi_regaddr = dmi_paddr[8:2];
|
||||||
|
@ -289,6 +309,125 @@ end
|
||||||
|
|
||||||
assign hart_req_resume = dmcontrol_resumereq_sticky;
|
assign hart_req_resume = dmcontrol_resumereq_sticky;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
// System bus access
|
||||||
|
|
||||||
|
reg [31:0] sbaddress;
|
||||||
|
reg [31:0] sbdata;
|
||||||
|
|
||||||
|
// Update logic for address/data registers:
|
||||||
|
|
||||||
|
reg sbbusy;
|
||||||
|
reg sbautoincrement;
|
||||||
|
reg [2:0] sbaccess; // Size of the transfer
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
sbaddress <= {32{1'b0}};
|
||||||
|
sbdata <= {32{1'b0}};
|
||||||
|
end else if (!dmactive) begin
|
||||||
|
sbaddress <= {32{1'b0}};
|
||||||
|
sbdata <= {32{1'b0}};
|
||||||
|
end else if (HAVE_SBA) begin
|
||||||
|
if (dmi_write && dmi_regaddr == ADDR_SBDATA0 && !sbbusy) begin
|
||||||
|
sbdata <= dmi_pwdata;
|
||||||
|
end else if (sbus_vld && sbus_rdy && !sbus_write) begin
|
||||||
|
sbdata <= sbus_rdata;
|
||||||
|
end
|
||||||
|
if (dmi_write && dmi_regaddr == ADDR_SBADDRESS0 && !sbbusy) begin
|
||||||
|
sbaddress <= dmi_pwdata;
|
||||||
|
end else if (sbus_vld && sbus_rdy && sbautoincrement) begin
|
||||||
|
sbaddress <= sbaddress + (
|
||||||
|
sbaccess[1:0] == 2'b00 ? 3'h1 :
|
||||||
|
sbaccess[1:0] == 2'b01 ? 3'h2 : 3'h4
|
||||||
|
);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// Control logic:
|
||||||
|
|
||||||
|
reg sbbusyerror;
|
||||||
|
reg sbreadonaddr;
|
||||||
|
reg sbreadondata;
|
||||||
|
reg [2:0] sberror;
|
||||||
|
reg sb_current_is_write;
|
||||||
|
|
||||||
|
localparam SBERROR_OK = 3'h0;
|
||||||
|
localparam SBERROR_BADADDR = 3'h2;
|
||||||
|
localparam SBERROR_BADALIGN = 3'h3;
|
||||||
|
localparam SBERROR_BADSIZE = 3'h4;
|
||||||
|
|
||||||
|
wire sb_want_start_write = dmi_write && dmi_regaddr == ADDR_SBDATA0;
|
||||||
|
|
||||||
|
wire sb_want_start_read =
|
||||||
|
(sbreadonaddr && dmi_write && dmi_regaddr == ADDR_SBADDRESS0) ||
|
||||||
|
(sbreadondata && dmi_read && dmi_regaddr == ADDR_SBDATA0);
|
||||||
|
|
||||||
|
wire sb_badalign =
|
||||||
|
(sbaccess == 3'h1 && sbaddress[0]) ||
|
||||||
|
(sbaccess == 3'h2 && |sbaddress[1:0]);
|
||||||
|
|
||||||
|
wire sb_badsize = sbaccess > 3'h2;
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
sbbusy <= 1'b0;
|
||||||
|
sbbusyerror <= 1'b0;
|
||||||
|
sbreadonaddr <= 1'b0;
|
||||||
|
sbreadondata <= 1'b0;
|
||||||
|
sbaccess <= 3'h0;
|
||||||
|
sbautoincrement <= 1'b0;
|
||||||
|
sberror <= 3'h0;
|
||||||
|
sb_current_is_write <= 1'b0;
|
||||||
|
end else if (!dmactive) begin
|
||||||
|
sbbusy <= 1'b0;
|
||||||
|
sbbusyerror <= 1'b0;
|
||||||
|
sbreadonaddr <= 1'b0;
|
||||||
|
sbreadondata <= 1'b0;
|
||||||
|
sbaccess <= 3'h0;
|
||||||
|
sbautoincrement <= 1'b0;
|
||||||
|
sberror <= 3'h0;
|
||||||
|
sb_current_is_write <= 1'b0;
|
||||||
|
end else if (HAVE_SBA) begin
|
||||||
|
if (dmi_write && dmi_regaddr == ADDR_SBCS) begin
|
||||||
|
// Assume a transfer is not in progress when written (per spec)
|
||||||
|
sbbusyerror <= sbbusyerror && !dmi_pwdata[22];
|
||||||
|
sbreadonaddr <= dmi_pwdata[20];
|
||||||
|
sbaccess <= dmi_pwdata[19:17];
|
||||||
|
sbautoincrement <= dmi_pwdata[16];
|
||||||
|
sbreadondata <= dmi_pwdata[15];
|
||||||
|
sberror <= sberror & ~dmi_pwdata[14:12];
|
||||||
|
end
|
||||||
|
if (sbbusy) begin
|
||||||
|
if (sb_want_start_read || sb_want_start_write) begin
|
||||||
|
sbbusyerror <= 1'b1;
|
||||||
|
end
|
||||||
|
if (sbus_vld && sbus_rdy) begin
|
||||||
|
sbbusy <= 1'b0;
|
||||||
|
if (sbus_err) begin
|
||||||
|
sberror <= SBERROR_BADADDR;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end else if (sb_want_start_read || sb_want_start_write) begin
|
||||||
|
if (sb_badsize) begin
|
||||||
|
sberror <= SBERROR_BADSIZE;
|
||||||
|
end else if (sb_badalign) begin
|
||||||
|
sberror <= SBERROR_BADALIGN;
|
||||||
|
end else begin
|
||||||
|
sbbusy <= 1'b1;
|
||||||
|
sb_current_is_write <= sb_want_start_write;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign sbus_addr = sbaddress;
|
||||||
|
assign sbus_write = sb_current_is_write;
|
||||||
|
assign sbus_size = sbaccess[1:0];
|
||||||
|
assign sbus_vld = sbbusy;
|
||||||
|
assign sbus_wdata = sbdata;
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Abstract command data registers
|
// Abstract command data registers
|
||||||
|
|
||||||
|
@ -649,6 +788,21 @@ always @ (*) begin
|
||||||
15'h0,
|
15'h0,
|
||||||
abstractauto_autoexecdata // only data0 present
|
abstractauto_autoexecdata // only data0 present
|
||||||
};
|
};
|
||||||
|
ADDR_SBCS: dmi_prdata = {
|
||||||
|
3'h1, // version = 1
|
||||||
|
6'h00,
|
||||||
|
sbbusyerror,
|
||||||
|
sbbusy,
|
||||||
|
sbreadonaddr,
|
||||||
|
sbaccess,
|
||||||
|
sbautoincrement,
|
||||||
|
sbreadondata,
|
||||||
|
sberror,
|
||||||
|
7'h20, // sbasize = 32
|
||||||
|
5'b00111 // 8, 16, 32-bit transfers supported
|
||||||
|
} & {32{|HAVE_SBA}};
|
||||||
|
ADDR_SBDATA0: dmi_prdata = sbdata & {32{|HAVE_SBA}};
|
||||||
|
ADDR_SBADDRESS0: dmi_prdata = sbaddress & {32{|HAVE_SBA}};
|
||||||
ADDR_CONFSTRPTR0: dmi_prdata = 32'h4c296328;
|
ADDR_CONFSTRPTR0: dmi_prdata = 32'h4c296328;
|
||||||
ADDR_CONFSTRPTR1: dmi_prdata = 32'h20656b75;
|
ADDR_CONFSTRPTR1: dmi_prdata = 32'h20656b75;
|
||||||
ADDR_CONFSTRPTR2: dmi_prdata = 32'h6e657257;
|
ADDR_CONFSTRPTR2: dmi_prdata = 32'h6e657257;
|
||||||
|
|
|
@ -64,6 +64,15 @@ module hazard3_cpu_2port #(
|
||||||
output wire dbg_instr_data_rdy,
|
output wire dbg_instr_data_rdy,
|
||||||
output wire dbg_instr_caught_exception,
|
output wire dbg_instr_caught_exception,
|
||||||
output wire dbg_instr_caught_ebreak,
|
output wire dbg_instr_caught_ebreak,
|
||||||
|
// Optional debug system bus access patch-through
|
||||||
|
input wire [31:0] dbg_sbus_addr,
|
||||||
|
input wire dbg_sbus_write,
|
||||||
|
input wire [1:0] dbg_sbus_size,
|
||||||
|
input wire dbg_sbus_vld,
|
||||||
|
output wire dbg_sbus_rdy,
|
||||||
|
output wire dbg_sbus_err,
|
||||||
|
input wire [31:0] dbg_sbus_wdata,
|
||||||
|
output wire [31:0] dbg_sbus_rdata,
|
||||||
|
|
||||||
// Level-sensitive interrupt sources
|
// Level-sensitive interrupt sources
|
||||||
input wire [NUM_IRQ-1:0] irq, // -> mip.meip
|
input wire [NUM_IRQ-1:0] irq, // -> mip.meip
|
||||||
|
@ -192,39 +201,78 @@ assign i_hprot = {
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
// Load/store port
|
// Load/store port
|
||||||
|
|
||||||
assign d_haddr = core_haddr_d;
|
// The debug module has optional System Bus Access support, which can be muxed
|
||||||
assign d_htrans = core_aph_req_d ? HTRANS_NSEQ : HTRANS_IDLE;
|
// into the processor's D port here (or connected to a standalone AHB shim).
|
||||||
assign d_hwrite = core_hwrite_d;
|
// This confers absolutely no advantage for debugger bus throughput, but
|
||||||
assign d_hsize = core_hsize_d;
|
// allows the debugger to access the bus with minimal disturbance to the
|
||||||
assign d_hexcl = core_aph_excl_d;
|
// processor.
|
||||||
|
|
||||||
reg dphase_active_d;
|
wire bus_gnt_d;
|
||||||
always @ (posedge clk or negedge rst_n)
|
wire bus_gnt_s;
|
||||||
if (!rst_n)
|
|
||||||
dphase_active_d <= 1'b0;
|
reg bus_hold_aph;
|
||||||
else if (d_hready)
|
reg [1:0] bus_gnt_ds_prev;
|
||||||
dphase_active_d <= core_aph_req_d;
|
reg bus_active_dph_d;
|
||||||
|
reg bus_active_dph_s;
|
||||||
|
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
bus_hold_aph <= 1'b0;
|
||||||
|
bus_gnt_ds_prev <= 2'h0;
|
||||||
|
end else begin
|
||||||
|
bus_hold_aph <= d_htrans[1] && !d_hready && !d_hresp;
|
||||||
|
bus_gnt_ds_prev <= {bus_gnt_d, bus_gnt_s};
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign {bus_gnt_d, bus_gnt_s} =
|
||||||
|
bus_hold_aph ? bus_gnt_ds_prev :
|
||||||
|
core_aph_req_d ? 2'b10 :
|
||||||
|
dbg_sbus_vld && !bus_active_dph_s ? 2'b01 :
|
||||||
|
2'b00 ;
|
||||||
|
always @ (posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
bus_active_dph_d <= 1'b0;
|
||||||
|
bus_active_dph_s <= 1'b0;
|
||||||
|
end else if (d_hready) begin
|
||||||
|
bus_active_dph_d <= bus_gnt_d;
|
||||||
|
bus_active_dph_s <= bus_gnt_s;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
assign d_htrans = bus_gnt_d || bus_gnt_s ? HTRANS_NSEQ : HTRANS_IDLE;
|
||||||
|
|
||||||
|
assign d_haddr = bus_gnt_s ? dbg_sbus_addr : core_haddr_d;
|
||||||
|
assign d_hwrite = bus_gnt_s ? dbg_sbus_write : core_hwrite_d;
|
||||||
|
assign d_hsize = bus_gnt_s ? dbg_sbus_size : core_hsize_d;
|
||||||
|
assign d_hexcl = bus_gnt_s ? 1'b0 : core_aph_excl_d;
|
||||||
|
|
||||||
|
assign d_hprot = {
|
||||||
|
2'b00, // Noncacheable/nonbufferable
|
||||||
|
bus_gnt_s || core_priv_d, // Privileged or Normal as per core state
|
||||||
|
1'b1 // Data access
|
||||||
|
};
|
||||||
|
|
||||||
|
assign d_hwdata = bus_active_dph_s ? dbg_sbus_wdata : core_wdata_d;
|
||||||
|
|
||||||
// D-side errors are reported even when not ready, so that the core can make
|
// D-side errors are reported even when not ready, so that the core can make
|
||||||
// use of the two-phase error response to cleanly squash a second load/store
|
// use of the two-phase error response to cleanly squash a second load/store
|
||||||
// chasing the faulting one down the pipeline.
|
// chasing the faulting one down the pipeline.
|
||||||
assign core_aph_ready_d = d_hready && core_aph_req_d;
|
assign core_aph_ready_d = d_hready && bus_gnt_d;
|
||||||
assign core_dph_ready_d = d_hready && dphase_active_d;
|
assign core_dph_ready_d = bus_active_dph_d && d_hready;
|
||||||
assign core_dph_err_d = dphase_active_d && d_hresp;
|
assign core_dph_err_d = bus_active_dph_d && d_hresp;
|
||||||
assign core_dph_exokay_d = dphase_active_d && d_hexokay;
|
assign core_dph_exokay_d = bus_active_dph_d && d_hexokay;
|
||||||
|
|
||||||
assign core_rdata_d = d_hrdata;
|
assign core_rdata_d = d_hrdata;
|
||||||
assign d_hwdata = core_wdata_d;
|
|
||||||
|
assign dbg_sbus_err = bus_active_dph_s && d_hresp;
|
||||||
|
assign dbg_sbus_rdy = bus_active_dph_s && d_hready;
|
||||||
|
assign dbg_sbus_rdata = d_hrdata;
|
||||||
|
|
||||||
assign d_hburst = 3'h0;
|
assign d_hburst = 3'h0;
|
||||||
assign d_hmastlock = 1'b0;
|
assign d_hmastlock = 1'b0;
|
||||||
|
|
||||||
assign d_hprot = {
|
|
||||||
2'b00, // Noncacheable/nonbufferable
|
|
||||||
core_priv_d, // Privileged or Normal as per core state
|
|
||||||
1'b1 // Data access
|
|
||||||
};
|
|
||||||
|
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
|
`ifndef YOSYS
|
||||||
`default_nettype wire
|
`default_nettype wire
|
||||||
|
`endif
|
||||||
|
|
|
@ -124,6 +124,15 @@ wire [N_HARTS-1:0] hart_instr_data_rdy;
|
||||||
wire [N_HARTS-1:0] hart_instr_caught_exception;
|
wire [N_HARTS-1:0] hart_instr_caught_exception;
|
||||||
wire [N_HARTS-1:0] hart_instr_caught_ebreak;
|
wire [N_HARTS-1:0] hart_instr_caught_ebreak;
|
||||||
|
|
||||||
|
wire [31:0] sbus_addr;
|
||||||
|
wire sbus_write;
|
||||||
|
wire [1:0] sbus_size;
|
||||||
|
wire sbus_vld;
|
||||||
|
wire sbus_rdy;
|
||||||
|
wire sbus_err;
|
||||||
|
wire [31:0] sbus_wdata;
|
||||||
|
wire [31:0] sbus_rdata;
|
||||||
|
|
||||||
hazard3_dm #(
|
hazard3_dm #(
|
||||||
.N_HARTS (N_HARTS),
|
.N_HARTS (N_HARTS),
|
||||||
.NEXT_DM_ADDR (0)
|
.NEXT_DM_ADDR (0)
|
||||||
|
@ -159,7 +168,16 @@ hazard3_dm #(
|
||||||
.hart_instr_data_vld (hart_instr_data_vld),
|
.hart_instr_data_vld (hart_instr_data_vld),
|
||||||
.hart_instr_data_rdy (hart_instr_data_rdy),
|
.hart_instr_data_rdy (hart_instr_data_rdy),
|
||||||
.hart_instr_caught_exception (hart_instr_caught_exception),
|
.hart_instr_caught_exception (hart_instr_caught_exception),
|
||||||
.hart_instr_caught_ebreak (hart_instr_caught_ebreak)
|
.hart_instr_caught_ebreak (hart_instr_caught_ebreak),
|
||||||
|
|
||||||
|
.sbus_addr (sbus_addr),
|
||||||
|
.sbus_write (sbus_write),
|
||||||
|
.sbus_size (sbus_size),
|
||||||
|
.sbus_vld (sbus_vld),
|
||||||
|
.sbus_rdy (sbus_rdy),
|
||||||
|
.sbus_err (sbus_err),
|
||||||
|
.sbus_wdata (sbus_wdata),
|
||||||
|
.sbus_rdata (sbus_rdata)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
@ -230,6 +248,15 @@ hazard3_cpu_2port #(
|
||||||
.dbg_instr_caught_exception (hart_instr_caught_exception),
|
.dbg_instr_caught_exception (hart_instr_caught_exception),
|
||||||
.dbg_instr_caught_ebreak (hart_instr_caught_ebreak),
|
.dbg_instr_caught_ebreak (hart_instr_caught_ebreak),
|
||||||
|
|
||||||
|
.dbg_sbus_addr (sbus_addr),
|
||||||
|
.dbg_sbus_write (sbus_write),
|
||||||
|
.dbg_sbus_size (sbus_size),
|
||||||
|
.dbg_sbus_vld (sbus_vld),
|
||||||
|
.dbg_sbus_rdy (sbus_rdy),
|
||||||
|
.dbg_sbus_err (sbus_err),
|
||||||
|
.dbg_sbus_wdata (sbus_wdata),
|
||||||
|
.dbg_sbus_rdata (sbus_rdata),
|
||||||
|
|
||||||
.irq (irq),
|
.irq (irq),
|
||||||
.soft_irq (soft_irq[0]),
|
.soft_irq (soft_irq[0]),
|
||||||
.timer_irq (timer_irq)
|
.timer_irq (timer_irq)
|
||||||
|
|
Loading…
Reference in New Issue