cancel
Showing results for 
Search instead for 
Did you mean: 

STM8 Standard Library and Cosmic produces huge code?

Willunen
Associate III

I'm using ST Visual develop and Cosmic to program a STM8S003K3 and I'm thought it would be nice to use the STM8 Standard Library (downloaded from ST.COM).

Within a very short time I got errors telling me that I tried to put more code in the MCU than possible (8K). I hadn't even finished the initialization of all the peripherals I intend to use.

So I took a look at the compiler settings and changed the optimizations from None to Minimize Code Size.

That did help.

For a few minutes.

A look at the map file shows this: (not the entire file)

                              --------
                               Segments
                               --------
 
start 00008080 end 00008103 length   131 segment .const
start 00008106 end 00009bab length  6821 segment .text
start 00004000 end 00004000 length     0 segment .eeprom
start 00000000 end 00000000 length     0 segment .bsct
start 00000000 end 0000000a length    10 segment .ubsct
start 0000000a end 0000000a length     0 segment .bit
start 0000000a end 0000000a length     0 segment .share
start 00000100 end 00000100 length     0 segment .data
start 00000100 end 00000100 length     0 segment .bss
start 00000000 end 00000584 length  1412 segment .info.
start 00008000 end 00008080 length   128 segment .const
start 00008103 end 00008106 length     3 segment .init
 
 
                               -------
                               Modules
                               -------
 
C:\Program Files (x86)\COSMIC\FSE_Compilers\CXSTM8\Lib\crtsi0.sm8:
start 00008106 end 00008156 length    80 section .text
start 00000100 end 00000100 length     0 section .bss
start 00000000 end 00000000 length     0 section .ubsct
start 00000000 end 00000034 length    52 section .info.
 
Release\main.o:
start 00008156 end 000081da length   132 section .text
start 00000034 end 000000c0 length   140 section .info.
 
Release\stm8s_it.o:
start 000081da end 000081ef length    21 section .text
start 000000c0 end 00000150 length   144 section .info.
 
Release\stm8s_clk.o:
start 000081ef end 000087a3 length  1460 section .text
start 00000150 end 000001e1 length   145 section .info.
start 00008080 end 000080b3 length    51 section .const
 
Release\stm8s_gpio.o:
start 000087a3 end 000088a1 length   254 section .text
start 000001e1 end 00000273 length   146 section .info.
start 000080b3 end 000080db length    40 section .const
 
Release\stm8s_tim1.o:
start 000088a1 end 00009a60 length  4543 section .text
start 00000273 end 00000305 length   146 section .info.
start 000080db end 00008103 length    40 section .const

The timer1 initialization takes 4k ?

This is worth 4k ?

static void TIM1_Config(void)
{
  TIM1_DeInit();																		
  TIM1_TimeBaseInit(15999, TIM1_COUNTERMODE_UP, 999, 0);
  TIM1_ITConfig(TIM1_IT_UPDATE, ENABLE);
  TIM1_ARRPreloadConfig(ENABLE);
  TIM1_Cmd(ENABLE);
}

Do I have to go back to "bare-metal" register setting?

Now I'm only just starting with STM8, so it is more than likely that I'm doing something very wrong, am I?

Wilko

1 ACCEPTED SOLUTION

Accepted Solutions
cs
Associate II

I also had this kind of trouble, and I solved it by using the C compiler optimization option "Split Functions in Separate Sections (+split)".

This allows the linker to discard the generated code of unused STM8S_StdPeriph_Lib functions.

View solution in original post

30 REPLIES 30
Willunen
Associate III

Answering my own question? Bare_metal code does help. A lot. I changed the init of the CLK and the init of TIM1 like this:

static void TIM1_Config(void)
{
	/*
	TIM1_DeInit();																			
	TIM1_TimeBaseInit(15999, TIM1_COUNTERMODE_UP, 999, 0);
	TIM1_ITConfig(TIM1_IT_UPDATE, ENABLE);
	TIM1_ARRPreloadConfig(ENABLE);
	TIM1_Cmd(ENABLE);
	*/
	
	TIM1->CR1 = 0x81;
	TIM1->CR2 = 0x00;
	TIM1->SMCR = 0x00;
	TIM1->ETR = 0x00;
	TIM1->IER = 0x01;
	TIM1->EGR = 0x00;
	TIM1->CCMR1 = 0x00;
	TIM1->CCMR2 = 0x00;
	TIM1->CCMR3 = 0x00;
	TIM1->CCMR4 = 0x00;
	TIM1->CCER1 = 0x00;
	TIM1->CCER2 = 0x00;
	TIM1->PSCRH = 0x3E;
	TIM1->PSCRL = 0x7F;
	TIM1->ARRH = 0x03;
	TIM1->ARRL = 0xE7;
	TIM1->RCR = 0x00;
	TIM1->BKR = 0x00;
	TIM1->DTR = 0x00;
	TIM1->OISR = 0x00;
}
 
static void CLK_Config(void)
{
	/*
	CLK_DeInit();
	CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO, CLK_SOURCE_HSE, DISABLE, CLK_CURRENTCLOCKSTATE_DISABLE);
	CLK_SYSCLKConfig(CLK_PRESCALER_CPUDIV1);
	CLK_CCOConfig(CLK_OUTPUT_CPUDIV4);
	*/
	
	CLK->ICKR = 0x02;
	CLK->ECKR = 0x03;
	CLK->SWR = 0xB4;
	CLK->SWCR = 0x0A;
	CLK->CKDIVR = 0x18;
	CLK->PCKENR1 = 0xFF;
	CLK->CSSR = 0x00;
	CLK->CCOR = 0x6D;
	CLK->PCKENR2 = 0xFF; 
	CLK->HSITRIMR = 0x00;
	CLK->SWIMCCR = 0x00;
}

And this is the result:

                               --------
                               Segments
                               --------
 
start 00008080 end 000080a8 length    40 segment .const
start 000080ab end 000082d6 length   555 segment .text
start 00004000 end 00004000 length     0 segment .eeprom
start 00000000 end 00000000 length     0 segment .bsct
start 00000000 end 00000006 length     6 segment .ubsct
start 00000006 end 00000006 length     0 segment .bit
start 00000006 end 00000006 length     0 segment .share
start 00000100 end 00000100 length     0 segment .data
start 00000100 end 00000100 length     0 segment .bss
start 00000000 end 00000584 length  1412 segment .info.
start 00008000 end 00008080 length   128 segment .const
start 000080a8 end 000080ab length     3 segment .init
 
 
                               -------
                               Modules
                               -------
 
C:\Program Files (x86)\COSMIC\FSE_Compilers\CXSTM8\Lib\crtsi0.sm8:
start 000080ab end 000080fb length    80 section .text
start 00000100 end 00000100 length     0 section .bss
start 00000000 end 00000000 length     0 section .ubsct
start 00000000 end 00000034 length    52 section .info.
 
Release\main.o:
start 000080fb end 000081c3 length   200 section .text
start 00000034 end 000000c0 length   140 section .info.
 
Release\stm8s_it.o:
start 000081c3 end 000081d8 length    21 section .text
start 000000c0 end 00000150 length   144 section .info.
 
Release\stm8s_clk.o:
start ******** end ******** length     0 section .text *** removed ***
start 00000150 end 000001e1 length   145 section .info.
start ******** end ******** length     0 section .const *** removed ***
 
Release\stm8s_gpio.o:
start 000081d8 end 000082d6 length   254 section .text
start 000001e1 end 00000273 length   146 section .info.
start 00008080 end 000080a8 length    40 section .const
 
Release\stm8s_tim1.o:
start ******** end ******** length     0 section .text *** removed ***
start 00000273 end 00000305 length   146 section .info.
start ******** end ******** length     0 section .const *** removed ***

The code goes from almost 7k to just over 0.5k

So what is wrong with the code in the Standard Library? Or what do I do wrong with it?

Wilko

JoniS
Senior

If standart periephal library code is like HAL code, it's bloated(massive size) and slow(slow to execute), well because ​it tries to be general, it's abstraction layer and the cost is what you observed here.

It's not really a proplem with arm microcontrollers, but quickly becomes an issue with 8bit microcontroller families or other momery constrained systems, which is why usually one programs those bare metal with direct register access instead of fancy libraries. which tries to do everything.

Ozone
Lead

Not really familiar with the STM8 and it's standard library.

But the usual way is to start with the map file, to see which linker sections are the offenders.

Often, project "wizards" set large link libraries with almost complete clib functionality support. That is easily recognized by the familiar function names like printf in the map file.

Discarding clibs at all, or using the smallest possible, is a good way to start. Not sure how much the STM8 std lib depends on that.

Build settings are the next point. Often, compilers/linkers do not remove unused code if optimization is turned off.

Willunen
Associate III

After some more experimenting

I found that turning off FULL ASSERT reduced the code size by half, but still the init of just a few peripherals takes 4k of flash. And when all you have is 8k, that's a lot.

The map file shows no other library code than what I included from the Standard Library, and while I'm not very good at reading (and understanding) what is in the .ls files, it seems to me that there really is no other code than what is in the STDLib.

It is just that it concerns itself with all kind of setting registers that I did not ask it to do such as the output-compare registers. I do not (yet) use these so why doesn't the STDLib just leave them at the default settings.

So it is direct-register-setting for STM8 (for me).

Thanks for thinking with me.

Wilko

Just have some experience with the (now discarded) STM32 peripheral library, of supposedly similar structure.

Example projects based on that lib use to include all the lib's peripheral source files, and rely on the linker to throw out unused code.

Perhaps you can remove unused stuff here.

> So it is direct-register-setting for STM8 (for me).

Copying / adapting relevant sections from the lib code seems a good way to reduce clutter, and avoid re-inventing the wheel.

Supposedly, the peripheral library code is of much better quality (debug/test) then the currently pushed Cube/HAL libs for the STM32 ...

Copying the relevant sections from the STDLib is probably the best idea, together with direct register setting.

And keeping the STDLib as an extra reference manual.

The STM32 MCU's I use, all have plenty more flash and I only use the Low Layer part of the HAL. I haven't run out of flash memory with any of them.

cs
Associate II

I also had this kind of trouble, and I solved it by using the C compiler optimization option "Split Functions in Separate Sections (+split)".

This allows the linker to discard the generated code of unused STM8S_StdPeriph_Lib functions.

Willunen
Associate III

Now that works well! My first example that produced over 7k of code is reduced to just 1k by this option. It really makes me wonder why it isn't the default... Thanks.

Wilko

pacman
Associate III

This is indeed a good post.

I'm using SDCC (or at least attempting to use it), so I don't have the sophisticated feature to split functions or exclude unused code.

One could probably also use the 'old' approach, where each feature should be enabled by #define, before the code-block is enabled, but that's likely very cumbersome in the long run.

-But at least it's easier than copying/pasting all the time and having several variants of the same code spread over a whole bunch of sources... 😉

... One can of course make a small script, which generates the #defines for the command-line, depending on what a configuration file instructs.

-or even make a nutty approach where the sources are scanned for subroutine-names and the #defines are generated when needed (this script could be executed by the makefile).