feat: publish FreeRTOS C FC11 card
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
file hazard3_ecp5_jtag_dtm.v
|
||||
file hazard3_jtag_dtm_core.v
|
||||
|
||||
file ../cdc/hazard3_apb_async_bridge.v
|
||||
file ../cdc/hazard3_reset_sync.v
|
||||
file ../cdc/hazard3_sync_1bit.v
|
||||
@@ -0,0 +1,194 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2021-2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
// The ECP5 JTAGG primitive (yes that is the correct spelling) allows you to
|
||||
// add two custom DRs to the FPGA's chip TAP, selected using the 8-bit ER1
|
||||
// (0x32) and ER2 (0x38) instructions.
|
||||
//
|
||||
// Brian Swetland pointed out on Twitter that the standard RISC-V JTAG-DTM
|
||||
// only uses two DRs (DTMCS and DMI), besides the standard IDCODE and BYPASS
|
||||
// which are provided already by the ECP5 TAP. This file instantiates the
|
||||
// guts of Hazard3's standard JTAG-DTM and connects the DTMCS and DMI
|
||||
// registers to the JTAGG primitive's ER1/ER2 DRs.
|
||||
//
|
||||
// The exciting part is that upstream OpenOCD already allows you to set the IR
|
||||
// length *and* set custom DTMCS/DMI IR values for RISC-V JTAG DTMs. This
|
||||
// means with the right config file, you can access a debug module hung from
|
||||
// the ECP5 TAP in this fashion using only upstream OpenOCD and gdb.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module hazard3_ecp5_jtag_dtm #(
|
||||
parameter DTMCS_IDLE_HINT = 3'd4,
|
||||
parameter W_PADDR = 9,
|
||||
parameter ABITS = W_PADDR - 2 // do not modify
|
||||
) (
|
||||
// This is synchronous to TCK and asserted for one TCK cycle only
|
||||
output wire dmihardreset_req,
|
||||
|
||||
// Bus clock + reset for Debug Module Interface
|
||||
input wire clk_dmi,
|
||||
input wire rst_n_dmi,
|
||||
|
||||
// Debug Module Interface (APB)
|
||||
output wire dmi_psel,
|
||||
output wire dmi_penable,
|
||||
output wire dmi_pwrite,
|
||||
output wire [W_PADDR-1:0] dmi_paddr,
|
||||
output wire [31:0] dmi_pwdata,
|
||||
input wire [31:0] dmi_prdata,
|
||||
input wire dmi_pready,
|
||||
input wire dmi_pslverr
|
||||
);
|
||||
|
||||
// Signals to/from the ECP5 TAP
|
||||
|
||||
wire jtdo2;
|
||||
wire jtdo1;
|
||||
wire jtdi;
|
||||
wire jtck_posedge_dont_use;
|
||||
wire jshift;
|
||||
wire jupdate;
|
||||
wire jrst_n;
|
||||
wire jce2;
|
||||
wire jce1;
|
||||
|
||||
JTAGG jtag_u (
|
||||
.JTDO2 (jtdo2),
|
||||
.JTDO1 (jtdo1),
|
||||
.JTDI (jtdi),
|
||||
.JTCK (jtck_posedge_dont_use),
|
||||
.JRTI2 (/* unused */),
|
||||
.JRTI1 (/* unused */),
|
||||
.JSHIFT (jshift),
|
||||
.JUPDATE (jupdate),
|
||||
.JRSTN (jrst_n),
|
||||
.JCE2 (jce2),
|
||||
.JCE1 (jce1)
|
||||
);
|
||||
|
||||
// JTAGG primitive asserts its signals synchronously to JTCK's posedge, but
|
||||
// you get weird and inconsistent results if you try to consume them
|
||||
// synchronously on JTCK's posedge, possibly due to a lack of hold
|
||||
// constraints in nextpnr.
|
||||
//
|
||||
// A quick hack is to move the sampling onto the negedge of the clock. This
|
||||
// then creates more problems because we would be running our shift logic on
|
||||
// a different edge from the control + CDC logic in the DTM core.
|
||||
//
|
||||
// So, even worse hack, move all our JTAG-domain logic onto the negedge
|
||||
// (or near enough) by inverting the clock.
|
||||
|
||||
wire jtck = !jtck_posedge_dont_use;
|
||||
|
||||
localparam W_DR_SHIFT = ABITS + 32 + 2;
|
||||
|
||||
reg core_dr_wen;
|
||||
reg core_dr_ren;
|
||||
reg core_dr_sel_dmi_ndtmcs;
|
||||
reg dr_shift_en;
|
||||
wire [W_DR_SHIFT-1:0] core_dr_wdata;
|
||||
wire [W_DR_SHIFT-1:0] core_dr_rdata;
|
||||
|
||||
// Decode our shift controls from the interesting ECP5 ones, and re-register
|
||||
// onto JTCK negedge (our posedge). Note without re-registering we observe
|
||||
// them a half-cycle (effectively one cycle) too early. This is another
|
||||
// consequence of the stupid JTDI thing
|
||||
|
||||
always @ (posedge jtck or negedge jrst_n) begin
|
||||
if (!jrst_n) begin
|
||||
core_dr_sel_dmi_ndtmcs <= 1'b0;
|
||||
core_dr_wen <= 1'b0;
|
||||
core_dr_ren <= 1'b0;
|
||||
dr_shift_en <= 1'b0;
|
||||
end else begin
|
||||
if (jce1 || jce2)
|
||||
core_dr_sel_dmi_ndtmcs <= jce2;
|
||||
core_dr_ren <= (jce1 || jce2) && !jshift;
|
||||
core_dr_wen <= jupdate;
|
||||
dr_shift_en <= jshift;
|
||||
end
|
||||
end
|
||||
|
||||
reg [W_DR_SHIFT-1:0] dr_shift;
|
||||
assign core_dr_wdata = dr_shift;
|
||||
|
||||
always @ (posedge jtck or negedge jrst_n) begin
|
||||
if (!jrst_n) begin
|
||||
dr_shift <= {W_DR_SHIFT{1'b0}};
|
||||
end else if (core_dr_ren) begin
|
||||
dr_shift <= core_dr_rdata;
|
||||
end else if (dr_shift_en) begin
|
||||
dr_shift <= {jtdi, dr_shift[W_DR_SHIFT-1:1]};
|
||||
if (!core_dr_sel_dmi_ndtmcs)
|
||||
dr_shift[31] <= jtdi;
|
||||
end
|
||||
end
|
||||
|
||||
// Not documented on ECP5: as well as the posedge flop on JTDI, the ECP5 puts
|
||||
// a negedge flop on JTDO1, JTDO2. (Conjecture based on dicking around with a
|
||||
// logic analyser.) To get JTDOx to appear with the same timing as our shifter
|
||||
// LSB (which we update on every JTCK negedge) we:
|
||||
//
|
||||
// - Register the LSB of the *next* value of dr_shift on the JTCK posedge, so
|
||||
// half a cycle earlier than the actual dr_shift update
|
||||
//
|
||||
// - This then gets re-registered with the pointless JTDO negedge flops, so
|
||||
// that it appears with the same timing as our DR shifter update.
|
||||
|
||||
reg dr_shift_next_halfcycle;
|
||||
always @ (negedge jtck or negedge jrst_n) begin
|
||||
if (!jrst_n) begin
|
||||
dr_shift_next_halfcycle <= 1'b0;
|
||||
end else begin
|
||||
dr_shift_next_halfcycle <=
|
||||
core_dr_ren ? core_dr_rdata[0] :
|
||||
dr_shift_en ? dr_shift[1] : dr_shift[0];
|
||||
end
|
||||
end
|
||||
|
||||
// We have only a single shifter for the ER1 and ER2 chains, so these are tied
|
||||
// together:
|
||||
|
||||
assign jtdo1 = dr_shift_next_halfcycle;
|
||||
assign jtdo2 = dr_shift_next_halfcycle;
|
||||
|
||||
// The actual DTM is in here:
|
||||
|
||||
hazard3_jtag_dtm_core #(
|
||||
.DTMCS_IDLE_HINT (DTMCS_IDLE_HINT),
|
||||
.W_ADDR (ABITS)
|
||||
) inst_hazard3_jtag_dtm_core (
|
||||
.tck (jtck),
|
||||
.trst_n (jrst_n),
|
||||
|
||||
.clk_dmi (clk_dmi),
|
||||
.rst_n_dmi (rst_n_dmi),
|
||||
|
||||
.dr_wen (core_dr_wen),
|
||||
.dr_ren (core_dr_ren),
|
||||
.dr_sel_dmi_ndtmcs (core_dr_sel_dmi_ndtmcs),
|
||||
.dr_wdata (core_dr_wdata),
|
||||
.dr_rdata (core_dr_rdata),
|
||||
|
||||
.dmihardreset_req (dmihardreset_req),
|
||||
|
||||
.dmi_psel (dmi_psel),
|
||||
.dmi_penable (dmi_penable),
|
||||
.dmi_pwrite (dmi_pwrite),
|
||||
.dmi_paddr (dmi_paddr[W_PADDR-1:2]),
|
||||
.dmi_pwdata (dmi_pwdata),
|
||||
.dmi_prdata (dmi_prdata),
|
||||
.dmi_pready (dmi_pready),
|
||||
.dmi_pslverr (dmi_pslverr)
|
||||
);
|
||||
|
||||
assign dmi_paddr[1:0] = 2'b00;
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
@@ -0,0 +1,6 @@
|
||||
file hazard3_jtag_dtm.v
|
||||
file hazard3_jtag_dtm_core.v
|
||||
|
||||
file ../cdc/hazard3_apb_async_bridge.v
|
||||
file ../cdc/hazard3_reset_sync.v
|
||||
file ../cdc/hazard3_sync_1bit.v
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2021-2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
// Implementation of standard RISC-V JTAG-DTM with an APB Debug Module
|
||||
// Interface. The TAP itself is clocked directly by JTAG TCK; a clock
|
||||
// crossing is instantiated internally between the TCK domain and the DMI bus
|
||||
// clock domain.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module hazard3_jtag_dtm #(
|
||||
parameter IDCODE = 32'h0000_0001,
|
||||
parameter DTMCS_IDLE_HINT = 3'd4,
|
||||
parameter W_PADDR = 9,
|
||||
parameter ABITS = W_PADDR - 2 // do not modify
|
||||
) (
|
||||
// Standard JTAG signals -- the JTAG hardware is clocked directly by TCK.
|
||||
input wire tck,
|
||||
input wire trst_n,
|
||||
input wire tms,
|
||||
input wire tdi,
|
||||
output reg tdo,
|
||||
|
||||
// This is synchronous to TCK and asserted for one TCK cycle only
|
||||
output wire dmihardreset_req,
|
||||
|
||||
// Bus clock + reset for Debug Module Interface
|
||||
input wire clk_dmi,
|
||||
input wire rst_n_dmi,
|
||||
|
||||
// Debug Module Interface (APB)
|
||||
output wire dmi_psel,
|
||||
output wire dmi_penable,
|
||||
output wire dmi_pwrite,
|
||||
output wire [W_PADDR-1:0] dmi_paddr,
|
||||
output wire [31:0] dmi_pwdata,
|
||||
input wire [31:0] dmi_prdata,
|
||||
input wire dmi_pready,
|
||||
input wire dmi_pslverr
|
||||
);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// TAP state machine
|
||||
|
||||
reg [3:0] tap_state;
|
||||
localparam S_RESET = 4'd0;
|
||||
localparam S_RUN_IDLE = 4'd1;
|
||||
localparam S_SELECT_DR = 4'd2;
|
||||
localparam S_CAPTURE_DR = 4'd3;
|
||||
localparam S_SHIFT_DR = 4'd4;
|
||||
localparam S_EXIT1_DR = 4'd5;
|
||||
localparam S_PAUSE_DR = 4'd6;
|
||||
localparam S_EXIT2_DR = 4'd7;
|
||||
localparam S_UPDATE_DR = 4'd8;
|
||||
localparam S_SELECT_IR = 4'd9;
|
||||
localparam S_CAPTURE_IR = 4'd10;
|
||||
localparam S_SHIFT_IR = 4'd11;
|
||||
localparam S_EXIT1_IR = 4'd12;
|
||||
localparam S_PAUSE_IR = 4'd13;
|
||||
localparam S_EXIT2_IR = 4'd14;
|
||||
localparam S_UPDATE_IR = 4'd15;
|
||||
|
||||
always @ (posedge tck or negedge trst_n) begin
|
||||
if (!trst_n) begin
|
||||
tap_state <= S_RESET;
|
||||
end else case(tap_state)
|
||||
S_RESET : tap_state <= tms ? S_RESET : S_RUN_IDLE ;
|
||||
S_RUN_IDLE : tap_state <= tms ? S_SELECT_DR : S_RUN_IDLE ;
|
||||
|
||||
S_SELECT_DR : tap_state <= tms ? S_SELECT_IR : S_CAPTURE_DR;
|
||||
S_CAPTURE_DR : tap_state <= tms ? S_EXIT1_DR : S_SHIFT_DR ;
|
||||
S_SHIFT_DR : tap_state <= tms ? S_EXIT1_DR : S_SHIFT_DR ;
|
||||
S_EXIT1_DR : tap_state <= tms ? S_UPDATE_DR : S_PAUSE_DR ;
|
||||
S_PAUSE_DR : tap_state <= tms ? S_EXIT2_DR : S_PAUSE_DR ;
|
||||
S_EXIT2_DR : tap_state <= tms ? S_UPDATE_DR : S_SHIFT_DR ;
|
||||
S_UPDATE_DR : tap_state <= tms ? S_SELECT_DR : S_RUN_IDLE ;
|
||||
|
||||
S_SELECT_IR : tap_state <= tms ? S_RESET : S_CAPTURE_IR;
|
||||
S_CAPTURE_IR : tap_state <= tms ? S_EXIT1_IR : S_SHIFT_IR ;
|
||||
S_SHIFT_IR : tap_state <= tms ? S_EXIT1_IR : S_SHIFT_IR ;
|
||||
S_EXIT1_IR : tap_state <= tms ? S_UPDATE_IR : S_PAUSE_IR ;
|
||||
S_PAUSE_IR : tap_state <= tms ? S_EXIT2_IR : S_PAUSE_IR ;
|
||||
S_EXIT2_IR : tap_state <= tms ? S_UPDATE_IR : S_SHIFT_IR ;
|
||||
S_UPDATE_IR : tap_state <= tms ? S_SELECT_DR : S_RUN_IDLE ;
|
||||
endcase
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Instruction register
|
||||
|
||||
localparam W_IR = 5;
|
||||
// All other encodings behave as BYPASS:
|
||||
localparam IR_IDCODE = 5'h01;
|
||||
localparam IR_DTMCS = 5'h10;
|
||||
localparam IR_DMI = 5'h11;
|
||||
|
||||
reg [W_IR-1:0] ir_shift;
|
||||
reg [W_IR-1:0] ir;
|
||||
|
||||
always @ (posedge tck or negedge trst_n) begin
|
||||
if (!trst_n) begin
|
||||
ir_shift <= {W_IR{1'b0}};
|
||||
ir <= IR_IDCODE;
|
||||
end else if (tap_state == S_RESET) begin
|
||||
ir_shift <= {W_IR{1'b0}};
|
||||
ir <= IR_IDCODE;
|
||||
end else if (tap_state == S_CAPTURE_IR) begin
|
||||
ir_shift <= ir;
|
||||
end else if (tap_state == S_SHIFT_IR) begin
|
||||
ir_shift <= {tdi, ir_shift[W_IR-1:1]};
|
||||
end else if (tap_state == S_UPDATE_IR) begin
|
||||
ir <= ir_shift;
|
||||
end
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Data registers
|
||||
|
||||
// Shift register is sized to largest DR, which is DMI:
|
||||
// {addr[7:0], data[31:0], op[1:0]}
|
||||
localparam W_DR_SHIFT = ABITS + 32 + 2;
|
||||
|
||||
reg [W_DR_SHIFT-1:0] dr_shift;
|
||||
|
||||
// Signals to/from the DTM core, which implements the DTMCS and DMI registers
|
||||
wire core_dr_wen;
|
||||
wire core_dr_ren;
|
||||
wire core_dr_sel_dmi_ndtmcs;
|
||||
wire [W_DR_SHIFT-1:0] core_dr_wdata;
|
||||
wire [W_DR_SHIFT-1:0] core_dr_rdata;
|
||||
|
||||
always @ (posedge tck or negedge trst_n) begin
|
||||
if (!trst_n) begin
|
||||
dr_shift <= {W_DR_SHIFT{1'b0}};
|
||||
end else if (tap_state == S_SHIFT_DR) begin
|
||||
dr_shift <= {tdi, dr_shift[W_DR_SHIFT-1:1]};
|
||||
// Shorten DR shift chain according to IR
|
||||
if (ir == IR_DMI)
|
||||
dr_shift[W_DR_SHIFT - 1] <= tdi;
|
||||
else if (ir == IR_IDCODE || ir == IR_DTMCS)
|
||||
dr_shift[31] <= tdi;
|
||||
else // BYPASS
|
||||
dr_shift[0] <= tdi;
|
||||
end else if (tap_state == S_CAPTURE_DR) begin
|
||||
if (ir == IR_DMI || ir == IR_DTMCS) begin
|
||||
dr_shift <= core_dr_rdata;
|
||||
end else if (ir == IR_IDCODE) begin
|
||||
dr_shift <= {{W_DR_SHIFT-32{1'b0}}, IDCODE};
|
||||
end else begin // BYPASS
|
||||
dr_shift <= {W_DR_SHIFT{1'b0}};
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// Must retime shift data onto negedge before presenting on TDO
|
||||
|
||||
always @ (negedge tck or negedge trst_n) begin
|
||||
if (!trst_n) begin
|
||||
tdo <= 1'b0;
|
||||
end else begin
|
||||
tdo <= tap_state == S_SHIFT_IR ? ir_shift[0] :
|
||||
tap_state == S_SHIFT_DR ? dr_shift[0] : 1'b0;
|
||||
end
|
||||
end
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// Core logic and bus interface
|
||||
|
||||
assign core_dr_sel_dmi_ndtmcs = ir == IR_DMI;
|
||||
assign core_dr_wen = (ir == IR_DMI || ir == IR_DTMCS) && tap_state == S_UPDATE_DR;
|
||||
assign core_dr_ren = (ir == IR_DMI || ir == IR_DTMCS) && tap_state == S_CAPTURE_DR;
|
||||
|
||||
assign core_dr_wdata = dr_shift;
|
||||
|
||||
hazard3_jtag_dtm_core #(
|
||||
.DTMCS_IDLE_HINT (DTMCS_IDLE_HINT),
|
||||
.W_ADDR (ABITS)
|
||||
) dtm_core (
|
||||
.tck (tck),
|
||||
.trst_n (trst_n),
|
||||
.clk_dmi (clk_dmi),
|
||||
.rst_n_dmi (rst_n_dmi),
|
||||
|
||||
.dmihardreset_req (dmihardreset_req),
|
||||
|
||||
.dr_wen (core_dr_wen),
|
||||
.dr_ren (core_dr_ren),
|
||||
.dr_sel_dmi_ndtmcs (core_dr_sel_dmi_ndtmcs),
|
||||
.dr_wdata (core_dr_wdata),
|
||||
.dr_rdata (core_dr_rdata),
|
||||
|
||||
.dmi_psel (dmi_psel),
|
||||
.dmi_penable (dmi_penable),
|
||||
.dmi_pwrite (dmi_pwrite),
|
||||
.dmi_paddr (dmi_paddr[W_PADDR-1:2]),
|
||||
.dmi_pwdata (dmi_pwdata),
|
||||
.dmi_prdata (dmi_prdata),
|
||||
.dmi_pready (dmi_pready),
|
||||
.dmi_pslverr (dmi_pslverr)
|
||||
);
|
||||
|
||||
assign dmi_paddr[1:0] = 2'b00;
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
@@ -0,0 +1,185 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2021-2022 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
// DTMCS + DMI control logic, bus interface and bus clock domain crossing for
|
||||
// a standard RISC-V APB JTAG-DTM. Essentially everything apart from the
|
||||
// actual TAP controller, IR and shift registers. Instantiated by
|
||||
// hazard3_jtag_dtm.v.
|
||||
//
|
||||
// This core logic can be reused and connected to some other serial transport
|
||||
// or, for example, the ECP5 JTAGG primitive (see hazard5_ecp5_jtag_dtm.v)
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module hazard3_jtag_dtm_core #(
|
||||
parameter DTMCS_IDLE_HINT = 3'd4,
|
||||
parameter W_ADDR = 8,
|
||||
parameter W_DR_SHIFT = W_ADDR + 32 + 2 // do not modify
|
||||
) (
|
||||
input wire tck,
|
||||
input wire trst_n,
|
||||
|
||||
input wire clk_dmi,
|
||||
input wire rst_n_dmi,
|
||||
|
||||
// DR capture/update (read/write) signals
|
||||
input wire dr_wen,
|
||||
input wire dr_ren,
|
||||
input wire dr_sel_dmi_ndtmcs,
|
||||
input wire [W_DR_SHIFT-1:0] dr_wdata,
|
||||
output wire [W_DR_SHIFT-1:0] dr_rdata,
|
||||
|
||||
// This is synchronous to TCK and asserted for one TCK cycle only
|
||||
output reg dmihardreset_req,
|
||||
|
||||
// Debug Module Interface (APB)
|
||||
output wire dmi_psel,
|
||||
output wire dmi_penable,
|
||||
output wire dmi_pwrite,
|
||||
output wire [W_ADDR-1:0] dmi_paddr,
|
||||
output wire [31:0] dmi_pwdata,
|
||||
input wire [31:0] dmi_prdata,
|
||||
input wire dmi_pready,
|
||||
input wire dmi_pslverr
|
||||
);
|
||||
|
||||
wire write_dmi = dr_wen && dr_sel_dmi_ndtmcs;
|
||||
wire write_dtmcs = dr_wen && !dr_sel_dmi_ndtmcs;
|
||||
wire read_dmi = dr_ren && dr_sel_dmi_ndtmcs;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DMI bus adapter
|
||||
|
||||
reg [1:0] dmi_cmderr;
|
||||
reg dmi_busy;
|
||||
|
||||
// DTM-domain bus, connected to a matching DM-domain bus via an APB crossing:
|
||||
wire dtm_psel;
|
||||
wire dtm_penable;
|
||||
wire dtm_pwrite;
|
||||
wire [W_ADDR-1:0] dtm_paddr;
|
||||
wire [31:0] dtm_pwdata;
|
||||
wire [31:0] dtm_prdata;
|
||||
wire dtm_pready;
|
||||
wire dtm_pslverr;
|
||||
|
||||
// We are relying on some particular features of our APB clock crossing here
|
||||
// to save some registers:
|
||||
//
|
||||
// - The transfer is launched immediately when psel is seen, no need to
|
||||
// actually assert an access phase (as the standard allows the CDC to
|
||||
// assume that access immediately follows setup) and no need to maintain
|
||||
// pwrite/paddr/pwdata valid after the setup phase
|
||||
//
|
||||
// - prdata/pslverr remain valid after the transfer completes, until the next
|
||||
// transfer completes
|
||||
//
|
||||
// These allow us to connect the upstream side of the CDC directly to our DR
|
||||
// shifter without any sample/hold registers in between.
|
||||
|
||||
// psel is only pulsed for one cycle, penable is not asserted.
|
||||
assign dtm_psel = write_dmi &&
|
||||
(dr_wdata[1:0] == 2'd1 || dr_wdata[1:0] == 2'd2) &&
|
||||
!(dmi_busy || dmi_cmderr != 2'd0) && dtm_pready;
|
||||
assign dtm_penable = 1'b0;
|
||||
|
||||
// paddr/pwdata/pwrite are valid momentarily when psel is asserted.
|
||||
assign dtm_paddr = dr_wdata[34 +: W_ADDR];
|
||||
assign dtm_pwrite = dr_wdata[1];
|
||||
assign dtm_pwdata = dr_wdata[2 +: 32];
|
||||
|
||||
always @ (posedge tck or negedge trst_n) begin
|
||||
if (!trst_n) begin
|
||||
dmi_busy <= 1'b0;
|
||||
dmi_cmderr <= 2'd0;
|
||||
end else if (read_dmi) begin
|
||||
// Reading while busy sets the busy sticky error. Note the capture
|
||||
// into shift register should also reflect this update on-the-fly
|
||||
if (dmi_busy && dmi_cmderr == 2'd0)
|
||||
dmi_cmderr <= 2'h3;
|
||||
end else if (write_dtmcs) begin
|
||||
// Writing dtmcs.dmireset = 1 clears a sticky error
|
||||
if (dr_wdata[16])
|
||||
dmi_cmderr <= 2'd0;
|
||||
end else if (write_dmi) begin
|
||||
if (dtm_psel) begin
|
||||
dmi_busy <= 1'b1;
|
||||
end else if (dr_wdata[1:0] != 2'd0) begin
|
||||
// DMI ignored operation, so set sticky busy
|
||||
if (dmi_cmderr == 2'd0)
|
||||
dmi_cmderr <= 2'd3;
|
||||
end
|
||||
end else if (dmi_busy && dtm_pready) begin
|
||||
dmi_busy <= 1'b0;
|
||||
if (dmi_cmderr == 2'd0 && dtm_pslverr)
|
||||
dmi_cmderr <= 2'd2;
|
||||
end
|
||||
end
|
||||
|
||||
// DTM logic is in TCK domain, actual DMI + DM is in processor domain
|
||||
|
||||
hazard3_apb_async_bridge #(
|
||||
.W_ADDR (W_ADDR),
|
||||
.W_DATA (32),
|
||||
.N_SYNC_STAGES (2)
|
||||
) inst_hazard3_apb_async_bridge (
|
||||
.clk_src (tck),
|
||||
.rst_n_src (trst_n),
|
||||
|
||||
.clk_dst (clk_dmi),
|
||||
.rst_n_dst (rst_n_dmi),
|
||||
|
||||
.src_psel (dtm_psel),
|
||||
.src_penable (dtm_penable),
|
||||
.src_pwrite (dtm_pwrite),
|
||||
.src_paddr (dtm_paddr),
|
||||
.src_pwdata (dtm_pwdata),
|
||||
.src_prdata (dtm_prdata),
|
||||
.src_pready (dtm_pready),
|
||||
.src_pslverr (dtm_pslverr),
|
||||
|
||||
.dst_psel (dmi_psel),
|
||||
.dst_penable (dmi_penable),
|
||||
.dst_pwrite (dmi_pwrite),
|
||||
.dst_paddr (dmi_paddr),
|
||||
.dst_pwdata (dmi_pwdata),
|
||||
.dst_prdata (dmi_prdata),
|
||||
.dst_pready (dmi_pready),
|
||||
.dst_pslverr (dmi_pslverr)
|
||||
);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// DR read/write
|
||||
|
||||
wire [W_DR_SHIFT-1:0] dtmcs_rdata = {
|
||||
{W_ADDR{1'b0}},
|
||||
19'h0,
|
||||
DTMCS_IDLE_HINT[2:0],
|
||||
dmi_cmderr,
|
||||
W_ADDR[5:0], // abits
|
||||
4'd1 // version
|
||||
};
|
||||
|
||||
wire [W_DR_SHIFT-1:0] dmi_rdata = {
|
||||
{W_ADDR{1'b0}},
|
||||
dtm_prdata,
|
||||
dmi_busy && dmi_cmderr == 2'd0 ? 2'd3 : dmi_cmderr
|
||||
};
|
||||
|
||||
assign dr_rdata = dr_sel_dmi_ndtmcs ? dmi_rdata : dtmcs_rdata;
|
||||
|
||||
always @ (posedge tck or negedge trst_n) begin
|
||||
if (!trst_n) begin
|
||||
dmihardreset_req <= 1'b0;
|
||||
end else begin
|
||||
dmihardreset_req <= write_dtmcs && dr_wdata[17];
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
@@ -0,0 +1,162 @@
|
||||
/*****************************************************************************\
|
||||
| Copyright (C) 2021-2025 Luke Wren |
|
||||
| SPDX-License-Identifier: Apache-2.0 |
|
||||
\*****************************************************************************/
|
||||
|
||||
// Implement a RISC-V JTAG DTM tunnelled through a Xilinx BSCANE2 primitive.
|
||||
//
|
||||
// Xilinx allows up to four custom DRs to be added to the FPGA TAP controller.
|
||||
// A JTAG-DTM only needs two: DTMCS and DMI.
|
||||
//
|
||||
// With the correct config, OpenOCD can treat the FPGA TAP as a JTAG DTM and
|
||||
// access RISC-V debug directly. This allows you to debug internal RISC-V
|
||||
// cores with the same JTAG interface you use to load the FPGA.
|
||||
//
|
||||
// CHAIN_DTMCS and CHAIN_DMI select which JTAG IR values are used to access
|
||||
// these DRs. Values 1 through 4 correspond to Xilinx USER1 through USER4
|
||||
// instructions, which have IR values 0x02, 0x03, 0x22, 0x23.
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module hazard3_xilinx7_jtag_dtm #(
|
||||
parameter SEL_DTMCS = 3,
|
||||
parameter SEL_DMI = 4,
|
||||
parameter DTMCS_IDLE_HINT = 3'd4,
|
||||
parameter W_PADDR = 9,
|
||||
parameter ABITS = W_PADDR - 2 // do not modify
|
||||
) (
|
||||
// This is synchronous to TCK and asserted for one TCK cycle only
|
||||
output wire dmihardreset_req,
|
||||
|
||||
// Bus clock + reset for Debug Module Interface
|
||||
input wire clk_dmi,
|
||||
input wire rst_n_dmi,
|
||||
|
||||
// Debug Module Interface (APB)
|
||||
output wire dmi_psel,
|
||||
output wire dmi_penable,
|
||||
output wire dmi_pwrite,
|
||||
output wire [W_PADDR-1:0] dmi_paddr,
|
||||
output wire [31:0] dmi_pwdata,
|
||||
input wire [31:0] dmi_prdata,
|
||||
input wire dmi_pready,
|
||||
input wire dmi_pslverr
|
||||
);
|
||||
|
||||
// Signals to/from the Xilinx TAP
|
||||
|
||||
wire jtck_unbuf;
|
||||
wire jtck;
|
||||
wire jtdo2;
|
||||
wire jtdo1;
|
||||
wire jtdi;
|
||||
wire jshift;
|
||||
wire jupdate;
|
||||
wire jcapture;
|
||||
wire jrst;
|
||||
wire jrst_n = !jrst;
|
||||
wire jce2;
|
||||
wire jce1;
|
||||
|
||||
BSCANE2 #(
|
||||
.JTAG_CHAIN (SEL_DTMCS) // Value for USER command.
|
||||
) bscan_dtmcs (
|
||||
.CAPTURE (jcapture), // CAPTURE output from TAP controller.
|
||||
.DRCK (/* unused */), // Gated TCK output. When SEL is asserted, DRCK toggles when CAPTURE or SHIFT are asserted.
|
||||
.RESET (jrst), // Reset output for TAP controller.
|
||||
.RUNTEST (/* unused */), // Output asserted when TAP controller is in Run Test/Idle state.
|
||||
.SEL (jce1), // USER instruction active output.
|
||||
.SHIFT (jshift), // SHIFT output from TAP controller.
|
||||
.TCK (jtck_unbuf), // Test Clock output. Fabric connection to TAP Clock pin.
|
||||
.TDI (jtdi), // Test Data Input (TDI) output from TAP controller.
|
||||
.TMS (/* unused */), // Test Mode Select output. Fabric connection to TAP.
|
||||
.UPDATE (jupdate), // UPDATE output from TAP controller
|
||||
.TDO (jtdo1) // Test Data Output (TDO) input for USER function.
|
||||
);
|
||||
|
||||
BSCANE2 #(
|
||||
.JTAG_CHAIN (SEL_DMI)
|
||||
) bscan_dmi (
|
||||
.CAPTURE (/* unused */),
|
||||
.DRCK (/* unused */),
|
||||
.RESET (/* unused */),
|
||||
.RUNTEST (/* unused */),
|
||||
.SEL (jce2),
|
||||
.SHIFT (/* unused */),
|
||||
.TCK (/* unused */),
|
||||
.TDI (/* unused */),
|
||||
.TMS (/* unused */),
|
||||
.UPDATE (/* unused */),
|
||||
.TDO (jtdo2)
|
||||
);
|
||||
|
||||
BUFG bufg_jtck (
|
||||
.I (jtck_unbuf),
|
||||
.O (jtck)
|
||||
);
|
||||
|
||||
localparam W_DR_SHIFT = ABITS + 32 + 2;
|
||||
|
||||
wire core_dr_wen = jupdate;
|
||||
wire core_dr_ren = jcapture;
|
||||
wire core_dr_sel_dmi_ndtmcs = !jce1;
|
||||
wire dr_shift_en = jshift;
|
||||
wire [W_DR_SHIFT-1:0] core_dr_wdata;
|
||||
wire [W_DR_SHIFT-1:0] core_dr_rdata;
|
||||
|
||||
reg [W_DR_SHIFT-1:0] dr_shift;
|
||||
assign core_dr_wdata = dr_shift;
|
||||
|
||||
always @ (posedge jtck or negedge jrst_n) begin
|
||||
if (!jrst_n) begin
|
||||
dr_shift <= {W_DR_SHIFT{1'b0}};
|
||||
end else if (core_dr_ren) begin
|
||||
dr_shift <= core_dr_rdata;
|
||||
end else if (dr_shift_en) begin
|
||||
dr_shift <= {jtdi, dr_shift[W_DR_SHIFT-1:1]};
|
||||
if (!core_dr_sel_dmi_ndtmcs)
|
||||
dr_shift[31] <= jtdi;
|
||||
end
|
||||
end
|
||||
|
||||
// We have only a single shifter for the two DRs, so these are tied together:
|
||||
assign jtdo1 = dr_shift[0];
|
||||
assign jtdo2 = dr_shift[0];
|
||||
|
||||
// The actual DTM is in here:
|
||||
|
||||
hazard3_jtag_dtm_core #(
|
||||
.DTMCS_IDLE_HINT (DTMCS_IDLE_HINT),
|
||||
.W_ADDR (ABITS)
|
||||
) inst_hazard3_jtag_dtm_core (
|
||||
.tck (jtck),
|
||||
.trst_n (jrst_n),
|
||||
|
||||
.clk_dmi (clk_dmi),
|
||||
.rst_n_dmi (rst_n_dmi),
|
||||
|
||||
.dr_wen (core_dr_wen),
|
||||
.dr_ren (core_dr_ren),
|
||||
.dr_sel_dmi_ndtmcs (core_dr_sel_dmi_ndtmcs),
|
||||
.dr_wdata (core_dr_wdata),
|
||||
.dr_rdata (core_dr_rdata),
|
||||
|
||||
.dmihardreset_req (dmihardreset_req),
|
||||
|
||||
.dmi_psel (dmi_psel),
|
||||
.dmi_penable (dmi_penable),
|
||||
.dmi_pwrite (dmi_pwrite),
|
||||
.dmi_paddr (dmi_paddr[W_PADDR-1:2]),
|
||||
.dmi_pwdata (dmi_pwdata),
|
||||
.dmi_prdata (dmi_prdata),
|
||||
.dmi_pready (dmi_pready),
|
||||
.dmi_pslverr (dmi_pslverr)
|
||||
);
|
||||
|
||||
assign dmi_paddr[1:0] = 2'b00;
|
||||
|
||||
endmodule
|
||||
|
||||
`ifndef YOSYS
|
||||
`default_nettype wire
|
||||
`endif
|
||||
Reference in New Issue
Block a user