To output a bit-stream on an ARM chip, it is necessary to write to the registers of the GPIO peripheral.
On an Energy Micro EFM32 chip and many other ARM chips, there are separate GPIO peripheral registers to set and clear output pins. This separation into SET and CLEAR registers enables one or more pins to be set or cleared with a single write operation.
However simply writing a constant into one of these registers will produce more than a single CPU write operation. Due to the RISC nature of the ARM chip, the value of the constant and the address of the GPIO peripheral register must be in CPU registers. So the compiler must generate code to:
1. load the constant into a CPU register,
2. load the address of the GPIO peripheral register into a CPU register and
3. perform the write operation
Depending on the address of the GPIO peripheral register, on a Cortex-M3 chip this may required 4 CPU instructions as follows:
g_pGPIO->PA_DOUTSET = 2; // set PA1
MOV R0,#24592
MOVT R0,#16384
MOV R1,#2
STR R1,[R0]
Only the final STR instruction is setting the pin, the other three instructions are loading the CPU registers.
On an ARM7 or ARM9 chip, the address of the GPIO peripheral register may need to be stored in a memory pool requiring a data read from memory. Even less efficient.
A bit-stream requires a sequence of writes. To make sure that once loaded the CPU register stay loaded, the value of the constant and the address of the GPIO peripheral register can be pre-assigned to local variables.
By default the compiler will allocate the local variables to registers.
So if this is the C code:
main()
{
unsigned long nBit = 2;
unsigned long* pSet = &g_pGPIO->PA_DOUTSET;
unsigned long* pClear = &g_pGPIO->PA_DOUTCLR;
while (1)
{
*pSet = nBit; //set PA1
*pClear = nBit; // clear PA1
*pSet = nBit;
*pClear = nBit;
*pSet = nBit;
*pClear = nBit;
*pSet = nBit;
*pClear = nBit;
*pSet = nBit;
*pClear = nBit;
*pSet = nBit;
*pClear = nBit;
*pSet = nBit;
*pClear = nBit;
*pSet = nBit;
*pClear = nBit;
*pSet = nBit;
*pClear = nBit;
*pSet = nBit;
*pClear = nBit;
}
}
The resulting CPU instructions generated by the compiler are:
MOV R2,#24592
MOVT R2,#16384
MOV R1,#2
STR R1,[R2]
MOV R0,#24596
MOVT R0,#16384
.@8
STR R1,[R0]
STR R1,[R2]
STR R1,[R0]
STR R1,[R2]
STR R1,[R0]
STR R1,[R2]
STR R1,[R0]
STR R1,[R2]
STR R1,[R0]
STR R1,[R2]
STR R1,[R0]
STR R1,[R2]
STR R1,[R0]
STR R1,[R2]
STR R1,[R0]
STR R1,[R2]
STR R1,[R0]
STR R1,[R2]
STR R1,[R0]
B .@8
The compiler has generated a continues uninterrupted seqeuence of writes.
This post shows how efficiently output a fixed bit-stream. In the next post we will be looking at how to output a variable bit-stream.
Copyright © 2011 Crossware Products. All rights reserved.