[Solved] ARM Cortex-M0 - How to load large literal? - Page 1
Forum section: electronics / general

author: DavidAlfa
author_rank: Super Contributor
timestamp: January 07, 2024, 06:15:04 pm
content: I have a hardfault wrapper copying the registers before going to the real hardfault function (Made for CM0) which I got fromhere:Code:LDR R1, =HardFault_Handler_cpIt always worked in all conditions, until I tried enabling flto optimization (Got additional 10KB flash in CM3), then I got;"Error: invalid offset, value too big (0x00003B4C)".I understand the literal is too large (Largest literal for LDR in CM0 is 1020), how could I split this instruction into several ones?Like: Load first byte, load second byte, or whatever? My knowledge in ARM assembly is close to zero.I checked theCM0 LDR instruction docs.I tried searching how to make a const-struc expression, perhabs I could find a way to load it in steps, but found nothing.QuoteAlso it seems strange to me that it works without flto, it's loading the literal for HardFault_Handler_cp address after all?

author: langwadt
author_rank: Super Contributor
timestamp: January 07, 2024, 06:26:12 pm
content: https://developer.arm.com/documentation/dui0204/j/writing-arm-assembly-language/loading-constants-into-registers/loading-with-ldr-rd---const?lang=en?

author: DavidAlfa
author_rank: Super Contributor
timestamp: January 07, 2024, 07:21:29 pm
content: Thanks. I understand it's due the "Literal pool" thing:QuoteEdit: It was ".LTORG", not "LTORG":Code:__attribute__((used)) __attribute__((naked)) void HardFault_Handler_(void){asm("MOV R1, LR \n""LDR R0, =HardFault_Handler_cp \n""MOV LR, R0 \n""MOVS R0, #4 \n" // Determine correct stack"TST R0, R1 \n""MRS R0, MSP \n" // Read MSP (Main)"BEQ .+6 \n" // BEQ 2, MRS R0,PSP 4"MRS R0, PSP \n" // Read PSP (Process)"MOV R1, R4 \n" // Registers R4-R6, as parameters 2-4 of the function called"MOV R2, R5 \n""MOV R3, R6 \n" // sourcer32@gmail.com"BX LR \n"".LTORG \n" // Place literal pool outside of code execution);}

author: abyrvalg
author_rank: Frequent Contributor
timestamp: January 07, 2024, 07:44:45 pm
content: Your constant gets placed right where.ltorgis and in current form you are executing those 4 bytes as code (CPU doesn’t treat them as a part of LDR instruction and doesn’t skip them). Move it out of execution flow (i.e. behindbx lr).

author: DavidAlfa
author_rank: Super Contributor
timestamp: January 07, 2024, 07:46:17 pm
content: Thanks!As I say this is not my job nor my studies (At least for now), just DIY/ hobby, so lots to learn yet.After more RTFM I found exactly what you said:Quote

author: Tation
author_rank: Regular Contributor
timestamp: January 07, 2024, 11:05:11 pm
content: Quote from: DavidAlfa on January 07, 2024, 07:21:29 pmIt's "LTORG" in the armasm syntax, but ".ltorg" in the gas one. Obviously you are using the 2nd one. In armasm directives do not begin with a dot, whereas in gas all start with it. Not all armasm directives are ported to gas by just prepending a dot, though.

author: DavidAlfa
author_rank: Super Contributor
timestamp: January 07, 2024, 11:34:41 pm
content: I have no idea what gas is, I don't do arm asm

author: abyrvalg
author_rank: Frequent Contributor
timestamp: January 08, 2024, 12:47:08 am
content: Your toolchain is GCC, so your asm is “gas”With Keil MDK you’d need the “armasm” syntax (ARMCC/ARMClang toolchains).Looks like there is a typo in your code: the LDR in question has R1 destination, but the next instruction moves some uninitialized R0 to LR (where you jump finally).

author: DavidAlfa
author_rank: Super Contributor
timestamp: January 08, 2024, 01:49:31 am
content: Damm, yeah it was R0, no idea how it became R1 during the process!

author: Tation
author_rank: Regular Contributor
timestamp: January 08, 2024, 10:46:55 am
content: Quote from: abyrvalg on January 08, 2024, 12:47:08 amIn Keil you can also select the asm syntax to use:

author: bson
author_rank: Unknown
timestamp: January 17, 2024, 08:21:19 pm
content: No need to use R0, you can load directly to LR: LDR LR, =labelLR is just R14.Note that if label refers to a function, the linker will automatically set the low bit in the literal if you compile with -mthumb.

author: DavidAlfa
author_rank: Super Contributor
timestamp: January 17, 2024, 08:31:43 pm
content: Yeah it did normally work without adding the literal pool, but failed when enabling -flto optimization.Of course, stm32 is compiled with -mthumb!Don't know why, I just wanted to get it sorted and fly away!BTW your suggestion didn't work. Maybe it would for CM3, but it seems CM0 can only perform LDR operations on R0-R7 registers.QuoteEdit: Indeed:https://developer.arm.com/documentation/dui0497/a/the-cortex-m0-instruction-set/memory-access-instructions/ldr-and-str--immediate-offsetQuote

author: brucehoult
author_rank: Super Contributor
timestamp: January 18, 2024, 12:34:01 am
content: Quote from: DavidAlfa on January 17, 2024, 08:31:43 pmAll Cortex-M support only Thumb mode.You have to go back to ARM7TDMI (or ARM9, maybe there's something else newer pre -M) or up to an A-series to get non-Thumb mode.QuoteSure. Thumb1 / ARMv6-M support using r8-r15 only with ADD, CMP, MOV and BX. Plus the special (implied) operations that use SP, LR, PC for their special purpose.

author: westfw
author_rank: Super Contributor
timestamp: January 18, 2024, 08:46:39 am
content: Quote-fltoessentially causes the compiler to inline a lot of code that wouldn't normally be inlined, resulting in (for example) a very largemain()function with all your nice modular function special-cased and inlined, with occasional calls to library functions that didn't come under the-fltoumbrella.So it's not surprising that a function would get too large to access a literal pool in its normal position "after" the function body.

author: DavidAlfa
author_rank: Super Contributor
timestamp: January 18, 2024, 10:50:37 am
content: Still, why does it miss placing a literal pool? Shouldn't handle this automatically?

author: ejeffrey
author_rank: Super Contributor
timestamp: January 18, 2024, 08:16:02 pm
content: Quote from: DavidAlfa on January 18, 2024, 10:50:37 amAs near as I can tell, and would love to be corrected by someone who actually knows, the compiler handles making sure the literal pools it needs are there, with or without LTO. If the function grows too large for a single literal pool it will create more . But if you write an assembly function and don't provide a literal pool it will happily use one of the ones provided by the compiler assuming it's in range. But if the pool you are squatting on moves it won't try to fix it because that was your responsibility.