Skip to content

Instantly share code, notes, and snippets.

@elfmimi
Forked from TheLastMutt/mini51.c
Last active March 11, 2024 00:24
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save elfmimi/3b5f8e42c594c670adc1422d7a38ccf7 to your computer and use it in GitHub Desktop.
Save elfmimi/3b5f8e42c594c670adc1422d7a38ccf7 to your computer and use it in GitHub Desktop.
OpenOCD utils for Nuvoton NUC123 , NUC126 and others
# Housekeeping stuff for OpenOCD targeting Nuvoton NUC123 , NUC126 and others.
# by Ein Terakawa <applause@elfmimi.jp>
#
# 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