From d8e11975a36200a2e6f531009154044245971ac9 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Sat, 20 Jun 2026 16:32:53 -0400 Subject: [PATCH 1/6] Add shared PendSV deferred service dispatcher --- cores/arduino/PendSV.cpp | 116 +++++++++++++++++++++++++++++++++++++++ cores/arduino/PendSV.h | 34 ++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 cores/arduino/PendSV.cpp create mode 100644 cores/arduino/PendSV.h diff --git a/cores/arduino/PendSV.cpp b/cores/arduino/PendSV.cpp new file mode 100644 index 000000000..bb5f32c7b --- /dev/null +++ b/cores/arduino/PendSV.cpp @@ -0,0 +1,116 @@ +#include "PendSV.h" + +#include +#include + +namespace { +PendSV pendSv; +} + +PendSV &PendSV::instance() { + return pendSv; +} + +uint32_t PendSV::enterCritical() { + const uint32_t primask = __get_PRIMASK(); + __disable_irq(); + return primask; +} + +void PendSV::exitCritical(uint32_t primask) { + __set_PRIMASK(primask); +} + +bool PendSV::registerService(uint8_t serviceId, ServiceFn fn, void *context) { + if (serviceId >= kMaxServices || fn == nullptr) + return false; + + const uint32_t primask = enterCritical(); + pendingCount_[serviceId] = 0; + pendingMask_ &= ~(1u << serviceId); + services_[serviceId].fn = fn; + services_[serviceId].context = context; + exitCritical(primask); + + return true; +} + +void PendSV::clearService(uint8_t serviceId) { + if (serviceId >= kMaxServices) + return; + + const uint32_t primask = enterCritical(); + services_[serviceId].fn = nullptr; + services_[serviceId].context = nullptr; + pendingCount_[serviceId] = 0; + pendingMask_ &= ~(1u << serviceId); + exitCritical(primask); +} + +void PendSV::setPending(uint8_t serviceId) { + if (serviceId >= kMaxServices) + return; + + const uint32_t primask = enterCritical(); + uint16_t &pendingCount = pendingCount_[serviceId]; + if (pendingCount < UINT16_MAX) + ++pendingCount; + pendingMask_ |= (1u << serviceId); + exitCritical(primask); + + __DMB(); + SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; +} + +void PendSV::dispatchPending() { + // Bound one PendSV entry so high-rate producers do not monopolize return to + // thread mode. Remaining work re-pends PendSV below. + uint8_t dispatched = 0; + + while (dispatched < kDispatchBudget) { + uint32_t primask = enterCritical(); + const uint32_t pending = pendingMask_; + if (pending == 0) { + exitCritical(primask); + return; + } + + const uint8_t serviceId = static_cast(__builtin_ctz(pending)); + uint16_t &pendingCount = pendingCount_[serviceId]; + + if (pendingCount == 0) { + // Defensive scrub in case mask and count drift out of sync. + pendingMask_ &= ~(1u << serviceId); + exitCritical(primask); + continue; + } + + --pendingCount; + if (pendingCount == 0) + pendingMask_ &= ~(1u << serviceId); + + ServiceEntry entry = services_[serviceId]; + exitCritical(primask); + + // Pending work without a registered service is intentionally dropped. + // Producers are expected to register before calling setPending(), and + // clearService() cancels queued work for that service. + if (entry.fn != nullptr) + entry.fn(serviceId, entry.context); + + ++dispatched; + } + + const uint32_t primask = enterCritical(); + const bool hasRemaining = (pendingMask_ != 0); + exitCritical(primask); + + if (hasRemaining) { + __DMB(); + SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; + } +} + +extern "C" void PendSV_Handler(void) { + PendSV::instance().dispatchPending(); +} diff --git a/cores/arduino/PendSV.h b/cores/arduino/PendSV.h new file mode 100644 index 000000000..ab22a43e6 --- /dev/null +++ b/cores/arduino/PendSV.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include + +class PendSV { + public: + using ServiceFn = void (*)(uint8_t serviceId, void *context); + static constexpr uint8_t kMaxServices = 32; + static constexpr uint8_t kDispatchBudget = 16; + static_assert(kMaxServices <= 32, "PendSV pending mask supports at most 32 services"); + + static PendSV &instance(); + + bool registerService(uint8_t serviceId, ServiceFn fn, void *context = nullptr); + // Cancels queued work that has not started dispatching. If a callback was + // already copied for dispatch, its context must remain valid until it returns. + void clearService(uint8_t serviceId); + void dispatchPending(); + void setPending(uint8_t serviceId); + + private: + static uint32_t enterCritical(); + static void exitCritical(uint32_t primask); + + struct ServiceEntry { + ServiceFn fn = nullptr; + void *context = nullptr; + }; + + std::array services_{}; + std::array pendingCount_{}; + volatile uint32_t pendingMask_ = 0; +}; From 8cfeeaf9001d3fdaa63950d3ff71606a88d0e3ec Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Tue, 23 Jun 2026 22:16:10 -0400 Subject: [PATCH 2/6] Add PendSV peripheral channel map --- cores/arduino/PendSV.h | 87 +++++++++++++++++++++++++++++++- cores/arduino/PendSVChannelMap.h | 57 +++++++++++++++++++++ 2 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 cores/arduino/PendSVChannelMap.h diff --git a/cores/arduino/PendSV.h b/cores/arduino/PendSV.h index ab22a43e6..36771a4df 100644 --- a/cores/arduino/PendSV.h +++ b/cores/arduino/PendSV.h @@ -1,14 +1,99 @@ #pragma once +#include "PendSVChannelMap.h" +#include "sam.h" + #include #include +#if defined(SERCOM0) || defined(SERCOM0_REGS) +#define PENDSV_HAS_SERCOM0 true +#else +#define PENDSV_HAS_SERCOM0 false +#endif +#if defined(SERCOM1) || defined(SERCOM1_REGS) +#define PENDSV_HAS_SERCOM1 true +#else +#define PENDSV_HAS_SERCOM1 false +#endif +#if defined(SERCOM2) || defined(SERCOM2_REGS) +#define PENDSV_HAS_SERCOM2 true +#else +#define PENDSV_HAS_SERCOM2 false +#endif +#if defined(SERCOM3) || defined(SERCOM3_REGS) +#define PENDSV_HAS_SERCOM3 true +#else +#define PENDSV_HAS_SERCOM3 false +#endif +#if defined(SERCOM4) || defined(SERCOM4_REGS) +#define PENDSV_HAS_SERCOM4 true +#else +#define PENDSV_HAS_SERCOM4 false +#endif +#if defined(SERCOM5) || defined(SERCOM5_REGS) +#define PENDSV_HAS_SERCOM5 true +#else +#define PENDSV_HAS_SERCOM5 false +#endif +#if defined(SERCOM6) || defined(SERCOM6_REGS) +#define PENDSV_HAS_SERCOM6 true +#else +#define PENDSV_HAS_SERCOM6 false +#endif +#if defined(SERCOM7) || defined(SERCOM7_REGS) +#define PENDSV_HAS_SERCOM7 true +#else +#define PENDSV_HAS_SERCOM7 false +#endif + +#if defined(USB) || defined(USB_REGS) +#define PENDSV_HAS_USB true +#else +#define PENDSV_HAS_USB false +#endif + +#if defined(GMAC) || defined(GMAC_REGS) +#define PENDSV_HAS_GMAC true +#else +#define PENDSV_HAS_GMAC false +#endif +#if defined(ADC) || defined(ADC_REGS) || defined(ADC0) || defined(ADC0_REGS) +#define PENDSV_HAS_ADC true +#else +#define PENDSV_HAS_ADC false +#endif +#if defined(AES) || defined(AES_REGS) +#define PENDSV_HAS_AES true +#else +#define PENDSV_HAS_AES false +#endif +#if defined(PUKCC) || defined(PUKCC_REGS) || defined(ID_PUKCC) || \ + defined(PUKCC_INSTANCE_ID) || defined(PUKCC_IRQn) +#define PENDSV_HAS_PUKCC true +#else +#define PENDSV_HAS_PUKCC false +#endif +#if defined(TRNG) || defined(TRNG_REGS) +#define PENDSV_HAS_TRNG true +#else +#define PENDSV_HAS_TRNG false +#endif + +using PendSVChannels = PendSVChannelMap< + PENDSV_HAS_SERCOM0, PENDSV_HAS_SERCOM1, PENDSV_HAS_SERCOM2, + PENDSV_HAS_SERCOM3, PENDSV_HAS_SERCOM4, PENDSV_HAS_SERCOM5, + PENDSV_HAS_SERCOM6, PENDSV_HAS_SERCOM7, PENDSV_HAS_USB, PENDSV_HAS_GMAC, + PENDSV_HAS_ADC, PENDSV_HAS_AES, PENDSV_HAS_PUKCC, PENDSV_HAS_TRNG>; + class PendSV { public: using ServiceFn = void (*)(uint8_t serviceId, void *context); - static constexpr uint8_t kMaxServices = 32; + static constexpr uint8_t kMaxServices = PendSVChannels::kMaxServices; static constexpr uint8_t kDispatchBudget = 16; static_assert(kMaxServices <= 32, "PendSV pending mask supports at most 32 services"); + static_assert(PendSVChannels::Count <= kMaxServices, + "PendSV channel allocation exceeds dispatcher capacity"); static PendSV &instance(); diff --git a/cores/arduino/PendSVChannelMap.h b/cores/arduino/PendSVChannelMap.h new file mode 100644 index 000000000..b97b2b2db --- /dev/null +++ b/cores/arduino/PendSVChannelMap.h @@ -0,0 +1,57 @@ +#pragma once + +#include + +template +struct PendSVChannelMap { + static constexpr uint8_t kMaxServices = 32; + static constexpr uint8_t kUnavailable = 0xFF; + +private: + static constexpr uint8_t assign(bool present, uint8_t next) { + return present ? next : kUnavailable; + } + + static constexpr uint8_t advance(bool present, uint8_t next) { + return present ? static_cast(next + 1) : next; + } + + static constexpr uint8_t kBase = 0; + static constexpr uint8_t kAfterSercom0 = advance(HasSercom0, kBase); + static constexpr uint8_t kAfterSercom1 = advance(HasSercom1, kAfterSercom0); + static constexpr uint8_t kAfterSercom2 = advance(HasSercom2, kAfterSercom1); + static constexpr uint8_t kAfterSercom3 = advance(HasSercom3, kAfterSercom2); + static constexpr uint8_t kAfterSercom4 = advance(HasSercom4, kAfterSercom3); + static constexpr uint8_t kAfterSercom5 = advance(HasSercom5, kAfterSercom4); + static constexpr uint8_t kAfterSercom6 = advance(HasSercom6, kAfterSercom5); + static constexpr uint8_t kAfterSercom7 = advance(HasSercom7, kAfterSercom6); + static constexpr uint8_t kAfterUsb = advance(HasUsb, kAfterSercom7); + static constexpr uint8_t kAfterGmac = advance(HasGmac, kAfterUsb); + static constexpr uint8_t kAfterAdc = advance(HasAdc, kAfterGmac); + static constexpr uint8_t kAfterAes = advance(HasAes, kAfterAdc); + static constexpr uint8_t kAfterPukcc = advance(HasPukcc, kAfterAes); + +public: + static constexpr uint8_t Sercom0 = assign(HasSercom0, kBase); + static constexpr uint8_t Sercom1 = assign(HasSercom1, kAfterSercom0); + static constexpr uint8_t Sercom2 = assign(HasSercom2, kAfterSercom1); + static constexpr uint8_t Sercom3 = assign(HasSercom3, kAfterSercom2); + static constexpr uint8_t Sercom4 = assign(HasSercom4, kAfterSercom3); + static constexpr uint8_t Sercom5 = assign(HasSercom5, kAfterSercom4); + static constexpr uint8_t Sercom6 = assign(HasSercom6, kAfterSercom5); + static constexpr uint8_t Sercom7 = assign(HasSercom7, kAfterSercom6); + static constexpr uint8_t Usb = assign(HasUsb, kAfterSercom7); + static constexpr uint8_t Gmac = assign(HasGmac, kAfterUsb); + static constexpr uint8_t Adc = assign(HasAdc, kAfterGmac); + static constexpr uint8_t Aes = assign(HasAes, kAfterAdc); + static constexpr uint8_t Pukcc = assign(HasPukcc, kAfterAes); + static constexpr uint8_t Trng = assign(HasTrng, kAfterPukcc); + static constexpr uint8_t Count = advance(HasTrng, kAfterPukcc); + + static constexpr bool isAvailable(uint8_t channel) { + return channel != kUnavailable; + } +}; From 1a13c8f7b2a89565ac569bf9267b0054ee49d188 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 26 Jun 2026 14:13:01 -0400 Subject: [PATCH 3/6] Add PHY PendSV channel allocation --- cores/arduino/PendSV.h | 5 ++++- cores/arduino/PendSVChannelMap.h | 10 ++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/cores/arduino/PendSV.h b/cores/arduino/PendSV.h index 36771a4df..b1ca59a90 100644 --- a/cores/arduino/PendSV.h +++ b/cores/arduino/PendSV.h @@ -55,8 +55,10 @@ #if defined(GMAC) || defined(GMAC_REGS) #define PENDSV_HAS_GMAC true +#define PENDSV_HAS_PHY true #else #define PENDSV_HAS_GMAC false +#define PENDSV_HAS_PHY false #endif #if defined(ADC) || defined(ADC_REGS) || defined(ADC0) || defined(ADC0_REGS) #define PENDSV_HAS_ADC true @@ -84,7 +86,8 @@ using PendSVChannels = PendSVChannelMap< PENDSV_HAS_SERCOM0, PENDSV_HAS_SERCOM1, PENDSV_HAS_SERCOM2, PENDSV_HAS_SERCOM3, PENDSV_HAS_SERCOM4, PENDSV_HAS_SERCOM5, PENDSV_HAS_SERCOM6, PENDSV_HAS_SERCOM7, PENDSV_HAS_USB, PENDSV_HAS_GMAC, - PENDSV_HAS_ADC, PENDSV_HAS_AES, PENDSV_HAS_PUKCC, PENDSV_HAS_TRNG>; + PENDSV_HAS_PHY, PENDSV_HAS_ADC, PENDSV_HAS_AES, PENDSV_HAS_PUKCC, + PENDSV_HAS_TRNG>; class PendSV { public: diff --git a/cores/arduino/PendSVChannelMap.h b/cores/arduino/PendSVChannelMap.h index b97b2b2db..fcc6864f8 100644 --- a/cores/arduino/PendSVChannelMap.h +++ b/cores/arduino/PendSVChannelMap.h @@ -4,8 +4,8 @@ template + bool HasUsb, bool HasGmac, bool HasPhy, bool HasAdc, bool HasAes, + bool HasPukcc, bool HasTrng> struct PendSVChannelMap { static constexpr uint8_t kMaxServices = 32; static constexpr uint8_t kUnavailable = 0xFF; @@ -30,7 +30,8 @@ struct PendSVChannelMap { static constexpr uint8_t kAfterSercom7 = advance(HasSercom7, kAfterSercom6); static constexpr uint8_t kAfterUsb = advance(HasUsb, kAfterSercom7); static constexpr uint8_t kAfterGmac = advance(HasGmac, kAfterUsb); - static constexpr uint8_t kAfterAdc = advance(HasAdc, kAfterGmac); + static constexpr uint8_t kAfterPhy = advance(HasPhy, kAfterGmac); + static constexpr uint8_t kAfterAdc = advance(HasAdc, kAfterPhy); static constexpr uint8_t kAfterAes = advance(HasAes, kAfterAdc); static constexpr uint8_t kAfterPukcc = advance(HasPukcc, kAfterAes); @@ -45,7 +46,8 @@ struct PendSVChannelMap { static constexpr uint8_t Sercom7 = assign(HasSercom7, kAfterSercom6); static constexpr uint8_t Usb = assign(HasUsb, kAfterSercom7); static constexpr uint8_t Gmac = assign(HasGmac, kAfterUsb); - static constexpr uint8_t Adc = assign(HasAdc, kAfterGmac); + static constexpr uint8_t Phy = assign(HasPhy, kAfterGmac); + static constexpr uint8_t Adc = assign(HasAdc, kAfterPhy); static constexpr uint8_t Aes = assign(HasAes, kAfterAdc); static constexpr uint8_t Pukcc = assign(HasPukcc, kAfterAes); static constexpr uint8_t Trng = assign(HasTrng, kAfterPukcc); From 12eeb029ab57894e976f876f9c4f6eb81314ed6d Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 3 Jul 2026 21:51:43 -0400 Subject: [PATCH 4/6] Add RTC PendSV channel allocation Signed-off-by: Cal Abel --- cores/arduino/PendSV.h | 7 ++++++- cores/arduino/PendSVChannelMap.h | 6 ++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/cores/arduino/PendSV.h b/cores/arduino/PendSV.h index b1ca59a90..6200c8de1 100644 --- a/cores/arduino/PendSV.h +++ b/cores/arduino/PendSV.h @@ -81,13 +81,18 @@ #else #define PENDSV_HAS_TRNG false #endif +#if defined(RTC) || defined(RTC_REGS) +#define PENDSV_HAS_RTC true +#else +#define PENDSV_HAS_RTC false +#endif using PendSVChannels = PendSVChannelMap< PENDSV_HAS_SERCOM0, PENDSV_HAS_SERCOM1, PENDSV_HAS_SERCOM2, PENDSV_HAS_SERCOM3, PENDSV_HAS_SERCOM4, PENDSV_HAS_SERCOM5, PENDSV_HAS_SERCOM6, PENDSV_HAS_SERCOM7, PENDSV_HAS_USB, PENDSV_HAS_GMAC, PENDSV_HAS_PHY, PENDSV_HAS_ADC, PENDSV_HAS_AES, PENDSV_HAS_PUKCC, - PENDSV_HAS_TRNG>; + PENDSV_HAS_TRNG, PENDSV_HAS_RTC>; class PendSV { public: diff --git a/cores/arduino/PendSVChannelMap.h b/cores/arduino/PendSVChannelMap.h index fcc6864f8..441bb92cb 100644 --- a/cores/arduino/PendSVChannelMap.h +++ b/cores/arduino/PendSVChannelMap.h @@ -5,7 +5,7 @@ template + bool HasPukcc, bool HasTrng, bool HasRtc> struct PendSVChannelMap { static constexpr uint8_t kMaxServices = 32; static constexpr uint8_t kUnavailable = 0xFF; @@ -34,6 +34,7 @@ struct PendSVChannelMap { static constexpr uint8_t kAfterAdc = advance(HasAdc, kAfterPhy); static constexpr uint8_t kAfterAes = advance(HasAes, kAfterAdc); static constexpr uint8_t kAfterPukcc = advance(HasPukcc, kAfterAes); + static constexpr uint8_t kAfterTrng = advance(HasTrng, kAfterPukcc); public: static constexpr uint8_t Sercom0 = assign(HasSercom0, kBase); @@ -51,7 +52,8 @@ struct PendSVChannelMap { static constexpr uint8_t Aes = assign(HasAes, kAfterAdc); static constexpr uint8_t Pukcc = assign(HasPukcc, kAfterAes); static constexpr uint8_t Trng = assign(HasTrng, kAfterPukcc); - static constexpr uint8_t Count = advance(HasTrng, kAfterPukcc); + static constexpr uint8_t Rtc = assign(HasRtc, kAfterTrng); + static constexpr uint8_t Count = advance(HasRtc, kAfterTrng); static constexpr bool isAvailable(uint8_t channel) { return channel != kUnavailable; From 58f6e68a326de33741f99128de4cc3da85f44d98 Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Sat, 18 Jul 2026 17:49:46 -0400 Subject: [PATCH 5/6] fix(pendsv): coalesce TinyUSB deferred service --- cores/arduino/PendSV.cpp | 45 ++++++++++++++++++++++++++++++++++++++++ cores/arduino/PendSV.h | 2 ++ 2 files changed, 47 insertions(+) diff --git a/cores/arduino/PendSV.cpp b/cores/arduino/PendSV.cpp index bb5f32c7b..743fa866e 100644 --- a/cores/arduino/PendSV.cpp +++ b/cores/arduino/PendSV.cpp @@ -3,14 +3,46 @@ #include #include +#if defined(USE_TINYUSB) +extern "C" void TinyUSB_Device_Task(void); +#endif + namespace { PendSV pendSv; + +#if defined(USE_TINYUSB) +void tinyUsbDeviceTaskService(uint8_t serviceId, void *context) { + (void)serviceId; + (void)context; + // One invocation drains TinyUSB's queued controller events under OPT_OS_NONE. + TinyUSB_Device_Task(); +} +#endif } PendSV &PendSV::instance() { return pendSv; } +#if defined(USE_TINYUSB) +extern "C" void tud_event_hook_cb(uint8_t rhport, uint32_t eventId, bool inIsr) { + (void)rhport; + (void)eventId; + (void)inIsr; + // TinyUSB owns the event queue. PendSV is only its deferred wake signal, so + // multiple controller events before dispatch require only one service call. + PendSV::instance().setPendingOnce(PendSVChannels::Usb); +} +#endif + +bool PendSV::initializeCoreServices() { +#if defined(USE_TINYUSB) + return instance().registerService(PendSVChannels::Usb, tinyUsbDeviceTaskService, nullptr); +#else + return true; +#endif +} + uint32_t PendSV::enterCritical() { const uint32_t primask = __get_PRIMASK(); __disable_irq(); @@ -62,6 +94,19 @@ void PendSV::setPending(uint8_t serviceId) { SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; } +void PendSV::setPendingOnce(uint8_t serviceId) { + if (serviceId >= kMaxServices) + return; + + const uint32_t primask = enterCritical(); + pendingCount_[serviceId] = 1; + pendingMask_ |= (1u << serviceId); + exitCritical(primask); + + __DMB(); + SCB->ICSR = SCB_ICSR_PENDSVSET_Msk; +} + void PendSV::dispatchPending() { // Bound one PendSV entry so high-rate producers do not monopolize return to // thread mode. Remaining work re-pends PendSV below. diff --git a/cores/arduino/PendSV.h b/cores/arduino/PendSV.h index 6200c8de1..68090a252 100644 --- a/cores/arduino/PendSV.h +++ b/cores/arduino/PendSV.h @@ -104,6 +104,7 @@ class PendSV { "PendSV channel allocation exceeds dispatcher capacity"); static PendSV &instance(); + static bool initializeCoreServices(); bool registerService(uint8_t serviceId, ServiceFn fn, void *context = nullptr); // Cancels queued work that has not started dispatching. If a callback was @@ -111,6 +112,7 @@ class PendSV { void clearService(uint8_t serviceId); void dispatchPending(); void setPending(uint8_t serviceId); + void setPendingOnce(uint8_t serviceId); private: static uint32_t enterCritical(); From dc59abb8da1f7c18de2a40f54b66e35374d30a9a Mon Sep 17 00:00:00 2001 From: Cal Abel Date: Fri, 24 Jul 2026 15:11:13 -0400 Subject: [PATCH 6/6] refactor(pendsv): map services across SAME5x peripherals --- cores/arduino/PendSV.cpp | 1 + cores/arduino/PendSV.h | 212 +++++++++++++++++++++++++-------------- 2 files changed, 137 insertions(+), 76 deletions(-) diff --git a/cores/arduino/PendSV.cpp b/cores/arduino/PendSV.cpp index 743fa866e..6eb84af90 100644 --- a/cores/arduino/PendSV.cpp +++ b/cores/arduino/PendSV.cpp @@ -36,6 +36,7 @@ extern "C" void tud_event_hook_cb(uint8_t rhport, uint32_t eventId, bool inIsr) #endif bool PendSV::initializeCoreServices() { + NVIC_SetPriority(PendSV_IRQn, (1 << __NVIC_PRIO_BITS) - 1); #if defined(USE_TINYUSB) return instance().registerService(PendSVChannels::Usb, tinyUsbDeviceTaskService, nullptr); #else diff --git a/cores/arduino/PendSV.h b/cores/arduino/PendSV.h index 68090a252..98222b2c7 100644 --- a/cores/arduino/PendSV.h +++ b/cores/arduino/PendSV.h @@ -1,129 +1,189 @@ #pragma once -#include "PendSVChannelMap.h" -#include "sam.h" +#include #include #include #if defined(SERCOM0) || defined(SERCOM0_REGS) -#define PENDSV_HAS_SERCOM0 true +#define SIMIO_PENDSV_HAS_SERCOM0 true #else -#define PENDSV_HAS_SERCOM0 false +#define SIMIO_PENDSV_HAS_SERCOM0 false #endif #if defined(SERCOM1) || defined(SERCOM1_REGS) -#define PENDSV_HAS_SERCOM1 true +#define SIMIO_PENDSV_HAS_SERCOM1 true #else -#define PENDSV_HAS_SERCOM1 false +#define SIMIO_PENDSV_HAS_SERCOM1 false #endif #if defined(SERCOM2) || defined(SERCOM2_REGS) -#define PENDSV_HAS_SERCOM2 true +#define SIMIO_PENDSV_HAS_SERCOM2 true #else -#define PENDSV_HAS_SERCOM2 false +#define SIMIO_PENDSV_HAS_SERCOM2 false #endif #if defined(SERCOM3) || defined(SERCOM3_REGS) -#define PENDSV_HAS_SERCOM3 true +#define SIMIO_PENDSV_HAS_SERCOM3 true #else -#define PENDSV_HAS_SERCOM3 false +#define SIMIO_PENDSV_HAS_SERCOM3 false #endif #if defined(SERCOM4) || defined(SERCOM4_REGS) -#define PENDSV_HAS_SERCOM4 true +#define SIMIO_PENDSV_HAS_SERCOM4 true #else -#define PENDSV_HAS_SERCOM4 false +#define SIMIO_PENDSV_HAS_SERCOM4 false #endif #if defined(SERCOM5) || defined(SERCOM5_REGS) -#define PENDSV_HAS_SERCOM5 true +#define SIMIO_PENDSV_HAS_SERCOM5 true #else -#define PENDSV_HAS_SERCOM5 false +#define SIMIO_PENDSV_HAS_SERCOM5 false #endif #if defined(SERCOM6) || defined(SERCOM6_REGS) -#define PENDSV_HAS_SERCOM6 true +#define SIMIO_PENDSV_HAS_SERCOM6 true #else -#define PENDSV_HAS_SERCOM6 false +#define SIMIO_PENDSV_HAS_SERCOM6 false #endif #if defined(SERCOM7) || defined(SERCOM7_REGS) -#define PENDSV_HAS_SERCOM7 true +#define SIMIO_PENDSV_HAS_SERCOM7 true #else -#define PENDSV_HAS_SERCOM7 false +#define SIMIO_PENDSV_HAS_SERCOM7 false #endif - -#if defined(USB) || defined(USB_REGS) -#define PENDSV_HAS_USB true +#if defined(DMAC) || defined(DMAC_REGS) +#define SIMIO_PENDSV_HAS_DMAC true #else -#define PENDSV_HAS_USB false +#define SIMIO_PENDSV_HAS_DMAC false #endif - -#if defined(GMAC) || defined(GMAC_REGS) -#define PENDSV_HAS_GMAC true -#define PENDSV_HAS_PHY true -#else -#define PENDSV_HAS_GMAC false -#define PENDSV_HAS_PHY false -#endif -#if defined(ADC) || defined(ADC_REGS) || defined(ADC0) || defined(ADC0_REGS) -#define PENDSV_HAS_ADC true +#if defined(AES) || defined(AES_REGS) +#define SIMIO_PENDSV_HAS_AES true #else -#define PENDSV_HAS_ADC false +#define SIMIO_PENDSV_HAS_AES false #endif -#if defined(AES) || defined(AES_REGS) -#define PENDSV_HAS_AES true +#if defined(ICM) || defined(ICM_REGS) +#define SIMIO_PENDSV_HAS_ICM true #else -#define PENDSV_HAS_AES false +#define SIMIO_PENDSV_HAS_ICM false #endif -#if defined(PUKCC) || defined(PUKCC_REGS) || defined(ID_PUKCC) || \ - defined(PUKCC_INSTANCE_ID) || defined(PUKCC_IRQn) -#define PENDSV_HAS_PUKCC true +#if defined(PUKCC) || defined(PUKCC_REGS) || defined(ID_PUKCC) || defined(PUKCC_INSTANCE_ID) || \ + defined(PUKCC_IRQn) +#define SIMIO_PENDSV_HAS_PUKCC true #else -#define PENDSV_HAS_PUKCC false +#define SIMIO_PENDSV_HAS_PUKCC false #endif #if defined(TRNG) || defined(TRNG_REGS) -#define PENDSV_HAS_TRNG true +#define SIMIO_PENDSV_HAS_TRNG true #else -#define PENDSV_HAS_TRNG false +#define SIMIO_PENDSV_HAS_TRNG false #endif -#if defined(RTC) || defined(RTC_REGS) -#define PENDSV_HAS_RTC true +#if defined(GMAC) || defined(GMAC_REGS) +#define SIMIO_PENDSV_HAS_GMAC true +#define SIMIO_PENDSV_HAS_PHY true #else -#define PENDSV_HAS_RTC false +#define SIMIO_PENDSV_HAS_GMAC false +#define SIMIO_PENDSV_HAS_PHY false #endif +template +struct PendSVChannelMap { + static constexpr uint8_t kUnavailable = 0xFF; + +private: + static constexpr uint8_t assign(bool present, uint8_t next) { + return present ? next : kUnavailable; + } + static constexpr uint8_t advance(bool present, uint8_t next) { + return present ? static_cast(next + 1) : next; + } + + static constexpr uint8_t kBase = 0; + static constexpr uint8_t kAfterSercom0 = advance(HasSercom0, kBase); + static constexpr uint8_t kAfterSercom1 = advance(HasSercom1, kAfterSercom0); + static constexpr uint8_t kAfterSercom2 = advance(HasSercom2, kAfterSercom1); + static constexpr uint8_t kAfterSercom3 = advance(HasSercom3, kAfterSercom2); + static constexpr uint8_t kAfterSercom4 = advance(HasSercom4, kAfterSercom3); + static constexpr uint8_t kAfterSercom5 = advance(HasSercom5, kAfterSercom4); + static constexpr uint8_t kAfterSercom6 = advance(HasSercom6, kAfterSercom5); + static constexpr uint8_t kAfterSercom7 = advance(HasSercom7, kAfterSercom6); + static constexpr uint8_t kAfterRtc = advance(true, kAfterSercom7); + static constexpr uint8_t kAfterUsb = advance(true, kAfterRtc); + static constexpr uint8_t kAfterAdc = advance(true, kAfterUsb); + static constexpr uint8_t kAfterDmac = advance(HasDmac, kAfterAdc); + static constexpr uint8_t kAfterAes = advance(HasAes, kAfterDmac); + static constexpr uint8_t kAfterIcm = advance(HasIcm, kAfterAes); + static constexpr uint8_t kAfterPukcc = advance(HasPukcc, kAfterIcm); + static constexpr uint8_t kAfterTrng = advance(HasTrng, kAfterPukcc); + static constexpr uint8_t kAfterGmac = advance(HasGmac, kAfterTrng); + static constexpr uint8_t kAfterPhy = advance(HasPhy, kAfterGmac); + static constexpr uint8_t kAfterUsbProtocol = advance(true, kAfterPhy); + static constexpr uint8_t kAfterDiscreteInput = advance(true, kAfterUsbProtocol); + +public: + static constexpr uint8_t Sercom0 = assign(HasSercom0, kBase); + static constexpr uint8_t Sercom1 = assign(HasSercom1, kAfterSercom0); + static constexpr uint8_t Sercom2 = assign(HasSercom2, kAfterSercom1); + static constexpr uint8_t Sercom3 = assign(HasSercom3, kAfterSercom2); + static constexpr uint8_t Sercom4 = assign(HasSercom4, kAfterSercom3); + static constexpr uint8_t Sercom5 = assign(HasSercom5, kAfterSercom4); + static constexpr uint8_t Sercom6 = assign(HasSercom6, kAfterSercom5); + static constexpr uint8_t Sercom7 = assign(HasSercom7, kAfterSercom6); + static constexpr uint8_t Rtc = kAfterSercom7; + static constexpr uint8_t Usb = kAfterRtc; + static constexpr uint8_t Adc = kAfterUsb; + static constexpr uint8_t Dmac = assign(HasDmac, kAfterAdc); + static constexpr uint8_t Aes = assign(HasAes, kAfterDmac); + static constexpr uint8_t Icm = assign(HasIcm, kAfterAes); + static constexpr uint8_t Pukcc = assign(HasPukcc, kAfterIcm); + static constexpr uint8_t Trng = assign(HasTrng, kAfterPukcc); + static constexpr uint8_t Gmac = assign(HasGmac, kAfterTrng); + static constexpr uint8_t Phy = assign(HasPhy, kAfterGmac); + static constexpr uint8_t UsbProtocol = kAfterPhy; + static constexpr uint8_t DiscreteInput = kAfterUsbProtocol; + static constexpr uint8_t Count = kAfterDiscreteInput; + + static constexpr bool isAvailable(uint8_t channel) { return channel != kUnavailable; } + static constexpr uint8_t sercom(uint8_t index) { + return index == 0 ? Sercom0 : index == 1 ? Sercom1 : index == 2 ? Sercom2 + : index == 3 ? Sercom3 : index == 4 ? Sercom4 : index == 5 ? Sercom5 + : index == 6 ? Sercom6 : index == 7 ? Sercom7 : kUnavailable; + } + static constexpr uint8_t sercomIndex(uint8_t channel) { + return channel == Sercom0 ? 0 : channel == Sercom1 ? 1 : channel == Sercom2 ? 2 + : channel == Sercom3 ? 3 : channel == Sercom4 ? 4 : channel == Sercom5 ? 5 + : channel == Sercom6 ? 6 : channel == Sercom7 ? 7 : kUnavailable; + } +}; + using PendSVChannels = PendSVChannelMap< - PENDSV_HAS_SERCOM0, PENDSV_HAS_SERCOM1, PENDSV_HAS_SERCOM2, - PENDSV_HAS_SERCOM3, PENDSV_HAS_SERCOM4, PENDSV_HAS_SERCOM5, - PENDSV_HAS_SERCOM6, PENDSV_HAS_SERCOM7, PENDSV_HAS_USB, PENDSV_HAS_GMAC, - PENDSV_HAS_PHY, PENDSV_HAS_ADC, PENDSV_HAS_AES, PENDSV_HAS_PUKCC, - PENDSV_HAS_TRNG, PENDSV_HAS_RTC>; + SIMIO_PENDSV_HAS_SERCOM0, SIMIO_PENDSV_HAS_SERCOM1, SIMIO_PENDSV_HAS_SERCOM2, + SIMIO_PENDSV_HAS_SERCOM3, SIMIO_PENDSV_HAS_SERCOM4, SIMIO_PENDSV_HAS_SERCOM5, + SIMIO_PENDSV_HAS_SERCOM6, SIMIO_PENDSV_HAS_SERCOM7, SIMIO_PENDSV_HAS_DMAC, + SIMIO_PENDSV_HAS_AES, SIMIO_PENDSV_HAS_ICM, SIMIO_PENDSV_HAS_PUKCC, + SIMIO_PENDSV_HAS_TRNG, SIMIO_PENDSV_HAS_GMAC, SIMIO_PENDSV_HAS_PHY>; class PendSV { - public: - using ServiceFn = void (*)(uint8_t serviceId, void *context); - static constexpr uint8_t kMaxServices = PendSVChannels::kMaxServices; - static constexpr uint8_t kDispatchBudget = 16; - static_assert(kMaxServices <= 32, "PendSV pending mask supports at most 32 services"); - static_assert(PendSVChannels::Count <= kMaxServices, - "PendSV channel allocation exceeds dispatcher capacity"); +public: + using ServiceFn = void (*)(uint8_t serviceId, void *context); + static constexpr uint8_t kMaxServices = PendSVChannels::Count; + static constexpr uint8_t kDispatchBudget = 16; + static_assert(kMaxServices <= 32, "PendSV pending mask supports at most 32 services"); - static PendSV &instance(); - static bool initializeCoreServices(); + static PendSV &instance(); + static bool initializeCoreServices(); - bool registerService(uint8_t serviceId, ServiceFn fn, void *context = nullptr); - // Cancels queued work that has not started dispatching. If a callback was - // already copied for dispatch, its context must remain valid until it returns. - void clearService(uint8_t serviceId); - void dispatchPending(); - void setPending(uint8_t serviceId); - void setPendingOnce(uint8_t serviceId); + bool registerService(uint8_t serviceId, ServiceFn fn, void *context = nullptr); + void clearService(uint8_t serviceId); + void dispatchPending(); + void setPending(uint8_t serviceId); + void setPendingOnce(uint8_t serviceId); - private: - static uint32_t enterCritical(); - static void exitCritical(uint32_t primask); +private: + static uint32_t enterCritical(); + static void exitCritical(uint32_t primask); - struct ServiceEntry { - ServiceFn fn = nullptr; - void *context = nullptr; - }; + struct ServiceEntry { + ServiceFn fn = nullptr; + void *context = nullptr; + }; - std::array services_{}; - std::array pendingCount_{}; - volatile uint32_t pendingMask_ = 0; + std::array services_{}; + std::array pendingCount_{}; + volatile uint32_t pendingMask_ = 0; };