📜 ⬆️ ⬇️

How to compress bootloader for STM8 to 18 bytes in FLASH memory

In the process of searching for the bootloader for the STM8S103F3 microcontroller, it was found that the existing bootloaders are mostly written in “C”, “steal” a significant amount of FLASH memory, transfer the interrupt vector table.

The loader was necessary for some device to which it is impossible to connect the programmer.

It was decided to try to write the loader with the following requirements:
')
- the bootloader was supposed to be called STM8uLoader;
- the code should be written in assembler (since assembler is not legally prohibited by law);
- the loader should occupy the minimum possible amount in FLASH memory, the volume occupied in the computer will be considered unlimited;
- the loader should not move the table of interrupt vectors;
- the bootloader must have the minimum functionality, the entire main functionality must be assumed by the computer;
- the loader must transfer control to the application program within a reasonable time after resetting / turning on when there is no connection with the computer.

The first condition was instantly fulfilled , but over subsequent requirements had to work.

First stage. 65 byte code in FLASH memory


To save the vector table in its place, it was decided to place the code at the end of the FLASH memory and switch to it immediately from the reset vector $ 8000.

When loading, control is transferred to the loader code at $ 9FC2. The loader configures the UART 9600 8N1, waits for two bytes on the UART, and without waiting for it passes control to the application program at the address stored in the $ 9FFE pair: $ 9FFF.

If the bootloader accepts the high and low bytes of the size of the expected dump from the host program, then takes the dump itself, puts the dump in the RAM memory and transfers control to it.
Further, all care falls on the program in the computer and the dump sent by it. It should send exactly those dumps that are needed to perform the current task (reading / erasing / writing / copying STM8 memory cells). Dumps should be able to replace each other in RAM memory and transfer control to the application program.

The address of the transition to the application program here is $ 9FFE: $ 9FFF.

File boot_FLASH.asm:
stm8/ TITLE "boot_FLASH.asm" .NOLIST #include "STM8S103F3P.inc" .LIST MOTOROLA WORDS segment byte at 8000 'boot_start' boot_start: jp boot_FLASH_start dc.b $00 ; boot_FLASH ; ******************************************************** ;  0x8004...0x9FC1      WORDS ; segment byte at 8004 'main_FLASH' main_FLASH_start: ldw X, #$03FF ldw SP, X mov UART1_BRR1, #13 mov UART1_CR2, #%00001100 main_FLASH_cycle: callr main_delay ;   bset PB_DDR,#5 bset PB_CR1,#5 ;   byte1_tx: mov UART1_DR, #$80 byte1_wait_tx btjf UART1_SR, #7, byte1_wait_tx callr main_delay boot_RAM_exit1: ;   bres PB_DDR,#5 ; bres PB_CR1,#5 ; ;   byte2_tx: mov UART1_DR, #$08 byte2_wait_tx btjf UART1_SR, #7, byte2_wait_tx jra main_FLASH_cycle main_delay: decw X jrne main_delay ret segment byte at 9FC2 'boot_FLASH' boot_FLASH_start: mov UART1_BRR1, #13; Fmaster=16/8=2/9600/16 mov UART1_CR2, #%00001100;  / ;   UART1   RST_SR boot_FLASH_RST_SR_tx: mov UART1_DR, RST_SR ;    ,      ;      ;    X    ( 200 ) ldw X,#0 boot_FLASH_wait_byte1: decw X jreq boot_FLASH_exit;       btjf UART1_SR, #5, boot_FLASH_wait_byte1 ;   ,   , ;  X     ld A, UART1_DR ld XH, A ;      boot_FLASH_wait_byte2: btjf UART1_SR, #5, boot_FLASH_wait_byte2 ;    ld A, UART1_DR ld XL, A ;  X -    ;   X       ldw Y, #$0400 ;  Y   0x0400 (RAM_END + 1) ;    boot_FLASH_rx_block_wait: btjf UART1_SR, #5, boot_FLASH_rx_block_wait boot_EEPROM_rx_block_entry: decw Y ;      Y     ld A, UART1_DR ld (Y), A decw X ;      X      jrne boot_FLASH_rx_block_wait ;      jp (Y) ;    ()  boot_FLASH_exit: dc.b $CC boot_FLASH_exit_addr: dc.w main_FLASH_start end ; 


Second phase. 21 byte code in FLASH and 52 bytes in EEPROM memory


Selecting 65 bytes from the FLASH memory (in STM8S103F3, its total is 8192 bytes) is not human. After all, the unused EEPROM memory with its 640 bytes is lying around. Let's divide the bootloader code into two parts, boot_FLASH and boot_EEPROM.

When loading, control of the boot_FLASH code is transferred to the address $ 9FEF. boot_FLASH copies the boot_EEPROM code image from EEPROM memory into RAM memory and transfers control to it.

Now the boot_EEPROM configures the UART 9600 8N1, waits for UART bytes, and without waiting for it transfers control to the application program (the address is $ 9FFE: $ 9FFF).

If the boot_EEPROM accepts a byte with the size of the expected dump for RAM memory, then takes a dump, dumps it to another area of ​​RAM memory and transfers control to it.

Then everything is like in the first stage.

File boot_FLASH_EEPROM.asm:
 stm8/ TITLE "boot_FLASH_EEPROM.asm" .NOLIST #include "STM8S103F3P.inc" .LIST MOTOROLA WORDS segment byte at 4000 'eeprom' ;  boot_EEPROM dc.b $35, $0D, $52, $32, $35, $0C, $52, $35 dc.b $35, $01, $52, $31, $5A, $27, $16, $72 dc.b $0B, $52, $30, $F8, $C6, $52, $31, $72 dc.b $0B, $52, $30, $FB, $3B, $52, $31, $4A dc.b $26, $F5, $96, $5C, $FC, $CE, $9F, $FE dc.b $2B, $FA, $90, $AE, $42, $7F, $AE, $02 dc.b $7F, $CC, $9F, $F4 segment byte at 8000 'boot_start' boot_start: jp boot_FLASH_start dc.b $01 ; boot_FLASH_EEPROM ; ******************************************************** ;  0x8004...0x9FEE      segment byte at 8004 'main_FLASH' ;   main_FLASH_start: ldw X, #$03FF ldw SP, X mov UART1_BRR1, #13 mov UART1_CR2, #%00001100 main_FLASH_cycle: callr main_delay ;   bset PB_DDR,#5 bset PB_CR1,#5 ;   byte1_tx: mov UART1_DR, #$80 byte1_wait_tx btjf UART1_SR, #7, byte1_wait_tx callr main_delay boot_RAM_exit1: ;   bres PB_DDR,#5 ; bres PB_CR1,#5 ; ;   byte2_tx: mov UART1_DR, #$08 byte2_wait_tx btjf UART1_SR, #7, byte2_wait_tx jra main_FLASH_cycle main_delay: decw X jrne main_delay ret ;   EEPROM -> RAM segment byte at 9FEF 'boot_FLASH' boot_FLASH_start: ldw X, SP ; Y <- { EEPROM_START + RAM_END} ; Y <- { $4000 + $03FF = $43FF } ldw Y, #$43FF boot_FLASH_copy: ld A, (Y) ld (X), A decw Y decw X jrpl boot_FLASH_copy incw X jp (X) boot_FLASH_exit_address: dc.w main_FLASH_start end ; 


Run the file runSTM8uLoader.bat , press the reset button on the board, the loader sends byte 0x01. A memory dump with the code from the main_RAM.hex file is sent to the STM8 RAM memory. The board starts blinking the LED and sending bytes 0x20 and 0x02. Press the reset button again. The application program starts from FLASH memory, the LED starts blinking faster and sending bytes 0x80 and 0x08.


The third stage. Code size of 18 bytes in FLASH memory and 52 bytes in OPTION Bytes


With the EEPROM memory, of course, we hurried. Where now to store the sinuses and other tables? Yes, and with FLASH memory is not all clear. Who decided to store the transfer address of the control application program in memory FLASH? And the same byte of the bootloader version is generally stored in two places at once. Where to squeeze in 52 bytes intended for EEPROM?

Here we can help lithography. EEPROM memory consists of 10 blocks of 64 bytes each. To add one more block to these blocks, but with a different size is not economically feasible. STMicroelectronics did just that, added another block of 64 bytes in size, called this area OPTION Bytes and stores there the important non-volatile settings of the microcontroller (in STM8S103F3 it is as much as 11 bytes). And of course, STM has forgotten to mention that there are still 53 workable cells in this area. Apparently there are a lot of STM8 models, you need to leave room for future important settings.

Our loader claims only for STM8 models without built-in loaders. For this, we are taking backup cells of the OPTION Bytes block while nobody sees it. True, there is one small, but solvable , inconvenience. Normal programmer will not allow you to write information in these cells.

When loading, control of the initial copier boot_FLASH is transferred to the address $ 9FF2. boot_FLASH transfers the boot_OPTION bootloader image from the OPTION Bytes area to the RAM memory.

boot_OPTION configures the UART 9600 8N1, sends the UART bytes with its version, waits for the UART bytes from the host program, and without waiting for 0.2 seconds, transfers control to the application program at the address located in the pair $ 4831: $ 4832.

If, after sending a byte with its version, the boot_OPTION accepts a byte of the size of the expected dump, then the dump follows, dumps it into RAM memory and transfers control to it.
Further, all care falls on the program in the computer and the dump sent by it. It should send exactly those dumps that are needed to perform the current task (reading / erasing / writing / copying STM8 memory cells). Dumps should be able to replace each other in RAM memory and transfer control to the application program.

The address of the transition to the application program here is $ 4831: $ 4832.

Code loader and application program to run in flash memory:
 stm8/ TITLE "boot_FLASH_OPTION.asm" .NOLIST #include "STM8S103F3P.inc" .LIST MOTOROLA WORDS segment byte at 4800 'boot_OPTION' ;    boot_OPTION dc.b $00, $00, $FF, $00, $FF, $00, $FF, $00 dc.b $FF, $00, $FF, $35, $0D, $52, $32, $35 dc.b $0C, $52, $35, $35, $25, $52, $31, $5A dc.b $27, $16, $72, $0B, $52, $30, $F8, $C6 dc.b $52, $31, $72, $0B, $52, $30, $FB, $3B dc.b $52, $31, $4A, $26, $F5, $96, $5C, $FC dc.b $AE, $80, $04, $2B, $FA, $90, $AE, $42 dc.b $7F, $AE, $02, $7F, $CC, $9F, $F6, $00 segment byte at 8000 'boot_start' boot_start: ldw X, SP jp boot_FLASH_start ; ******************************************************** ;  0x8004...0x9FF1      segment byte at 8004 'main_FLASH' ;   main_FLASH_start: ldw X, #$03FF ldw SP, X mov UART1_BRR1, #13 mov UART1_CR2, #%00001100 main_FLASH_cycle: callr main_delay ;   bset PB_DDR,#5 bset PB_CR1,#5 ;   byte1_tx: mov UART1_DR, #$80 byte1_wait_tx btjf UART1_SR, #7, byte1_wait_tx callr main_delay boot_RAM_exit1: ;   bres PB_DDR,#5 ; bres PB_CR1,#5 ; ;   byte2_tx: mov UART1_DR, #$08 byte2_wait_tx btjf UART1_SR, #7, byte2_wait_tx jra main_FLASH_cycle main_delay: decw X jrne main_delay ret ;   OPTION -> RAM segment byte at 9FF2 'boot_FLASH' boot_FLASH_start: ; Y <- { OPTION_START + RAM_END} ; Y <- { $4800 + $03FF = $43FF } ldw Y, #$43FF boot_FLASH_copy: ld A, (Y) ld (X), A decw Y decw X jrpl boot_FLASH_copy incw X jp (X) boot_FLASH_exit_address: dc.w main_FLASH_start end ; 


The code of the application program for execution in RAM:
 stm8/ TITLE “boot_RAM.asm” MOTOROLA #include "STM8S103F3P.inc" BYTES segment byte at 0000 'boot_RAM_data' boot_RAM_start: ;  pull-up   (     )   ,   ,  14  ; ld A, #%01001100 ; [A6 4C] ; cpl A ; [43] ; ld PA_CR1, A ; [C7 50 03] ; ld PB_CR1, A ; [C7 50 08] ; ld PC_CR1, A ; [C7 50 0D] ; ld PD_CR1, A ; [C7 50 12]   PD6(UART1_RX), PD2, PD1 ;  UART1  /   9600,     (8 ,   , 1  ) ; mov UART1_BRR2, #0 ; [35 00 52 33]  Fmaster=16/8=2  9600 mov UART1_BRR1, #13 ; [35 0D 52 32]  Fmaster=16/8=2  9600 mov UART1_CR2, #%00001100 ; [35 0C 52 35] UART1_CR2.TEN <- 1 UART1_CR2.REN <- 1  / ;    UART1 boot_RAM_byte1_tx: mov UART1_DR, #$02 boot_RAM_byte1_wait_tx btjf UART1_SR, #7, boot_RAM_byte1_wait_tx ldw X,#0 ; [AE 00 00] boot_FLASH     X boot_RAM_wait1: decw X ; [5A] jreq boot_RAM_exit1 ; jra boot_RAM_wait1 boot_RAM_exit1: ;   bres PB_DDR,#5 ; bres PB_CR1,#5 ; ;    UART1 boot_RAM_byte2_tx: mov UART1_DR, #$20 ; [35 11 52 31] boot_RAM_byte2_wait_tx btjf UART1_SR, #7, boot_RAM_byte2_wait_tx ldw X,#0 ; [AE 00 00] boot_FLASH     X boot_RAM_wait2: decw X ; [5A] jreq boot_RAM_exit2 ; jra boot_RAM_wait2 boot_RAM_exit2: ;   bset PB_DDR,#5 ; bset PB_CR1,#5 ; jra boot_RAM_byte1_tx end 

Run the file runSTM8uLoader.bat , press the reset button on the board, the loader sends byte 0x25. A memory dump with the code from the main_RAM.hex file is sent to the STM8 RAM memory. The board starts blinking the LED and sending bytes 0x20 and 0x02. Press the reset button again. The application program starts from FLASH memory, the LED starts blinking faster and sending bytes 0x80 and 0x08.


At the last stage, to write the image of the loader in the OPTION Bytes area, you need to use the method The essence of the method is that you first need to write the firmware file boot_OPTION_rev25.hex to the FLAH memory STM8 in the FLAH , reload the microcontroller, fill the OPTION Bytes area with the necessary information and the LED will turn on. Then again, the programmer to write in FLASH firmware file from this article boot_FLASH_OPTION.hex .

Added “clean” bootloader version 0x14 code without application code. Deployed the boot_OPTION image to the source code. Corrected comments. Unlike the version $ 25, the address for transferring control to an application program is in $ 9FFE cells: $ 9FFFF of the FLASH memory. The size in FLASH memory is respectively 20 bytes.
boot_uC_rev14.asm:
 stm8/ TITLE "boot_uC_rev14.asm" ; boot_uC = boot_OPTION + boot_FLASH MOTOROLA .NOLIST #include "STM8S103F3P.inc" .LIST WORDS ; ******************************************************** segment byte at 4800 'boot_O_IMG' ;0000FF00FF00FF00FF00FF350D523235 ;0C5235351452315A2716720B5230F8C6 ;5231720B5230FB3B52314A26F5965CFC ;CE9FFE2BFA90AE427FAE027FCC9FF400 ;    ; $4800    RAM dc.b $00, $00, $FF, $00, $FF, $00, $FF, $00, $FF, $00, $FF ; OPTION (RAM) ; $480B ($0000)    boot_O boot_O_start: ;  UART 96008N1 Fmaster=16/8=2/9600/16 ; mov UART1_BRR2, #0 ; [35 00 52 33]     mov UART1_BRR1, #13 ; [35 0D 52 32] ; UART1_CR2.TEN <- 1 UART1_CR2.REN <- 1  / mov UART1_CR2, #%00001100 ; [35 0C 52 35] ; $4813 ($0008) boot_E_byte1_tx: ;   $14  mov UART1_DR, #$14 ; [35 14 52 31] ;    ,     ;      ;  X   ( 200 ) ; clrw X ; [5F]  X   boot_F ; $4817 ($000C) boot_O_rx_wait_byte: decw X ; [5A] jreq boot_O_exit ; [27 16]       btjf UART1_SR, #5, boot_O_rx_wait_byte ; [72 OB 52 30 F8] ;   ,   ,  A    ; $481F ($0014) ld A, UART1_DR ; [C6 52 31] ; $4822 ($0017)    boot_O_rx_wait_block: btjf UART1_SR, #5, boot_O_rx_wait_block ; [72 OB 52 30 FB] push UART1_DR ; [3B 52 31] dec A ; [4A] ;      A      jrne boot_O_rx_wait_block ; [26 F5] ; $482D ($0022)      ldw X, SP ; [96] incw X ; [5C] boot_O_exit_to_FLASH: jp (X) ; [FC] ; $4830 ($0025)     boot_O_exit: ldw X, boot_F_exit_address ; [CE 9F FE] jrmi boot_O_exit_to_FLASH ; [2B FA] ; if X < $8000     $0000 ;     EEPROM boot_O_exit_to_EEPROM: ; Y <- { EEPROM_END} ldw Y, #$427F ; [90 AE 42 7F] ; X <- { EEPROM_END - EEPROM_START } ;   EEPROM  RAM ldw X, #$027F ; [AE 02 7F] jp boot_F_copy ; [CC 9F F4] ; $483F ($0034) dc.b $00 ;   boot_O_end: ; ******************************************************** segment byte at 8000 'RESET_vector' ;96CC9FF0 ldw X, SP ; [96] X <- RAM_END jp boot_F_start ; [CC 9F F0] ; ******************************************************** ;  0x8004...0x9FEF      segment byte at 8004 'main_FLASH' ;20FE jra * ; [20 FE] ; ******************************************************** ;   boot_FLASH segment byte at 9FF0 'boot_F' ;90AE4C0A90F6F7905A5A2AF85CFC8004 boot_F_start: ; Y <- { boot_O_START + RAM_END} { $480B + $03FF = $4C0A } ldw Y, #$4C0A ; [90 AE 4C 0A] ;     ; boot_FLASH, boot_OPTION      boot_F_copy: ld A, (Y) ; [90 F6] ld (X), A ; [F7] decw Y ; [90 5A] decw X ; [5A] jrpl boot_F_copy ; [2A F8]  X(Y) >= RAM_START(boot_O_START) incw X ; [5C] jp (X) ; [FC] boot_F_exit_address: dc.w $8004 ; [80 04] ; dc.w $0000 ; [00 00] end ; 

Added “clean” loader code version 0x25 without application code. Deployed the boot_OPTION image to the source code. Corrected comments. In contrast to version $ 14, the address of control transfer to an application program is in cells $ 4831: $ 4832 of the OPTION Bytes area. The occupied size in the FLASH memory accordingly decreased to 18 bytes. The occupied size in the OPTION Bytes area has not changed (52 bytes + 1 backup).
boot_uC_rev14.asm:
 stm8/ TITLE "boot_uC_rev25.asm" ; boot_uC = boot_OPTION + boot_FLASH MOTOROLA .NOLIST #include "STM8S103F3P.inc" .LIST BYTES ; ******************************************************** ;        EEPROM  ; boot_O_exit_address    $0000 (   <$8000) ;       ;          segment byte at 0000 'boot_O_IMG' main_ram: ;20FE jra * ; [20 FE] WORDS ; ******************************************************** segment byte at 4800 'boot_O_IMG' ;0000FF00FF00FF00FF00FF350D523235 ;0C5235351452315A2716720B5230F8C6 ;5231720B5230FB3B52314A26F5965CFC ;AE80042BFA90AE427FAE027FCC9FF600 ;    ; $4800    RAM dc.b $00, $00, $FF, $00, $FF, $00, $FF, $00, $FF, $00, $FF ; OPTION (RAM) ; $480B ($0000)    boot_OPTION boot_O_start: ;  UART 96008N1 Fmaster=16/8=2/9600/16 ; mov UART1_BRR2, #0 ; [35 00 52 33]     mov UART1_BRR1, #13 ; [35 0D 52 32] ; UART1_CR2.TEN <- 1 UART1_CR2.REN <- 1  / mov UART1_CR2, #%00001100 ; [35 0C 52 35] ; $4813 ($0008) boot_E_byte1_tx: ;   $14  mov UART1_DR, #$14 ; [35 14 52 31] ;    ,     ;      ;  X   ( 200 ) ; clrw X ; [5F]  X   boot_F ; $4817 ($000C) boot_O_rx_wait_byte: decw X ; [5A] jreq boot_O_exit ; [27 16]       btjf UART1_SR, #5, boot_O_rx_wait_byte ; [72 OB 52 30 F8] ;   ,   ,  A    ; $481F ($0014) ld A, UART1_DR ; [C6 52 31] ; $4822 ($0017)    boot_O_rx_wait_block: btjf UART1_SR, #5, boot_O_rx_wait_block ; [72 OB 52 30 FB] push UART1_DR ; [3B 52 31] dec A ; [4A] ;      A      jrne boot_O_rx_wait_block ; [26 F5] ; $482D ($0022)      ldw X, SP ; [96] incw X ; [5C] boot_O_exit_to_FLASH: jp (X) ; [FC] ; $4830 ($0025)     boot_O_exit: dc.b $AE ; ldw X, #boot_O_exit_address ; [AE 80 04] ; $4831 ($0026) ;      boot_O_exit_address: dc.w main_flash ; [80 04] ; dc.w main_ram ; [00 00] jrmi boot_O_exit_to_FLASH ; [2B FA] ; if X < $8000     $0000 ;     EEPROM boot_O_exit_to_EEPROM: ; Y <- { EEPROM_END} ldw Y, #$427F ; [90 AE 42 7F] ; X <- { EEPROM_END - EEPROM_START } ;   EEPROM  RAM ldw X, #$027F ; [AE 02 7F] jp boot_F_copy ; [CC 9F F4] ; $483F ($0034) dc.b $00 ;   boot_O_end: ; ******************************************************** segment byte at 8000-8003 'RESET_vector' ;96CC9FF2 ldw X, SP ; [96] X <- RAM_END jp boot_F_start ; [CC 9F F2] ; ******************************************************** ;  0x8004...0x9FF1      segment byte at 8004 'main_FLASH' main_flash: ;20FE jra * ; [20 FE] ; ******************************************************** ;   boot_FLASH segment byte at 9FF2-9FFF 'boot_F' ;90AE4C0A90F6F7905A5A2AF85CFC boot_F_start: ; Y <- { boot_O_START + RAM_END} { $480B + $03FF = $4C0A } ldw Y, #$4C0A ; [90 AE 4C 0A] ;     ; boot_FLASH, boot_OPTION      boot_F_copy: ld A, (Y) ; [90 F6] ld (X), A ; [F7] decw Y ; [90 5A] decw X ; [5A] jrpl boot_F_copy ; [2A F8]  X(Y) >= RAM_START(boot_O_START) incw X ; [5C] jp (X) ; [FC] end ; 

Address transfer control application program in FLASH memory, you can choose from a range of $ 8004 ... $ 9FF1. For an application code image from EEPROM memory, transfer of control is possible only at $ 0000 in the RAM memory.
Host program the second command line argument can pass any address transfer control.
The source code of the host program can be found here . There are also contacts for more extensive communication.
I ask readers of targeted criticism and suggestions to further reduce the code.

I propose to read also the article “How to compress the bootloader for STM8 to the size of 8 bytes in FLASH memory” .

Source: https://habr.com/ru/post/417493/


All Articles