hsk-libs-dev  270
High Speed Karlsruhe XC878 library collection
Variables and Memory

The Infineon/XC878.h header defines some unsigned data types:

Signed types should only be used when necessary, floating point arithmetic should be avoided if in any way possible.

The correct place to store a variable depends on four factors:

Size, and frequency are the most obvious, considering Memory Limitations. It is desireable to use fast memory for frequently accessed variables. Large data structures like buffers, arrays and structs simply use too much of the precious memory space to put them anywhere but xdata.

Both the C51 and SDCC compilers use a technique called overlay to fit all variables into memory. A stack is only used in reentrant functions. Only data/idata variables can be stacked with push/pop instructions. Stacking xdata is emulated in software and thus very slow.

The overlay approach is to build a call graph and thus decide which variables are never used at the same time. These variables are mapped to the same fixed memory addresses.

Variables with a long lifetime are locals in the main() function, static variables and global variables. These variables have to keep their state during the entire runtime. Thus they use memory space that cannot be shared with other variables.

ISRs and functions called by ISRs also cannot share memory, because there is no sensible way to make sure that a given function is not running when an interrupt occurs. In technical terms, each ISR is the root node of its own call graph.

The following table lists recommended memory types:

Context Size Critical Memory
* bool * bit
parameter * * data
const * * code
local byte, word * data, idata
>= long no xdata
ISR/blocking pdata, xdata
static/global * no xdata
ISR/blocking pdata, xdata

ISR/blocking refers to memory accessed by ISRs, functions called back by ISRs and sections of code that block an interrupt.

Implications of Overlaying

First and foremost, Overlaying is only performed for the default memory. This project is built around the small memory model, i.e. only data memory is overlaid by SDCC and C51.

The data memory is only 128 bytes minus the used register banks large. With three register banks that means only 104 bytes of data memory are available. Thus non-overlayable variables should be placed in idata in order to use data memory for well overlayable variables.

Furthermore SDCC does not build a complete call tree, so it cannot eliminate unused functions like C51/LX51 and only overlays leaf functions. I.e. only functions that call no other functions are overlaid.

For SDCC overlaying ISRs is not possible, that is why locals of ISRs should in most cases be placed in idata, despite the performance impact.

A limitation of C51/LX51 is that it ignores explicit memory assignments in function parameters and always places them in the default memory, if they cannot be passed in registers. This limitation does not appear to be documented, but it was reported by an ARM support employee in case #530915.

SDCC does not share this limitation, it can place function parameters in all kinds of memory. Because such assignments are ignored by C51/LX51, parameter memory type should be optimised for SDCC. Explicit memory assignments prevent parameters from being passed in registers. SDCC only passes the first non-bool parameter in registers, so optimizations should be performed on the non-overlayable arguments following it.

For single use functions like init and enable functions, it might make sense to pass parameters in xdata. In such cases the SDCC memory assignment style should be used, to give the C51 preprocessor the chance to remove the assignments.

void hsk_can_init(const ubyte pins, const ulong __xdata baud);

If an application runs out of data space locals should be put into idata memory. Variables accessed by ISRs/ISR callbacks should be placed in pdata or idata. If that suffices the code should be checked for frequently accessed variables. The most frequent ones should be assigned to the default memory type if possible.

Both SDCC and C51 provide detailed information about memory use. The effects of relocating variables are often counter-intuitive, because it may interfere with several compiler and linker optimizations. This it is necessary to make use of this information in order to make sure that changes have the desired effect.

For SDCC check the assembler output for the DSEG area (search regex /\\\.area DSEG/):

;--------------------------------------------------------
; internal ram data
;--------------------------------------------------------
.area DSEG (DATA)
_hsk_adc_init_resolution_1_71:
.ds 1
_hsk_adc_init_ctc_1_72:
.ds 1
_hsk_adc_init_sloc0_1_0:
.ds 2
_hsk_adc_init_sloc1_1_0:
.ds 2

The .map file lists the complete memory layout produced by the linker (search regex /^DSEG/):

Area Addr Size Decimal Bytes (Attributes)
----------------- ---- ---- ------- ----- ------------
DSEG 00000000 00000080 = 128. bytes (REL,CON)
Value Global Global Defined In Module
----- -------------------------------- ------------------------
00000018 _hsk_pwm_init_PARM_2 hsk_pwm
0000001C _hsk_pwm_channel_set_PARM_2 hsk_pwm
0000001E _hsk_pwm_channel_set_PARM_3 hsk_pwm
00000025 _hsk_icm7228_writeDec_PARM_2 hsk_icm7228
00000027 _hsk_icm7228_writeDec_PARM_3 hsk_icm7228
00000028 _hsk_icm7228_writeDec_PARM_4 hsk_icm7228
...

In µVision the .map file can be accessed by double clicking the project in the project tree view (search string "D A T A"):

START STOP LENGTH ALIGN RELOC MEMORY CLASS SEGMENT NAME
=========================================================================
* * * * * * * * * * * D A T A M E M O R Y * * * * * * * * * * * * *
000000H 000007H 000008H --- AT.. DATA "REG BANK 0"
000008H 00000FH 000008H --- AT.. DATA "REG BANK 1"
000010H 000017H 000008H --- AT.. DATA "REG BANK 2"
000018H 00001BH 000004H BYTE UNIT IDATA _IDATA_GROUP_
00001CH.0 00001FH.7 000004H.0 --- --- **GAP**
000020H.0 000020H.4 000000H.5 BIT UNIT BIT _BIT_GROUP_
000020H.5 000020H.5 000000H.1 BIT UNIT BIT ?BI?HSK_CAN
000020H.6 000020H 000000H.2 --- --- **GAP**
000021H 00003FH 00001FH BYTE UNIT DATA _DATA_GROUP_
000040H 000040H 000001H BYTE UNIT IDATA ?STACK
See also
Overlaying in the SDCC manual
Global Registers used for Parameter Passing in the SDCC manual