4

I am currently developing a small project for an STM32F103 microcontroller which features a Cortex-M3 CPU. Due to CMSIS standard header files it is possible to use the exact same code with IAR and Keil µVision. Because of that, I found it interesting to compare both compilers regarding code size (which is the most critical for small microcontrollers).

Both compilers were set on max. optimization level (for size). Unfortunately, I am not quite sure how IAR and Keil measure code size.

For example, IAR gives me this output:

    868 bytes of readonly  code memory 
     28 bytes of readonly  data memory 
  2'056 bytes of readwrite data memory 

and Keil this:

Program Size: Code=676 RO-data=252 RW-data=0 ZI-data=1640

At a first glance I am not able to detect which amount of bytes relates to used flash size and which to used SRAM.

Of course I know, that flash is Read-only and that SRAM is read-write but then there is code memory and data memory on IAR's side, and ZI-data and Code on Keil's side.

Anyone here who has more in depth knowledge about this?

artless noise
  • 21,212
  • 6
  • 68
  • 105
binaryBigInt
  • 1,526
  • 2
  • 18
  • 44
  • 1
    looks to be saying the same things. read only code memory compares to coee. read nly data memory compares to R0-data, the ram (read/write) is just one number in IAR and Kiel appears to split into what I assume is zero-init and non-zero init. – old_timer Aug 03 '20 at 20:33
  • 1
    There is no real winner here as one uses more flash than the other but less ram...from what you have provided – old_timer Aug 03 '20 at 20:34
  • size isnt the most critical, it is just one factor, power is often most critical and that means at times slower clock speed which can mean higher performing optimized code that can do the same job with a slower clock. So there isnt one right answer for size vs speed vs other factors. Size matters if you are running out of flash that can be solved different ways, it esp matters if you are wanting to save the money using a part from the same family with less flash and as a result less cost – old_timer Aug 03 '20 at 20:36
  • and/or your code causes the product to be 10 cents more (that is a big deal), and then you start investigating compile for size vs other. And optimizers are algorithms, I have seen compile for performance to make a smaller binary than compile for size, it can happen. – old_timer Aug 03 '20 at 20:38
  • @old_timer How did you calculate the numbers for flash and sram usage? the STM32F103 I use has 64kByte of flash and only 20kByte of SRAM. So under those circumstances I would prefer the compiler with less SRAM usage – binaryBigInt Aug 04 '20 at 09:03

1 Answers1

3

Let's try to break this down in a systematic way. From a programmers point of view we want to differentiate between Code (Instructions) and Data.

Code is usually stored in some kind of non-volatile memory (ROM, FLASH, etc.) and is read and executed at runtime by the processor core. Modern MCU's usually read their instructions from FLASH but can also execute code from RAM. This is mainly useful in order to run the code faster (since FLASH is rather slow to access) or in order to implement some update functionality that can update the whole FLASH memory. Running code from RAM can also be used to construct self-modifying software, but that is a rather exotic use-case.

When talking about data we usually first think of variables, that are modified during run-time (read-write) and therefore need to be stored in random-access memory (RAM) that is usually volatile (values are lost at power-down). But there are more types to keep in mind:

  • Constants: Data values, that do not change during run-time, e.g. a peripheral register address or a hard-coded delay time. These values need to be placed into non-volatile memory, which can be read-only (e.g. FLASH).
  • Initialized variables: Most variables of a program need to have a defined initial value (e.g. the starting value of a loop count variable). This initial value is actually nothing else than a constant data value (see above), that is automatically copied to its associated variable at the beginning of its lifetime. Since a typical program requires quite a lot of those initialization values, modern compilers implement different optimization methods to reduce the memory footprint of this initializers. These includes clustering all variables that are initialized with the value zero (zero-initialization) and providing different data compression methods for non-zero initialized variables.

With these things in mind we can make an educated guess regarding the output of the IAR and Keil linker:

+---------------------+-----------------------+-------------------+
| Memory Object       | IAR term              | Keil term         |
+---------------------+-----------------------+-------------------+
| Code in ROM         | readonly code memory  | Code              |
| Code in RAM         | readwrite code memory | ?                 |
| Constants in ROM    | readonly data memory  | RO-Data           |
| Initializers in ROM | readonly data memory  | (RW-Data)         |
| Variables in RAM    | readwrite data memory | RW-Data + ZI-Data |
+---------------------+-----------------------+-------------------+

Calculating memory usage is pretty straight-forward with IAR:

  • ROM usage = (readonly code memory) + (readonly data memory)
  • RAM usage = (readwrite code memory) + (readwrite data memory)

For Keil it is a bit more complicated:

  • ROM usage = (Code) + (RO-data) + (RW-data)
  • RAM usage = (RW-data) + (ZI-data)
Blue
  • 820
  • 4
  • 17