Quality RTOS & Embedded Software

 Real time embedded FreeRTOS RSS feed 
Quick Start Supported MCUs PDF Books Trace Tools Ecosystem


Loading

Sneaky ARM/Thumb Inline Assembly

Posted by dave m on February 18, 2008
I figured out how to implement the interrupt disable/enable, save/restore context, and yield routines in inline assembly for both ARM and Thumb. It was an interesting exercise, but now that I think about it, the Thumb version is probably no faster than an ordinary function call, since the inline code has to branch at least twice (once to enter ARM mode and once to return to Thumb).

Code follows:

/*
* portmacro.h for Game Boy Advance
*/
#ifndef PORTMACRO_H
#define PORTMACRO_H 1

/*
*-----------------------------------------------------------
* Port specific definitions.
*
* The settings in this file configure FreeRTOS correctly for the
* given hardware and compiler.
*
* These settings should not need to be altered.
*-----------------------------------------------------------
*/

/* Type definitions. */
#define portCHARchar
#define portFLOATfloat
#define portDOUBLEdouble
#define portLONGlong
#define portSHORTshort
#define portSTACK_TYPEunsigned portLONG
#define portBASE_TYPEportLONG

#if (configUSE_16_BIT_TICKS == 1)
typedef unsigned portSHORT portTickType;
#else
typedef unsigned portLONG portTickType;
#endif

#define portMAX_DELAY ((portTickType)~0)

#define portSTACK_GROWTH( -1 )
#define portTICK_RATE_MS((portTickType)(1000/configTICK_RATE_HZ))
#define portBYTE_ALIGNMENT4
#define portNOP()asm volatile ("nop")

/*
* These defines permit non-standard task signatures (not necessary for GBA)
*/
#define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) \
void vFunction( void *pvParameters )

#define portTASK_FUNCTION( vFunction, pvParameters ) \
void vFunction( void *pvParameters )

extern volatile unsigned portLONG ulCriticalNesting;
#define portNO_CRITICAL_NESTING ((unsigned portLONG)0)

extern void vPortStartFirstTask( void );
inline void vTaskSwitchContext( void );
extern void vPortTick( void );

/*
* FreeRTOS requires a few port-specific services. These are declared
* or defined below. Since the GBA is an ARM-T device, we provide two
* versions: one for ARM mode code and one for Thumb code. The Thumb
* versions switch to ARM mode briefly to execute the necessary system
* instructions.
*/
#ifndef THUMB

static inline int portDISABLE_INTERRUPTS( void ) {
int retval;
asm volatile ( "mrs %0, CPSR\n"
"orr r1, %0, #0xc0\n"
"msr CPSR_c, r1"
: "=r" (retval) : : "r1" );
return retval;
}

static inline int portENABLE_INTERRUPTS( void ) {
int retval;
asm volatile ( "mrs %0, CPSR\n"
"bic r1, %0, #0xc0\n"
"msr CPSR_c, r1"
: "=r" (retval) : : "r1" );
return retval;
}

/*
* This will only work in a non-USR/SYS mode!
*/
static inline void portSAVE_CONTEXT( void ) {
asm volatile (
"stmdb sp!, {r0}\n" // Save R0 on this mode's stack
"mov r0 , sp\n" // Use R0 to point current stack
"sub sp , sp, #4\n" // Open space on current stack
"stmdb r0 , {sp}^\n" // Store usr SP on current stack

"ldmia sp!, {r0}\n" // R0 points to USR stack
"stmdb r0!, {lr}\n" // Put return address in place
"stmdb r0 , {r1-r14}^\n" // USR regs onto USR stack
"sub r0 , r0, #(14 * 4)\n" // Writeback is unpredictable
"ldmia sp!, {r3}\n" // Get usr R0
"mrs r2 , spsr\n" // Put final few things in context
"ldr r1 , 1f\n" // get ulCriticalNesting
"ldr r1 , [r1]\n"
"stmdb r0!, {r1-r3}\n" // Complete context saved

"ldr r1 , 2f\n" // Save usrSP in pxCurrentTCB
"ldr r1 , [r1]\n"
"str r0 , [r1]\n"

"b 3f\n"

"1:.word ulCriticalNesting\n"
"2:.word pxCurrentTCB\n"

"3:\n"
);
}

/*
* This will only work in a non-USR/SYS mode!
*/
static inline void portRESTORE_CONTEXT( void ) {
asm volatile (
"ldr lr , 1f\n" // LR points to saved context
"ldr lr , [lr]\n"
"ldr lr , [lr]\n"

"ldmia lr!, {r0, r1}\n" // Recover ulCriticalNesting & SPSR
"ldr r2 , 2f\n"
"str r0 , [r2]\n"
"msr spsr, r1\n"

"ldmia lr , {r0-r14}^\n" // Restore user regs

"ldr lr , [lr, #60]\n"

"movs pc , lr\n" // "return" to new context

"1: .word pxCurrentTCB\n"
"2: .word ulCriticalNesting\n"
);
}

static inline void portYIELD( void ) {
asm volatile (
"mrs r0, CPSR\n"
"msr CPSR_c, #0x13 | 0x80 | 0x40\n" // SVC mode, no interrupts
"msr SPSR, r0\n"
"bl 1f\n"
"movs pc, lr\n" // <-- RESTORE_CONTEXT "returns" to here
"1:\n"
:::"r0") ;
portSAVE_CONTEXT( );
vTaskSwitchContext( );
portRESTORE_CONTEXT( );
}

#else /* defined(THUMB) */

/*
* These are Thumb versions of the above functions. They just
* switch to ARM mode briefly when necessary, then back to Thumb.
*/

static inline int portDISABLE_INTERRUPTS( void ) {
int retval;
asm volatile ( "ldr %0, =1f\n"
"bx %0\n"
".arm\n"
"1:mrs %0, CPSR\n"
"orr r1, %0, #0xc0\n"
"msr CPSR_c, r1\n"
"ldr r1, =2f + 1\n"
"bx r1\n"
".thumb\n"
"2:\n"
: "=r" (retval) : : "r1" );
return retval;
}

static inline int portENABLE_INTERRUPTS( void ) {
int retval;
asm volatile ( "ldr %0, =1f\n"
"bx %0\n"
".arm\n"
"1:mrs %0, CPSR\n"
"bic r1, %0, #0xc0\n"
"msr CPSR_c, r1\n"
"ldr r1, =2f + 1\n"
"bx r1\n"
".thumb\n"
"2:\n"
: "=r" (retval) : : "r1" );
return retval;
}

/*
* This will only work in a non-USR/SYS mode!
*/
static inline void portSAVE_CONTEXT( void ) {
asm volatile (
"push {r0}\n" // Save R0 on this mode's stack

"ldr r0, =1f\n" // Switch to ARM mode
"bx r0\n"
".arm\n"
"1:\n" // We're in ARM mode and R0 is stacked...get on with it

"mov r0 , sp\n" // Use R0 to point current stack
"sub sp , sp, #4\n" // Open space on current stack
"stmdb r0 , {sp}^\n" // Store usr SP on current stack

"ldmia sp!, {r0}\n" // R0 points to USR stack
"stmdb r0!, {lr}\n" // Put return address in place
"stmdb r0 , {r1-r14}^\n" // USR regs onto USR stack
"sub r0 , r0, #(14 * 4)\n" // Writeback is unpredictable
"ldmia sp!, {r3}\n" // Get usr R0
"mrs r2 , spsr\n" // Put final few things in context
"ldr r1 , 1f\n" // get ulCriticalNesting
"ldr r1 , [r1]\n"
"stmdb r0!, {r1-r3}\n" // Complete context saved

"ldr r1 , 2f\n" // Save usrSP in pxCurrentTCB
"ldr r1 , [r1]\n"
"str r0 , [r1]\n"

"ldr r0, =3f + 1\n"
"bx r0\n"

"1:.word ulCriticalNesting\n"
"2:.word pxCurrentTCB\n"

".thumb\n"

"3:\n"
);
}

/*
* This will only work in a non-USR/SYS mode! We're in Thumb mode
* on entry and change to ARM mode to do our business, but we
* NEVER CHANGE BACK! Instead, we return to the context we're
* restoring, which may or may not be Thumb.
*/
static inline void portRESTORE_CONTEXT( void ) {
asm volatile (
"ldr r0 , =0f\n"
"bx r0\n"

".arm\n"

"0:ldr lr , 1f\n" // LR points to saved context
"ldr lr , [lr]\n"
"ldr lr , [lr]\n"

"ldmia lr!, {r0, r1}\n" // Recover ulCriticalNesting & SPSR
"ldr r2 , 2f\n"
"str r0 , [r2]\n"
"msr spsr, r1\n"

"ldmia lr , {r0-r14}^\n" // Restore user regs

"ldr lr , [lr, #60]\n"

"movs pc , lr\n" // "return" to new context

"1: .word pxCurrentTCB\n"
"2: .word ulCriticalNesting\n"
);
}

/*
* Yield is a little tricky -- we want to return in Thumb mode,
* but must switch to ARM mode to change CPSR. However, GCC
* thinks we're in Thumb mode always, so must change back to Thumb
* before calling SAVE_CONTEXT & friends.
*/
static inline void portYIELD( void ) {
asm volatile (
"ldr r0 , =1f\n"
"bx r0\n"
".arm\n"
"1:mrs r0, CPSR\n"
"msr CPSR_c, #0x13 | 0x80 | 0x40\n" // SVC mode, no interrupts
"orr r0, r0, #0x20\n" // Set T bit in faked SPSR
"msr SPSR, r0\n"
"bl 2f\n" // Put addr of next instr in LR

".thumb\n"
"movs pc, lr\n" // <-- RESTORE_CONTEXT will
// "return" here in Thumb mode
".arm\n"
"2:ldr r0, =3f + 1\n" // Switch back to Thumb
"bx r0\n"
".thumb\n"
"3:\n"
:::"r0") ;
portSAVE_CONTEXT( );
vTaskSwitchContext( );
portRESTORE_CONTEXT( );
}

#endif /* !defined(THUMB) */

static inline void portENTER_CRITICAL( void ) {
portDISABLE_INTERRUPTS( );
ulCriticalNesting++;
}

static inline void portEXIT_CRITICAL( void ) {
if (ulCriticalNesting > portNO_CRITICAL_NESTING) {
ulCriticalNesting--;
if (ulCriticalNesting == portNO_CRITICAL_NESTING)
portENABLE_INTERRUPTS( );
}
}

#endif /* PORTMACRO_H */


[ Back to the top ]    [ About FreeRTOS ]    [ Privacy ]    [ Sitemap ]    [ ]


Copyright (C) Amazon Web Services, Inc. or its affiliates. All rights reserved.

Latest News

NXP tweet showing LPC5500 (ARMv8-M Cortex-M33) running FreeRTOS.

Meet Richard Barry and learn about running FreeRTOS on RISC-V at FOSDEM 2019

Version 10.1.1 of the FreeRTOS kernel is available for immediate download. MIT licensed.

View a recording of the "OTA Update Security and Reliability" webinar, presented by TI and AWS.


Careers

FreeRTOS and other embedded software careers at AWS.



FreeRTOS Partners

ARM Connected RTOS partner for all ARM microcontroller cores

Espressif ESP32

IAR Partner

Microchip Premier RTOS Partner

RTOS partner of NXP for all NXP ARM microcontrollers

Renesas

STMicro RTOS partner supporting ARM7, ARM Cortex-M3, ARM Cortex-M4 and ARM Cortex-M0

Texas Instruments MCU Developer Network RTOS partner for ARM and MSP430 microcontrollers

OpenRTOS and SafeRTOS

Xilinx Microblaze and Zynq partner