-
-
Save elfmimi/3b5f8e42c594c670adc1422d7a38ccf7 to your computer and use it in GitHub Desktop.
OpenOCD utils for Nuvoton NUC123 , NUC126 and others
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
# Housekeeping stuff for OpenOCD targeting Nuvoton NUC123 , NUC126 and others. | |
# by Ein Terakawa <[email protected]> | |
# | |
# based on TheLastMutt's mini51.cfg | |
# https://gist.github.com/TheLastMutt/d1c1948acaace7444c1c#file-mini51-cfg | |
# Usage example | |
# | |
# Read config regs. | |
# openocd -f NUCxxx.cfg -c ReadConfigRegs -c exit | |
# | |
# Read config regs with INTERFACE. (stlink is default) | |
# env INTERFACE=stlink openocd -f NUCxxx.cfg -c ReadConfigRegs -c exit | |
# | |
# Read config regs with specified tcl script path and binary. | |
# env OPENOCD_SCRIPTS=~/GitHub/OpenOCD/tcl ~/GitHub/OpenOCD/src/openocd.exe -f NUCxxx.cfg -c ReadConfigRegs -c exit | |
# | |
# Write config regs and Read back. | |
# openocd -f NUCxxx.cfg -c"WriteConfigRegs 0xF7FFFF7E 0x0001C000" -cReadConfigRegs -cexit | |
# | |
# Write application to APROM and run. | |
# openocd -fNUCxxx.cfg -c"init; halt; program USBD_HID_Keyboard.elf; SysReset aprom run; exit" | |
# | |
# Read APROM. | |
# openocd -fNUCxxx.cfg -c"SysReset aprom halt; flash read_bank 0 aprom-filename.bin; reset run; exit" | |
# | |
# Read LDROM. | |
# openocd -fNUCxxx.cfg -c"SysReset ldrom halt; flash read_bank 1 ldrom-filename.bin; reset run; exit" | |
# | |
# Debug with gdb. (Run as gdb server.) | |
# openocd -fNUCxxx.cfg | |
# | |
# Erase all, write config regs, read back and write bootloader to LDROM. | |
# openocd -f NUCxxx.cfg -c "ChipErase; WriteConfigRegs 0xF7FFFF7E 0x0001C000; ReadConfigRegs; program ISP-HID.bin 0x100000; SysReset ldrom run; exit" | |
if [info exists ::env(OPENOCD_SCRIPTS)] { | |
puts [format "OPENOCD_SCRIPTS = %s" [env OPENOCD_SCRIPTS]] | |
} else { | |
set search_dir [format "%s/%s" [env HOME] GitHub/OpenOCD-code/tcl] | |
add_script_search_dir $search_dir | |
} | |
proc load_script {file} { | |
if [catch {set file [find $file]} msg] { | |
if ![info exists ::env(OPENOCD_SCRIPTS)] { | |
puts stderr "Please set environment variable OPENOCD_SCRIPTS and specify proper tcl path." | |
} | |
return -code error [format "Can't find %s" $file] | |
} | |
uplevel #0 [list source $file] | |
} | |
set INTERFACE none | |
if [string equal [adapter_name] undefined] { | |
if [info exists ::env(INTERFACE)] { | |
set INTERFACE [env INTERFACE] | |
} else { | |
# default interface | |
set INTERFACE stlink | |
} | |
} | |
# when using Nuvoton Nu-Link | |
if {[lsearch -exact {nulink nu-link} [string tolower $INTERFACE]] >= 0} { | |
load_script interface/nulink.cfg | |
transport select hla_swd | |
} | |
# when using ST-Link V2 and clones | |
if {[lsearch -exact {stlink st-link stlinkv2 st-linkv2 stlink-v2 st-link-v2 stlink/v2 st-link/v2} [string tolower $INTERFACE]] >= 0} { | |
load_script interface/stlink.cfg | |
transport select hla_swd | |
} | |
# when using CMSIS-DAP | |
if {[lsearch -exact {cmsis cmsis-dap} [string tolower $INTERFACE]] >= 0} { | |
load_script interface/cmsis-dap.cfg | |
transport select swd | |
cmsis_dap_vid_pid 0x16C0 0x0486 0x0416 0xDC00 0x1209 0xDA42 | |
} | |
# when using SEGGER J-Link | |
if {[lsearch -exact {jlink j-link} [string tolower $INTERFACE]] >= 0} { | |
load_script interface/jlink.cfg | |
transport select swd | |
# jlink serial 50118839 | |
# jlink usb 0 | |
} | |
# when using FTDI MPSSE (FT232H and alike) | |
if {[lsearch -exact {ft232h ft232hl ae-ft232hl} [string tolower $INTERFACE]] >= 0} { | |
load_script interface/ftdi/ft232h-module-swd.cfg | |
} | |
if [string equal [adapter_name] undefined] { | |
error "No Interface!" | |
} | |
load_script target/numicro.cfg | |
# adjust clock freq. | |
if {[lsearch -exact {ftdi jlink} [adapter_name]] >= 0} { | |
adapter_khz 20000 | |
} else { | |
adapter_khz 10000 | |
} | |
#load_script mem_helper.tcl | |
proc mrw {reg} { | |
set value "" | |
mem2array value 32 $reg 1 | |
return $value(0) | |
} | |
add_usage_text mrw "address" | |
add_help_text mrw "Returns value of word in memory." | |
#proc mww {reg val} { | |
# return [numicro write_isp $reg $val] | |
#} | |
proc GetPartName {id} { | |
set parts([expr 0x12305]) "NUC123SC2AN1" | |
set parts([expr 0x12315]) "NUC123SD4AN0" | |
set parts([expr 0x12325]) "NUC123LC2AN1" | |
set parts([expr 0x12335]) "NUC123LD4AN0" | |
set parts([expr 0x12345]) "NUC123ZC2AN1" | |
set parts([expr 0x12355]) "NUC123ZD4AN0" | |
set parts([expr 0x10012305]) "NUC123SC2AE1" | |
set parts([expr 0x10012315]) "NUC123SD4AE0" | |
set parts([expr 0x10012325]) "NUC123LC2AE1" | |
set parts([expr 0x10012335]) "NUC123LD4AE0" | |
set parts([expr 0x10012345]) "NUC123ZC2AE1" | |
set parts([expr 0x10012355]) "NUC123ZD4AE0" | |
set parts([expr 0xC05204]) "NUC126LG4AE" | |
set parts([expr 0xC05205]) "NUC126LE4AE" | |
set parts([expr 0xC05206]) "NUC126NE4AE" | |
set parts([expr 0xC05212]) "NUC126SG4AE" | |
set parts([expr 0xC05213]) "NUC126SE4AE" | |
set parts([expr 0xC05230]) "NUC126KG4AE" | |
set parts([expr 0xC05231]) "NUC126VG4AE" | |
set parts([expr 0x1205204]) "NUC1261LG4AE" | |
set parts([expr 0x1205205]) "NUC1261LE4AE" | |
set parts([expr 0x1205206]) "NUC1261NE4AE" | |
set parts([expr 0x1205212]) "NUC1261SG4AE" | |
set parts([expr 0x1205213]) "NUC1261SE4AE" | |
if [info exists parts($id)] { | |
return $parts($id) | |
} else { | |
return "" | |
} | |
} | |
# The following procedures to erase and unlock a locked MINI51 are taken from | |
# https://github.com/hackocopter/SWD-Hacking/blob/master/KEIL-Flashtools/Mini51flashtools.ini | |
# Ported from KEIL to OpenOCD tcl language and added some comments. | |
# The chip erase sequence got reverse engineered using a Nulink programmer, a logic analyzer | |
# and custom SWD log parser software. | |
# Info here: | |
# https://github.com/hackocopter/SWD-Hacking | |
# https://www.mikrocontroller.net/topic/309185 (German forum) | |
# This unlocks access to protected registers | |
# by writing to REGWRPROT register. | |
proc InitandUnlock {} { | |
# Halt target | |
mww 0xe000edf0 0x05f0003 | |
# ?? Something Debug access port / Breakpoint unit | |
mww 0xe0002008 0x000000 | |
mww 0xe000200C 0x000000 | |
mww 0xe0002010 0x000000 | |
mww 0xe0002014 0x000000 | |
# Unlock sequence for protected registers | |
mww 0x50000100 0x59 | |
mww 0x50000100 0x16 | |
mww 0x50000100 0x88 | |
} | |
# Read data from flash memory organization address, | |
# *not* system memory address. See datasheet section 6.7.4 | |
proc ReadViaISP {adr} { | |
# Enable ISP | |
mww 0x5000c000 0x39 | |
# ISP-Command = Flash Read | |
mww 0x5000c00c 0x00 | |
mww 0x5000c004 $adr | |
# Write ISP Trigger Control Register (ISPTRG) | |
# to start | |
mww 0x5000c010 1 | |
# Read ISPTRG until finished | |
while {[mrw 0x5000c010] != 0} { | |
puts stderr "." | |
} | |
# Read ISP Data Register (ISPDAT) | |
set out [mrw 0x5000c008] | |
# Disable ISP | |
mww 0x5000c000 0x00 | |
return $out | |
} | |
# Write data to flash memory organization address | |
proc WriteViaISP {adr dat} { | |
mww 0x5000c000 0x79 | |
# ISP-Command = Flash Program | |
mww 0x5000c00c 0x21 | |
mww 0x5000c004 $adr | |
mww 0x5000c008 $dat | |
mww 0x5000c010 1 | |
# Read ISPTRG until finished | |
while {[mrw 0x5000c010] != 0} { | |
puts stderr "." | |
} | |
if {[mrw 0x5000c000] & 0x40} { | |
error "ISP Error" | |
} | |
mww 0x5000c000 0x00 | |
} | |
proc PageErase {adr} { | |
mww 0x5000c000 0x79 | |
# ISP-Command = Flash page erase | |
mww 0x5000c00c 0x22 | |
mww 0x5000c004 $adr | |
mww 0x5000c010 1 | |
sleep 20 | |
# Read ISPTRG until finished | |
while {[mrw 0x5000c010] != 0} { | |
puts stderr "." | |
sleep 10 | |
} | |
if {[mrw 0x5000c000] & 0x40} { | |
error "ISP Error" | |
} | |
mww 0x5000c000 0x00 | |
} | |
# Set boot configuration (like AVR fuse bits) | |
proc WriteConfigRegs {conf0 args} { | |
init | |
halt | |
set id [mrw 0x50000000] | |
set part [GetPartName $id] | |
if {$part != ""} { | |
puts [format "Device ID (PDID) :0x%08X (%s)" $id $part] | |
} else { | |
puts [format "Device ID (PDID) :0x%08X" $id] | |
} | |
WriteConfigRegsImpl $conf0 $args | |
puts "Configuration registers have been Written" | |
if {[expr $conf0 & 2] == 0} { | |
echo "Reset is forced for LOCK bit to take effect." | |
SysReset chip | |
exit | |
} | |
} | |
proc WriteConfigRegsImpl {conf0 args} { | |
InitandUnlock | |
# Boot from APROM, no IAP, Flash Unlocked, data flash enabled, no BOD | |
# All unused bits set to 1 | |
# Works for "DE" and "AN" parts | |
#set conf0 0xFFFFFFFE | |
# Data flash start address | |
#set conf1 0x3E00 | |
set prev_conf0 [ReadViaISP 0x300000] | |
if {[expr $prev_conf0 & 2] == 0} { | |
puts [format "Config0 (0x00300000):0x%X" $prev_conf0] | |
puts [format "Config1 (0x00300004):0x%X" [ReadViaISP 0x300004]] | |
puts "Flash is locked! You need ChipErase to unlock." | |
exit | |
} | |
foreach arg $args { | |
if [info exists conf1] { | |
error "Too many arguments" | |
} | |
set conf1 $arg | |
} | |
if {![info exists conf1]} { | |
set conf1 [ReadViaISP 0x300004] | |
} | |
# If writing to the config registers fails on a "DE series" part | |
# (e.g. Mini54ZDE) uncomment this: | |
# Write one to undocumented flash control register | |
# to enable write access to flash | |
#mww 0x5000c01c 0x01 | |
PageErase 0x300000 | |
WriteViaISP 0x300000 $conf0 | |
WriteViaISP 0x300004 $conf1 | |
} | |
proc VerifyAllOne {adr len} { | |
if {($len == 0) || ($len & ~-512) != 0} { | |
return -code error [format "wrong value for len (%08X)" $len] | |
} | |
# Enable ISP | |
mww 0x5000c000 0x39 | |
# ISP-Command = Flash Read | |
mww 0x5000c004 $adr | |
mww 0x5000c008 $len | |
mww 0x5000c00c 0x28 | |
# Write ISP Trigger Control Register (ISPTRG) | |
# to start | |
mww 0x5000c010 1 | |
# Read ISPTRG until finished | |
while {[mrw 0x5000c010] != 0} { | |
#puts stderr "." | |
} | |
# Read ISP Data Register (ISPDAT) | |
set allone [expr ([mrw 0x5000c040] & 0x80) != 0] | |
# Disable ISP | |
mww 0x5000c000 0x00 | |
return $allone | |
} | |
proc EraseConfig {} { | |
set conf0 0xFFFFFFFF | |
set conf1 0xFFFFFFFF | |
WriteConfigRegsImpl $conf0 $conf1 | |
puts "Configuration registers have been updated to Unprogrammed State" | |
} | |
proc ReadConfigRegs {} { | |
init | |
puts "Reading Configuration registers." | |
set id [mrw 0x50000000] | |
set part [GetPartName $id] | |
if {$part != ""} { | |
puts [format "Device ID (PDID) :0x%08X (%s)" $id $part] | |
} else { | |
puts [format "Device ID (PDID) :0x%08X" $id] | |
} | |
halt | |
InitandUnlock | |
set conf0 [ReadViaISP 0x300000] | |
set conf1 [ReadViaISP 0x300004] | |
puts [format "Config0 (0x00300000):0x%X" $conf0] | |
puts [format "Config1 (0x00300004):0x%X" $conf1] | |
if {[expr $conf0 & 2] == 0} { | |
puts "Caution, LOCK bit is in set state." | |
} | |
} | |
proc BlankCheck {} { | |
init | |
puts "Checking if flash memory is blank." | |
set id [mrw 0x50000000] | |
set part [GetPartName $id] | |
if {$part != ""} { | |
puts [format "Device ID (PDID) :0x%08X (%s)" $id $part] | |
} else { | |
puts [format "Device ID (PDID) :0x%08X" $id] | |
} | |
InitandUnlock | |
set conf0 [ReadViaISP 0x00300000] | |
if {$conf0 & 2} { | |
# puts "Flash is not locked." | |
} else { | |
puts "Flash is locked! Cannot check properly." | |
return | |
} | |
# NUC126 | |
if {[lsearch -exact {C05200} [format %X [expr $id & ~0xFF]]] >= 0} { | |
flash probe 0 | |
flash probe 1 | |
set flash [flash list] | |
set aprom [lindex $flash 0] | |
set ldrom [lindex $flash 1] | |
puts -nonewline "APROM: " | |
if [VerifyAllOne $aprom(base) $aprom(size)] { | |
puts "Erased." | |
} else { | |
puts "Not Erased!" | |
} | |
puts -nonewline "LDROM: " | |
if [VerifyAllOne $ldrom(base) $ldrom(size)] { | |
puts "Erased." | |
} else { | |
puts "Not Erased!" | |
} | |
} else { | |
puts -nonewline "APROM: " | |
if {[ReadViaISP 0x00000004] == 0xffffffff} { | |
puts "Erased." | |
} else { | |
puts "Not Erased!" | |
} | |
puts -nonewline "LDROM: " | |
if {[ReadViaISP 0x00100004] == 0xffffffff} { | |
puts "Erased." | |
} else { | |
puts "Not Erased!" | |
} | |
} | |
puts -nonewline "CONFIG: " | |
if {[ReadViaISP 0x00300000] == 0xffffffff && [ReadViaISP 0x00300004] == 0xffffffff} { | |
puts "Erased." | |
} else { | |
puts "Not Erased!" | |
} | |
} | |
# Perform undocumented erase and unlock sequence | |
# if flash is locked (Config0 register bit1 cleared) | |
proc ChipErase {} { | |
init | |
halt | |
InitandUnlock | |
set conf0 [ReadViaISP 0x300000] | |
if {$conf0 & 2} { | |
puts "Flash is not locked." | |
} else { | |
puts "Flash is locked! Erasing anyway." | |
} | |
# Enable ISP | |
mww 0x5000c000 0x79 | |
# Write one to undocumented flash control register | |
mww 0x5000c01c 0x01 | |
if {[mrw 0x5000c000] & 0x40} { | |
error "ISP Error" | |
} | |
if {[mrw 0x5000c010] != 0} { | |
error "ISP Busy error" | |
} | |
# Undocumented ISP-Command Chip-Erase | |
mww 0x5000c00c 0x26 | |
mww 0x5000c004 0 | |
puts "Performing chip erase." | |
mww 0x5000c010 1 | |
sleep 50 | |
while {[mrw 0x5000c010] != 0} { | |
puts stderr "." | |
sleep 10 | |
} | |
if {[mrw 0x5000c000] & 0x40} { | |
error "ISP Error" | |
} | |
# Disable ISP | |
mww 0x5000c000 0x00 | |
BlankCheck | |
# Write zero to undocumented register | |
mww 0x5000c01c 0 | |
# need reset for LOCK bit to take effect | |
if {[expr $conf0 & 2] == 0} { | |
reset | |
} | |
} | |
#proc ChipReset {} { | |
# InitandUnlock | |
# mww 0x50000008 0x01 | |
#} | |
proc SysReset {args} { | |
init | |
foreach arg $args { | |
if [string equal [string tolower $arg] ldrom] { | |
set ldrom 1 | |
} elseif [string equal [string tolower $arg] aprom] { | |
set aprom 1 | |
} elseif [string equal [string tolower $arg] cpu] { | |
set cpurst 1 | |
} elseif [string equal [string tolower $arg] chip] { | |
set chiprst 1 | |
} elseif [string equal [string tolower $arg] por] { | |
set chiprst 1 | |
} elseif [string equal [string tolower $arg] run] { | |
set run 1 | |
} elseif [string equal [string tolower $arg] halt] { | |
set halt 1 | |
} else { | |
error [format "Unexpected argument '%o'" $arg] | |
} | |
} | |
if {[info exists aprom] && [info exists ldrom]} { | |
error "Too many arguments" | |
} | |
if {[info exists cpurst] && [info exists chiprst]} { | |
error "Too many arguments" | |
} | |
if {[info exists run] && [info exists halt]} { | |
error "Too many arguments" | |
} | |
halt | |
InitandUnlock | |
if [info exists chiprst] { | |
# Set CHIPRST (IPRSTC1[0]) to 1. Result of CHIP Reset is same as Power On Reset. | |
mww 0x50000008 [expr [mrw 0x50000008] | 0x01] | |
} else { | |
if [info exists aprom] { | |
# Set BS (ISPCTL[1]) to 0 | |
mww 0x5000c000 [expr [mrw 0x5000c000] & ~0x02] | |
} | |
if [info exists ldrom] { | |
# Set BS (ISPCTL[1]) to 1 | |
mww 0x5000c000 [expr [mrw 0x5000c000] | 0x02] | |
} | |
if [info exists cpurst] { | |
# Set CPURST (IPRSTC1[1]) to 1. | |
mww 0x50000008 [expr [mrw 0x50000008] | 0x02] | |
} else { | |
# Set SYSRESETREQ (AIRCR[2]) to 1 along with VECTORKEY | |
# mww 0xe000ed0c 0x05fa0004 | |
if [info exists run] { | |
reset run | |
} elseif [info exists halt] { | |
reset halt | |
} else { | |
reset | |
} | |
} | |
} | |
} | |
if 0 { } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment