Skip to content

Instantly share code, notes, and snippets.

@TG9541
Last active March 15, 2024 13:36
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save TG9541/79aa3bb1a56a8220eac8f57ab21302e1 to your computer and use it in GitHub Desktop.
Save TG9541/79aa3bb1a56a8220eac8f57ab21302e1 to your computer and use it in GitHub Desktop.
Test run with stm8-gdb and openmcd

Experiments with OPENOCD and GDB for the STM8

Attempts to support the STM8 with OPENOCD, a standard hardware debugger front-end, and GDB, the GNU Debugger, was a long while in the making. Both OPENOCD and GDB are powerful tools that are often packaged by tool vendors (integrated in a turnkey IDE) or independently used by a community advanced users. Usuage examples for µCs often barely scratch the surface, don't mention testing procedures, modes of operation, or which features are supported.

Here is a list of features that worked so far:

  • flashing a binary in ELF format to an STM8S003F3P6
  • running, interrupting and continuing execution
  • setting breakpoints
  • reading registers

Here is also a list of things that probably won't work:

  • examples for the GDB TUI (Text User Interface) that use commands besidesenable, disable or show registers

Install STM8-BINUTILS-GDB

On a well-used Ubuntu 18.04, installing stm8-binutils-gdb worked without any problem by following the instructions the README.txt in the source archive.

The stm8-binutils-gdb toolkit is delivered as a series of patches on top of a specific version of binutils. It's well worth exploring the patches since it makes it easier for a newbie to assess what to expect. The initial author of the patches also appears as the contributor of the OPENOCD STM8 SWIM support. I owe him a beer, or a bottle of Suabian wine, if he prefers that.

Install OpenOCD

Clone https://github.com/ntfreak/openocd and follow the installation instructions.

Note: check the output of ./configure if st-link is present. Else check for dependencies, e.g. libusb-1.0 and re-run ./bootstrap. After the build the adapters can be listed with openocd -c "adapter list").

Running results in Error: Debug adapter doesn't support 'swim' transport.

A solution is shown in Développement STM8 à partir de linux (merci!):

Store the following in the OPENOCD's interface folder as st-link.cfg:

adapter driver st-link
st-link vid_pid 0x0483 0x3744 0x0483 0x3748 0x0483 0x374b 0x0483 0x374d 0x0483 0x374e 0x0483 0x374f 0x0483 0x3752 0x0483 0x3753

After that start in a 1st console:

(base) thomas@w500:~/source/stm8s/stm8ef$ openocd -f interface/st-link.cfg -f target/stm8s003.cfg -c "init" -c "reset halt"

Here is the console output after some interaction with GDB:

Open On-Chip Debugger 0.10.0+dev-01404-g3934483-dirty (2020-09-13-14:28)
Licensed under GNU GPL v2
For bug reports, read
	http://openocd.org/doc/doxygen/bugs.html
srst_only separate srst_gates_jtag srst_open_drain connect_deassert_srst

Info : STLINK V2J35S7 (API v2) VID:PID 0483:3748
Info : Target voltage: 3.190110
Info : clock speed 800 kHz
Info : starting gdb server for stm8s.cpu on 3333
Info : Listening on port 3333 for gdb connections
target halted due to debug-request, pc: 0x00008000
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : accepting 'gdb' connection on tcp/3333
Warn : keep_alive() was not invoked in the 1000 ms timelimit. GDB alive packet not sent! (1355 ms). Workaround: increase "set remotetimeout" in GDB
target halted due to debug-request, pc: 0x00008000

Other STM8 devices may need different target/stm8...cfg files (see below for a STM8L101.cfg and STM8L051.cfg).

More on the keep_alive() warning will to have emerge later.

STM8 eForth with stm8-gdb

I added a new target debug to forth.mk:

MDEPS   = forth.rel forth.h
MKDIR_P = mkdir -p out
BTARGET = $(BOARD)/target.inc
OUT     = out/$(BOARD)
SDCCOPT = --out-fmt-elf --all-callee-saves --debug --verbose --stack-auto --fverbose-asm --float-reent --no-peep

TARGET := $(shell echo `[ -f $(BTARGET) ] && awk '/TARGET/ {print tolower($$3)}' $(BTARGET) || echo "stm8s103f3"`)
OPTFILE := $(shell echo $(TARGET) | awk '{print "tools/" substr($$0,1,8) "FactoryDefaults.bin"}')

all: directories main.ihx

debug: main.c $(MDEPS)
        sdcc -mstm8 -I./$(BOARD) -I./inc -o$(OUT)/$(BOARD).elf main.c $(OUT)/forth.rel $(SDCCOPT)
        mkdir -p $(OUT)/target
        rm -f $(OUT)/target/*
        rm -f target
        ln -s $(OUT)/target/ target
        awk '/^ +([0-9A-F]){6}.* HI:/ {print "break 0x" $$1}' $(OUT)/forth.rst > $(OUT)/simbreak.txt
        awk -f tools/genalias.awk -v target="$(OUT)/target/" $(OUT)/forth.rst
        awk -f tools/genconst.awk -v target="$(OUT)/target/" $(OUT)/forth.rst
....

Running make BOARD=MINDEV debug will now create an ELF file in out/MINDEV:

(base) thomas@w500:~/source/stm8s/stm8ef$ make BOARD=MINDEV debug
mkdir -p out/MINDEV
sdasstm8 -I. -I./MINDEV -I./inc -plosgffw out/MINDEV/forth.rel forth.asm
# sdcc -mstm8 -I./MINDEV -I./inc -oout/MINDEV/MINDEV.ihx main.c --out-fmt-elf --all-callee-saves --debug --verbose --stack-auto --fverbose-asm --float-reent --no-peep out/MINDEV/forth.rel
sdcc -mstm8 -I./MINDEV -I./inc -oout/MINDEV/MINDEV.elf main.c out/MINDEV/forth.rel --out-fmt-elf --all-callee-saves --debug --verbose --stack-auto --fverbose-asm --float-reent --no-peep
sdcc: Calling preprocessor...
sdcc: sdcpp -nostdinc -Wall -std=c11 -I./MINDEV -I./inc -obj-ext=.rel -D__SDCC_STACK_AUTO -D__SDCC_CHAR_UNSIGNED -D__SDCC_MODEL_MEDIUM -D__SDCC_INT_LONG_REENT -D__SDCC_FLOAT_REENT -D__SDCC_ALL_CALLEE_SAVES -D__SDCC=4_0_3 -D__SDCC_VERSION_MAJOR=4 -D__SDCC_VERSION_MINOR=0 -D__SDCC_VERSION_PATCH=3 -D__SDCC_REVISION=11857 -D__SDCC_stm8 -D__STDC_NO_COMPLEX__=1 -D__STDC_NO_THREADS__=1 -D__STDC_NO_ATOMICS__=1 -D__STDC_NO_VLA__=1 -D__STDC_ISO_10646__=201409L -D__STDC_UTF_16__=1 -D__STDC_UTF_32__=1 -isystem /usr/local/bin/../share/sdcc/include/stm8 -isystem /usr/local/share/sdcc/include/stm8 -isystem /usr/local/bin/../share/sdcc/include -isystem /usr/local/share/sdcc/include  main.c 
sdcc: Generating code...
sdcc: Calling assembler...
sdcc: sdasstm8 -plosgffwy "out/MINDEV/MINDEV.asm"
sdcc: Calling linker...
sdcc: sdldstm8 -nf "out/MINDEV/MINDEV.lk"
mkdir -p out/MINDEV/target
rm -f out/MINDEV/target/*
rm -f target
ln -s out/MINDEV/target/ target
awk '/^ +([0-9A-F]){6}.* HI:/ {print "break 0x" $1}' out/MINDEV/forth.rst > out/MINDEV/simbreak.txt
awk -f tools/genalias.awk -v target="out/MINDEV/target/" out/MINDEV/forth.rst
awk -f tools/genconst.awk -v target="out/MINDEV/target/" out/MINDEV/forth.rst
(base) thomas@w500:~/source/stm8s/stm8ef$

Open a 2nd terminal for stm8-gdb:

(base) thomas@w500:~/source/stm8s/stm8ef$ stm8-gdb out/MINDEV/MINDEV.elf
GNU gdb (GDB) 8.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=x86_64-pc-linux-gnu --target=stm8-none-elf32".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from out/MINDEV/MINDEV.elf...done.
(gdb) run
Starting program: /home/thomas/source/stm8s/stm8ef/out/MINDEV/MINDEV.elf 
Remote debugging using localhost:3333
0x000081c4 in ?? ()
Loading section SSEG, size 0x1 lma 0x1
Loading section HOME, size 0x6b lma 0x8000
Loading section GSINIT, size 0x1a lma 0x806b
Loading section GSFINAL, size 0x3 lma 0x8085
Loading section CODE, size 0x120f lma 0x8088
Start address 0x806b, load size 4760
Transfer rate: 11 KB/sec, 952 bytes/write.
(gdb) 

Open a 3rd window for e4thcom (i.e. make term):

COLD
STM8EF2.2.26.pre3 ok
' . HEX . 8A07 ok

To wrap things up: at this point the following worked:

  1. building a recent version of OpenOCD with STM8 support
  2. patching and building GDB and binutils for the STM8
  3. building STM8 eForth with ELF support
  4. flashing an STM8S003F3P6 with stm8-gdb and OpenOCD

Debugging STM8 eForth with stm8-gdb

In GDB set a breakpoint at .. As GDB doesn't know about the dictionary (yet) let's use the address determined by ' . HEX . in the previous example.

(gdb) interrupt
(gdb) 
Program received signal SIGINT, Interrupt.
0x00008957 in ?? ()
(gdb) break * 0x8A07
Breakpoint 1 at 0x8a07
(gdb) continue
Continuing.

Now enter a line with . on the Forth console (3rd window):

TIM .<enter>

Which should trigger the breakpoint and result in the following in GDB:

Breakpoint 1, 0x00008a07 in ?? ()
 (gdb) backtrace
#0  0x00008a07 in ?? ()
#1  0x00008d3c in ?? ()
#2  0x00008d30 in ?? ()
#3  0x00000000 in ?? ()
 (gdb) continue
Continuing.

After which the TIM value is printed.

The command backtrace ("Print backtrace of all stack frames") assumes C stack frames (which Forth doesn't have). As long as Forth words don't use >R, R> or a counted loop it's still useful since (hypothesis: words then behave like void a(void) {}, i.e. C function without no parameters or local variables). It remains to be seen if "emulated" stack frame information can be somehow supplied.

While the STM8 is running no interaction with GDB is possible. In this case type CTRL-C to stop the target:

(gdb) continue
Continuing.

Program received signal SIGINT, Interrupt.
0x00008a1f in ?? ()
(gdb)

Various Commands

i r: dump register (i is a shorthand for info)

(gdb) i r
pc             0x895d   35165
a              0x0      0
x              0x31a    794
y              0x0      0
sp             0x3f9    1017
cc             0x22     34

i r $x dump register x

(gdb) i r $x
x              0x31a    794
(gdb) 

x/FMT address (x is shorthand for "examine" - FMT is explained here):

(gdb) x/16xb 0x8a06 
0x8a06: 0xb6    0x65    0xa8    0x0a    0x27    0x02    0x20    0xe4
0x8a0e: 0xcd    0x88    0x62    0xcd    0x89    0x6f    0x20    0xc1
(gdb) x/2i 0x8a06
   0x8a06:      ld A,0x65 ;0x65
   0x8a08:      xor A,#0x0a ;0xa
(gdb) 

Assuming the target is running (continue), we have a breakpoint at 0x8a06 (.) and we type 1 2 3 4 5 6 7 8 9 $aa55 . in the terminal:

Breakpoint 1, 0x00008a06 in ?? ()
(gdb) x/16h $x
0x30c:  0xaa55  0x0009  0x0008  0x0007  0x0006  0x0005  0x0004  0x0003
0x31c:  0x0002  0x0001  0x0000  0x0000  0x0000  0x0000  0x0000  0x0000
(gdb)

layout asm: show disssembled code at the breakpoint in the TUI:

   ┌───────────────────────────────────────────────────────────────────────────┐
B+>│0x8a06  ld A,0x65 ;0x65                                                    │
   │0x8a08  xor A,#0x0a ;0xa                                                   │
   │0x8a0a  jreq 0x8a0e ;0x8a0e                                                │
   │0x8a0c  jra 0x89f2 ;0x89f2                                                 │
   │0x8a0e  call 0x8862 ;0x8862                                                │
   │0x8a11  call 0x896f ;0x896f                                                │
   │0x8a14  jra 0x89d7 ;0x89d7                                                 │
   │0x8a16  push CC                                                            │
   │0x8a17  srl (0x01,SP) ;0x1                                                 │
   │0x8a19  clr 0xcd ;0xcd                                                     │
   │0x8a1b  int 0xe820e7 ;0xe820e7                                             │
   │0x8a1f  ldw Y,X                                                            │
   │0x8a21  incw X                                                             │
   │0x8a22  incw X                                                             │
   │0x8a23  ldw Y,(Y)                                                          │
   │0x8a25  ret                                                                │
   │0x8a26  incw X                                                             │
   └───────────────────────────────────────────────────────────────────────────┘
extended-r Remote target In:                                   L??   PC: 0x8a06 
(gdb) 

layout regs (same as layout asm followed by tui reg general) shows a combined register and disassembler view:

┌──Register group: general─────────────────────────────────────────────────────┐
│pc             0x8a06   35334          a              0x8c     140            │
│x              0x31e    798            y              0x8a06   35334          │
│sp             0x3fb    1019           cc             0x24     36             │
│                                                                              │
│                                                                              │
│                                                                              │
│                                                                              │
│                                                                              │
└──────────────────────────────────────────────────────────────────────────────┘
B+>│0x8a06  ld A,0x65 ;0x65                                                    │
   │0x8a08  xor A,#0x0a ;0xa                                                   │
   │0x8a0a  jreq 0x8a0e ;0x8a0e                                                │
   │0x8a0c  jra 0x89f2 ;0x89f2                                                 │
   │0x8a0e  call 0x8862 ;0x8862                                                │
   │0x8a11  call 0x896f ;0x896f                                                │
   │0x8a14  jra 0x89d7 ;0x89d7                                                 │
   │0x8a16  push CC                                                            │
   └───────────────────────────────────────────────────────────────────────────┘
extended-r Remote target In:                                   L??   PC: 0x8a06 

ni (alias for "step one instruction, see help aliases:

┌──Register group: general─────────────────────────────────────────────────────┐
│pc             0x8a08   35336          a              0xa      10             │
│x              0x31e    798            y              0x8a06   35334          │
│sp             0x3fb    1019           cc             0x20     32             │
│                                                                              │
│                                                                              │
│                                                                              │
│                                                                              │
│                                                                              │
   ────────────────────────────────────────────────────────────────────────────┘
B+ │0x8a06  ld A,0x65 ;0x65                                                    │
  >│0x8a08  xor A,#0x0a ;0xa                                                   │
   │0x8a0a  jreq 0x8a0e ;0x8a0e                                                │
   │0x8a0c  jra 0x89f2 ;0x89f2                                                 │
   │0x8a0e  call 0x8862 ;0x8862                                                │
   │0x8a11  call 0x896f ;0x896f                                                │
   │0x8a14  jra 0x89d7 ;0x89d7                                                 │
   │0x8a16  push CC                                                            │
   └───────────────────────────────────────────────────────────────────────────┘
extended-r Remote target In:                                   L??   PC: 0x8a08 

Note that changed register values are conveniently highlighted, in this example pc, a and cc.

Interacting with openocd through telnet

Openocd implements a gdbserver but it also has a life of its own: not only is it scriptable in the TCL subset Jim but also it has a telnet console that can be used for some basic debugging, e.g. memory inspection.

Supose we start openocd with

openocd -f interface/st-link.cfg -f target/stm8s003.cfg -c "init" -c "reset halt"

and then open a telnet session to it with telnet localhost 4444, entering help returns a shipload of commands. Some of them are described as stm8 specific, e.g.

stm8s.cpu mdd address [count]
        Display target memory as 64-bit words

This command is, however, aliased to the gdbserver command mdd.

Here is an example: since openocd was started with halt we can first run resume to start the target code:

> resume
> mdd 0x0000
target not halted
0x00000000: ffa40d67d5feebac 
>

In a 2nd session we run e4thcom to get a Forth console to our target (MINDEV from a previous session):

STM8EF2.2.26.pre3 ok
0 10 dump
   0  FF A4  D 67 D5 FE EB AC DF 4B D6 BC 7F 7E CA 2E  ___g_____K___~_. ok

This shows that memory can be examined with openocd without halting the target (which is nice).

There is another feature we can use without GDB: watchpoints

> help wp
rwp address
      remove watchpoint
wp [address length [('r'|'w'|'a') value [mask]]]
      list (no params) or create watchpoints
> wp 0 4 w 
target stm8s.cpu is not halted (add watchpoint)
can't add write watchpoint at 0x00000000, target running
Failure setting watchpoints

> halt
target halted due to debug-request, pc: 0x00008965
> wp 0 4 w 
Only watchpoints of length 1 are supported
can't add write watchpoint at 0x00000000, unrecognized error
Failure setting watchpoints

> wp 0 1 w
> wp 2 1 w 
> wp 4 1 w 
no hardware watchpoints available
can't add write watchpoint at 0x00000004, resource not available
Failure setting watchpoints

> resume

We now know that watchpoints to arbitray addresses can be set, but only to single bytes and only two of them. It could have been worse, but also better (e.g. one range could have been perimitted, see table 4 in UM430).

By entering 0 0 ! (write to RAM address 0) the breakpoint triggers:

target halted due to breakpoint, pc: 0x000082fb

STM8L101 target support

Adding this as stm8l101.cfg to the OpenOCD target folder will do the trick:

#config script for STM8L101

set FLASHEND 0x9FFF
set BLOCKSIZE 0x40

proc stm8_reset_rop {} {
   mwb 0x4800 0x00
   reset halt
}

source [find target/stm8l.cfg]

There are no EEPROM settings since RM0013 STM8L101 devices provide a "Data Flash" blocks at the end of the normal Flash. For all practical purposes Data Flash can be treated like Flash (I'm not sure if the opposite is the case).

STM8L051 target support

A very similar configuration, based on stm8l152.cfg works for RM0031 STM8L Low Density devices (e.g. STM8L051F3):

#config script for STM8L051

set FLASHEND 0x9FFF
set BLOCKSIZE 0x40
set EEPROMSTART 0x1000
set EEPROMEND 0x10ff

proc stm8_reset_rop {} {
   mwb 0x4800 0xaa
   mwb 0x4800 0xaa
   reset halt
}

source [find target/stm8l.cfg]

I'm not sure if it's necessary to apply the line mwb 0x4800 0xaa twice - there is no indication in the datasheet that it's needed.

@cocus
Copy link

cocus commented Mar 5, 2021

Thanks for this!
Do you know if it's possible to generate debug symbols on the elf file if you're linking multiple .rel files? I have tried but I always get the elf file without the debug symbols. I've tried something like this:

sdcc -mstm8 --opt-code-size --debug --verbose -I./ -D STM8S003 -o a.rel -c a.c
sdcc -mstm8 --opt-code-size --debug --verbose -I./ -D STM8S003 -o b.rel -c b.c
sdcc -o file.elf -mstm8 --nostdlib --code-size 8192 --iram-size 1024 --debug --out-fmt-elf a.rel b.rel

Where a.c has the main function, and b.c has a dummy function that changes a register (so it's not optimized out) and it's called from main().
I've tried with the ohter arguments from this gist, like --all-callee-saves and such, but no dice. I'm using the stm8-gdb tho.
If I use the single line build+link command, it generates an elf file which does contain debug symbols! (Although I can only use one .c file).

@TG9541
Copy link
Author

TG9541 commented Mar 6, 2021

Do you know if it's possible to generate debug symbols on the elf file if you're linking multiple .rel files? I have tried but I always get the elf file without the debug symbols. I've tried something like this:

I'm sorry but I don't know how to help you here - except to make sure that you use a recent version of SDCC (which, I guess, you tried) or to write to the SDCC mailing list (you might want to name the version number).

@cocus
Copy link

cocus commented Mar 6, 2021

Hi!
I've done exactly that, and I got an answer from the sourceforge forum. Have a look https://sourceforge.net/p/sdcc/discussion/1864/
In short, we need to add --out-fmt-elf even when creating object code!

@Althrone
Copy link

Hi!
I've done exactly that, and I got an answer from the sourceforge forum. Have a look https://sourceforge.net/p/sdcc/discussion/1864/
In short, we need to add --out-fmt-elf even when creating object code!

Thank you!!!!!
I have been troubled by this problem for a long time!!!
Thank you so much!!!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment