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:
parent
be43ef6424
commit
0a738002e0
|
@ -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",
|
||||||
])
|
])
|
||||||
|
|
|
@ -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)
|
|
||||||
|
|
Loading…
Reference in New Issue