Fast bit-stream output in C on ARM

by Crossware 5. October 2011 16:40

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.

Tags: , , , ,

Blog

Add comment

  Country flag

biuquote
  • Comment
  • Preview
Loading

About this blog

This is where you will find topical information that we think might be useful to you.

Month List