feat: add 0x0c_multicore_cbm bare-metal dual-core FIFO driver

This commit is contained in:
Kevin Thomas
2026-04-05 20:14:56 -04:00
parent e82bd3d326
commit 998518dcda
24 changed files with 1997 additions and 0 deletions
@@ -0,0 +1,35 @@
/**
******************************************************************************
* @file image_def.c
* @author Kevin Thomas
* @brief RP2350 IMAGE_DEF block for boot ROM image recognition.
*
* Must appear within the first 4 KB of flash for the boot ROM
* to accept the image.
*
******************************************************************************
* @attention
*
* Copyright (c) 2026 Kevin Thomas.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
#include <stdint.h>
/**
* @brief IMAGE_DEF block structure placed in flash
*/
__attribute__((section(".embedded_block"), used))
const uint8_t picobin_block[] = {
0xD3, 0xDE, 0xFF, 0xFF,
0x42, 0x01, 0x21, 0x10,
0xFF, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x79, 0x35, 0x12, 0xAB
};
+144
View File
@@ -0,0 +1,144 @@
/**
******************************************************************************
* @file main.c
* @author Kevin Thomas
* @brief Multicore FIFO messaging demonstration.
*
* Core 0 launches core 1, then sends an incrementing counter
* through the SIO inter-processor FIFO. Core 1 returns the
* value plus one. Both values are printed over UART every
* second.
*
* Wiring:
* GPIO0 -> UART TX (USB-to-UART adapter RX)
* GPIO1 -> UART RX (USB-to-UART adapter TX)
* No external wiring required (dual-core on-chip)
*
******************************************************************************
* @attention
*
* Copyright (c) 2026 Kevin Thomas.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
#include "rp2350_multicore.h"
#include "rp2350_uart.h"
#include "rp2350_delay.h"
/**
* @brief Extract decimal digits from a value in reverse order.
* @param value unsigned integer to convert
* @param tmp output buffer for reversed digits (min 11 bytes)
* @retval int number of digits written
*/
static int _extract_digits(uint32_t value, char *tmp)
{
int i = 0;
if (value == 0)
tmp[i++] = '0';
while (value > 0) {
tmp[i++] = '0' + (char)(value % 10);
value /= 10;
}
return i;
}
/**
* @brief Reverse-copy a digit buffer into a null-terminated string.
* @param tmp source buffer with reversed digits
* @param len number of digits in source
* @param buf destination buffer (must hold len + 1 bytes)
* @retval None
*/
static void _reverse_copy(const char *tmp, int len, char *buf)
{
int j = 0;
while (len > 0)
buf[j++] = tmp[--len];
buf[j] = '\0';
}
/**
* @brief Convert a uint32_t to a decimal null-terminated string.
* @param value unsigned integer to convert
* @param buf output buffer (min 12 bytes)
* @retval None
*/
static void _uint_to_str(uint32_t value, char *buf)
{
char tmp[11];
int len = _extract_digits(value, tmp);
_reverse_copy(tmp, len, buf);
}
/**
* @brief Print a labelled decimal value over UART.
* @param label text label to print before the value
* @param value unsigned integer to print
* @retval None
*/
static void _print_labeled_value(const char *label, uint32_t value)
{
char buf[12];
uart_puts(label);
_uint_to_str(value, buf);
uart_puts(buf);
}
/**
* @brief Print a full sent/returned line over UART.
* @param sent value sent by core 0
* @param received value returned by core 1
* @retval None
*/
static void _print_counter_line(uint32_t sent, uint32_t received)
{
_print_labeled_value("core0 sent: ", sent);
_print_labeled_value(", core1 returned: ", received);
uart_puts("\r\n");
}
/**
* @brief Core 1 entry point: receive a value and return it plus one.
* @retval None (does not return)
*/
static void _core1_main(void)
{
while (1) {
uint32_t value = multicore_fifo_pop();
multicore_fifo_push(value + 1);
}
}
/**
* @brief Send the counter to core 1, print the round-trip, delay.
* @param counter pointer to the running counter (post-incremented)
* @retval None
*/
static void _send_and_print(uint32_t *counter)
{
multicore_fifo_push(*counter);
uint32_t response = multicore_fifo_pop();
_print_counter_line(*counter, response);
(*counter)++;
delay_ms(1000);
}
/**
* @brief Application entry point for the multicore FIFO demo.
* @retval int does not return
*/
int main(void)
{
uint32_t counter = 0;
multicore_launch(_core1_main);
uart_puts("Multicore FIFO demo initialized\r\n");
while (1)
_send_and_print(&counter);
}
@@ -0,0 +1,34 @@
/**
******************************************************************************
* @file rp2350_coprocessor.c
* @author Kevin Thomas
* @brief Coprocessor access control driver implementation for RP2350.
*
* Grants access to coprocessors 0 and 1 by setting the
* corresponding bits in CPACR with DSB/ISB barriers.
*
******************************************************************************
* @attention
*
* Copyright (c) 2026 Kevin Thomas.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
#include "rp2350_coprocessor.h"
void coprocessor_enable(void)
{
uint32_t value;
value = *CPACR;
value |= (1U << CPACR_CP1_SHIFT);
value |= (1U << CPACR_CP0_SHIFT);
*CPACR = value;
__asm__ volatile ("dsb");
__asm__ volatile ("isb");
}
@@ -0,0 +1,55 @@
/**
******************************************************************************
* @file rp2350_delay.c
* @author Kevin Thomas
* @brief Delay driver implementation for RP2350.
*
* Busy-wait millisecond and microsecond delays calibrated for
* a 12 MHz clock.
*
******************************************************************************
* @attention
*
* Copyright (c) 2026 Kevin Thomas.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
#include "rp2350_delay.h"
void delay_ms(uint32_t ms)
{
if (ms == 0)
return;
__asm__ volatile (
"mov r4, #3600\n\t"
"mul r5, %0, r4\n\t"
"1:\n\t"
"subs r5, #1\n\t"
"bne 1b\n\t"
:
: "r" (ms)
: "r4", "r5", "cc"
);
}
void delay_us(uint32_t us)
{
if (us == 0)
return;
__asm__ volatile (
"mov r4, #4\n\t"
"mul r5, %0, r4\n\t"
"1:\n\t"
"subs r5, #1\n\t"
"bne 1b\n\t"
:
: "r" (us)
: "r4", "r5", "cc"
);
}
@@ -0,0 +1,148 @@
/**
******************************************************************************
* @file rp2350_multicore.c
* @author Kevin Thomas
* @brief Multicore driver implementation for RP2350.
*
* Implements bare-metal core 1 launch via the PSM reset and
* SIO FIFO handshake protocol (RP2350 datasheet Section 5.3).
* Provides blocking push/pop for inter-core 32-bit messaging.
*
******************************************************************************
* @attention
*
* Copyright (c) 2026 Kevin Thomas.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
#include "rp2350_multicore.h"
/**
* @brief Number of 32-bit words in the core 1 stack (4096 bytes).
*/
#define CORE1_STACK_WORDS 1024U
/**
* @brief Core 1 stack array allocated in BSS.
*/
static uint32_t _core1_stack[CORE1_STACK_WORDS];
/**
* @brief Drain all pending values from the RX FIFO.
* @retval None
*/
static void _fifo_drain(void)
{
while (SIO[SIO_FIFO_ST_OFFSET] & SIO_FIFO_ST_VLD_MASK)
(void)SIO[SIO_FIFO_RD_OFFSET];
}
/**
* @brief Push one 32-bit word to the TX FIFO, blocking until ready.
* @param data value to write
* @retval None
*/
static void _fifo_push_blocking(uint32_t data)
{
while (!(SIO[SIO_FIFO_ST_OFFSET] & SIO_FIFO_ST_RDY_MASK)) {
}
SIO[SIO_FIFO_WR_OFFSET] = data;
__asm__ volatile ("sev");
}
/**
* @brief Pop one 32-bit word from the RX FIFO, blocking until valid.
* @retval uint32_t value read from the FIFO
*/
static uint32_t _fifo_pop_blocking(void)
{
while (!(SIO[SIO_FIFO_ST_OFFSET] & SIO_FIFO_ST_VLD_MASK)) {
}
return SIO[SIO_FIFO_RD_OFFSET];
}
/**
* @brief Force core 1 into reset via PSM atomic set alias.
* @retval None
*/
static void _set_frce_off_proc1(void)
{
volatile uint32_t *set = (volatile uint32_t *)((uintptr_t)&PSM->FRCE_OFF + ATOMIC_SET_OFFSET);
*set = (1U << PSM_FRCE_OFF_PROC1_SHIFT);
while (!(PSM->FRCE_OFF & (1U << PSM_FRCE_OFF_PROC1_SHIFT))) {
}
}
/**
* @brief Release core 1 from reset via PSM atomic clear alias.
* @retval None
*/
static void _clr_frce_off_proc1(void)
{
volatile uint32_t *clr = (volatile uint32_t *)((uintptr_t)&PSM->FRCE_OFF + ATOMIC_CLR_OFFSET);
*clr = (1U << PSM_FRCE_OFF_PROC1_SHIFT);
}
/**
* @brief Reset core 1 and wait for its boot FIFO acknowledgement.
* @retval None
*/
static void _reset_core1(void)
{
_set_frce_off_proc1();
_clr_frce_off_proc1();
(void)_fifo_pop_blocking();
}
/**
* @brief Send one handshake word, draining the FIFO first for zeroes.
* @param cmd the command word to send
* @retval None
*/
static void _send_handshake_word(uint32_t cmd)
{
if (!cmd) {
_fifo_drain();
__asm__ volatile ("sev");
}
_fifo_push_blocking(cmd);
}
/**
* @brief Perform the six-word FIFO handshake to launch core 1.
* @param entry pointer to the core 1 entry function
* @retval None
*/
static void _launch_handshake(void (*entry)(void))
{
extern uint32_t __Vectors;
uint32_t *sp = &_core1_stack[CORE1_STACK_WORDS];
const uint32_t seq[] = {0, 0, 1, (uintptr_t)&__Vectors, (uintptr_t)sp, (uintptr_t)entry};
uint32_t idx = 0;
do {
_send_handshake_word(seq[idx]);
idx = (_fifo_pop_blocking() == seq[idx]) ? idx + 1 : 0;
} while (idx < 6);
}
void multicore_launch(void (*entry)(void))
{
_reset_core1();
_launch_handshake(entry);
}
void multicore_fifo_push(uint32_t data)
{
_fifo_push_blocking(data);
}
uint32_t multicore_fifo_pop(void)
{
return _fifo_pop_blocking();
}
@@ -0,0 +1,33 @@
/**
******************************************************************************
* @file rp2350_reset.c
* @author Kevin Thomas
* @brief Reset controller driver implementation for RP2350.
*
* Releases IO_BANK0 from reset and waits until the subsystem
* is ready.
*
******************************************************************************
* @attention
*
* Copyright (c) 2026 Kevin Thomas.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
#include "rp2350_reset.h"
void reset_init_subsystem(void)
{
uint32_t value;
value = RESETS->RESET;
value &= ~(1U << RESETS_RESET_IO_BANK0_SHIFT);
RESETS->RESET = value;
while ((RESETS->RESET_DONE & (1U << RESETS_RESET_IO_BANK0_SHIFT)) == 0) {
}
}
@@ -0,0 +1,45 @@
/**
******************************************************************************
* @file rp2350_reset_handler.c
* @author Kevin Thomas
* @brief Reset handler implementation for RP2350.
*
* Entry point after power-on or system reset. Initializes the
* stack, XOSC, subsystem resets, UART, coprocessor, then
* branches to main().
*
******************************************************************************
* @attention
*
* Copyright (c) 2026 Kevin Thomas.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
#include "rp2350_reset_handler.h"
#include "rp2350_stack.h"
#include "rp2350_xosc.h"
#include "rp2350_reset.h"
#include "rp2350_uart.h"
#include "rp2350_coprocessor.h"
extern int main(void);
void __attribute__((naked, noreturn)) Reset_Handler(void)
{
__asm__ volatile (
"bl stack_init\n\t"
"bl xosc_init\n\t"
"bl xosc_enable_peri_clk\n\t"
"bl reset_init_subsystem\n\t"
"bl uart_release_reset\n\t"
"bl uart_init\n\t"
"bl coprocessor_enable\n\t"
"b main\n\t"
);
}
@@ -0,0 +1,38 @@
/**
******************************************************************************
* @file rp2350_stack.c
* @author Kevin Thomas
* @brief Stack pointer initialization for RP2350.
*
* Sets MSP, PSP, MSPLIM, and PSPLIM using inline assembly.
*
******************************************************************************
* @attention
*
* Copyright (c) 2026 Kevin Thomas.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
#include "rp2350_stack.h"
void stack_init(void)
{
__asm__ volatile (
"ldr r0, =%0\n\t"
"msr PSP, r0\n\t"
"ldr r0, =%1\n\t"
"msr MSPLIM, r0\n\t"
"msr PSPLIM, r0\n\t"
"ldr r0, =%0\n\t"
"msr MSP, r0\n\t"
:
: "i" (STACK_TOP), "i" (STACK_LIMIT)
: "r0"
);
}
@@ -0,0 +1,126 @@
/**
******************************************************************************
* @file rp2350_uart.c
* @author Kevin Thomas
* @brief UART0 driver implementation for RP2350.
*
* Configures UART0 on GPIO 0 (TX) and GPIO 1 (RX) at 115200
* baud using the 12 MHz XOSC clock.
*
******************************************************************************
* @attention
*
* Copyright (c) 2026 Kevin Thomas.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
#include "rp2350_uart.h"
#define UART_BASE ((volatile uint32_t *) UART0_BASE)
/**
* @brief Clear the UART0 reset bit in the reset controller.
* @retval None
*/
static void _uart_clear_reset_bit(void)
{
uint32_t value;
value = RESETS->RESET;
value &= ~(1U << RESETS_RESET_UART0_SHIFT);
RESETS->RESET = value;
}
/**
* @brief Wait until the UART0 block is out of reset.
* @retval None
*/
static void _uart_wait_reset_done(void)
{
while ((RESETS->RESET_DONE & (1U << RESETS_RESET_UART0_SHIFT)) == 0) {
}
}
/**
* @brief Configure GPIO pins 0 (TX) and 1 (RX) for UART function.
* @retval None
*/
static void _uart_configure_pins(void)
{
IO_BANK0->GPIO[0].CTRL = IO_BANK0_CTRL_FUNCSEL_UART;
IO_BANK0->GPIO[1].CTRL = IO_BANK0_CTRL_FUNCSEL_UART;
PADS_BANK0->GPIO[0] = 0x04;
PADS_BANK0->GPIO[1] = 0x40;
}
/**
* @brief Set UART0 baud rate divisors for 115200 at 12 MHz.
* @retval None
*/
static void _uart_set_baud(void)
{
UART_BASE[UART_CR_OFFSET] = 0;
UART_BASE[UART_IBRD_OFFSET] = 6;
UART_BASE[UART_FBRD_OFFSET] = 33;
}
/**
* @brief Configure line control and enable UART0.
* @retval None
*/
static void _uart_enable(void)
{
UART_BASE[UART_LCR_H_OFFSET] = UART_LCR_H_8N1_FIFO;
UART_BASE[UART_CR_OFFSET] = UART_CR_ENABLE;
}
void uart_release_reset(void)
{
_uart_clear_reset_bit();
_uart_wait_reset_done();
}
void uart_init(void)
{
_uart_configure_pins();
_uart_set_baud();
_uart_enable();
}
bool uart_is_readable(void)
{
return (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) == 0;
}
char uart_getchar(void)
{
while (UART_BASE[UART_FR_OFFSET] & UART_FR_RXFE_MASK) {
}
return (char)(UART_BASE[UART_DR_OFFSET] & 0xFF);
}
void uart_putchar(char c)
{
while (UART_BASE[UART_FR_OFFSET] & UART_FR_TXFF_MASK) {
}
UART_BASE[UART_DR_OFFSET] = (uint32_t)c;
}
void uart_puts(const char *str)
{
while (*str) {
uart_putchar(*str++);
}
}
char uart_to_upper(char c)
{
if (c >= 'a' && c <= 'z')
return (char)(c - 32);
return c;
}
@@ -0,0 +1,46 @@
/**
******************************************************************************
* @file rp2350_xosc.c
* @author Kevin Thomas
* @brief XOSC driver implementation for RP2350.
*
* Configures the external crystal oscillator and enables the
* peripheral clock sourced from XOSC.
*
******************************************************************************
* @attention
*
* Copyright (c) 2026 Kevin Thomas.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
#include "rp2350_xosc.h"
void xosc_init(void)
{
XOSC->STARTUP = 0x00C4U;
XOSC->CTRL = 0x00FABAA0U;
while ((XOSC->STATUS & (1U << XOSC_STATUS_STABLE_SHIFT)) == 0) {
}
}
void xosc_enable_peri_clk(void)
{
uint32_t value;
value = CLOCKS->CLK_PERI_CTRL;
value &= ~CLK_PERI_CTRL_AUXSRC_MASK;
value |= (1U << CLK_PERI_CTRL_ENABLE_SHIFT);
value |= (CLK_PERI_CTRL_AUXSRC_XOSC << CLK_PERI_CTRL_AUXSRC_SHIFT);
CLOCKS->CLK_PERI_CTRL = value;
}
void xosc_set_clk_ref(void)
{
CLOCKS->CLK_REF_CTRL = CLK_REF_CTRL_SRC_XOSC;
}
@@ -0,0 +1,38 @@
/**
******************************************************************************
* @file vector_table.c
* @author Kevin Thomas
* @brief Vector table with initial stack pointer and reset handler.
*
* Placed in the .vectors section at the start of flash.
* The Thumb bit (bit 0 = 1) is automatically set by the
* linker for function pointers in Thumb mode.
*
******************************************************************************
* @attention
*
* Copyright (c) 2026 Kevin Thomas.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
#include <stdint.h>
extern uint32_t _stack_top;
extern void Reset_Handler(void);
typedef void (*vector_func_t)(void);
/**
* @brief Vector table placed in .vectors section
*/
__attribute__((section(".vectors"), used))
const void *_vectors[2] = {
&_stack_top,
Reset_Handler
};