hsk-libs-dev
270
High Speed Karlsruhe XC878 library collection
|
The XC878 is an Intel 8051/8052 compatible µC architecture. This entails strong memory limitations with severe implications to writing code.
The strength of the architecture is that the controller contains many specialized modules that, once set up, perform many tasks without intense interaction.
Critical for this project are the following kinds of modules:
The XC878 functions and modules are controlled through so called Special Function Registers (SFRs).
Due to the number of modules and functions of the controller a lot more registers are present than the 128 that can be addressed. These 128 register addressed in the upper dirctly addressable address range from 0x80
to 0xFF.
To circumvent the 128 register limit each functional block of registers has a paging register that can be used to access different Pages of registers. In C code this is done using the SFR_PAGE() macro defined in the Infineon/XC878.h header file that is provided by Keil µVision, the IDE used for this project or the headers can directly be downloaded from ARM.
Paging only affects code that directly interacts with the hardware. One of the benefits of using these libraries is that paging is not an issue in the logical code.
Each section of the XC878 Reference Manual has a Register Overview that contains a table of pages and registers.
The 8051 platform offers 128 bytes of data
memory in the address range 0x00-0x7F in front of the SFR address range. Because 128 bytes are insufficient, the 8051 architecture knows several kinds of memory that are accessed in different manners and thus quicker or slower to access.
The 8052 has an additional 128 bytes of indirectly addressable memory. This memory is accessed through the key word idata
. Access to idata
is slower than to data
. The syntax for declaring a variable in idtata
memory is:
The additional idata
memory is located in the upper half of the address range. The lower half accesses data
memory. Any data
access to a pointer actually is idata
access. This is why SFRs cannot be accessed with pointers. They are masked by idata
memory.
The slowest kind of memory used by this library is the xdata
memory. The xdata
memory makes 3kb of additional memory available and the libraries place all large data structures in them.
Variables are declared in xdata
with the following syntax:
The first 256 bytes of xdata
memory are also accessible as 8 bit addressed pdata
. Using pdata
is faster than xdata
. The p
in pdata
stands for paged. Historically the 8052 family of µCs used register P2
for paging. The XC878 instead provides an SFR named XADDRH.
However current 8051 C compilers don't support paging. I.e. one would have to ensure that structs and buffers do not cross page borders and update XADDRH
manually. So instead of making the code more complicated and messing with the linkers XADDRH
is fixed to the first xdata
page and pdata
is simply used as an additional 256 bytes of relatively fast memory.
There also is a 128 bits wide memory range of bit
variables, which is used by single bit variables of the type bool
.
In contrast to the small amount of available RAM, 64k of ROM are available to hold executable code. Thus a program well designed to the XC878 is one that produces a lot of static code to reduce the required amount of runtime memory use.
Code resides in its own address range, the code
block. The µC can be run from code residing in xdata
as well, to bootstrap the µC. Constants can also be placed in the code
block.
In order to mitigate the memory limitations of the platform the C51 and SDCC compilers perform an optimisation called overlaying.
The compilers build a call graph, much like the one in the documentation of main(). The call graph is a directed graph and functions (i.e. their local variables and parameters) may occupy the same space in memory, provided they cannot reach each other in the graph.
E.g. main() can reach both init() and run(), thus main() may not occupy the same memory as init() and run(). However run() and init() cannot reach each other, so they may store their data in the same memory.
This reduces the use of the stack, which is expensive in terms of runtime (this statement is not generally true, it just applies to the 8051 family of µCs).
Functions may not be called more than once at a time. I.e. they may not be recursive or called from regular code and interrupts both. Both compilers provide a reentrant
keyword to make functions operate on the stack. Its use should be avoided if possible.
Both C51 and SDCC do not track function pointers. Thus creating a function pointer results in a false call in the call tree. A call through a function pointer is not added to the call tree.
The C51 tool chain offers call tree manipulations and SDCC provides the nooverlay pragma to mitigate this.
The section about Implications of Overlaying lists best practices to optimize code for overlaying.
Due to the existence of different kinds of memory, pointers come in two variations, generic pointers and memory-specific pointers. Generic pointers take 3 bytes of memory and are by far the slowest to process. Memory-specific pointers take 1 byte for data
, idata
or pdata
and 2 bytes for xdata
or code
. They are also faster to process.
Note that the data
keyword needs to be explicitely specified for data
pointers. Pointers declared without explicit mention of the memory type always result in generic pointers.
Pointers can be stored in different kinds of memory than the memory they point to:
The following example creates an idata
pointer to a struct in xdata
memory: