Last active
May 27, 2023 14:00
-
-
Save elfmimi/3ef312d59878e7f0d8f5e820bfa68943 to your computer and use it in GitHub Desktop.
pyOCD user script for Nuvoton ARM MCUs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# How to invoke: | |
# pyocd cmd --script nuvoton.py -c "ident" | |
# pyocd cmd --script nuvoton.py -c "read_config_regs" | |
# pyocd cmd --script nuvoton.py -c "write_config_regs 0xFFFFFFFF 0xFFFFFFFF" | |
# pyocd cmd --script nuvoton.py -c "chip_erase" | |
# | |
# and for the commander prompt, invoke it like this: | |
# pyocd cmd --script nuvoton.py | |
from pyocd.core.exceptions import TransferFaultError, TransferTimeoutError | |
options.update({ | |
# "target_override": "cortex_m", | |
}) | |
if not "target_override" in options: | |
options.set("target_override", "cortex_m") | |
if not "frequency" in options: | |
options.set("frequency", 16000000) | |
PART_NAME_DICT = { | |
0x12000: "NUC120LE3AN", | |
0x12305: "NUC123SC2AN1", | |
0x12315: "NUC123SD4AN0", | |
0x12325: "NUC123LC2AN1", | |
0x12335: "NUC123LD4AN0", | |
0x12345: "NUC123ZC2AN1", | |
0x12355: "NUC123ZD4AN0", | |
0xC05204: "NUC126LG4AE", | |
0xC05205: "NUC126LE4AE", | |
0xC05212: "NUC126SG4AE", | |
0xC05213: "NUC126SE4AE", | |
0xC05231: "NUC126VG4AE", | |
0x295C50: "NUC029LGE", | |
0x295C51: "NUC029SGE", | |
0x295C52: "NUC029KGE", | |
0xF250E0: "M253ZE3AE", | |
0xF25210: "M252SD2AE", | |
0xF252B0: "M252FC2AE", | |
0x1132CB0: "M032FC1AE", | |
0x1132DE0: "M032TD2AE", | |
0x01647192: "M471R1E6AE", | |
0x00D4851F: "M48SSIDAE", | |
} | |
class Context: | |
pass | |
self = Context() | |
def read8(off): | |
return target.read8(self.sys_base + off) | |
def read32(off): | |
return target.read32(self.sys_base + off) | |
def write8(off, dat): | |
target.write8(self.sys_base + off, dat) | |
def write32(off, dat): | |
target.write32(self.sys_base + off, dat) | |
def unlock_reg(): | |
# Unlock | |
write8(0x0100, 0x59) | |
write8(0x0100, 0x16) | |
write8(0x0100, 0x88) | |
def lock_reg(): | |
write8(0x0100, 0x00) | |
def read_via_isp(addr): | |
# Enable ISP | |
write8(0xC000, 0x79) # ISPCTL | |
# ISP-Command = Flash Read | |
write8(0xC00C, 0x00) # ISPCMD | |
write32(0xC004, addr) # ISPADDR | |
# Write ISP Trigger Control Register (ISPTRG) | |
write8(0xC010, 0x01) # ISPTRG | |
# Read ISPTRG until finished | |
while read8(0xC010) != 0: # ISPTRG | |
pass | |
# Read ISP Data Register (ISPDAT) | |
dat = read32(0xC008) # ISPDAT | |
# Disable ISP | |
write8(0xC000, 0x00) | |
return dat | |
def write_via_isp(adr, dat): | |
write8(0xC000, 0x79) # ISPCTL | |
# ISP-Command = Flash Program | |
write8(0xC00C, 0x21) # ISPCMD | |
write32(0xC004, adr) # ISPADDR | |
write32(0xC008, dat) # ISPDAT | |
write8(0xC010, 0x01) # ISPTRG | |
# Read ISPTRG until finished | |
while read8(0xC010) != 0: # ISPTRG | |
pass | |
if read8(0xC000) & 0x40: # ISPCTL | |
print("ISP Error") | |
return | |
write8(0xC000, 0x00) # ISPCTL | |
def erase_page(adr): | |
write8(0xC000, 0x79) # ISPCTL | |
# ISP-Command = Flash page erase | |
write8(0xC00C, 0x22) # ISPCMD | |
write32(0xC004, adr) # ISPADDR | |
write8(0xC010, 0x01) # ISPTRG | |
# Read ISPTRG until finished | |
while True: | |
try: | |
if read8(0xC010) == 0: # ISPTRG | |
break | |
except (TransferFaultError, TransferTimeoutError): | |
pass | |
# print(".") | |
if read8(0xC000) & 0x40: # ISPCTL | |
print("ISP Error") | |
return | |
write8(0xC000, 0x00) # ISPCTL | |
def ident_sys_base(): | |
# Older Cortex-M0 variants use different address for peripherals' mapping | |
if target.read32(0x4000_0000) in [0x0000_0000, 0xFFFF_FFFF]: | |
self.sys_base = 0x5000_0000 | |
else: | |
self.sys_base = 0x4000_0000 | |
@command(help="Identify Part") | |
def ident(): | |
ident_sys_base() | |
core_type_name = target.selected_core.name | |
pdid = read32(0) # PDID | |
part_name = PART_NAME_DICT.get(pdid) | |
if part_name is not None: | |
print(f"PDID=0x{pdid:08X} {part_name} ({core_type_name})") | |
else: | |
print(f"PDID=0x{pdid:08X} ({core_type_name})") | |
@command(help="Read Configuration Registers") | |
def read_config_regs(): | |
ident() | |
unlock_reg() | |
config0 = read_via_isp(0x0030_0000) | |
config1 = read_via_isp(0x0030_0004) | |
print(f"CONFIG0 = 0x{config0:08X}") | |
print(f"CONFIG1 = 0x{config1:08X}") | |
lock_reg() | |
@command(help="Write Configuration Registers") | |
def write_config_regs(config0: int, config1: int): | |
ident() | |
unlock_reg() | |
# print(f"config0=0x{config0:08X} config1=0x{config1:08X}") | |
erase_page(0x0030_0000) | |
write_via_isp(0x0030_0000, config0) | |
write_via_isp(0x0030_0004, config1) | |
config0 = read_via_isp(0x0030_0000) | |
config1 = read_via_isp(0x0030_0004) | |
lock_reg() | |
print(f"CONFIG0 = 0x{config0:08X}") | |
print(f"CONFIG1 = 0x{config1:08X}") | |
@command(help="Boot Select") | |
def boot_select(sel: str): | |
ident_sys_base() | |
if sel.lower() in ["ap", "aprom"]: | |
unlock_reg() | |
write32(0xC000, 0) | |
elif sel.lower() in ["ld", "ldrom"]: | |
unlock_reg() | |
write32(0xC000, 2) | |
else: | |
print(f"Wrong parameter: {sel}") | |
@command(help="Chip Erase") | |
def chip_erase(): | |
ident_sys_base() | |
unlock_reg() | |
write8(0xC000, 0x79) # ISPCTL | |
write8(0xC01C, 0x01) # Write one to undocumented flash control register | |
# ISP-Command = Flash Mass Erase | |
write8(0xC00C, 0x26) # ISPCMD | |
write32(0xC004, 0) # ISPADDR | |
print("Performing chip erase.") | |
write8(0xC010, 0x01) # ISPTRG | |
# Read ISPTRG until finished | |
while True: | |
try: | |
if read8(0xC010) == 0: # ISPTRG | |
break | |
except (TransferFaultError, TransferTimeoutError): | |
pass | |
# print(".") | |
if read8(0xC000) & 0x40: # ISPCTL | |
print("ISP Error") | |
return | |
write8(0xC000, 0x00) # ISPCTL | |
print("Done.") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment