diff --git a/arm/cortexm.py b/arm/cortexm.py
index 5a709e9d..73288952 100644
--- a/arm/cortexm.py
+++ b/arm/cortexm.py
@@ -64,12 +64,13 @@ def has_double_precision_fpu(self):
def has_small_memory(self):
return True
- def __init__(self):
+ def __init__(self, use_armv6m_atomics=True):
super(ArmV6MTarget, self).__init__()
- self.add_gnat_sources(
- 'src/s-bbarat.ads',
- 'src/s-bbarat.adb')
+ if use_armv6m_atomics:
+ self.add_gnat_sources(
+ 'src/s-bbarat.ads',
+ 'src/s-bbarat.adb')
class ArmV7MTarget(ArmV6MTarget):
@@ -1157,6 +1158,9 @@ def compiler_switches(self):
def system_ads(self):
return {'light': 'system-xi-arm.ads'}
+ def __init__(self, use_armv6m_atomics=True):
+ super(CortexM0, self).__init__(use_armv6m_atomics)
+
class CortexM0P(CortexM0):
@property
@@ -1169,6 +1173,9 @@ def compiler_switches(self):
return ('-mlittle-endian', '-mthumb', '-mfloat-abi=soft',
'-mcpu=cortex-m0plus')
+ def __init__(self, use_armv6m_atomics=True):
+ super(CortexM0P, self).__init__(use_armv6m_atomics)
+
class CortexM1(ArmV6MTarget):
@property
@@ -1418,7 +1425,10 @@ def system_ads(self):
def __init__(self, smp):
self.smp = smp
- super(RP2040, self).__init__()
+ # Don't use the default System.BB.Armv6m_Atomic package as it's not
+ # safe for the SMP runtime. We use a alternative implementation
+ # that uses the RP2040 hardware spinlocks.
+ super(RP2040, self).__init__(use_armv6m_atomics=False)
smp_template_values = {
# The SMP runtime uses the TIMER for task delays, which runs from
@@ -1479,6 +1489,8 @@ def __init__(self, smp):
'arm/rpi/rp2040/svd/i-rp2040-watchdog.ads',
'arm/rpi/rp2040/svd/i-rp2040-xosc.ads',
'arm/rpi/rp2040/s-bbmcpa.ads.tmpl',
+ 'arm/rpi/rp2040/s-bbrpat.ads',
+ 'arm/rpi/rp2040/s-bbrpat.adb',
'arm/rpi/rp2040/start-rom.S.tmpl',
'arm/rpi/rp2040/s-bootro.ads',
'arm/rpi/rp2040/s-bootro.adb',
diff --git a/arm/rpi/rp2040/README b/arm/rpi/rp2040/README
index dbf39d12..cc9bcfb7 100644
--- a/arm/rpi/rp2040/README
+++ b/arm/rpi/rp2040/README
@@ -1,8 +1,8 @@
ARM RP2040 Runtimes
===================
-* Ravenscar-SFP
-* Ravenscar-Full
+* light-tasking
+* embedded
Targets Supported
-----------------
@@ -86,7 +86,6 @@ in the multiprocessor runtime:
end Example;
-
Resources Used
--------------
@@ -99,7 +98,7 @@ trigger a HardFault on the processor that uses it.
Multiprocessor Runtimes
,,,,,,,,,,,,,,,,,,,,,,,
-The Ravenscar runtime libraries on the multiprocessor runtime configuration
+The tasking runtime libraries on the multiprocessor runtime configuration
use the TIMER ALARM_3 interrupt to implement Ada semantics for time, i.e.
delay statements and package Ada.Real_Time. The ALARM_3 interrupt handler
runs at the highest priority. This implementation uses a tick-less approach
@@ -107,10 +106,14 @@ to configure the alarm interrupt to trigger exactly at the alarm time,
thereby avoiding most "useless" tick interrupts. See procedure Set_Alarm in
package body System.BB.Board_Support (gnarl/s-bbbosu.adb).
+The runtime additionally uses SPINLOCK31 to implement the GCC atomic built-in
+functions in a way that ensures atomicity between both cores. See the package
+System.BB.RP2040_Atomics (gnat/s-bbrpat.adb).
+
Single-processor Runtimes
,,,,,,,,,,,,,,,,,,,,,,,,,
-The Ravenscar runtime libraries on the single processor runtime configuration
+The tasking runtime libraries on the single processor runtime configuration
use the SysTick interrupt to implement Ada semantics for time, i.e., delay
statements and package Ada.Real_Time. The SysTick interrupt handler runs at
highest priority. See procedure Sys_Tick_Handler in package body
diff --git a/arm/rpi/rp2040/s-bbrpat.adb b/arm/rpi/rp2040/s-bbrpat.adb
new file mode 100644
index 00000000..48865b97
--- /dev/null
+++ b/arm/rpi/rp2040/s-bbrpat.adb
@@ -0,0 +1,199 @@
+------------------------------------------------------------------------------
+-- --
+-- GNAT RUN-TIME COMPONENTS --
+-- --
+-- Copyright (C) AdaCore and other contributors, 2022 --
+-- See https://github.com/AdaCore/bb-runtimes/graphs/contributors --
+-- for more information --
+-- --
+-- GNAT is free software; you can redistribute it and/or modify it under --
+-- terms of the GNU General Public License as published by the Free Soft- --
+-- ware Foundation; either version 3, or (at your option) any later ver- --
+-- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
+-- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
+-- or FITNESS FOR A PARTICULAR PURPOSE. --
+-- --
+-- As a special exception under Section 7 of GPL version 3, you are granted --
+-- additional permissions described in the GCC Runtime Library Exception, --
+-- version 3.1, as published by the Free Software Foundation. --
+-- --
+-- You should have received a copy of the GNU General Public License and --
+-- a copy of the GCC Runtime Library Exception along with this program; --
+-- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
+-- . --
+-- --
+-- GNAT was originally developed by the GNAT team at New York University. --
+-- Extensive contributions were provided by Ada Core Technologies Inc. --
+-- --
+------------------------------------------------------------------------------
+
+with System.Machine_Code; use System.Machine_Code;
+with System.BB.Parameters;
+with Interfaces; use Interfaces;
+with Interfaces.RP2040.SIO; use Interfaces.RP2040.SIO;
+
+package body System.BB.RP2040_Atomic is
+
+ -------------
+ -- PRIMASK --
+ -------------
+
+ function PRIMASK return Unsigned_32 is
+ R : Unsigned_32;
+ begin
+ Asm ("mrs %0, PRIMASK", Outputs => Unsigned_32'Asm_Output ("=r", R),
+ Volatile => True);
+ return R;
+ end PRIMASK;
+
+ ------------------------
+ -- Interrupt_Disabled --
+ ------------------------
+
+ function Interrupt_Disabled return Boolean
+ is ((PRIMASK and 1) /= 0);
+
+ ------------------------
+ -- Disable_Interrupts --
+ ------------------------
+
+ procedure Disable_Interrupts is
+ begin
+ Asm ("cpsid i" & ASCII.CR & ASCII.LF
+ & "dsb" & ASCII.CR & ASCII.LF
+ & "isb",
+ Clobber => "memory",
+ Volatile => True);
+ end Disable_Interrupts;
+
+ -----------------------
+ -- Enable_Interrupts --
+ -----------------------
+
+ procedure Enable_Interrupts is
+ begin
+ Asm ("cpsie i" & ASCII.CR & ASCII.LF
+ & "dsb" & ASCII.CR & ASCII.LF
+ & "isb",
+ Clobber => "memory",
+ Volatile => True);
+ end Enable_Interrupts;
+
+ -------------------
+ -- Spinlock_Lock --
+ -------------------
+
+ procedure Spinlock_Lock is
+ use type Interfaces.RP2040.UInt32;
+ begin
+ -- Reads attempt to claim the lock.
+ -- Read value is nonzero if the lock was successfully claimed,
+ -- or zero if the lock had already been claimed by a previous read.
+ loop
+ exit when SIO_Periph.SPINLOCK31 /= 0;
+ end loop;
+ end Spinlock_Lock;
+
+ ---------------------
+ -- Spinlock_Unlock --
+ ---------------------
+
+ procedure Spinlock_Unlock is
+ begin
+ -- Write any value to release the lock
+ SIO_Periph.SPINLOCK31 := 0;
+ end Spinlock_Unlock;
+
+ --------------------
+ -- Atomic_Wrapper --
+ --------------------
+
+ procedure Atomic_Wrapper is
+ Already_Disabled : constant Boolean := Interrupt_Disabled;
+ -- Make sure not to change the status of interrupt control by checking
+ -- if they are enabled when entering the function.
+ begin
+
+ if not Already_Disabled then
+ Disable_Interrupts;
+ end if;
+
+ if System.BB.Parameters.Multiprocessor then
+ Spinlock_Lock;
+ end if;
+
+ Wrapped_Proc;
+
+ if System.BB.Parameters.Multiprocessor then
+ Spinlock_Unlock;
+ end if;
+
+ -- If the interrupts were disabled when entering this function, we do
+ -- not want enable them.
+ if not Already_Disabled then
+ Enable_Interrupts;
+ end if;
+ end Atomic_Wrapper;
+
+ ----------------------------
+ -- Sync_Lock_Test_And_Set --
+ ----------------------------
+
+ function Sync_Lock_Test_And_Set (Addr : System.Address;
+ Value : T)
+ return T
+ is
+ Data : T with Address => Addr;
+ Ret : T;
+
+ procedure Inner
+ with Inline_Always;
+
+ procedure Inner
+ is
+ begin
+ Ret := Data;
+ Data := Value;
+ end Inner;
+
+ procedure Atomic_Action is new Atomic_Wrapper (Inner);
+
+ begin
+ Atomic_Action;
+ return Ret;
+ end Sync_Lock_Test_And_Set;
+
+ --------------------------------
+ -- Sync_Bool_Compare_And_Swap --
+ --------------------------------
+
+ function Sync_Bool_Compare_And_Swap (Addr : System.Address;
+ Old_Value : T;
+ New_Value : T)
+ return Interfaces.C.char
+ is
+ Data : T with Address => Addr;
+ Ret : Interfaces.C.char;
+
+ procedure Inner
+ with Inline_Always;
+
+ procedure Inner
+ is
+ begin
+ if Data = Old_Value then
+ Data := New_Value;
+ Ret := Interfaces.C.char'Succ (Interfaces.C.nul); -- True
+ else
+ Ret := Interfaces.C.nul; -- False
+ end if;
+ end Inner;
+
+ procedure Atomic_Action is new Atomic_Wrapper (Inner);
+
+ begin
+ Atomic_Action;
+ return Ret;
+ end Sync_Bool_Compare_And_Swap;
+
+end System.BB.RP2040_Atomic;
diff --git a/arm/rpi/rp2040/s-bbrpat.ads b/arm/rpi/rp2040/s-bbrpat.ads
new file mode 100644
index 00000000..6a89b767
--- /dev/null
+++ b/arm/rpi/rp2040/s-bbrpat.ads
@@ -0,0 +1,102 @@
+------------------------------------------------------------------------------
+-- --
+-- GNAT RUN-TIME COMPONENTS --
+-- --
+-- Copyright (C) AdaCore and other contributors, 2022 --
+-- See https://github.com/AdaCore/bb-runtimes/graphs/contributors --
+-- for more information --
+-- --
+-- GNAT is free software; you can redistribute it and/or modify it under --
+-- terms of the GNU General Public License as published by the Free Soft- --
+-- ware Foundation; either version 3, or (at your option) any later ver- --
+-- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
+-- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
+-- or FITNESS FOR A PARTICULAR PURPOSE. --
+-- --
+-- As a special exception under Section 7 of GPL version 3, you are granted --
+-- additional permissions described in the GCC Runtime Library Exception, --
+-- version 3.1, as published by the Free Software Foundation. --
+-- --
+-- You should have received a copy of the GNU General Public License and --
+-- a copy of the GCC Runtime Library Exception along with this program; --
+-- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
+-- . --
+-- --
+-- GNAT was originally developed by the GNAT team at New York University. --
+-- Extensive contributions were provided by Ada Core Technologies Inc. --
+-- --
+------------------------------------------------------------------------------
+
+-- This package implements some intrinsics not provided by GCC for the armv6m
+-- architecture. An RP2040 hardware spinlock is used to ensure atomicity
+-- across all processors.
+
+with Interfaces.C;
+
+package System.BB.RP2040_Atomic is
+
+ ------------------------------
+ -- __sync_lock_test_and_set --
+ ------------------------------
+
+ generic
+ type T is mod <>;
+ function Sync_Lock_Test_And_Set (Addr : System.Address;
+ Value : T)
+ return T;
+
+ function Sync_Lock_Test_And_Set_1 is
+ new Sync_Lock_Test_And_Set (Interfaces.Unsigned_8);
+ pragma Export (C, Sync_Lock_Test_And_Set_1,
+ "__sync_lock_test_and_set_1");
+
+ ----------------------------------
+ -- __sync_bool_compare_and_swap --
+ ----------------------------------
+
+ generic
+ type T is mod <>;
+ function Sync_Bool_Compare_And_Swap (Addr : System.Address;
+ Old_Value : T;
+ New_Value : T)
+ return Interfaces.C.char;
+
+ function Sync_Bool_Compare_And_Swap_4 is
+ new Sync_Bool_Compare_And_Swap (Interfaces.Unsigned_32);
+ pragma Export (C, Sync_Bool_Compare_And_Swap_4,
+ "__sync_bool_compare_and_swap_4");
+
+private
+
+ function PRIMASK return Interfaces.Unsigned_32
+ with Inline_Always;
+
+ function Interrupt_Disabled return Boolean
+ with Inline_Always;
+
+ procedure Disable_Interrupts
+ with Inline_Always;
+
+ procedure Enable_Interrupts
+ with Inline_Always;
+
+ procedure Spinlock_Lock
+ with Inline_Always,
+ Pre => Interrupt_Disabled;
+ -- Obtain the hardware spinlock.
+ --
+ -- This must be called with interrupts disabled to avoid deadlocks when
+ -- an interrupt occurs and tries to do an atomic operation immediately
+ -- after the spinlock was obtained by a lower priority task/interrupt.
+
+ procedure Spinlock_Unlock
+ with Inline_Always;
+
+ generic
+ with procedure Wrapped_Proc;
+ procedure Atomic_Wrapper
+ with Inline_Always;
+ -- Calls Wrapped_Proc with interrupts disabled
+ -- and the hardware spinlock obtained (locked).
+
+end System.BB.RP2040_Atomic;
\ No newline at end of file
diff --git a/src/s-bbbosu__rp2040.adb b/src/s-bbbosu__rp2040.adb
index bac8c419..94ae85ff 100644
--- a/src/s-bbbosu__rp2040.adb
+++ b/src/s-bbbosu__rp2040.adb
@@ -79,10 +79,17 @@ package body System.BB.Board_Support is
NVIC_ISPR0 : constant Address := NVIC_Base + 16#200#;
+ NVIC_ICPR0 : constant Address := NVIC_Base + 16#280#;
+ -- Writing a bit mask to this register clears the interrupt pending bit
+
NVIC_ISER : Word
with Volatile, Address => NVIC_ISER0;
-- NVIC Interrupt Set-Enable Register (ISER)
+ NVIC_ICPR : Word
+ with Volatile, Address => NVIC_ICPR0;
+ -- NVIC Interrupt Clear Pending Register (ISER)
+
NVIC_ISPR : Word
with Volatile, Address => NVIC_ISPR0;
-- NVIC Interrupt Set Pending Register (ISPR)
@@ -234,6 +241,7 @@ package body System.BB.Board_Support is
-- Clear pending timer interrupt if any
Time.Clear_Alarm_Interrupt;
+ NVIC_ICPR := NVIC_ISER or 2**Alarm_Interrupt_ID;
-- Enable interrupt
NVIC_ISER := NVIC_ISER or 2**Alarm_Interrupt_ID;