Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: GlasgowEmbedded/glasgow
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 42144aa6a487
Choose a base ref
...
head repository: GlasgowEmbedded/glasgow
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: fd1b3bbfa7c8
Choose a head ref
  • 4 commits
  • 1 file changed
  • 1 contributor

Commits on Nov 5, 2020

  1. applet.interface.jtag_probe: remove read_dr(idempotent=) argument.

    Before this commit, this unused option had to be threaded through
    a lot of unrelated code, which polluted interfaces. Moreover, since
    commit 42144aa, it is also worse than useless, because scan_dr()
    does the exact same thing more safely (it does not rely on provided
    DR length).
    whitequark committed Nov 5, 2020
    Copy the full SHA
    e408539 View commit details
  2. applet.interface.jtag_probe: improve TAP selection logic.

      * Require --tap-index option if more than one TAP is found.
        Before this commit, TAP #0 was implicitly chosen.
      * Extract redundant error handling code into select_tap().
      * Convert logger errors into exceptions where appropriate.
    whitequark committed Nov 5, 2020
    Copy the full SHA
    493f2b6 View commit details
  3. applet.interface.jtag_probe: don't track IR offsets. NFCI.

    Per-TAP IR offsets are redundant, pointless, and complicate the logic
    of specifying IR lengths manually.
    whitequark committed Nov 5, 2020
    Copy the full SHA
    7742553 View commit details
  4. Copy the full SHA
    fd1b3bb View commit details
Showing with 81 additions and 77 deletions.
  1. +81 −77 software/glasgow/applet/interface/jtag_probe/__init__.py
158 changes: 81 additions & 77 deletions software/glasgow/applet/interface/jtag_probe/__init__.py
Original file line number Diff line number Diff line change
@@ -576,18 +576,11 @@ async def exchange_dr(self, data):
self._log_h("exchange dr-o=<%s>", dump_bin(data))
return data

async def read_dr(self, count, idempotent=False):
async def read_dr(self, count):
await self.enter_shift_dr()
data = await self.shift_tdo(count, last=not idempotent)
if idempotent:
# Shift what we just read back in. This is useful to avoid disturbing any bits
# in R/W DRs when we go through Update-DR.
await self.shift_tdi(data)
data = await self.shift_tdo(count, last=True)
await self.enter_update_dr()
if idempotent:
self._log_h("read idempotent dr=<%s>", dump_bin(data))
else:
self._log_h("read dr=<%s>", dump_bin(data))
self._log_h("read dr=<%s>", dump_bin(data))
return data

async def write_dr(self, data):
@@ -615,12 +608,17 @@ async def _scan_xr(self, xr, *, max_length):
await self.enter_shift_dr()

try:
# Cancellation can cause the `finally` block to run before we have enough information
# to actually restore the value.
restore = False

# Add 1 so that registers of exactly `max_length` could be scanned successfully.
data_1 = await self.shift_tdio((1,) * (max_length + 1), last=False)
data_0 = await self.shift_tdio((0,) * (max_length + 1), last=False)
for length in range(max_length + 1):
if data_0[length] == 0:
if all(data_1[length:]):
restore = True
self._log_h("scan %s length=%d data=<%s>",
xr, length, dump_bin(data_1[:length]))
return data_1[:length]
@@ -632,14 +630,15 @@ async def _scan_xr(self, xr, *, max_length):
return

finally:
if xr == "ir":
# Fill the register with BYPASS instructions.
await self.shift_tdi((1,) * length, last=True)
if xr == "dr":
# Restore the old contents, just in case this matters.
await self.shift_tdi(data_1[:length], last=True)
if restore:
if xr == "ir":
# Fill the register with BYPASS instructions.
await self.shift_tdi((1,) * length, last=True)
if xr == "dr":
# Restore the old contents, just in case this matters.
await self.shift_tdi(data_1[:length], last=True)

await self.enter_run_test_idle()
await self.enter_run_test_idle()

async def _scan_xr_length(self, xr, *, max_length):
data = await self._scan_xr(xr, max_length=max_length)
@@ -671,10 +670,10 @@ def interrogate_dr(self, dr_value):
dr_chunk = dr_value[offset:offset + 32]
idcode = int(dr_chunk)
if dr_chunk[1:12] == bits("00001111111"):
self._log_h("invalid idcode=<%08x>", idcode)
self._log_h("invalid idcode=%08x", idcode)
return
else:
self._log_h("found idcode=<%08x>", idcode)
self._log_h("found idcode=%08x", idcode)
idcodes.append(idcode)
offset += 32
else:
@@ -707,60 +706,58 @@ def interrogate_ir(self, ir_value, dr_count):

# If there's only one device in the chain, then the entire captured IR belongs to it.
if dr_count == 1:
ir_offset = 0
ir_length = len(ir_value)
self._log_h("found ir[%d] (only tap)", ir_length)
return [(ir_offset, ir_length)]
return [ir_length]

# If there are no more captured IRs than devices in the chain, then IR lengths can be
# determined unambiguously.
elif dr_count == len(ir_starts):
irs = []
ir_offset = 0
ir_lengths = []
for ir_start0, ir_start1 in zip(ir_starts, ir_starts[1:] + [len(ir_value)]):
ir_length = ir_start1 - ir_start0
self._log_h("found ir[%d] (tap #%d)", ir_length, len(irs))
irs.append((ir_offset, ir_length))
ir_offset += ir_length
return irs
self._log_h("found ir[%d] (tap #%d)", ir_length, len(ir_lengths))
ir_lengths.append(ir_length)
return ir_lengths

# Otherwise IR lengths are ambiguous.
else:
ir_chunks = []
for ir_start0, ir_start1 in zip(ir_starts, ir_starts[1:] + [len(ir_value)]):
ir_chunks.append(ir_start1 - ir_start0)
self._log_h("ambiguous ir chain length=%d chunks=%s",
len(ir_value), ",".join("{:d}".format(chunk) for chunk in ir_chunks))
return None
self._log_h("ambiguous ir taps=%d chunks=[%s]",
dr_count, ",".join("{:d}".format(chunk) for chunk in ir_chunks))
return

async def select_tap(self, tap):
async def _scan_taps(self):
await self.test_reset()

dr_value = await self.scan_dr()
if dr_value is None:
return
return (None, None)

idcodes = self.interrogate_dr(dr_value)
if idcodes is None:
return
return (None, None)

ir_value = await self.scan_ir()
if ir_value is None:
return
return (idcodes, None)

irs = self.interrogate_ir(ir_value, dr_count=len(idcodes))
if not irs:
return
ir_lengths = self.interrogate_ir(ir_value, dr_count=len(idcodes))
if not ir_lengths:
return (idcodes, None)

if tap >= len(irs):
self._log_h("missing tap %d")
return
return (idcodes, ir_lengths)

def _create_tap_iface(self, ir_lengths, index):
assert index in range(len(ir_lengths))

ir_offset, ir_length = irs[tap]
total_ir_length = sum(length for offset, length in irs)
ir_offset, ir_length = sum(ir_lengths[:index]), ir_lengths[index]
total_ir_length = sum(ir_lengths)

dr_offset, dr_length = tap, 1
total_dr_length = len(idcodes)
dr_offset, dr_length = index, 1
total_dr_length = len(ir_lengths)

bypass = bits((1,))
def affix(offset, length, total_length):
@@ -772,6 +769,14 @@ def affix(offset, length, total_length):
*affix(ir_offset, ir_length, total_ir_length),
*affix(dr_offset, dr_length, total_dr_length))

async def select_tap(self, index):
idcodes, irs = await self._scan_taps()
if irs is None:
raise JTAGProbeError("TAP interrogation failed")
if index not in range(len(irs)):
raise JTAGProbeError("TAP #{:d} not found".format(index))
return self._create_tap_iface(irs, index)


class TAPInterface:
def __init__(self, lower, ir_length, ir_prefix, ir_suffix, dr_prefix, dr_suffix):
@@ -819,8 +824,8 @@ async def exchange_dr(self, data):
else:
return data[len(self._dr_prefix):]

async def read_dr(self, count, idempotent=False):
data = await self.lower.read_dr(self._dr_overhead + count, idempotent=idempotent)
async def read_dr(self, count):
data = await self.lower.read_dr(self._dr_overhead + count)
if self._dr_suffix:
return data[len(self._dr_prefix):-len(self._dr_suffix)]
else:
@@ -903,15 +908,25 @@ async def run(self, device, args):
@classmethod
def add_run_tap_arguments(cls, parser):
parser.add_argument(
"--tap-index", metavar="INDEX", type=int, default=0,
help="select TAP #INDEX for communication (default: %(default)s)")
"--tap-index", metavar="INDEX", type=int,
help="select TAP #INDEX for communication (default: sole TAP)")

async def run_tap(self, cls, device, args):
jtag_iface = await self.run_lower(cls, device, args)
tap_iface = await jtag_iface.select_tap(args.tap_index)
if not tap_iface:
raise JTAGProbeError("cannot select TAP #%d" % args.tap_index)
return tap_iface

idcodes, ir_lengths = await jtag_iface._scan_taps()
if ir_lengths is None:
raise JTAGProbeError("TAP interrogation failed")

tap_index = args.tap_index
if tap_index is None:
if len(ir_lengths) > 1:
raise JTAGProbeError("more than one TAP found; use explicit --tap-index")
else:
tap_index = 0
if tap_index not in range(len(ir_lengths)):
raise JTAGProbeError("TAP #{:d} not found".format(tap_index))
return jtag_iface._create_tap_iface(ir_lengths, tap_index)

@classmethod
def add_interact_arguments(cls, parser):
@@ -956,7 +971,7 @@ def add_interact_arguments(cls, parser):
""")
p_enumerate_ir.add_argument(
"tap_indexes", metavar="INDEX", type=int, nargs="+",
help="enumerate IR values for TAP #INDEX")
help="enumerate IR values for TAP(s) #INDEX")

# This one is identical to run-repl, and is just for consistency when using the subcommands
# tap-repl and jtag-repl alternately.
@@ -966,42 +981,39 @@ def add_interact_arguments(cls, parser):
p_tap_repl = p_operation.add_parser(
"tap-repl", help="select a TAP and drop into Python REPL")
p_tap_repl.add_argument(
"tap_index", metavar="INDEX", type=int, default=0, nargs="?",
help="select TAP #INDEX for communication (default: %(default)s)")
"tap_index", metavar="INDEX", type=int,
help="select TAP #INDEX for communication")

async def interact(self, device, args, jtag_iface):
if args.operation in ("scan", "enumerate-ir"):
await jtag_iface.test_reset()

dr_value = await jtag_iface.scan_dr()
if dr_value is None:
self.logger.error("DR length scan did not terminate")
return
raise JTAGProbeError("DR scan did not terminate")
self.logger.info("shifted %d-bit DR=<%s>", len(dr_value), dump_bin(dr_value))

ir_value = await jtag_iface.scan_ir()
if ir_value is None:
self.logger.error("IR length scan did not terminate")
return
raise JTAGProbeError("IR scan did not terminate")
self.logger.info("shifted %d-bit IR=<%s>", len(ir_value), dump_bin(ir_value))

idcodes = jtag_iface.interrogate_dr(dr_value)
if idcodes is None:
self.logger.error("DR interrogation failed")
return
raise JTAGProbeError("DR interrogation failed")
if len(idcodes) == 0:
self.logger.warning("DR interrogation discovered no TAPs")
return
self.logger.info("discovered %d TAPs", len(idcodes))

irs = jtag_iface.interrogate_ir(ir_value, dr_count=len(idcodes))
ir_lengths = jtag_iface.interrogate_ir(ir_value, dr_count=len(idcodes))

if args.operation == "scan":
if not irs:
if not ir_lengths:
self.logger.warning("IR interrogation failed")
irs = [(None, "?") for _ in idcodes]
ir_lengths = ["?" for _ in idcodes]

for tap_index, (idcode_value, (ir_offset, ir_length)) in enumerate(zip(idcodes, irs)):
for tap_index, (idcode_value, ir_length) in enumerate(zip(idcodes, ir_lengths)):
if idcode_value is None:
self.logger.info("TAP #%d: IR[%s] BYPASS",
tap_index, ir_length)
@@ -1017,18 +1029,14 @@ async def interact(self, device, args, jtag_iface):
idcode.mfg_id, mfg_name, idcode.part_id, idcode.version)

if args.operation == "enumerate-ir":
if not irs:
self.logger.error("IR interrogation failed")
return
if not ir_lengths:
raise JTAGProbeError("IR interrogation failed")

for tap_index in args.tap_indexes or range(len(irs)):
ir_offset, ir_length = irs[tap_index]
for tap_index in args.tap_indexes:
ir_length = ir_lengths[tap_index]
self.logger.info("TAP #%d: IR[%d]", tap_index, ir_length)

tap_iface = await jtag_iface.select_tap(tap_index)
if not tap_iface:
raise GlasgowAppletError("cannot select TAP #%d" % tap_index)

for ir_value in range(0, (1 << ir_length)):
ir_value = bits(ir_value & (1 << bit) for bit in range(ir_length))
await tap_iface.test_reset()
@@ -1056,10 +1064,6 @@ async def interact(self, device, args, jtag_iface):

if args.operation == "tap-repl":
tap_iface = await jtag_iface.select_tap(args.tap_index)
if not tap_iface:
self.logger.error("cannot select TAP #%d" % args.tap_index)
return

self.logger.info("dropping to REPL; use 'help(iface)' to see available APIs")
await AsyncInteractiveConsole(
locals={"iface":tap_iface},