C extensions for embedded systems
Whenever writing in a high level language, there are always times when
there is a need to go down to assembler level coding, either for speed reasons
or because it is simpler to do so. Accessing memory ports is another example of
where this is needed. C in itself does not necessarily support this. Assembler
routines can be written as library routines and included at link time — a
technique that has been explained and used in this and later chapters. It is
possible to define access a specific memory mapped peripheral by defining a pointer
and then assigning the peripheral’s memory address to the pointer. Vector
tables can be created by an array of pointers to functions. While these
techniques work in many cases, they are susceptible to failing.
Many compilers provide extensions to the compilers that allow embedded
system software designers facilities to help them use low level code. These
extensions are compiler specific and may need changing or not be supported if a
different compiler is substituted. Many of these extensions are supplied as
additional #pragma definitions that
supply addi-tional information to the compiler on how to handle the rou-tines.
These routines may be in C or in assembler and the number and variety will vary
considerably. It is worth check-ing out the compiler documentation to see what
it does sup-port.
#pragma interrupt func2
This declares the function func2 as an interrupt func-tion and therefore will ensure that the compiler
will save all registers so that the function code does not need to do this. It
also instructs the compiler that the return mechanism is differ-ent — with a
PowerPC instruction a special assembler level instruction has to be used to
synchronise and restart the processor. #pragma
pure_function func2
This declares that the function func2 does not use or modify any global or static data and that it is a pure
function. This can be used to identify assembler-based routines that configure
the processor without accessing any data. This could be to change the cache
control, disable or enable interrupts.
#pragma no_side_effects func2
This declares that the function func2 does not modify any global or static data and that it has no side
effects. This could be used in preference to the pure_function option to allow
access to data to allow an interrupt mask to be changed depending on a global
value, for example.
#pragma no_return func2
This declares that the function func2 does not return and therefore the normal preparation of retaining the
subrou-tine return address can be dispensed with. This is used when an exit or
abort function is used. Jumps can also be better implemented using this as the
stack will be correctly main-tained and not filled with return addresses that
will never be used. This can cause stack overflows.
#pragma mem_port int2
This declares that the variable int2 is a value of a specific memory address and therefore should be treated
ac-cordingly. This is normally used with a definition that defines where the
address is.
asm and _ _asm
The asm and _ _asm directives — note that the number of underlines
varies from compiler to compiler — provide a way to generate assembly code from
a C program. Both usually have similar functionality that allows assembler code
to be directly inserted in the middle of C without having to use the external
routine and linking technique. In most cases, the terms are interchangeable,
but beware since this is not always the case. Care must also be taken with them
as they break the main standards and enforcing strict compatibility with the
compiler can cause them to either be flagged up as an error or simply ignored.
There are two ways of using the asm/_ _asm directives. The first is a
simple way to pass a string to the assembler, an asm string. The second is an
advanced method to define an asm macro that in-lines different assembly code
sections, depend-ing on the type of arguments given. The examples shown are
based on the Diab PowerPC compiler.
asm strings
An asm string can be specified wherever a statement or an external
declaration is allowed. It must have exactly one argument, which should be a
string constant to be passed to the assembly output. Some optimisations will be
turned off when an asm string statement is encountered.
int f() { /* returns value at $$address */ asm(“
addis r3,rO,$$address)@ha”); asm(“ lwz r3,r3,$$address@1”);
This technique is very useful for executing small func-tions such as
enabling and disabling interrupts, flushing caches and other processor level
activities. With the code directly in-lined into the assembler, it is very
quick with little or no overhead.
asm macros
An asm macro definition looks like a function definition in that the
body of the function is replaced with one or more assembly code sequences. The
compiler chooses one of these sequences depending on which types of arguments
are pro-vided when using the asm macro, e.g.
asm
int busy_wait(char *addr)
{ % reg addr; lab loop; addi r4,rO,l
loop: #
label is replaced by
compiler
lwarx
r5,rO,addr #
argument is forced to
register
cmpi
crO,r5,0 bne loop
stwcx.
r4,rO,addr bae loop
}
extern char *sem fn(char *addr) {
busy_wait(addr);
/* wait for semaphore */ busy_wait(sem) ; /* wait for semaphore */
}
The first part of the source defines the assembler routine that waits
for the semaphore or event to change. The second part of the source calls this
assembler function twice with the event name as its parameter.
addi r4,rO,1
.L11: #
label is replaced by compiler
lwarx r5,rO,r31
# argument is forced to
register
cmpi crO,r5,0
bne .L11
stwcx. r4,rO,r31
bne .L11
addis r3,rO,sem@ha
lwz r3,sem@1(r3)
addi r4,rO,1
.L12: # label is replaced by
compiler
lwarx 5,rO,r3 # argument is forced to
register
cmpl crO,r5,0
bne .L12
stwcx. r4,rO,r3
bne .L12
Related Topics
Privacy Policy, Terms and Conditions, DMCA Policy and Compliant
Copyright © 2018-2023 BrainKart.com; All Rights Reserved. Developed by Therithal info, Chennai.