openocd/jtag: Add JTAG-UART/JTABBone support to Zynq7000/ZynqMPSoC and define all Xilinx IRs for USERX.

Thanks @smunaut for the initial investigation/implementation. The changes have been minimized to:
- Adding an optional delay in TDI: On Zynq devices, TDI is delayed by 1 TCK to bypass the PS tap.
- Avoiding OpenOCD's -endstate DRPAUSE on Xilinx that does not seem required.
This commit is contained in:
Florent Kermarrec 2022-04-14 09:43:44 +02:00
parent be43ef6424
commit 0a738002e0
2 changed files with 73 additions and 23 deletions

View File

@ -40,19 +40,42 @@ class OpenOCD(GenericProgrammer):
self.call(["openocd", "-f", config, "-c", script])
def get_ir(self, chain, config):
# On ECP5, force IR to 0x32.
cfg_str = open(config).read()
ecp5 = "ecp5" in cfg_str
altera = "10m50" in cfg_str # TODO: or cyclone 10
if ecp5:
# Lattice ECP5.
if "ecp5" in cfg_str:
chain = 0x32
elif altera:
chain = 0xC
# Else IR = 1 + CHAIN.
# Intel Max10.
elif "10m50" in cfg_str:
chain = 0xc
# Xilinx ZynqMP.
elif "zynqmp" in cfg_str:
chain = {
1: 0x902, # USER1.
2: 0x903, # USER2.
3: 0x922, # USER3.
4: 0x923, # USER4.
}[chain]
# Xilinx 7-Series.
else:
chain = 0x1 + chain
chain = {
1: 0x02, # USER1.
2: 0x03, # USER2.
3: 0x22, # USER3.
4: 0x23, # USER4.
}[chain]
return chain
def get_endstate(self, config):
cfg_str = open(config).read()
# Lattice ECP5.
if "ecp5" in cfg_str:
return "-endstate DRPAUSE" # CHECKME: Can we avoid it?
# Intel Max10.
elif "10m50" in cfg_str:
return "-endstate DRPAUSE" # CHECKME: Is it required on Intel?
else:
return ""
def stream(self, port=20000, chain=1):
"""
Create a TCP server to stream data to/from the internal JTAG TAP of the FPGA
@ -68,7 +91,9 @@ class OpenOCD(GenericProgrammer):
- TX data : bit 1 to 8
- TX valid : bit 9
"""
config = self.find_config()
config = self.find_config()
ir = self.get_ir(chain, config)
endstate = self.get_endstate(config)
cfg = """
proc jtagstream_poll {tap tx n} {
set m [string length $tx]
@ -81,7 +106,11 @@ proc jtagstream_poll {tap tx n} {
#echo tx[scan $txj %c]
}
set txi [concat {*}$txi]
set rxi [split [drscan $tap {*}$txi -endstate DRPAUSE] " "]
"""
cfg += f"""
set rxi [split [drscan $tap {{*}}$txi {endstate}] " "]
"""
cfg += """
#echo $txi:$rxi
set rx ""
set writable 1
@ -153,7 +182,7 @@ proc jtagstream_serve {tap port} {
write_to_file("stream.cfg", cfg)
script = "; ".join([
"init",
"irscan $_CHIPNAME.tap {:d}".format(self.get_ir(chain, config)),
"irscan $_CHIPNAME.tap {:d}".format(ir),
"jtagstream_serve $_CHIPNAME.tap {:d}".format(port),
"exit",
])

View File

@ -304,11 +304,10 @@ class XilinxJTAG(Module):
@staticmethod
def get_primitive(device):
# TODO: Add support for all devices.
prim_dict = {
# Primitive Name Ðevice (startswith)
"BSCAN_SPARTAN6" : ["xc6"],
"BSCANE2" : ["xc7", "xcku", "xcvu", "xczu"],
"BSCANE2" : ["xc7a", "xc7k", "xc7v", "xc7z"] + ["xcku", "xcvu", "xczu"],
}
for prim, prim_devs in prim_dict.items():
for prim_dev in prim_devs:
@ -316,6 +315,15 @@ class XilinxJTAG(Module):
return prim
return None
@staticmethod
def get_tdi_delay(device):
# Input delay if 1 TCK on TDI on Zynq/ZynqMPSoC devices.
delay_dict = {"xc7z" : 1, "xczu" : 1}
for dev, delay in delay_dict.items():
if device.lower().startswith(dev):
return 1
return 0
# ECP5 JTAG ----------------------------------------------------------------------------------------
class ECP5JTAG(Module):
@ -393,15 +401,14 @@ class JTAGPHY(Module):
# # #
valid = Signal()
data = Signal(data_width)
count = Signal(max=data_width)
# JTAG TAP ---------------------------------------------------------------------------------
if jtag is None:
jtag_tdi_delay = 0
# Xilinx.
if XilinxJTAG.get_primitive(device) is not None:
jtag = XilinxJTAG(primitive=XilinxJTAG.get_primitive(device), chain=chain)
jtag_tdi_delay = XilinxJTAG.get_tdi_delay(device)
# Lattice.
elif device[:5] == "LFE5U":
jtag = ECP5JTAG()
@ -436,16 +443,29 @@ class JTAGPHY(Module):
]
sink, source = tx_cdc.source, rx_cdc.sink
# JTAG TDI/TDO Delay -----------------------------------------------------------------------
jtag_tdi = jtag.tdi
jtag_tdo = jtag.tdo
if jtag_tdi_delay:
jtag_tdi_sr = Signal(data_width + 2 - jtag_tdi_delay)
self.sync.jtag += If(jtag.shift, jtag_tdi_sr.eq(Cat(jtag.tdi, jtag_tdi_sr)))
jtag_tdi = jtag_tdi_sr[-1]
# JTAG Xfer FSM ----------------------------------------------------------------------------
valid = Signal()
ready = Signal()
data = Signal(data_width)
count = Signal(max=data_width)
fsm = FSM(reset_state="XFER-READY")
fsm = ClockDomainsRenamer("jtag")(fsm)
fsm = ResetInserter()(fsm)
self.submodules += fsm
self.comb += fsm.reset.eq(jtag.reset | jtag.capture)
fsm.act("XFER-READY",
jtag.tdo.eq(source.ready),
jtag_tdo.eq(ready),
If(jtag.shift,
sink.ready.eq(jtag.tdi),
sink.ready.eq(jtag_tdi),
NextValue(valid, sink.valid),
NextValue(data, sink.data),
NextValue(count, 0),
@ -453,20 +473,21 @@ class JTAGPHY(Module):
)
)
fsm.act("XFER-DATA",
jtag.tdo.eq(data),
jtag_tdo.eq(data),
If(jtag.shift,
NextValue(count, count + 1),
NextValue(data, Cat(data[1:], jtag.tdi)),
NextValue(data, Cat(data[1:], jtag_tdi)),
If(count == (data_width - 1),
NextState("XFER-VALID")
)
)
)
fsm.act("XFER-VALID",
jtag.tdo.eq(valid),
jtag_tdo.eq(valid),
If(jtag.shift,
source.valid.eq(jtag.tdi),
source.valid.eq(jtag_tdi),
source.data.eq(data),
NextValue(ready, source.ready),
NextState("XFER-READY")
)
)
self.comb += source.data.eq(data)