From f8775dce3a4cd3118910e84d42495c39dfa91316 Mon Sep 17 00:00:00 2001
From: Terraneo Federico <fede.tft@miosix.org>
Date: Thu, 9 Jan 2025 11:35:31 +0100
Subject: [PATCH] Towards supporting processes on Cortex M0+

---
 .../interfaces-impl/arch_registers_impl.h     |   6 +-
 .../rp2040_2M+264k_processes.ld               | 169 ++++++++++++++++++
 .../rp2040_2M+264k_rom.ld                     |   2 +-
 miosix/config/Makefile.inc                    |  14 +-
 miosix/libsyscalls/crt1.cpp                   |  16 ++
 5 files changed, 199 insertions(+), 8 deletions(-)
 create mode 100644 miosix/arch/cortexM0plus_rp2040/rp2040_raspberry_pi_pico/rp2040_2M+264k_processes.ld

diff --git a/miosix/arch/cortexM0plus_rp2040/common/interfaces-impl/arch_registers_impl.h b/miosix/arch/cortexM0plus_rp2040/common/interfaces-impl/arch_registers_impl.h
index a4bd9777..100cfa2b 100644
--- a/miosix/arch/cortexM0plus_rp2040/common/interfaces-impl/arch_registers_impl.h
+++ b/miosix/arch/cortexM0plus_rp2040/common/interfaces-impl/arch_registers_impl.h
@@ -4,12 +4,10 @@
 #include "../rp2040/hardware/rp2040.h"
 #include "../rp2040/hardware/properties.h"
 #include "../rp2040/hardware/platform.h"
-#if defined(BOARD_VARIANT_PICO)
-#include "../rp2040/hardware/boards/pico.h"
-#elif defined(BOARD_VARIANT_PICO_W)
+#if defined(BOARD_VARIANT_PICO_W)
 #include "../rp2040/hardware/boards/pico_w.h"
 #else
-#error "Unknown board variant"
+#include "../rp2040/hardware/boards/pico.h"
 #endif
 
 //Peripheral interrupt start from 0 and the last one is 25, so there are 26
diff --git a/miosix/arch/cortexM0plus_rp2040/rp2040_raspberry_pi_pico/rp2040_2M+264k_processes.ld b/miosix/arch/cortexM0plus_rp2040/rp2040_raspberry_pi_pico/rp2040_2M+264k_processes.ld
new file mode 100644
index 00000000..c5a168ea
--- /dev/null
+++ b/miosix/arch/cortexM0plus_rp2040/rp2040_raspberry_pi_pico/rp2040_2M+264k_processes.ld
@@ -0,0 +1,169 @@
+/*
+ * Linker script for RP2040 (2MB external flash, 264K RAM)
+ * Optimized for use with the Miosix kernel
+ */
+
+/*
+ * This linker script puts:
+ * - read only data and code (.text, .rodata, .eh_*) in flash
+ * - stacks, heap and sections .data and .bss in ram 
+ */
+
+/*
+ * The main stack is used for interrupt handling by the kernel.
+ *
+ * *** Readme ***
+ * This linker script places the main stack (used by the kernel for interrupts)
+ * at the bottom of the ram, instead of the top. This is done for two reasons:
+ *
+ * - as an optimization for microcontrollers with little ram memory. In fact
+ *   the implementation of malloc from newlib requests memory to the OS in 4KB
+ *   block (except the first block that can be smaller). This is probably done
+ *   for compatibility with OSes with an MMU and paged memory. To see why this
+ *   is bad, consider a microcontroller with 8KB of ram: when malloc finishes
+ *   up the first 4KB it will call _sbrk_r asking for a 4KB block, but this will
+ *   fail because the top part of the ram is used by the main stack. As a
+ *   result, the top part of the memory will not be used by malloc, even if
+ *   available (and it is nearly *half* the ram on an 8KB mcu). By placing the
+ *   main stack at the bottom of the ram, the upper 4KB block will be entirely
+ *   free and available as heap space.
+ *
+ * - In case of main stack overflow the cpu will fault because access to memory
+ *   before the beginning of the ram faults. Instead with the default stack
+ *   placement the main stack will silently collide with the heap.
+ */
+_main_stack_size = 0x00000200;                     /* main stack = 512Bytes */
+_main_stack_top  = 0x20000000 + _main_stack_size;
+ASSERT(_main_stack_size % 8 == 0, "MAIN stack size error");
+
+/* Mapping the heap into the bottom 128KB of the RAM */
+_heap_end = 0x20020000;
+/* Mapping the process pool into the upper 136KB of the RAM */
+_process_pool_start = _heap_end;
+_process_pool_end = 0x20042000;                    /* end of available ram  */
+
+ENTRY(_ZN6miosix13Reset_HandlerEv)
+
+/* specify the memory areas  */
+MEMORY
+{
+    flash(rx) : ORIGIN = 0x10000000, LENGTH = 2048k
+    /*
+     * Note, the ram starts at 0x20000000 but it is necessary to add the size
+     * of the main stack, so it is 0x20000200.
+     */
+    ram(wx)   : ORIGIN = 0x20000000+_main_stack_size, LENGTH = 128k-_main_stack_size
+}
+
+/* now define the output sections  */
+SECTIONS
+{
+    . = 0;
+    
+    /* .text section: code goes to flash */
+    .text :
+    {
+        /* Stage 2 bootloader must be at the base address of flash */
+        KEEP(*(.boot2))
+        /* Interrupt vectors go at address 0x100 */
+        KEEP(*(.isr_vector))
+        
+        *(.text)
+        *(.text.*)
+        *(.gnu.linkonce.t.*)
+        /* these sections for thumb interwork? */
+        *(.glue_7)
+        *(.glue_7t)
+        /* these sections for C++? */
+        *(.gcc_except_table)
+        *(.gcc_except_table.*)
+        *(.ARM.extab*)
+        *(.gnu.linkonce.armextab.*)
+
+        . = ALIGN(4);
+        /* .rodata: constant data */
+        *(.rodata)
+        *(.rodata.*)
+        *(.gnu.linkonce.r.*)
+
+        /* C++ Static constructors/destructors (eabi) */
+        . = ALIGN(4);
+        KEEP(*(.init))
+        
+        . = ALIGN(4);
+        __miosix_init_array_start = .;
+        KEEP (*(SORT(.miosix_init_array.*)))
+        KEEP (*(.miosix_init_array))
+        __miosix_init_array_end = .;
+
+        . = ALIGN(4);
+        __preinit_array_start = .;
+        KEEP (*(.preinit_array))
+        __preinit_array_end = .;
+
+        . = ALIGN(4);
+        __init_array_start = .;
+        KEEP (*(SORT(.init_array.*)))
+        KEEP (*(.init_array))
+        __init_array_end = .;
+
+        . = ALIGN(4);
+        KEEP(*(.fini))
+
+        . = ALIGN(4);
+        __fini_array_start = .;
+        KEEP (*(.fini_array))
+        KEEP (*(SORT(.fini_array.*)))
+        __fini_array_end = .;
+
+        /* C++ Static constructors/destructors (elf)  */
+        . = ALIGN(4);
+        _ctor_start = .;
+        KEEP (*crtbegin.o(.ctors))
+        KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
+        KEEP (*(SORT(.ctors.*)))
+        KEEP (*crtend.o(.ctors))
+       _ctor_end = .;
+
+        . = ALIGN(4);
+        KEEP (*crtbegin.o(.dtors))
+        KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
+        KEEP (*(SORT(.dtors.*)))
+        KEEP (*crtend.o(.dtors))
+    } > flash
+
+    /* .ARM.exidx is sorted, so has to go in its own output section.  */
+    __exidx_start = .;
+    .ARM.exidx :
+    {
+        *(.ARM.exidx* .gnu.linkonce.armexidx.*)
+    } > flash
+    __exidx_end = .;
+
+	/* .data section: global variables go to ram, but also store a copy to
+       flash to initialize them */
+    .data : ALIGN(8)
+    {
+        _data = .;
+        *(.data)
+        *(.data.*)
+        *(.gnu.linkonce.d.*)
+        . = ALIGN(8);
+        _edata = .;
+    } > ram AT > flash
+    _etext = LOADADDR(.data);
+
+    /* .bss section: uninitialized global variables go to ram */
+    _bss_start = .;
+    .bss :
+    {
+        *(.bss)
+        *(.bss.*)
+        *(.gnu.linkonce.b.*)
+        . = ALIGN(8);
+    } > ram
+    _bss_end = .;
+
+    _end = .;
+    PROVIDE(end = .);
+}
diff --git a/miosix/arch/cortexM0plus_rp2040/rp2040_raspberry_pi_pico/rp2040_2M+264k_rom.ld b/miosix/arch/cortexM0plus_rp2040/rp2040_raspberry_pi_pico/rp2040_2M+264k_rom.ld
index 84018e2a..c3be498e 100644
--- a/miosix/arch/cortexM0plus_rp2040/rp2040_raspberry_pi_pico/rp2040_2M+264k_rom.ld
+++ b/miosix/arch/cortexM0plus_rp2040/rp2040_raspberry_pi_pico/rp2040_2M+264k_rom.ld
@@ -49,7 +49,7 @@ MEMORY
      * Note, the ram starts at 0x20000000 but it is necessary to add the size
      * of the main stack, so it is 0x20000200.
      */
-    ram(rwx)  : ORIGIN = 0x20000000+_main_stack_size, LENGTH = 264k-_main_stack_size
+    ram(wx)   : ORIGIN = 0x20000000+_main_stack_size, LENGTH = 264k-_main_stack_size
 }
 
 /* now define the output sections  */
diff --git a/miosix/config/Makefile.inc b/miosix/config/Makefile.inc
index 6ebf760f..c10843b4 100644
--- a/miosix/config/Makefile.inc
+++ b/miosix/config/Makefile.inc
@@ -812,6 +812,14 @@ ifeq ($(OPT_BOARD),rp2040_raspberry_pi_pico)
     #BOARD_VARIANT := BOARD_VARIANT_PICO
     BOARD_VARIANT := BOARD_VARIANT_PICO_W
 
+    ## Linker script type, there are two options
+    ## 1) Code in FLASH, stack + heap in internal RAM (file *_rom.ld)
+    ## 2) Same as 1) but space has been reserved for a process pool, allowing
+    ##    to configure the kernel with "#define WITH_PROCESSES"
+    LINKER_SCRIPT_PATH := arch/cortexM0plus_rp2040/rp2040_raspberry_pi_pico/
+    LINKER_SCRIPT := $(LINKER_SCRIPT_PATH)rp2040_2M+264k_rom.ld
+    #LINKER_SCRIPT := $(LINKER_SCRIPT_PATH)rp2040_2M+264k_processes.ld
+
 endif
 
 ##---------------------------------------------------------------------------
@@ -3325,7 +3333,7 @@ else ifeq ($(ARCH),cortexM0plus_rp2040)
         BOARD_INC := arch/cortexM0plus_rp2040/rp2040_raspberry_pi_pico
 
         ## Select linker script
-        LINKER_SCRIPT := $(BOARD_INC)/rp2040_2M+264k_rom.ld
+        #LINKER_SCRIPT := already selected in board options
 
         ## Select architecture specific files
         ## These are the files in arch/<arch name>/<board name>
@@ -3352,8 +3360,8 @@ else ifeq ($(ARCH),cortexM0plus_rp2040)
 
     ## Select compiler
     PREFIX := arm-miosix-eabi-
-    ## This architecture does not support processes
-    #POSTLD :=
+    ## This architecture supports processes
+    POSTLD := mx-postlinker
 
     CPU_INC := arch/cpu/armv6m
     ## Select appropriate compiler flags for both ASM/C/C++/linker
diff --git a/miosix/libsyscalls/crt1.cpp b/miosix/libsyscalls/crt1.cpp
index 6bcbdfea..c39f5164 100644
--- a/miosix/libsyscalls/crt1.cpp
+++ b/miosix/libsyscalls/crt1.cpp
@@ -432,6 +432,8 @@ pid_t wait(int *status)
     return waitpid(-1,status,0);
 }
 
+#if __CORTEX_M != 0
+
 static int __LDREXW(volatile int *addr)
 {
     int result;
@@ -461,6 +463,20 @@ static int atomicCompareAndSwap(volatile int *p, int prev, int next)
     return result;
 }
 
+#else //__CORTEX_M != 0
+
+inline int atomicCompareAndSwap(volatile int *p, int prev, int next)
+{
+    //FIXME: not atomic at all, we can't even disable interrupts in a process
+    //we'll need a syscall to let the kernel do this operation atomically
+    int result = *p;
+    if(*p == prev) *p = next;
+    asm volatile("":::"memory");
+    return result;
+}
+
+#endif //__CORTEX_M != 0
+
 static void enterCriticalSection(pthread_mutex_t *m)
 {
     //TODO: when we add the flag in the mutex stop using the global flag
-- 
GitLab