Looking at Blinky example (Keil MDK5 installation) particularly for ST32F769, I noticed SysTick_Handler twice, apart from third- weak definition in startup assembler file. First one in irq_cm4f.s:
SysTick_Handler PROC
EXPORT SysTick_Handler
IMPORT osRtxTick_Handler
PUSH {R0,LR} ; Save EXC_RETURN
BL osRtxTick_Handler ; Call osRtxTick_Handler
POP {R0,LR} ; Restore EXC_RETURN
MRS R12,PSP
B SVC_Context
and another in EventRecorder.c:
void SysTick_Handler (void) {
SysTickOverflowCounter++;
SysTickOverflowUpdated = 1U;
}
What confuses me is how it happens that two procedures "override" the weak one (mentioned above) ?
Related
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.
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.
I'm working with an IAR project where there are ILINK Configuration Files (.icf) for both a bootloader and the main application. Each file defines the __ICFEDIT_intvec_start__ symbol and later places it referencing their respective .intvec sections (there are 2 cstartup.s files, each with their own .intvec section):
Bootloader .icf:
define symbol __ICFEDIT_intvec_start__ = 0x18000000;
...
place at address mem:__ICFEDIT_intvec_start__ { readonly section .intvec };
Application .icf:
define symbol __ICFEDIT_intvec_start__ = 0x18080000;
...
place at address mem:__ICFEDIT_intvec_start__ { readonly section .intvec };
From what I understand, each of these .icf files are used to generate their own .map files. Seeing as how each of these reference two different sections (that share the same name), I'm confused as to why only the .map file for the bootloader references the .intvec section:
Bootloader .map:
*******************************************************************************
*** PLACEMENT SUMMARY
***
"A1": place at 0x18000000 { ro section .intvec };
"P1": place in [from 0x18000040 to 0x1807ffff] { ro };
"P2": place in [from 0x20020000 to 0x209fffff] {
rw, block CSTACK, block SVC_STACK, block IRQ_STACK, block FIQ_STACK,
block UND_STACK, block ABT_STACK, block HEAP };
Section Kind Address Size Object
------- ---- ------- ---- ------
"A1": 0x3c
.intvec ro code 0x18000000 0x3c cstartup.o [1]
- 0x1800003c 0x3c
...
Application .map:
*******************************************************************************
*** PLACEMENT SUMMARY
***
"INT_VEC_RAM":
place at 0x20020000 { section .intvec_RAM };
"ROM": place in [from 0x18080040 to 0x1bffffff] {
ro section .cstartup, block ROM_CONTENT };
"RAM": place in [from 0x20020040 to 0x209fffff] { block RAM_CONTENT };
Section Kind Address Size Object
------- ---- ------- ---- ------
"ROM": 0x2405e0
ROM_CONTENT 0x18080040 0x2405e0 <Block>
.text ro code 0x18080040 0x104 access.o [8]
.text ro code 0x18080144 0x18c cstartup.o [1]
What's happening here? I'm just starting to understand the role of the linker, so I'm fairly new to all of this.
Also, to further clarify, the two .intvec sections comprise the same interrupt vector table:
SECTION .intvec:CODE:NOROOT(2)
...
__vector: ; Make this a DATA label, so that stack usage
; analysis doesn't consider it an uncalled fun
ARM
; All default exception handlers (except reset) are
; defined as weak symbol definitions.
; If a handler is defined by the application it will take precedence.
LDR PC,Reset_Addr ; Reset
LDR PC,Undefined_Addr ; Undefined instructions
LDR PC,SWI_Addr ; Software interrupt (SWI/SVC)
LDR PC,Prefetch_Addr ; Prefetch abort
LDR PC,Abort_Addr ; Data abort
DCD 0 ; RESERVED
LDR PC,IRQ_Addr ; IRQ
LDR PC,FIQ_Addr ; FIQ
It seems that the answer is a lot more obvious than I thought. According to the section "Linking—an overview" in "IAR C/C++ Development Guide", IAR's linker software ILINK ignores duplicate sections. Thus, if a section is already referenced in one binary object or ILINK configuration file (ICF), all other references to it are ignored.
In this project, since the bootloader takes precedence (is loaded and flashed before the application [defined in the project's .board files; more info here]), the application code's .intvec is seen as a duplicate and is thus ignored/discarded.
////// EDIT: SOLVED, read solution below
I'm trying to use the fpu with the stm32f4Discovery board, programmed with Keil 4 (free version) but, when trying to use it, enters in an infinite loop.
I don't know exactly why, I'm using a very simple code in C and the debugger:
#include "stm32f4_discovery.h"
#include <stdio.h>
float a = 1.332, b = 2.994;
int main(void)
{
printf("Hola");
printf("%f",a*b);
return(0);
}
Here is the results from the debugger: nothing in printf viewer and infinite loop because of "Hard Fault exception occurs" (image here, imgur)
Without line printf("%f",a*b) the debugger shows perfectly "Hola" and ends the program.
I've been searching a possible solution in google since I used this board for a project in university months ago, but anyone knows how to fix it.
I know I can disable fpu and use libraries but that's not the point...
Thank you for your help
/////////////// SOLUTION
I had to change the code in startup_stm32f4xx.s and the function SystemInit() from system_stm32f4xx.c
In startup_stm32f4xx.s, search for the Reset Handler, the code should look like this, but the part below ";FPU settings" is mostly not in the original:
; Reset handler
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT SystemInit
IMPORT __main
;FPU settings
LDR R0, =0xE000ED88 ; Enable CP10,CP11
LDR R1,[R0]
ORR R1,R1,#(0xF << 20)
STR R1,[R0]
LDR R0, =SystemInit
BLX R0
LDR R0, =__main
BX R0
ENDP
And then add in system_stm32f4xx.c, function SystemInit(void), this lines:
void SystemInit(void)
{
/* FPU settings ------------------------------------------------------------*/
#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */
#endif
The debugger will show now the operation result in printf viewer. I don't really know if it's using FPU, but I will test it tomorrow (should be faster now).
Source here
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.