feat: publish FreeRTOS C FC06 card

This commit is contained in:
2026-07-19 16:36:03 +02:00
commit 0fc6158501
195 changed files with 60591 additions and 0 deletions
+6
View File
@@ -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
+194
View File
@@ -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
+6
View File
@@ -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
View File
@@ -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
+185
View File
@@ -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
+162
View File
@@ -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