Add 0x0f_flash_cbm bare-metal on-chip flash driver

This commit is contained in:
Kevin Thomas
2026-04-05 21:21:12 -04:00
parent 9f7cd133aa
commit 1b4dfb3af4
23 changed files with 1918 additions and 0 deletions
+35
View File
@@ -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
};
+78
View File
@@ -0,0 +1,78 @@
/**
******************************************************************************
* @file main.c
* @author Kevin Thomas
* @brief On-chip flash write / read demonstration.
*
* Writes "Embedded Hacking flash driver demo" to the last
* sector of flash, reads it back via XIP, and prints the
* result over UART.
*
* Wiring:
* GPIO0 -> UART TX (USB-to-UART adapter RX)
* GPIO1 -> UART RX (USB-to-UART adapter TX)
* No external components required
*
******************************************************************************
* @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_flash.h"
#include "rp2350_uart.h"
/**
* @brief Target offset: last sector of the 4 MB flash chip.
*/
#define FLASH_TARGET_OFFSET (FLASH_SIZE - FLASH_SECTOR_SIZE)
/**
* @brief Fill a page buffer with 0xFF and copy the demo string.
* @param buf destination buffer (FLASH_PAGE_SIZE bytes in RAM)
* @retval None
*/
static void _prepare_write_buf(uint8_t *buf)
{
uint32_t i;
const char *msg = "Embedded Hacking flash driver demo";
for (i = 0; i < FLASH_PAGE_SIZE; i++)
buf[i] = 0xFFU;
for (i = 0; msg[i] != '\0'; i++)
buf[i] = (uint8_t)msg[i];
buf[i] = 0x00U;
}
/**
* @brief Write the demo string to flash and print the read-back.
* @retval None
*/
static void _write_and_verify(void)
{
uint8_t write_buf[FLASH_PAGE_SIZE];
uint8_t read_buf[FLASH_PAGE_SIZE];
_prepare_write_buf(write_buf);
flash_write(FLASH_TARGET_OFFSET, write_buf, FLASH_PAGE_SIZE);
flash_read(FLASH_TARGET_OFFSET, read_buf, FLASH_PAGE_SIZE);
uart_puts("Flash readback: ");
uart_puts((const char *)read_buf);
uart_puts("\r\n");
}
/**
* @brief Application entry point for the on-chip flash demo.
* @retval int does not return
*/
int main(void)
{
_write_and_verify();
while (1)
__asm volatile ("wfi");
}
@@ -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");
}
+131
View File
@@ -0,0 +1,131 @@
/**
******************************************************************************
* @file rp2350_flash.c
* @author Kevin Thomas
* @brief On-chip flash driver implementation for RP2350.
*
* Erases and programs flash sectors using ROM bootrom
* functions. The erase/program trampoline executes from RAM
* (placed in .ram_func) because XIP is disabled while the
* flash chip is being modified.
*
******************************************************************************
* @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_flash.h"
/**
* @brief ROM table lookup function pointer type (RP2350 ARM).
*/
typedef void *(*rom_table_lookup_fn)(uint32_t code, uint32_t mask);
/**
* @brief ROM void function pointer (no parameters, no return).
*/
typedef void (*rom_void_fn)(void);
/**
* @brief ROM flash range erase function pointer.
*/
typedef void (*rom_flash_erase_fn)(uint32_t addr, uint32_t count,
uint32_t block_size, uint8_t block_cmd);
/**
* @brief ROM flash range program function pointer.
*/
typedef void (*rom_flash_program_fn)(uint32_t addr, const uint8_t *data,
uint32_t count);
/**
* @brief Collection of ROM flash function pointers.
*/
typedef struct
{
rom_void_fn connect; /**< @brief Connect to internal flash */
rom_void_fn exit_xip; /**< @brief Exit XIP mode */
rom_flash_erase_fn erase; /**< @brief Erase flash range */
rom_flash_program_fn program; /**< @brief Program flash range */
rom_void_fn flush_cache; /**< @brief Flush XIP cache */
rom_void_fn enter_xip; /**< @brief Re-enter XIP mode */
} FlashRomFns;
/**
* @brief Look up a ROM function by its two-character code.
* @param code ROM_FUNC_* code from rp2350.h
* @retval void* pointer to the ROM function
*/
static void *_rom_func_lookup(uint32_t code)
{
rom_table_lookup_fn fn =
(rom_table_lookup_fn)(uintptr_t)(*(uint16_t *)BOOTROM_TABLE_LOOKUP_OFFSET);
return fn(code, RT_FLAG_FUNC_ARM_SEC);
}
/**
* @brief Populate all ROM flash function pointers.
* @param fns pointer to the struct to fill
* @retval None
*/
static void _lookup_rom_fns(FlashRomFns *fns)
{
fns->connect = (rom_void_fn)_rom_func_lookup(ROM_FUNC_CONNECT_INTERNAL_FLASH);
fns->exit_xip = (rom_void_fn)_rom_func_lookup(ROM_FUNC_FLASH_EXIT_XIP);
fns->erase = (rom_flash_erase_fn)_rom_func_lookup(ROM_FUNC_FLASH_RANGE_ERASE);
fns->program = (rom_flash_program_fn)_rom_func_lookup(ROM_FUNC_FLASH_RANGE_PROGRAM);
fns->flush_cache = (rom_void_fn)_rom_func_lookup(ROM_FUNC_FLASH_FLUSH_CACHE);
fns->enter_xip = (rom_void_fn)_rom_func_lookup(ROM_FUNC_FLASH_ENTER_CMD_XIP);
}
/**
* @brief RAM-resident trampoline that erases and programs flash.
*
* Must not call any function residing in flash. All ROM
* function pointers are passed via the fns struct.
*
* @param fns pointer to ROM function pointers (in RAM/stack)
* @param offset byte offset from start of flash
* @param data pointer to source buffer (must be in RAM)
* @param len number of bytes to program
* @retval None
*/
__attribute__((section(".ram_func"), noinline))
static void _flash_erase_program_ram(const FlashRomFns *fns, uint32_t offset,
const uint8_t *data, uint32_t len)
{
fns->connect();
fns->exit_xip();
uint32_t erase_addr = offset & ~(FLASH_SECTOR_SIZE - 1U);
uint32_t erase_end = (offset + len + FLASH_SECTOR_SIZE - 1U) & ~(FLASH_SECTOR_SIZE - 1U);
fns->erase(erase_addr, erase_end - erase_addr, FLASH_BLOCK_SIZE, FLASH_BLOCK_ERASE_CMD);
fns->program(offset, data, len);
fns->flush_cache();
fns->enter_xip();
}
void flash_write(uint32_t offset, const uint8_t *data, uint32_t len)
{
FlashRomFns fns;
_lookup_rom_fns(&fns);
uint32_t primask;
__asm volatile ("mrs %0, primask" : "=r" (primask));
__asm volatile ("cpsid i");
_flash_erase_program_ram(&fns, offset, data, len);
__asm volatile ("msr primask, %0" :: "r" (primask));
}
void flash_read(uint32_t offset, uint8_t *out, uint32_t len)
{
const uint8_t *src = (const uint8_t *)(XIP_BASE + offset);
for (uint32_t i = 0; i < len; i++)
out[i] = src[i];
}
+33
View File
@@ -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,74 @@
/**
******************************************************************************
* @file rp2350_reset_handler.c
* @author Kevin Thomas
* @brief Reset handler implementation for RP2350.
*
* Entry point after power-on or system reset. Copies the
* .data section from flash to RAM, 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);
/**
* @brief Linker-defined symbol: flash LMA of .data section.
*/
extern uint32_t __data_lma;
/**
* @brief Linker-defined symbol: RAM VMA start of .data section.
*/
extern uint32_t __data_start;
/**
* @brief Linker-defined symbol: RAM VMA end of .data section.
*/
extern uint32_t __data_end;
/**
* @brief Copy initialized data and RAM-resident code from flash to RAM.
* @retval None
*/
void data_copy_init(void)
{
uint32_t *src = &__data_lma;
uint32_t *dst = &__data_start;
while (dst < &__data_end)
*dst++ = *src++;
}
void __attribute__((naked, noreturn)) Reset_Handler(void)
{
__asm__ volatile (
"bl stack_init\n\t"
"bl data_copy_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"
);
}
+38
View File
@@ -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"
);
}
+126
View File
@@ -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;
}
+46
View File
@@ -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;
}
+38
View File
@@ -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
};