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]) self.call(["openocd", "-f", config, "-c", script])
def get_ir(self, chain, config): def get_ir(self, chain, config):
# On ECP5, force IR to 0x32.
cfg_str = open(config).read() cfg_str = open(config).read()
ecp5 = "ecp5" in cfg_str # Lattice ECP5.
altera = "10m50" in cfg_str # TODO: or cyclone 10 if "ecp5" in cfg_str:
if ecp5:
chain = 0x32 chain = 0x32
elif altera: # Intel Max10.
chain = 0xC elif "10m50" in cfg_str:
# Else IR = 1 + CHAIN. 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: else:
chain = 0x1 + chain chain = {
1: 0x02, # USER1.
2: 0x03, # USER2.
3: 0x22, # USER3.
4: 0x23, # USER4.
}[chain]
return 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): def stream(self, port=20000, chain=1):
""" """
Create a TCP server to stream data to/from the internal JTAG TAP of the FPGA 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 data : bit 1 to 8
- TX valid : bit 9 - TX valid : bit 9
""" """
config = self.find_config() config = self.find_config()
ir = self.get_ir(chain, config)
endstate = self.get_endstate(config)
cfg = """ cfg = """
proc jtagstream_poll {tap tx n} { proc jtagstream_poll {tap tx n} {
set m [string length $tx] set m [string length $tx]
@ -81,7 +106,11 @@ proc jtagstream_poll {tap tx n} {
#echo tx[scan $txj %c] #echo tx[scan $txj %c]
} }
set txi [concat {*}$txi] 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 #echo $txi:$rxi
set rx "" set rx ""
set writable 1 set writable 1
@ -153,7 +182,7 @@ proc jtagstream_serve {tap port} {
write_to_file("stream.cfg", cfg) write_to_file("stream.cfg", cfg)
script = "; ".join([ script = "; ".join([
"init", "init",
"irscan $_CHIPNAME.tap {:d}".format(self.get_ir(chain, config)), "irscan $_CHIPNAME.tap {:d}".format(ir),
"jtagstream_serve $_CHIPNAME.tap {:d}".format(port), "jtagstream_serve $_CHIPNAME.tap {:d}".format(port),
"exit", "exit",
]) ])

View File

@ -304,11 +304,10 @@ class XilinxJTAG(Module):
@staticmethod @staticmethod
def get_primitive(device): def get_primitive(device):
# TODO: Add support for all devices.
prim_dict = { prim_dict = {
# Primitive Name Ðevice (startswith) # Primitive Name Ðevice (startswith)
"BSCAN_SPARTAN6" : ["xc6"], "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, prim_devs in prim_dict.items():
for prim_dev in prim_devs: for prim_dev in prim_devs:
@ -316,6 +315,15 @@ class XilinxJTAG(Module):
return prim return prim
return None 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 ---------------------------------------------------------------------------------------- # ECP5 JTAG ----------------------------------------------------------------------------------------
class ECP5JTAG(Module): class ECP5JTAG(Module):
@ -393,15 +401,14 @@ class JTAGPHY(Module):
# # # # # #
valid = Signal()
data = Signal(data_width)
count = Signal(max=data_width)
# JTAG TAP --------------------------------------------------------------------------------- # JTAG TAP ---------------------------------------------------------------------------------
if jtag is None: if jtag is None:
jtag_tdi_delay = 0
# Xilinx. # Xilinx.
if XilinxJTAG.get_primitive(device) is not None: if XilinxJTAG.get_primitive(device) is not None:
jtag = XilinxJTAG(primitive=XilinxJTAG.get_primitive(device), chain=chain) jtag = XilinxJTAG(primitive=XilinxJTAG.get_primitive(device), chain=chain)
jtag_tdi_delay = XilinxJTAG.get_tdi_delay(device)
# Lattice. # Lattice.
elif device[:5] == "LFE5U": elif device[:5] == "LFE5U":
jtag = ECP5JTAG() jtag = ECP5JTAG()
@ -436,16 +443,29 @@ class JTAGPHY(Module):
] ]
sink, source = tx_cdc.source, rx_cdc.sink 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 ---------------------------------------------------------------------------- # JTAG Xfer FSM ----------------------------------------------------------------------------
valid = Signal()
ready = Signal()
data = Signal(data_width)
count = Signal(max=data_width)
fsm = FSM(reset_state="XFER-READY") fsm = FSM(reset_state="XFER-READY")
fsm = ClockDomainsRenamer("jtag")(fsm) fsm = ClockDomainsRenamer("jtag")(fsm)
fsm = ResetInserter()(fsm) fsm = ResetInserter()(fsm)
self.submodules += fsm self.submodules += fsm
self.comb += fsm.reset.eq(jtag.reset | jtag.capture) self.comb += fsm.reset.eq(jtag.reset | jtag.capture)
fsm.act("XFER-READY", fsm.act("XFER-READY",
jtag.tdo.eq(source.ready), jtag_tdo.eq(ready),
If(jtag.shift, If(jtag.shift,
sink.ready.eq(jtag.tdi), sink.ready.eq(jtag_tdi),
NextValue(valid, sink.valid), NextValue(valid, sink.valid),
NextValue(data, sink.data), NextValue(data, sink.data),
NextValue(count, 0), NextValue(count, 0),
@ -453,20 +473,21 @@ class JTAGPHY(Module):
) )
) )
fsm.act("XFER-DATA", fsm.act("XFER-DATA",
jtag.tdo.eq(data), jtag_tdo.eq(data),
If(jtag.shift, If(jtag.shift,
NextValue(count, count + 1), NextValue(count, count + 1),
NextValue(data, Cat(data[1:], jtag.tdi)), NextValue(data, Cat(data[1:], jtag_tdi)),
If(count == (data_width - 1), If(count == (data_width - 1),
NextState("XFER-VALID") NextState("XFER-VALID")
) )
) )
) )
fsm.act("XFER-VALID", fsm.act("XFER-VALID",
jtag.tdo.eq(valid), jtag_tdo.eq(valid),
If(jtag.shift, If(jtag.shift,
source.valid.eq(jtag.tdi), source.valid.eq(jtag_tdi),
source.data.eq(data),
NextValue(ready, source.ready),
NextState("XFER-READY") NextState("XFER-READY")
) )
) )
self.comb += source.data.eq(data)