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: whitequark/glasgow
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: afd5f7a0a661
Choose a base ref
...
head repository: whitequark/glasgow
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: af45db5708db
Choose a head ref
  • 2 commits
  • 3 files changed
  • 1 contributor

Commits on Nov 20, 2018

  1. Copy the full SHA
    8136c16 View commit details
  2. Copy the full SHA
    af45db5 View commit details
Showing with 129 additions and 12 deletions.
  1. +1 −1 software/glasgow/applet/jtag/arc.py
  2. +127 −10 software/glasgow/applet/jtag/mec16xx.py
  3. +1 −1 software/glasgow/applet/jtag/xc9500.py
2 changes: 1 addition & 1 deletion software/glasgow/applet/jtag/arc.py
Original file line number Diff line number Diff line change
@@ -114,7 +114,7 @@ class JTAGARCApplet(JTAGApplet, name="jtag-arc"):
There is currently no debug server implemented. This applet only allows manipulating Memory,
Core and Aux spaces via a Python REPL.
""".format(
devices="\n".join(map(lambda x: " * {.name}\n".format(x), devices.values()))
devices="\n".join(map(lambda x: " * {.name}".format(x), devices.values()))
)

@classmethod
137 changes: 127 additions & 10 deletions software/glasgow/applet/jtag/mec16xx.py
Original file line number Diff line number Diff line change
@@ -55,13 +55,109 @@ async def emergency_flash_erase(self):
dr_reset_test.VTR_POR = 0
await tap_iface.write_dr(dr_reset_test.to_bitarray())

async def enable_flash_access(self, enabled):
# Enable access to Reg_Ctl bit.
flash_config = Flash_Config(Reg_Ctl_En=enabled)
self._log("write Flash_Config %s", flash_config.bits_repr(omit_zero=True))
await self.lower.write(Flash_Config_addr, flash_config.to_int(), space="memory")

if not enabled:
# Clearing Reg_Ctl_En automatically clears Reg_Ctl.
return

# Enable access to Flash controller registers. Also, bring Flash controller to standby
# mode if it wasn't already in it, since otherwise it will refuse commands.
flash_command = Flash_Command(Reg_Ctl=1, Flash_Mode=Flash_Mode_Standby)
self._log("write Flash_Command %s", flash_command.bits_repr(omit_zero=True))
await self.lower.write(Flash_Command_addr, flash_command.to_int(), space="memory")

# Clear Flash controller error status.
flash_clear_status = Flash_Status(Busy_Err=1, CMD_Err=1, Protect_Err=1)
self._log("clear Flash_Status %s", flash_clear_status.bits_repr(omit_zero=True))
await self.lower.write(Flash_Status_addr, flash_clear_status.to_int(), space="memory")

async def _flash_command(self, mode, address=0, burst=False):
flash_command = Flash_Command(Reg_Ctl=1, Flash_Mode=mode, Burst=burst)
self._log("write Flash_Command %s", flash_command.bits_repr(omit_zero=True))
await self.lower.write(Flash_Command_addr, flash_command.to_int(), space="memory")

self._log("write Flash_Address=%08x", address)
await self.lower.write(Flash_Address_addr, address, space="memory")

flash_status = Flash_Status(Busy=1)
while flash_status.Busy:
flash_status = Flash_Status.from_int(
await self.lower.read(Flash_Status_addr, space="memory"))
self._log("read Flash_Status %s", flash_status.bits_repr(omit_zero=True))

if flash_status.Busy_Err or flash_status.CMD_Err or flash_status.Protect_Err:
raise GlasgowAppletError("Flash command %s failed with status %s"
% (flash_command.bits_repr(omit_zero=True),
flash_status.bits_repr(omit_zero=True)))

async def read_flash(self, address, count):
words = []
for offset in range(count):
await self._flash_command(mode=Flash_Mode_Read, address=address + offset * 4)
data_1 = await self.lower.read(Flash_Data_addr, space="memory")
self._log("read Flash_Address=%05x Flash_Data=%08x",
address + offset * 4, data_1)

# This is hella cursed. In theory, we should be able to just enable Burst in
# Flash_Command and do a long series of reads from Flash_Data. However... sometimes
# we silently get zeroes back for no discernible reason. I spent like two days trying
# to figure out *anything* that correlates with glitches, and could not, so I guess
# this works as a workaround. I'm sorry.
#
# Remarkably, none of this happens during *programming* for some inexplicable reason,
# which is why we can just do a simple burst there.
await self.lower.write(Flash_Address_addr, address + offset * 4, space="memory")
data_2 = await self.lower.read(Flash_Data_addr, space="memory")
self._log("read Flash_Address=%05x Flash_Data=%08x",
address + offset * 4, data_2)

if data_1 == data_2:
data = data_1
else:
# Third time's the charm.
await self.lower.write(Flash_Address_addr, address + offset * 4, space="memory")
data_3 = await self.lower.read(Flash_Data_addr, space="memory")
self._log("read Flash_Address=%05x Flash_Data=%08x",
address + offset * 4, data_3)

self._logger.warn("read glitch Flash_Address=%05x Flash_Data=%08x/%08x/%08x",
address + offset * 4, data_1, data_2, data_3)

if data_1 == data_2:
data = data_1
elif data_2 == data_3:
data = data_2
elif data_1 == data_3:
data = data_3
else:
raise GlasgowAppletError("cannot select a read by majority")

words.append(data)
return words

async def erase_flash(self, address=0b11111 << 19):
await self._flash_command(mode=Flash_Mode_Erase, address=address)

async def program_flash(self, address, words):
await self._flash_command(mode=Flash_Mode_Program, address=address, burst=1)
for offset, data in enumerate(words):
await self.lower.write(Flash_Data_addr, data, space="memory")
self._log("program Flash_Address=%05x Flash_Data=%08x", address + offset * 4, data)


class JTAGMEC16xxApplet(JTAGARCApplet, name="jtag-mec16xx"):
preview = True
logger = logging.getLogger(__name__)
help = "debug Microchip MEC16xx embedded controller via JTAG"
help = "flash Microchip MEC16xx embedded controller via JTAG"
description = """
Debug Microchip MEC16xx/MEC16xxi embedded controller via the JTAG interface.
Read and write Microchip MEC16xx/MEC16xxi embedded controller integrated Flash
via the JTAG interface.
Per the MEC16xx datasheets, the minimum JTAG frequency should be 1 MHz.
"""

async def run(self, device, args):
@@ -75,22 +171,43 @@ def add_interact_arguments(cls, parser):
p_emergency_erase = p_operation.add_parser(
"emergency-erase", help="emergency erase firmware")

p_read_firmware = p_operation.add_parser(
"read-firmware", help="read EC firmware")
p_read_firmware.add_argument(
p_read = p_operation.add_parser(
"read", help="read EC firmware")
p_read.add_argument(
"file", metavar="FILE", type=argparse.FileType("wb"),
help="write EC firmware to FILE")

p_write = p_operation.add_parser(
"write", help="write EC firmware")
p_write.add_argument(
"file", metavar="FILE", type=argparse.FileType("rb"),
help="read EC firmware from FILE")

p_repl = p_operation.add_parser(
"repl", help="drop into Python shell; use `mec_iface` to communicate")

async def interact(self, device, args, mec_iface):
if args.operation == "emergency-erase":
await mec_iface.emergency_flash_erase()
if args.operation == "read":
await mec_iface.enable_flash_access(enabled=True)
words = await mec_iface.read_flash(0, FIRMWARE_SIZE // 4)
await mec_iface.enable_flash_access(enabled=False)

if args.operation == "read-firmware":
for word in await mec_iface.read_firmware_mapped(size=FIRMWARE_SIZE):
for word in words:
args.file.write(struct.pack("<L", word))

if args.operation == "write":
words = []
for _ in range(FIRMWARE_SIZE // 4):
word, = struct.unpack("<L", args.file.read(4))
words.append(word)

await mec_iface.enable_flash_access(enabled=True)
await mec_iface.erase_flash()
await mec_iface.program_flash(0, words)
await mec_iface.enable_flash_access(enabled=False)

if args.operation == "emergency-erase":
await mec_iface.emergency_flash_erase()

if args.operation == "repl":
await AsyncInteractiveConsole(locals={"mec_iface":mec_iface}).interact()
2 changes: 1 addition & 1 deletion software/glasgow/applet/jtag/xc9500.py
Original file line number Diff line number Diff line change
@@ -418,7 +418,7 @@ class JTAGXC9500Applet(JTAGApplet, name="jtag-xc9500"):
from both .jed and .svf bitstream formats, but matches the internal device programming
architecture.
""".format(
devices="\n".join(map(lambda x: " * {.name}\n".format(x), devices.values()))
devices="\n".join(map(lambda x: " * {.name}".format(x), devices.values()))
)

@classmethod