Skip to content

Commit 5b4a57d

Browse files
Merge pull request #2439 from Quantum-Serendipity/fix-jtagphy-pr2410
Fix jtagphy pr2410 vibeslop regressions
2 parents d4233f9 + b940838 commit 5b4a57d

File tree

3 files changed

+542
-43
lines changed

3 files changed

+542
-43
lines changed

litex/build/openocd.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ def stream(self, port=20000, chain=1):
154154
set readable [expr { "0x${rxj}" & 0x200 }]
155155
set writable [expr { "0x${rxj}" & $writable }]
156156
if {$readable} {
157-
append rx [format %c [expr { ("0x${rxj}" >> 1) & 0xff }]]
157+
append rx [binary format c [expr { ("0x${rxj}" >> 1) & 0xff }]]
158158
}
159159
}
160160
return [list $rx $readable $writable]

litex/soc/cores/jtag.py

Lines changed: 41 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -429,8 +429,8 @@ def __init__(self, jtag=None, device=None, data_width=8, clock_domain="sys", cha
429429

430430

431431
# JTAG TAP ---------------------------------------------------------------------------------
432+
jtag_tdi_delay = 0
432433
if jtag is None:
433-
jtag_tdi_delay = 0
434434
# Xilinx.
435435
if XilinxJTAG.get_primitive(device) is not None:
436436
jtag = XilinxJTAG(primitive=XilinxJTAG.get_primitive(device), chain=chain)
@@ -504,15 +504,6 @@ def __init__(self, jtag=None, device=None, data_width=8, clock_domain="sys", cha
504504
# via sync.jtag outside the FSM, it only resets on the clock domain reset.
505505
ready = Signal()
506506
update_ready = Signal()
507-
ready_value = Signal()
508-
509-
# TDO timing fix: The JTAGG primitive samples JTDO1 on the FALLING edge of TCK,
510-
# but the FSM state changes on the RISING edge. By the falling edge, the FSM
511-
# has already transitioned to the next state, so the wrong TDO value would be
512-
# sampled. We use a registered TDO output that captures the value on the rising
513-
# edge, so it's stable when sampled on the falling edge.
514-
tdo_reg = Signal(reset=1) # Start with ready=1 assumption
515-
self.comb += jtag_tdo.eq(tdo_reg)
516507

517508
# Detect shift falling edge (transition from Shift-DR to Exit1-DR)
518509
# This is when the valid bit (bit9) is available but jtag.shift is already 0
@@ -526,68 +517,76 @@ def __init__(self, jtag=None, device=None, data_width=8, clock_domain="sys", cha
526517
fsm = ResetInserter()(fsm)
527518
self.submodules += fsm
528519

529-
# Only reset FSM on jtag.reset, NOT jtag.capture.
530-
# Resetting on capture clears 'ready' before it can be shifted out.
531-
self.comb += fsm.reset.eq(jtag.reset)
520+
# Reset FSM on both jtag.reset and jtag.capture to ensure the FSM
521+
# starts in XFER-READY at the beginning of each DR scan. The 'ready'
522+
# signal is updated outside the FSM (via sync.jtag), so it survives
523+
# FSM resets and is correctly output via combinational TDO.
524+
self.comb += fsm.reset.eq(jtag.reset | jtag.capture)
532525

533526
fsm.act("XFER-READY",
534-
If(jtag.capture,
535-
# On capture, prepare to output ready on next shift
536-
NextValue(tdo_reg, ready),
537-
),
527+
jtag_tdo.eq(ready),
538528
If(jtag.shift,
539529
sink.ready.eq(jtag_tdi),
540530
NextValue(valid, sink.valid),
541531
NextValue(data, sink.data),
542532
NextValue(count, 0),
543-
# Update tdo_reg to output ready (will be sampled on falling edge)
544-
NextValue(tdo_reg, ready),
545533
NextState("XFER-DATA")
546534
)
547535
)
548536
fsm.act("XFER-DATA",
537+
jtag_tdo.eq(data[0]),
549538
If(jtag.shift,
550539
NextValue(count, count + 1),
551-
# Update tdo_reg to output current data bit BEFORE shifting
552-
NextValue(tdo_reg, data[0]),
553540
NextValue(data, Cat(data[1:], jtag_tdi)),
554-
If(count == data_width,
555-
# After outputting all data bits, transition to XFER-VALID.
556-
# The valid bit is output on the NEXT shift cycle.
557-
# NOTE: We need data_width + 3 shift cycles total for the
558-
# data_width + 2 bit wire format because the JTAGG primitive
559-
# only captures TDO on falling edge in Shift-DR, and the last
560-
# falling edge is in Exit1-DR (doesn't capture).
561-
NextValue(tdo_reg, valid),
541+
If(count == (data_width - 1),
562542
NextState("XFER-VALID")
563543
)
564544
)
565545
)
566546
fsm.act("XFER-VALID",
567-
# Stay in this state for one shift cycle to output valid bit.
568-
# Then on the next shift, output padding bit (repeat of valid).
569-
# The actual valid bit from host arrives on shift_falling when TAP exits Shift-DR.
547+
jtag_tdo.eq(valid),
570548
If(jtag.shift,
571-
# We're still in Shift-DR, output padding (valid repeated)
572-
NextValue(tdo_reg, valid),
549+
NextValue(rx_valid_in, jtag_tdi),
573550
NextState("XFER-PADDING")
574551
)
575552
)
576553
fsm.act("XFER-PADDING",
577-
# The valid bit arrives when jtag.shift goes low (TAP exits Shift-DR).
578-
# We use the falling edge of shift to capture the valid bit from jtag_tdi.
554+
# Padding cycle: finalize the current word and return to XFER-READY.
555+
# rx_valid_in was registered in XFER-VALID; data holds the 8 RX bits.
556+
# Handle both concatenated scans (jtag.shift stays high) and individual
557+
# scans (shift_falling fires when TAP exits Shift-DR).
558+
If(jtag.shift,
559+
update_rx.eq(1),
560+
update_ready.eq(1),
561+
NextState("XFER-READY")
562+
),
579563
If(shift_falling,
580-
# Capture RX valid bit for update_rx trigger (combinatorial)
581-
rx_valid_in.eq(jtag_tdi),
582-
# Trigger rx_valid/rx_data update (handled outside FSM)
583564
update_rx.eq(1),
584-
# Update tdo_reg to output valid
585-
NextValue(tdo_reg, valid),
586-
update_ready.eq(1), # Trigger ready update (outside FSM)
565+
update_ready.eq(1),
587566
NextState("XFER-READY")
588567
)
589568
)
590569

570+
# RX path - connect to CDC FIFO write side.
571+
self.comb += [
572+
source.valid.eq(rx_valid),
573+
source.data.eq(rx_data),
574+
]
575+
576+
# Update ready from FIFO writable (outside FSM, survives resets).
577+
self.sync.jtag += If(update_ready, ready.eq(source.ready))
578+
579+
# Update RX registers (outside FSM, survives resets).
580+
# Clear rx_valid after FIFO accepts the data to prevent duplicates.
581+
self.sync.jtag += [
582+
If(update_rx,
583+
rx_valid.eq(rx_valid_in),
584+
rx_data.eq(data),
585+
).Elif(source.valid & source.ready,
586+
rx_valid.eq(0),
587+
)
588+
]
589+
591590
# ECP5 JTAG PHY (Verilog + AsyncFIFO) -----------------------------------------------------------------
592591

593592
class ECP5JTAGPHY(LiteXModule):

0 commit comments

Comments
 (0)