Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
54309a5
Normalize variant SERCOM peripheral and IRQ handler macros
crabel99 Jun 20, 2026
230b3ed
Refactor UART variants to use shared core serial emitters
crabel99 Jun 20, 2026
d8e1197
Add shared PendSV deferred service dispatcher
crabel99 Jun 20, 2026
b04854c
define sercom transaction structure for enqueueing async transactions.
crabel99 Jan 28, 2026
60af7ce
provide unified structure for maintaining and accessing sercom protoc…
crabel99 Jan 28, 2026
042458f
add support for mux pin deconfliction during development.
crabel99 Jan 28, 2026
755e70a
integrate needed dma sercom structure into the SERCOM class
crabel99 Jan 28, 2026
b6768fa
refactored initMaster/SlaveWIRE() to support rapid swapping of roles …
crabel99 Jan 29, 2026
d16a831
generalize RingBuffer to support any type not just uint8_t while pres…
crabel99 Jan 29, 2026
57c89a0
refactored stopTransmissionWIRE to support DMA and async operations, …
crabel99 Jan 29, 2026
8d1b6b5
add 10-bit address support for client operations
crabel99 Jan 29, 2026
c9d1336
finish implementing wire async and dma callback structure and inlinin…
crabel99 Jan 30, 2026
a789b2e
add async and automatic dma support for client mode operations.
crabel99 Jan 31, 2026
0e91a3f
refactor SPI to use SERCOM dma implementation
crabel99 Feb 1, 2026
37bca6e
stage more UART DMA structure
crabel99 Feb 15, 2026
639e539
unify structure between different sercoms to mirror each other more c…
crabel99 Feb 15, 2026
fedf6db
get sercom's wire protocol working in master mode
crabel99 Feb 15, 2026
f1ffbcc
clean up SPI
crabel99 Feb 15, 2026
d186cab
finish refactor of Wire to fully use async SERCOM
crabel99 Feb 15, 2026
b81a58a
master wire fully tested sync/async
crabel99 Feb 17, 2026
8decc12
test SPI sync and async operations
crabel99 Feb 17, 2026
cc3657a
high level testing no hardware testing done on UART
crabel99 Feb 17, 2026
fdd11fd
refactor to use async sercom api's
crabel99 Feb 17, 2026
7ce665e
samd datasheet multiplexing tables as csv
crabel99 Feb 17, 2026
3228488
script to generate wire sercom specific pin assignments can be used …
crabel99 Feb 17, 2026
cb94be4
Fix CI build errors
crabel99 Feb 17, 2026
c543bb2
Make SPI interrupt handlers weak and fix example ambiguity
crabel99 Feb 17, 2026
b841afb
add getters for sercom class object
crabel99 Feb 20, 2026
edac060
add support to allow transaction chaining for uart/spi/i2c
crabel99 Feb 21, 2026
e97d102
finish support for chaining transactions and formatting cleanup
crabel99 Feb 22, 2026
29728a6
allow readDataWIRE() to allow passing deferStopWIRE() on the final read.
crabel99 Apr 2, 2026
0d615d8
Revert "allow readDataWIRE() to allow passing deferStopWIRE() on the …
crabel99 Apr 6, 2026
b84405f
Reapply "allow readDataWIRE() to allow passing deferStopWIRE() on the…
crabel99 Apr 13, 2026
6b5dd11
generate SPI and Wire pin mux arrays
crabel99 Apr 14, 2026
260da0b
document sercom_txn.h purpose more clearly
crabel99 Apr 14, 2026
dec082e
warn user if SPI or Wire pins are not properly assigned with a boolea…
crabel99 Apr 14, 2026
fff3c38
fix UART test regression
crabel99 Apr 14, 2026
5beb170
fix(spi): derive IRQ handlers from PERIPH_SPIx and add SPI6/SPI7 support
crabel99 Apr 17, 2026
eff0511
Preserve Wire sync compatibility while tightening async transaction h…
crabel99 May 17, 2026
ef295ff
Normalize async branch whitespace
crabel99 Jun 21, 2026
f4dc420
Route SERCOM deferred services through shared PendSV
crabel99 Jun 21, 2026
8cfeeaf
Add PendSV peripheral channel map
crabel99 Jun 24, 2026
1a13c8f
Add PHY PendSV channel allocation
crabel99 Jun 26, 2026
12eeb02
Add RTC PendSV channel allocation
crabel99 Jul 4, 2026
e9e81f7
fix(wire): restore multimaster role transitions
crabel99 Jul 16, 2026
8d80221
refactor(core): define SAME5x chip families
crabel99 Jul 17, 2026
4b63b95
feat(variant): add SAME54 Xplained Pro board
crabel99 Jul 17, 2026
23d2515
build(core): expose framework package metadata
crabel99 Jul 17, 2026
533a33e
feat(core): add native SAME53/E54 peripheral support
crabel99 Jul 17, 2026
8344356
feat(usb): add native SAME53/E54 legacy USB support
crabel99 Jul 17, 2026
039e80c
merge: integrate SAME5x support with SERCOM async DMA
crabel99 Jul 18, 2026
5c3efe5
merge: integrate UART variant emitter cleanup
crabel99 Jul 18, 2026
58f6e68
fix(pendsv): coalesce TinyUSB deferred service
crabel99 Jul 18, 2026
2aeeb1a
Merge remote-tracking branch 'origin/pendsv-support-clean' into serco…
crabel99 Jul 18, 2026
8ea86e5
fix(core): expand SAME5x crystal DFLL support
crabel99 Jul 19, 2026
2129703
Merge branch 'same5x-integration' into sercom-async-dma-merge-same5x
crabel99 Jul 19, 2026
73e669d
fix(core): use CMSIS SAME5x family headers
crabel99 Jul 24, 2026
409cdd0
fix(core): complete SAME5x pin and interrupt guards
crabel99 Jul 24, 2026
e371884
fix(core): complete SAME5x startup vectors
crabel99 Jul 24, 2026
2b6458a
fix(core): complete SAME5x timing and reset guards
crabel99 Jul 24, 2026
dd62604
fix(usb): complete SAME5x device and host guards
crabel99 Jul 24, 2026
aef45aa
fix(uart): complete SAME5x family guards
crabel99 Jul 24, 2026
2fc453a
fix(spi): complete SAME5x family guards
crabel99 Jul 24, 2026
dc59abb
refactor(pendsv): map services across SAME5x peripherals
crabel99 Jul 24, 2026
9f1d78f
feat(wire): add deferred multimaster slave DMA
crabel99 Jul 24, 2026
068321c
fix(wire): service final slave NACK after DMA
crabel99 Jul 24, 2026
1581f36
build(core): update SAME5x USB and DMA dependencies
crabel99 Jul 24, 2026
12bbc72
Merge branch 'pendsv-support-clean' into sercom-async-dma-merge-same5x
crabel99 Jul 24, 2026
5299f12
fix(sercom): generate pinmux guards from chip families
crabel99 Jul 24, 2026
0d3d40c
build(sercom): update USB and DMA dependencies
crabel99 Jul 24, 2026
7daaf0c
fix(wire): recover DMA stop and slave role transitions
crabel99 Jul 26, 2026
bb1a49a
feat(wire): stream slave DMA transfers
crabel99 Jul 27, 2026
b127b18
fix(wire): clear shared error interrupt in slave mode
crabel99 Jul 27, 2026
359700c
feat(wire): expose semantic completion state
crabel99 Jul 27, 2026
10a7c1f
feat(wire): restart queued transfer after arbitration loss
crabel99 Jul 27, 2026
40fadb8
chore(variant): correct Xplained debug script label
crabel99 Jul 27, 2026
4cf798b
feat(wire): harden async DMA multimaster transactions
crabel99 Aug 11, 2026
9d655dd
fix(wire): start queued master after slave retirement
crabel99 Aug 21, 2026
d7523f3
refactor(wire): reuse ISR status snapshot
crabel99 Aug 21, 2026
02d425f
build(dma): pin shared descriptor implementation
crabel99 Aug 22, 2026
6e3e0b7
fix(analog): classify SAMD51 and SAME5x families
crabel99 Aug 22, 2026
712b9fa
build(usb): pin Metro M0-aware TinyUSB revision
crabel99 Aug 22, 2026
0bd5c77
build(usb): pin formatted TinyUSB revision
crabel99 Aug 22, 2026
0b814e1
fix(usb): correct TinyUSB revision pin
crabel99 Aug 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
bootloaders/*/build/
*~
/libraries/**/build/
.ci-home
.ci_home
*.log
8 changes: 6 additions & 2 deletions cores/arduino/Arduino.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,11 @@ extern "C"{
#endif // __cplusplus

// Include Atmel headers
#include "sam.h"
#include <sam.h>
#include "wiring_constants.h"

extern uint32_t SystemCoreClock;

#define clockCyclesPerMicrosecond() ( SystemCoreClock / 1000000L )
#define clockCyclesToMicroseconds(a) ( ((a) * 1000L) / (SystemCoreClock / 1000L) )
#define microsecondsToClockCycles(a) ( (a) * (SystemCoreClock / 1000000L) )
Expand Down Expand Up @@ -156,7 +158,9 @@ void loop( void ) ;
#include "USB/USBDesc.h"
#include "USB/USBCore.h"
#include "USB/USBAPI.h"
#include "USB/USB_host.h"
#if !defined(__SAME53__) && !defined(__SAME54__)
#include "USB/USB_host.h"
#endif
#endif

#endif // Arduino_h
162 changes: 162 additions & 0 deletions cores/arduino/PendSV.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
#include "PendSV.h"

#include <Arduino.h>
#include <limits.h>

#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() {
NVIC_SetPriority(PendSV_IRQn, (1 << __NVIC_PRIO_BITS) - 1);
#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();
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::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.
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<uint8_t>(__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();
}
189 changes: 189 additions & 0 deletions cores/arduino/PendSV.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
#pragma once

#include <sam.h>

#include <array>
#include <stdint.h>

#if defined(SERCOM0) || defined(SERCOM0_REGS)
#define SIMIO_PENDSV_HAS_SERCOM0 true
#else
#define SIMIO_PENDSV_HAS_SERCOM0 false
#endif
#if defined(SERCOM1) || defined(SERCOM1_REGS)
#define SIMIO_PENDSV_HAS_SERCOM1 true
#else
#define SIMIO_PENDSV_HAS_SERCOM1 false
#endif
#if defined(SERCOM2) || defined(SERCOM2_REGS)
#define SIMIO_PENDSV_HAS_SERCOM2 true
#else
#define SIMIO_PENDSV_HAS_SERCOM2 false
#endif
#if defined(SERCOM3) || defined(SERCOM3_REGS)
#define SIMIO_PENDSV_HAS_SERCOM3 true
#else
#define SIMIO_PENDSV_HAS_SERCOM3 false
#endif
#if defined(SERCOM4) || defined(SERCOM4_REGS)
#define SIMIO_PENDSV_HAS_SERCOM4 true
#else
#define SIMIO_PENDSV_HAS_SERCOM4 false
#endif
#if defined(SERCOM5) || defined(SERCOM5_REGS)
#define SIMIO_PENDSV_HAS_SERCOM5 true
#else
#define SIMIO_PENDSV_HAS_SERCOM5 false
#endif
#if defined(SERCOM6) || defined(SERCOM6_REGS)
#define SIMIO_PENDSV_HAS_SERCOM6 true
#else
#define SIMIO_PENDSV_HAS_SERCOM6 false
#endif
#if defined(SERCOM7) || defined(SERCOM7_REGS)
#define SIMIO_PENDSV_HAS_SERCOM7 true
#else
#define SIMIO_PENDSV_HAS_SERCOM7 false
#endif
#if defined(DMAC) || defined(DMAC_REGS)
#define SIMIO_PENDSV_HAS_DMAC true
#else
#define SIMIO_PENDSV_HAS_DMAC false
#endif
#if defined(AES) || defined(AES_REGS)
#define SIMIO_PENDSV_HAS_AES true
#else
#define SIMIO_PENDSV_HAS_AES false
#endif
#if defined(ICM) || defined(ICM_REGS)
#define SIMIO_PENDSV_HAS_ICM true
#else
#define SIMIO_PENDSV_HAS_ICM false
#endif
#if defined(PUKCC) || defined(PUKCC_REGS) || defined(ID_PUKCC) || defined(PUKCC_INSTANCE_ID) || \
defined(PUKCC_IRQn)
#define SIMIO_PENDSV_HAS_PUKCC true
#else
#define SIMIO_PENDSV_HAS_PUKCC false
#endif
#if defined(TRNG) || defined(TRNG_REGS)
#define SIMIO_PENDSV_HAS_TRNG true
#else
#define SIMIO_PENDSV_HAS_TRNG false
#endif
#if defined(GMAC) || defined(GMAC_REGS)
#define SIMIO_PENDSV_HAS_GMAC true
#define SIMIO_PENDSV_HAS_PHY true
#else
#define SIMIO_PENDSV_HAS_GMAC false
#define SIMIO_PENDSV_HAS_PHY false
#endif

template <bool HasSercom0, bool HasSercom1, bool HasSercom2, bool HasSercom3, bool HasSercom4,
bool HasSercom5, bool HasSercom6, bool HasSercom7, bool HasDmac, bool HasAes,
bool HasIcm, bool HasPukcc, bool HasTrng, bool HasGmac, bool HasPhy>
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<uint8_t>(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<
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::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();

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);

struct ServiceEntry {
ServiceFn fn = nullptr;
void *context = nullptr;
};

std::array<ServiceEntry, kMaxServices> services_{};
std::array<uint16_t, kMaxServices> pendingCount_{};
volatile uint32_t pendingMask_ = 0;
};
Loading
Loading