From 0a738002e0d24e8a34006c4535b0e5c8a64beae3 Mon Sep 17 00:00:00 2001 From: Florent Kermarrec Date: Thu, 14 Apr 2022 09:43:44 +0200 Subject: [PATCH] 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. --- litex/build/openocd.py | 51 ++++++++++++++++++++++++++++++++--------- litex/soc/cores/jtag.py | 45 ++++++++++++++++++++++++++---------- 2 files changed, 73 insertions(+), 23 deletions(-) diff --git a/litex/build/openocd.py b/litex/build/openocd.py index 87354a4ee..f0de91b90 100644 --- a/litex/build/openocd.py +++ b/litex/build/openocd.py @@ -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", ]) diff --git a/litex/soc/cores/jtag.py b/litex/soc/cores/jtag.py index d3c3c553f..74e5928c0 100644 --- a/litex/soc/cores/jtag.py +++ b/litex/soc/cores/jtag.py @@ -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)