Atomic test-and-set for LPC1788 microcontroller - c

I'm working with the NXP LPC1788 microcontroller and I'm developing a multi-threaded application in C. In part of my application I define a custom linked-list data structure. I was previously having problems with my program due to concurrent access to a particular list which I seem to have solved by implementing a "lock acquire" method and a "lock release" method for lists which threads can call before accessing the list itself.
I did this by adding a 'sema' data member to the list struct:
typedef struct linked_list
{
list_node_t *head;
list_node_t *tail;
uint32_t len;
NODE_ITEM_TYPE_T itemType;
uint32_t itemSize;
uint8_t sema;
} linked_list_t;
My "lock acquire" method is given below:
void LIST_AcquireLock(linked_list_t *list)
{
while(list->sema);
list->sema = 1;
}
My "lock release" method is given below:
void LIST_ReleaseLock(linked_list_t *list)
{
list->sema = 0;
}
Generally this seems to work okay, since my application involves adding and removing items to a list like this thousands of times a second and I have not noticed any bugs related to concurrent access since.
However, to be more confident that this works, I was wondering if there was any way of implementing a test-and-set approach. The LPC1788 relies on a version of the Thumb instruction set specific to Cortex-M3 microcontrollers, which can be found here or in the user manual on page 918+.
Looking through it, though, I can't find anything like a test-and-set instruction. I might just be overlooking it.
Ideally, I would like to have something like this:
void LIST_AcquireLock(linked_list_t *list)
{
do{
while(list->sema);
} while(TestAndSet(list->sema));
}
EDIT
Based on Nemo's answer, I've attempted the following:
void LIST_AcquireLock(linked_list_t *list)
{
// Wait until lock seems free.
while(list->sema);
// Make sure lock is actually free.
do {
// If the semaphore is locked, we continue.
// OTHERWISE we try to lock it ourselves.
if(__LDREXB(&(list->sema))) continue;
// If __STREXB returns 1, then another thread might have accessed that
// memory location and we can't be sure the lock operation is atomic,
// so try the locking procedure again.
} while(__STREXB(1, &(list->sema)));
}
If it's helpful, this is the corresponding assembly code:
LIST_AcquireLock:
??LIST_AcquireLock_0:
0x56de: 0x7d01 LDRB R1, [R0, #0x14]
0x56e0: 0x2900 CMP R1, #0
0x56e2: 0xd1fc BNE.N ??LIST_AcquireLock_0 ; 0x56de
??LIST_AcquireLock_1:
0x56e4: 0xf110 0x0114 ADDS.W R1, R0, #20 ; 0x14
0x56e8: 0xe8d1 0x1f4f LDREXB R1, [R1]
0x56ec: 0xb2c9 UXTB R1, R1
0x56ee: 0x2900 CMP R1, #0
??LIST_AcquireLock_2:
0x56f0: 0xf110 0x0114 ADDS.W R1, R0, #20 ; 0x14
0x56f4: 0x2201 MOVS R2, #1
0x56f6: 0xe8c1 0x2f43 STREXB R3, R2, [R1]
0x56fa: 0x2b00 CMP R3, #0
0x56fc: 0xd1f2 BNE.N ??LIST_AcquireLock_1 ; 0x56e4
0x56fe: 0x4770 BX LR
I'm having trouble reproducing the concurrent access issues (assuming it was concurrency issues I was having) so I don't know for sure that this works.

ARM uses a "load-linked/store-exclusive" paradigm for atomic operations. See this question and Section 39.2.4.8 of the user manual you linked for details.
[Update]
Based on the code in the link #HansPassant provided, I would suggest some slight changes to your routine:
void LIST_AcquireLock(linked_list_t *list)
{
// Wait until lock seems free.
//while(list->sema); // unnecessary
// Make sure lock is actually free.
do {
// If the semaphore is locked, we continue.
// OTHERWISE we try to lock it ourselves.
if(__LDREXB(&(list->sema))) continue;
// If __STREXB returns 1, then another thread might have accessed that
// memory location and we can't be sure the lock operation is atomic,
// so try the locking procedure again.
} while(__STREXB(1, &(list->sema)));
// Ensure CPU does not reorder any memory accesses across lock acquisition.
__DMB();
}
The __DMB() is probably irrelevant on very simple ARM cores, but it is definitely needed on more complex ones. Modern CPUs have complicated memory models.

Related

Volatile variable not updated despite unoptimized assembly

I'm working on a dual-core Cortex-R52 ARM chip, with an instance of FreeRTOS running in each core (AMP), and using ICCARM (IAR) as my compiler.
I need to ensure that CPU1 initialize some tasks, in order to pass their handler to CPU0 through the shared memory, but both cores are executed at the same time, which creates a problem in the scenario where CPU0 gets to using the supposedly passed handler, that wasn't created yet by CPU1.
A solution I tried, was creating a volatile variable pdSTART at a dedicated address space, which keeps CPU0 looping as long as its equal to 0:
#pragma location = 0x100F900C
__no_init volatile uint8_t pdSTART;
while (pdSTART == 0)
{
vTaskDelay(10 / portTICK_PERIOD_MS);
}
As expected the generated assembly was as follows:
vTaskDelay(10 / portTICK_PERIOD_MS);
0xc3a: 0x200a MOVS R0, #10 ; 0xa
0xc3c: 0xf000 0xf93c BL vTaskDelay ; 0xeb8
while (pdSTART == 0)
0xc40: 0x7b28 LDRB R0, [R5, #0xc]
0xc42: 0x2800 CMP R0, #0
0xc44: 0xd0f9 BEQ.N 0xc3a
With register R5 containing the address 0x100F9000.
Using the debugger I made sure CPU0 reaches the while condition first and gets in the loop, I then made CPU1 change the value of pdSTART, which I confirmed on the memory map
pdSTART:
0x100f'900c: 0x0000'0001 DC32 VECTOR_RBLOCK$$Base
And yet the condition on CPU0 remains false and pdSTART is never updated, both the memory map and "Watch" window of the debugger show the variable updated.
I tried explicitly writing a read from the address of pdSTART:
void func(void)
{
asm volatile ("" : : "r" (*(uint8_t *)0x100F900C));
}
But the generated assembly was the same as the while condition.
Is the old value of pdSTART saved into some kind of stack or cache? is there a way to forcefully update it?
Thank you.

Why doesn't my ARM LDREX/STREX C function work?

I wrote a claim_lock function in C, according to the "Barrier Litmus Tests and Cookbook" document. I examined the generated code, and it all looks good, but it didn't work.
// This code conforms to the section 7.2 of PRD03-GENC-007826:
// "Acquiring and Releasing a Lock"
static inline void claim_lock( uint32_t volatile *lock )
{
uint32_t failed = 1;
uint32_t value;
while (failed) {
asm volatile ( "ldrex %[value], [%[lock]]"
: [value] "=&r" (value)
: [lock] "r" (lock) );
if (value == 0) {
// The failed and lock registers are not allowed to be the same, so
// pretend to gcc that the lock pointer may be written as well as read.
asm volatile ( "strex %[failed], %[value], [%[lock]]"
: [failed] "=&r" (failed)
, [lock] "+r" (lock)
: [value] "r" (1) );
}
else {
asm ( "clrex" );
}
}
asm ( "dmb sy" );
}
Generated code (gcc):
1000: e3a03001 mov r3, #1
1004: e1902f9f ldrex r2, [r0]
1008: e3520000 cmp r2, #0
100c: 1a000004 bne 1024 <claim_lock+0x24>
1010: e1802f93 strex r2, r3, [r0]
1014: e3520000 cmp r2, #0
1018: 1afffff9 bne 1004 <claim_lock+0x4>
101c: f57ff05f dmb sy
1020: e12fff1e bx lr
1024: f57ff01f clrex
1028: eafffff5 b 1004 <claim_lock+0x4>
Corresponding release function:
static inline void release_lock( uint32_t volatile *lock )
{
// Ensure that any changes made while holding the lock are
// visible before the lock is seen to have been released
asm ( "dmb sy" );
*lock = 0;
}
It worked in QEMU, but either hung, or allowed all cores to "claim" the so-called "lock" on real hardware (Raspberry Pi 3 Cortex-A53).
this is what i found in Context switch section of ARMv7-M Architecture
Reference Manual
Blockquote
It is necessary to ensure that the local monitor is in the Open Access state after a context switch. In
ARMv7-M, the local monitor is changed to Open Access automatically as part of an exception entry or exit
sequence. The local monitor can also be forced to the Open Access state by a CLREX instruction.
Note
Context switching is not an application level operation. However, this information is included here to
complete the description of the exclusive operations.
A context switch might cause a subsequent Store-Exclusive to fail, requiring a load … store sequence to be
replayed. To minimize the possibility of this happening, ARM recommends that the Store-Exclusive
instruction is kept as close as possible to the associated Load-Exclusive instruction, see Load-Exclusive and
Store-Exclusive usage restrictions.
Blockquote
The LDREX instruction will hang the core (unless my test failed to report an exception) if:
The MMU is not enabled
The virtual memory area containing the lock is not cached
The cores will appear to ignore each other's claims if:
Symmetric Multi-processing has not been enabled
The SMP enable mechanism seems to vary from device to device; check the TRM for the partular core, it's outside the scope of the ARM ARM.
For the Cortex-A53, the bit to set is SMPEN, bit 6 of The CPU Extended Control Register, CPUECTLR.
Earlier devices have bit 5 of the Auxiliary Control Register, for example (ARM11 MPcore), where there's also the SCU to consider. I don't have such a device, but it's that documentation where I first noticed an SMP/nAMP bit.

Inject a memory exception by accessing forbidden memory location

I want to test a exception handler function that I have written for an embedded system and want to write a test code that injects an access to memory that forbidden.
void Test_Mem_exception
{
__asm(
"LDR R0, =0xA0100000\n\t"
"MOV R1, 0x77777777\n\t"
"STR R1, [R0,#0]"
);
This is the code I want to write that access memory location at 0xA010000. Somehow this does not seem a generic test code to me.
Is there a standard way of writing such test codes in C or C++. By Generic I mean a code that is independent of the memory map of the system that it runs on.
I wouldn't use asm for this, simply use a pointer.
void Test_Mem_exception
{
/* volatile, to suppress optimizing/removing the read statement */
volatile int *ptr = 0xC0C0C0C0;
int value = *ptr;
}
This won't always result to an exception, because reading from address 0 can be valid on some systems.
The same applies to any other address, there doesn't exist any address that will fail on all systems.

Using #defined values before RAM has been initialised

I am writing the boot-up code for an ARM CPU. There is no internal RAM, but there is 1GB of DDRAM connected to the CPU, which is not directly accessible before initialisation. The code is stored in flash, initialises RAM, then copies itself and the data segment to RAM and continue execution there. My program is:
#define REG_BASE_BOOTUP 0xD0000000
#define INTER_REGS_BASE REG_BASE_BOOTUP
#define SDRAM_FTDLL_REG_DEFAULT_LEFT 0x887000
#define DRAM_BASE 0x0
#define SDRAM_FTDLL_CONFIG_LEFT_REG (DRAM_BASE+ 0x1484)
... //a lot of registers
void sdram_init() __attribute__((section(".text_sdram_init")));
void ram_init()
{
static volatile unsigned int* const sdram_ftdll_config_left_reg = (unsigned int*)(INTER_REGS_BASE + SDRAM_FTDLL_CONFIG_LEFT_REG);
... //a lot of registers assignments
*sdram_ftdll_config_left_reg = SDRAM_FTDLL_REG_DEFAULT_LEFT;
}
At the moment my program is not working correctly because the register values end up being linked to RAM, and at the moment the program tries to access them only the flash is usable.
How could I change my linker script or my program so that those values have their address in flash? Is there a way I can have those values in the text segment?
And actually are those defined values global or static data when they are declared at file scope?
Edit:
The object file is linked with the following linker script:
MEMORY
{
RAM (rw) : ORIGIN = 0x00001000, LENGTH = 12M-4K
ROM (rx) : ORIGIN = 0x007f1000, LENGTH = 60K
VECTOR (rx) : ORIGIN = 0x007f0000, LENGTH = 4K
}
SECTIONS
{
.startup :
{
KEEP((.text.vectors))
sdram_init.o(.sdram_init)
} > VECTOR
...
}
Disassembly from the register assignment:
*sdram_ftdll_config_left_reg = SDRAM_FTDLL_REG_DEFAULT_LEFT;
7f0068: e59f3204 ldr r3, [pc, #516] ; 7f0274 <sdram_init+0x254>
7f006c: e5932000 ldr r2, [r3]
7f0070: e59f3200 ldr r3, [pc, #512] ; 7f0278 <sdram_init+0x258>
7f0074: e5823000 str r3, [r2]
...
7f0274: 007f2304 .word 0x007f2304
7f0278: 00887000 .word 0x00887000
To answer your question directly -- #defined values are not stored in the program anywhere (besides possibly in debug sections). Macros are expanded at compile time as if you'd typed them out in the function, something like:
*((unsigned int *) 0xd0010000) = 0x800f800f;
The values do end up in the text segment, as part of your compiled code.
What's much more likely here is that there's something else you're doing wrong. Off the top of my head, my first guess would be that your stack isn't initialized properly, or is located in a memory region that isn't available yet.
There are a few options to solve this problem.
Use PC relative data access.
Use a custom linker script.
Use assembler.
Use PC relative data access
The trouble you have with this method is you must know details of how the compiler will generate code. #define register1 (volatile unsigned int *)0xd0010000UL is that this is being stored as a static variable which is loaded from the linked SDRAM address.
7f0068: ldr r3, [pc, #516] ; 7f0274 <sdram_init+0x254>
7f006c: ldr r2, [r3] ; !! This is a problem !!
7f0070: ldr r3, [pc, #512] ; 7f0278 <sdram_init+0x258>
7f0074: str r3, [r2]
...
7f0274: .word 0x007f2304 ; !! This memory doesn't exist.
7f0278: .word 0x00887000
You must do this,
void ram_init()
{
/* NO 'static', you can not do that. */
/* static */ volatile unsigned int* const sdram_reg =
(unsigned int*)(INTER_REGS_BASE + SDRAM_FTDLL_CONFIG_LEFT_REG);
*sdram_ftdll_config_left_reg = SDRAM_FTDLL_REG_DEFAULT_LEFT;
}
Or you may prefer to implement this in assembler as it is probably pretty obtuse as to what you can and can't do here. The main effect of the above C code is that every thing is calculated or PC relative. If you opt not to use a linker script, this must be the case. As Duskwuff points out, you also can have stack issues. If you have no ETB memory, etc, that you can use as a temporary stack then it probably best to code this in assembler.
Linker script
See gnu linker map... and many other question on using a linker script in this case. If you want specifics, you need to give actual addresses use by the processor. With this option you can annotate your function to specify which section it will live in. For instance,
void ram_init() __attribute__((section("FLASH")));
In this case, you would use the Gnu Linkers MEMORY statement and AT statements to put this code at the flash address where you desire it to run from.
Use assembler
Assembler gives you full control over memory use. You can garentee that no stack is used, that no non-PC relative code is generated and it will probably be faster to boot. Here is some table driven ARM assembler I have used for the case you describe, initializing an SDRAM controller.
/* Macro for table of register writes. */
.macro DCDGEN,type,addr,data
.long \type
.long \addr
.long \data
.endm
.set FTDLL_CONFIG_LEFT, 0xD0001484
sdram_init:
DCDGEN 4, FTDLL_CONFIG_LEFT, 0x887000
1:
init_sdram_bank:
adr r0,sdram_init
adr r1,1b
1:
/* Delay. */
mov r5,#0x100
2: subs r5,r5,#1
bne 2b
ldmia r0!, {r2,r3,r4} /* Load DCD entry. */
cmp r2,#1 /* byte? */
streqb r4,[r3] /* Store byte... */
strne r4,[r3] /* Store word. */
cmp r0,r1 /* table done? */
blo 1b
bx lr
/* Dump literal pool. */
.ltorg
Assembler has many benefits. You can also clear the bss section and setup the stack with simple routines. There are many on the Internet and I think you can probably code one yourself. The gnu ld script is also beneficial with assembler as you can ensure that sections like bss are aligned and a multiple of 4,8,etc. so that the clearing routine doesn't need special cases. Also, you will have to copy the code from flash to SDRAM after it is initialized. This is a fairly expensive/long running task and you can speed it up with some short assembler.

Using GCC inline assembly with instructions that take immediate values

The problem
I'm working on a custom OS for an ARM Cortex-M3 processor. To interact with my kernel, user threads have to generate a SuperVisor Call (SVC) instruction (previously known as SWI, for SoftWare Interrupt). The definition of this instruction in the ARM ARM is:
Which means that the instruction requires an immediate argument, not a register value.
This is making it difficult for me to architect my interface in a readable fashion. It requires code like:
asm volatile( "svc #0");
when I'd much prefer something like
svc(SVC_YIELD);
However, I'm at a loss to construct this function, because the SVC instruciton requires an immediate argument and I can't provide that when the value is passed in through a register.
The kernel:
For background, the svc instruction is decoded in the kernel as follows
#define SVC_YIELD 0
// Other SVC codes
// Called by the SVC interrupt handler (not shown)
void handleSVC(char code)
{
switch (code) {
case SVC_YIELD:
svc_yield();
break;
// Other cases follow
This case statement is getting rapidly out of hand, but I see no way around this problem. Any suggestions are welcome.
What I've tried
SVC with a register argument
I initially considered
__attribute__((naked)) svc(char code)
{
asm volatile ("scv r0");
}
but that, of course, does not work as SVC requires a register argument.
Brute force
The brute-force attempt to solve the problem looks like:
void svc(char code)
switch (code) {
case 0:
asm volatile("svc #0");
break;
case 1:
asm volatile("svc #1");
break;
/* 253 cases omitted */
case 255:
asm volatile("svc #255");
break;
}
}
but that has a nasty code smell. Surely this can be done better.
Generating the instruction encoding on the fly
A final attempt was to generate the instruction in RAM (the rest of the code is running from read-only Flash) and then run it:
void svc(char code)
{
asm volatile (
"orr r0, 0xDF00 \n\t" // Bitwise-OR the code with the SVC encoding
"push {r1, r0} \n\t" // Store the instruction to RAM (on the stack)
"mov r0, sp \n\t" // Copy the stack pointer to an ordinary register
"add r0, #1 \n\t" // Add 1 to the address to specify THUMB mode
"bx r0 \n\t" // Branch to newly created instruction
"pop {r1, r0} \n\t" // Restore the stack
"bx lr \n\t" // Return to caller
);
}
but this just doesn't feel right either. Also, it doesn't work - There's something I'm doing wrong here; perhaps my instruction isn't properly aligned or I haven't set up the processor to allow running code from RAM at this location.
What should I do?
I have to work on that last option. But still, it feels like I ought to be able to do something like:
__attribute__((naked)) svc(char code)
{
asm volatile ("scv %1"
: /* No outputs */
: "i" (code) // Imaginary directive specifying an immediate argument
// as opposed to conventional "r"
);
}
but I'm not finding any such option in the documentation and I'm at a loss to explain how such a feature would be implemented, so it probably doesn't exist. How should I do this?
You want to use a constraint to force the operand to be allocated as an 8-bit immediate. For ARM, that is constraint I. So you want
#define SVC(code) asm volatile ("svc %0" : : "I" (code) )
See the GCC documentation for a summary of what all the constaints are -- you need to look at the processor-specific notes to see the constraints for specific platforms. In some cases, you may need to look at the .md (machine description) file for the architecture in the gcc source for full information.
There's also some good ARM-specific gcc docs here. A couple of pages down under the heading "Input and output operands" it provides a table of all the ARM constraints
What about using a macro:
#define SVC(i) asm volatile("svc #"#i)
As noted by Chris Dodd in the comments on the macro, it doesn't quite work, but this does:
#define STRINGIFY0(v) #v
#define STRINGIFY(v) STRINGIFY0(v)
#define SVC(i) asm volatile("svc #" STRINGIFY(i))
Note however that it won't work if you pass an enum value to it, only a #defined one.
Therefore, Chris' answer above is the best, as it uses an immediate value, which is what's required, for thumb instructions at least.
My solution ("Generating the instruction encoding on the fly"):
#define INSTR_CODE_SVC (0xDF00)
#define INSTR_CODE_BX_LR (0x4770)
void svc_call(uint32_t svc_num)
{
uint16_t instrs[2];
instrs[0] = (uint16_t)(INSTR_CODE_SVC | svc_num);
instrs[1] = (uint16_t)(INSTR_CODE_BX_LR);
// PC = instrs (or 1 -> thumb mode)
((void(*)(void))((uint32_t)instrs | 1))();
}
It works and its much better than switch-case variant, which takes ~2kb ROM for 256 svc's. This func does not have to be placed in RAM section, FLASH is ok.
You can use it if svc_num should be a runtime variable.
As discussed in this question, the operand of SVC is fixed, that is it should be known to the preprocessor, and it is different from immediate Data-processing operands.
The gcc manual reads
'I'- Integer that is valid as an immediate operand in a data processing instruction. That is, an integer in the range 0 to 255 rotated by a multiple of 2.
Therefore the answers here that use a macro are preferred, and the answer of Chris Dodd is not guaranteed to work, depending on the gcc version and optimization level. See the discussion of the other question.
I wrote one handler recently for my own toy OS on Cortex-M. Works if tasks use PSP pointer.
Idea:
Get interrupted process's stack pointer, get process's stacked PC, it will have the instruction address of instruction after SVC, look up the immediate value in the instruction. It's not as hard as it sounds.
uint8_t __attribute__((naked)) get_svc_code(void){
__asm volatile("MSR R0, PSP"); //Get Process Stack Pointer (We're in SVC ISR, so currently MSP in use)
__asm volatile("ADD R0, #24"); //Pointer to stacked process's PC is in R0
__asm volatile("LDR R1, [R0]"); //Instruction Address after SVC is in R1
__asm volatile("SUB R1, R1, #2"); //Subtract 2 bytes from the address of the current instruction. Now R1 contains address of SVC instruction
__asm volatile("LDRB R0, [R1]"); //Load lower byte of 16-bit instruction into R0. It's immediate value.
//Value is in R0. Function can return
}

Resources