Completed the Cortex-M0 barriers and made its example build with both toolchains (#595)

Two unrelated Cortex-M0 gaps, both left over from earlier work.

The memory barriers from #523 reached the Cortex-M0 gnu port but not its ac6 or
iar siblings, in either the inline system return in tx_port.h or the assembly
routine. Both take the same GNU or IAR code path, and iar already carried the
entry barrier, so the missing pieces were the entry pair for ac6 and the barrier
after restoring the interrupt posture for both. All three tools now match.

The Cortex-M0 example could not link with any toolchain. cortexm0_crt0.S
references 23 linker script symbols and the script defined only 12 of them, so
__text_start__, __text_end__, __text_load_start__, the rodata and fast section
symbols, and the ctors and dtors load addresses were all unresolved. The
Cortex-M4 script defines all 23, including a .fast section with no content whose
symbols exist so that the startup copy is a no-op, and its comment says as much.
The Cortex-M0 script is brought to that same shape.

That left the example failing under LLVM only, on instructions that ARMv6-M can
encode just one way. The file declared .code 16 but no syntax mode, so GNU as
used the legacy divided syntax in which a plain add or sub sets the flags
implicitly, while LLVM implements unified syntax only and rejected the
non-flag-setting spelling. Declaring .syntax unified and writing movs, adds and
subs makes both assemblers agree, and the encodings GNU produces are byte
identical before and after, verified by disassembling both objects.

The Cortex-M0 example now links with GNU at 22,520 bytes of text and with Arm
Toolchain for Embedded at 22,866, so it comes off the list of examples not
expected to link and scripts/check_clang.sh now links eight of eight.

Assisted-by: Claude Code (Opus 5) <noreply@anthropic.com>
This commit is contained in:
Frédéric Desbiens
2026-08-10 17:34:13 -04:00
committed by GitHub
parent f3ab36dacc
commit a8aebc5206
7 changed files with 56 additions and 19 deletions
+2
View File
@@ -322,11 +322,13 @@ unsigned int interrupt_save;
/* Set PendSV to invoke ThreadX scheduler. */
*((volatile ULONG *) 0xE000ED04) = ((ULONG) 0x10000000);
__asm__ volatile ("dsb 0xF \n isb 0xF " : : : "memory");
if (__get_ipsr_value() == 0)
{
interrupt_save = __get_primask_value();
__enable_interrupts();
__restore_interrupts(interrupt_save);
__asm__ volatile ("isb 0xF " : : : "memory");
}
}
@@ -72,12 +72,15 @@ _tx_thread_system_return:
LDR r0, =0x10000000 @ Load PENDSVSET bit
LDR r1, =0xE000ED04 @ Load NVIC base
STR r0, [r1] @ Set PENDSVBIT in ICSR
DSB #0xF @ Ensure memory access is complete
ISB #0xF @ Flush pipeline
MRS r0, IPSR @ Pickup IPSR
CMP r0, #0 @ Is it a thread returning?
BNE _isr_context @ If ISR, skip interrupt enable
MRS r1, PRIMASK @ Thread context returning, pickup PRIMASK
CPSIE i @ Enable interrupts
MSR PRIMASK, r1 @ Restore original interrupt posture
ISB #0xF @ Flush pipeline
_isr_context:
BX lr @ Return to caller
@/* } */
@@ -4,6 +4,12 @@
.section .init, "ax"
.code 16
/* Unified assembler syntax. Without it GNU as falls back to the legacy
divided syntax, in which a plain add or sub sets the flags implicitly.
LLVM implements only unified syntax, where ARMv6-M has no
flag-preserving encoding, so the S-forms below are required. Declaring
the mode makes both assemblers agree. */
.syntax unified
.align 2
.thumb_func
@@ -43,16 +49,16 @@ _start:
/* Zero bss. */
ldr r0, =__bss_start__
ldr r1, =__bss_end__
mov r2, #0
movs r2, #0
bl crt0_memory_set
/* Setup heap - not recommended for Threadx but here for compatibility reasons */
ldr r0, = __heap_start__
ldr r1, = __heap_end__
sub r1, r1, r0
mov r2, #0
subs r1, r1, r0
movs r2, #0
str r2, [r0]
add r0, r0, #4
adds r0, r0, #4
str r1, [r0]
/* constructors in case of using C++ */
@@ -62,7 +68,7 @@ crt0_ctor_loop:
cmp r0, r1
beq crt0_ctor_end
ldr r2, [r0]
add r0, #4
adds r0, #4
push {r0-r1}
blx r2
pop {r0-r1}
@@ -70,14 +76,14 @@ crt0_ctor_loop:
crt0_ctor_end:
/* Setup call frame for main() */
mov r0, #0
movs r0, #0
mov lr, r0
mov r12, sp
start:
/* Jump to main() */
mov r0, #0
mov r1, #0
movs r0, #0
movs r1, #0
ldr r2, =main
blx r2
/* when main returns, loop forever. */
@@ -91,14 +97,14 @@ crt0_exit_loop:
crt0_memory_copy:
cmp r0, r1
beq memory_copy_done
sub r2, r2, r1
subs r2, r2, r1
beq memory_copy_done
memory_copy_loop:
ldrb r3, [r0]
add r0, r0, #1
adds r0, r0, #1
strb r3, [r1]
add r1, r1, #1
sub r2, r2, #1
adds r1, r1, #1
subs r2, r2, #1
bne memory_copy_loop
memory_copy_done:
bx lr
@@ -107,7 +113,7 @@ crt0_memory_set:
cmp r0, r1
beq memory_set_done
strb r2, [r0]
add r0, r0, #1
adds r0, r0, #1
b crt0_memory_set
memory_set_done:
bx lr
@@ -17,6 +17,8 @@ SECTIONS
.text :
{
__text_start__ = .;
*(.text*)
KEEP(*(.init))
@@ -26,6 +28,7 @@ SECTIONS
*crtbegin.o(.ctors)
*crtbegin?.o(.ctors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
__ctors_load_start__ = .;
__ctors_start__ = ALIGN(4);
*(SORT(.ctors.*))
*(.ctors)
@@ -35,16 +38,41 @@ SECTIONS
*crtbegin.o(.dtors)
*crtbegin?.o(.dtors)
*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
__dtors_load_start__ = .;
__dtors_start__ = ALIGN(4);
*(SORT(.dtors.*))
*(.dtors)
__dtors_end__ = ALIGN(4);
KEEP(*(.eh_frame*))
__text_end__ = ALIGN(4);
} > FLASH
__text_load_start__ = LOADADDR(.text);
.rodata :
{
__rodata_start__ = .;
*(.rodata*)
KEEP(*(.eh_frame*))
__rodata_end__ = ALIGN(4);
} > FLASH
__rodata_load_start__ = LOADADDR(.rodata);
/* Fast section: for time-critical code/data to be copied from FLASH to RAM at startup.
No .fast content in this sample; symbols defined so crt0 startup copy is a no-op. */
.fast (NOLOAD) :
{
__fast_start__ = .;
*(.fast*)
__fast_end__ = .;
} > RAM
__fast_load_start__ = __fast_start__;
.ARM.extab :
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
+1
View File
@@ -319,6 +319,7 @@ __istate_t interrupt_save;
interrupt_save = __get_interrupt_state();
__enable_interrupt();
__set_interrupt_state(interrupt_save);
__asm__ volatile ("isb 0xF " : : : "memory");
}
}
@@ -79,6 +79,7 @@ _tx_thread_system_return:
MRS r1, PRIMASK ; Thread context returning, pickup PRIMASK
CPSIE i ; Enable interrupts
MSR PRIMASK, r1 ; Restore original interrupt posture
ISB SY ; Flush pipeline
_isr_context:
BX lr ; Return to caller
;}
+1 -5
View File
@@ -148,10 +148,6 @@ C_CORES="cortex_m0 cortex_m4 cortex_m23 cortex_m33 cortex_m55 cortex_a7 cortex_a
# explicitly rather than silently skipped, so the gaps stay visible.
#
# These fail with the GNU toolchain too, so they are not LLVM problems:
# cortex_m0 cortexm0_crt0.S references __text_load_start__,
# __text_start__ and __text_end__, which its linker
# script never defines. The Cortex-M4 script defines the
# equivalents, so this is a gap in that one example.
# arm9 arm11 need newlib multilib variants for those CPUs, which are
# cortex_r4 cortex_r5 not present in every GNU toolchain packaging.
#
@@ -160,7 +156,7 @@ C_CORES="cortex_m0 cortex_m4 cortex_m23 cortex_m33 cortex_m55 cortex_a7 cortex_a
# own crt0 is linked, and it needs picolibc's
# __data_start, __data_source, __data_size and __bss_size,
# which the example's linker script does not define.
EXAMPLES_EXPECTED_TO_FAIL="arm9 arm11 cortex_m0 cortex_r4 cortex_r5 cortex_a12 cortex_a15 cortex_a17"
EXAMPLES_EXPECTED_TO_FAIL="arm9 arm11 cortex_r4 cortex_r5 cortex_a12 cortex_a15 cortex_a17"
failures=0
skipped=""