diff --git a/boards/rpi-pico-2-riscv/Kconfig b/boards/rpi-pico-2-riscv/Kconfig new file mode 100644 index 000000000000..e9e2ff4413ab --- /dev/null +++ b/boards/rpi-pico-2-riscv/Kconfig @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: 2025 Tom Hert +# SPDX-FileCopyrightText: 2025 HAW Hamburg +# SPDX-License-Identifier: LGPL-2.1-only + +config BOARD + default "rpi-pico-2-riscv" if BOARD_RPI_PICO_2_RISCV + +config BOARD_RPI_PICO_2_RISV + bool + default y + select CPU_MODEL_RP2350_RISCV diff --git a/boards/rpi-pico-2-riscv/Makefile b/boards/rpi-pico-2-riscv/Makefile new file mode 100644 index 000000000000..f8fcbb53a065 --- /dev/null +++ b/boards/rpi-pico-2-riscv/Makefile @@ -0,0 +1,3 @@ +MODULE = board + +include $(RIOTBASE)/Makefile.base diff --git a/boards/rpi-pico-2-riscv/Makefile.features b/boards/rpi-pico-2-riscv/Makefile.features new file mode 100644 index 000000000000..0971934ca60b --- /dev/null +++ b/boards/rpi-pico-2-riscv/Makefile.features @@ -0,0 +1,5 @@ +CPU := rp2350_riscv + +FEATURES_PROVIDED += periph_uart +FEATURES_PROVIDED += periph_gpio +FEATURES_PROVIDED += periph_pmp diff --git a/boards/rpi-pico-2-riscv/Makefile.include b/boards/rpi-pico-2-riscv/Makefile.include new file mode 100644 index 000000000000..b440109c92d5 --- /dev/null +++ b/boards/rpi-pico-2-riscv/Makefile.include @@ -0,0 +1,7 @@ +CPU_MODEL := RP2350_RISCV +PORT_LINUX ?= /dev/ttyACM0 + +# JLink isnt tested yet on RP2350 +# ifeq ($(PROGRAMMER),jlink) +# JLINK_DEVICE = RP2350_M33_0 +# endif diff --git a/boards/rpi-pico-2-riscv/board.c b/boards/rpi-pico-2-riscv/board.c new file mode 100644 index 000000000000..49f7740789db --- /dev/null +++ b/boards/rpi-pico-2-riscv/board.c @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2025 Tom Hert + * SPDX-FileCopyrightText: 2025 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#include "board.h" + +void board_init(void) { + /* Re-enable the LED0 pin + * Otherwise the LED will not work after a reset + * This is needed, esp. when the LED is used via + * the define macros */ + gpio_init(LED0_PIN_ID, GPIO_OUT); +} diff --git a/boards/rpi-pico-2-riscv/dist/openocd.cfg b/boards/rpi-pico-2-riscv/dist/openocd.cfg new file mode 100644 index 000000000000..d645e30e8890 --- /dev/null +++ b/boards/rpi-pico-2-riscv/dist/openocd.cfg @@ -0,0 +1,6 @@ +echo "Make sure to use the Raspberry Pi OpenOCD version!" +source [find target/rp2350-riscv.cfg] +set USE_CORE SMP +set RESCUE 1 +$_TARGETNAME_0 configure -rtos auto +adapter speed 5000 diff --git a/boards/rpi-pico-2-riscv/doc.md b/boards/rpi-pico-2-riscv/doc.md new file mode 100644 index 000000000000..714afb1ebc26 --- /dev/null +++ b/boards/rpi-pico-2-riscv/doc.md @@ -0,0 +1,116 @@ +@defgroup boards_rpi_pico_2_riscv Raspberry Pi Pico 2 +@ingroup boards +@brief Support for the RP2350 RISCV based Raspberry Pi Pico board + +@warning The support for the Raspberry Pi Pico 2 is still in a very early stage! +See [Known Issues](#rpi_pico_2_riscv_known_issues). + +## Overview + +The Raspberry Pi Pico 2 is a microcontroller board based on the RP2350 chip, +featuring dual-core Arm Cortex-M0+ processors and RISC-V Hazard secondary +architecture. It is designed for a wide range of applications, +from hobbyist projects to professional embedded systems +for a fairly affordable price. + +![The Raspberry Pi Pico 2 Board](https://www.raspberrypi.com/documentation/microcontrollers/images/pico-2.png) + +## Hardware + +| MCU | RP2350 | +|:-----------|:------------------------------------------------------------| +| Family | Dual Cortex-M33 or Hazard3 (RISC-V) | +| Vendor | Raspberry Pi | +| RAM | 520 kB on-chip SRAM (10 independent banks) | +| Flash | Up to 16 MB external QSPI flash (Pico 2 has 4 MB by default)| +| Frequency | up to 150 MHz (Set to 125 MHz in RIOT) | +| Security | Boot signing, key storage, SHA-256 accelerator | +| PIOs | 12 state machines | +| UARTs | 2 | +| SPIs | 2 | +| I2Cs | 2 | +| PWM | 24 channels | +| USB | USB 1.1 controller with host and device support | +| Power | On-chip switched-mode power supply with LDO sleep mode | +| OTP | 8 kB of one-time-programmable storage | +| Datasheet | [RP2350 Datasheet](https://datasheets.raspberrypi.com/rp2350/rp2350-datasheet.pdf) | + +## User Interfaces + +| Interface | Description | +|:-----------|:-------------------------------------------------------------| +| LED0 | User LED (GPIO 0 at Pin 25) | +| SW0 | Button used in flash process, can be accessed using registers but difficult | + +## Pinout + +![Pinout Diagram](https://www.raspberrypi.com/documentation/microcontrollers/images/pico-2-r4-pinout.svg) + +## Flashing the Board + +The Raspberry Pi Pico 2 has a built-in bootloader that allows flashing via USB. +However, you can also use OpenOCD for flashing the board. +If you are using picotool, you need to hold the bootselect button +(the only button on the board) while connecting the board to +your computer via USB. This will put the board into bootloader mode, +allowing you to flash it. + +### Flashing using OpenOCD + +If you have two Raspberry Pi Pico boards, +you can utilize one as a programmer to program the other board. + +Please refer to the +[Debugprobe documentation](https://www.raspberrypi.com/documentation/microcontrollers/debug-probe.html#getting-started) +for more details. + +Note that Raspberry Pi actually uses their own OpenOCD fork, which is available +in the [RP2040 OpenOCD repository](https://github.com/raspberrypi/openocd). +While technically you can use the standard OpenOCD, +it is recommended to use the Raspberry Pi fork for better compatibility with the +RP2350, as its still fairly "new" and under development, +which is why even their own Pico SDK Extension +uses the Raspberry Pi fork of OpenOCD, instead of the standard one. + +To do this, you need to connect the board to your computer +and use the following command: + +```bash +PROGRAMMER=openocd BOARD=rpi-pico-2 make flash +``` + +You can then debug your application using GDB with the following command: + +```bash +PROGRAMMER=openocd BOARD=rpi-pico-2 make debug +``` + +### Flashing using Picotool + +Simply connect the board to your computer via USB and use the following command: + +```bash +BOARD=rpi-pico-2 make flash +``` + +This is the default method for flashing the Raspberry Pi Pico 2. +However, it does not allow for debugging using GDB. + +@note When programming the board with the Picotool for the first time, +RIOT will download and install the Picotool locally in the RIOT folder. +This process will take some minutes to complete. + +## Known Issues {#rpi_pico_2_riscv_known_issues} + +Currently RP2350 support is rather minimal, +as such peripheral support is extremely limited. +The following peripherals are supported: + +- GPIO +- Non-configurable write-only UART (UART0 using Pin 0 and 1) + - The UART Baudrate is set to 115200. + - UART does not work via USB, you need to connect it directly to the GPIO pins. + +More peripherals will be added in the future. +It should also be noted that we currently only support the Cortex M33 cores, +not the RISC-V Hazard cores. diff --git a/boards/rpi-pico-2-riscv/include/board.h b/boards/rpi-pico-2-riscv/include/board.h new file mode 100644 index 000000000000..fafbc14a88c9 --- /dev/null +++ b/boards/rpi-pico-2-riscv/include/board.h @@ -0,0 +1,42 @@ +/* + * SPDX-FileCopyrightText: 2025 Tom Hert + * SPDX-FileCopyrightText: 2025 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#pragma once + +/** + * @ingroup boards_rpi_pico_2_riscv + * @{ + * + * @file + * @brief Board specific definitions for the Raspberry Pi Pico 2 + * + * @author Tom Hert + */ + +#include "cpu.h" +#include "periph_conf.h" + +#include "periph/gpio.h" + +/** GPIO Pin ID for the onboard LED */ +#define LED0_PIN_ID 25u +#define LED0_ON gpio_set(LED0_PIN_ID) +#define LED0_OFF gpio_clear(LED0_PIN_ID) +#define LED0_TOGGLE gpio_toggle(LED0_PIN_ID) +#define LED0_NAME "LED(Green)" + +#ifdef __cplusplus +extern "C" { +#endif + +/** Initialize the board, called from the cpu startup code */ +void board_init(void); + +#ifdef __cplusplus +} +#endif + +/** @} */ diff --git a/boards/rpi-pico-2-riscv/include/gpio_params.h b/boards/rpi-pico-2-riscv/include/gpio_params.h new file mode 100644 index 000000000000..1998b8afaa24 --- /dev/null +++ b/boards/rpi-pico-2-riscv/include/gpio_params.h @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: 2025 Tom Hert + * SPDX-FileCopyrightText: 2025 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#pragma once + +#include "board.h" +#include "saul/periph.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif diff --git a/boards/rpi-pico-2-riscv/include/periph_conf.h b/boards/rpi-pico-2-riscv/include/periph_conf.h new file mode 100644 index 000000000000..45244ae12641 --- /dev/null +++ b/boards/rpi-pico-2-riscv/include/periph_conf.h @@ -0,0 +1,63 @@ +/* + * SPDX-FileCopyrightText: 2025 Tom Hert + * SPDX-FileCopyrightText: 2025 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#pragma once + +/** + * @ingroup boards_rpi_pico_2_riscv + * @{ + * + * @file + * @brief Board specific periph definitions + * for the Raspberry Pi Pico 2 + * + * @author Tom Hert + */ + +#include "kernel_defines.h" +#include +#include "periph_cpu.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Configuration details for an UART interface needed by the RPX0XX peripheral + @todo this is shared between both + */ +typedef struct { + UART0_Type *dev; /**< Base address of the I/O registers of the device */ + gpio_t rx_pin; /**< GPIO pin to use for RX */ + gpio_t tx_pin; /**< GPIO pin to use for TX */ + IRQn_Type irqn; /**< IRQ number of the UART interface */ +} uart_conf_t; + +static const uart_conf_t uart_config[] = { + { + .dev = UART0, + .rx_pin = GPIO_PIN(0, 1), + .tx_pin = GPIO_PIN(0, 0), + .irqn = UART0_IRQ_IRQn + }, + { + .dev = UART1, + .rx_pin = GPIO_PIN(0, 9), + .tx_pin = GPIO_PIN(0, 8), + .irqn = UART1_IRQ_IRQn + } +}; + +#define UART_0_ISR (isr_uart0) +#define UART_1_ISR (isr_uart1) + +#define UART_NUMOF ARRAY_SIZE(uart_config) + +#ifdef __cplusplus +} +#endif + +/** @} */ diff --git a/cpu/riscv_common/include/xh3irq.h b/cpu/riscv_common/include/xh3irq.h new file mode 100644 index 000000000000..a710379f48c9 --- /dev/null +++ b/cpu/riscv_common/include/xh3irq.h @@ -0,0 +1,67 @@ +/* + * SPDX-FileCopyrightText: 2025 Tom Hert + * SPDX-FileCopyrightText: 2025 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#pragma once + +#include +#include "cpu_conf.h" +#include "panic.h" + +/** + * @ingroup cpu_riscv_common + * @{ + * + * @file + * @brief xh3irq.h interrupt controller support + * + * @author Tom Hert + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** CPU specific interrupt vector table + * @see 3.2 Interrupts and IRQn_Type in RP2350.h + */ +extern const void *vector_cpu[CPU_IRQ_NUMOF]; + +/** + * @brief Check if there are any pending interrupts + * @return 1 if there are pending interrupts, 0 otherwise + */ +uint32_t xh3irq_has_pending(void); + +/** + * @brief The main IRQ handler, called from the assembly IRQ handler + * @note This function must clear the pending interrupt in the interrupt controller + */ +void xh3irq_handler(void); + +/** + * @brief Enable the given IRQ number + * @param irq_no The IRQ number to enable + */ +void xh3irq_enable_irq(uint32_t irq_no); + +/** + * @brief Disable the given IRQ number + * @param irq_no The IRQ number to disable + */ +void xh3irq_disable_irq(uint32_t irq_no); + +/** + * @brief Force the given IRQ number to be pending + * @param irq_no The IRQ number to force + * @note The IRQ still must be enabled to be handled + */ +void xh3irq_force_irq(uint32_t irq_no); + +#ifdef __cplusplus +} +#endif + +/** @} */ diff --git a/cpu/riscv_common/irq_arch.c b/cpu/riscv_common/irq_arch.c index f37b58e55050..0c1541cb8115 100644 --- a/cpu/riscv_common/irq_arch.c +++ b/cpu/riscv_common/irq_arch.c @@ -32,10 +32,14 @@ #include "clic.h" #include "architecture.h" +#if MODULE_PERIPH_XH3IRQ || DOXYGEN +#include "xh3irq.h" +#endif + #include "vendor/riscv_csr.h" /* Default state of mstatus register */ -#define MSTATUS_DEFAULT (MSTATUS_MPP | MSTATUS_MPIE) +#define MSTATUS_DEFAULT (MSTATUS_MPP | MSTATUS_MPIE) volatile int riscv_in_isr = 0; @@ -83,8 +87,7 @@ void riscv_irq_init(void) /** * @brief Global trap and interrupt handler */ -__attribute((used)) -static void handle_trap(uword_t mcause) +__attribute((used)) static void handle_trap(uword_t mcause) { /* Tell RIOT to set sched_context_switch_request instead of * calling thread_yield(). */ @@ -92,11 +95,37 @@ static void handle_trap(uword_t mcause) uword_t trap = mcause & CPU_CSR_MCAUSE_CAUSE_MSK; +#ifdef DEVELHELP + printf("Trap: mcause=0x%" PRIx32 " mepc=0x%lx mtval=0x%lx\n", + (uint32_t)mcause, read_csr(mepc), read_csr(mtval)); + + if ((mcause & ~MCAUSE_INT) <= 0xb) { + const char *error_messages[] = { + "Instruction alignment: Does not occur on RP2350, because 16-bit compressed instructions are implemented, and it is impossible to jump to a byte-aligned address.", + "Instruction fetch fault: Attempted to fetch from an address that does not support instruction fetch (like APB/AHB peripherals on RP2350), or lacks PMP execute permission, or is forbidden by ACCESSCTRL, or returned a fault from the memory device itself.", + "Illegal instruction: Encountered an instruction that was not a valid RISC-V opcode implemented by this processor, or attempted to access a nonexistent CSR, or attempted to execute a privileged instruction or access a privileged CSR without sufficient privilege.", + "Breakpoint: An ebreak or c.ebreak instruction was executed, and no external debug host caught it (DCSR.EBREAKM or DCSR.EBREAKU was not set).", + "Load alignment: Attempted to load from an address that was not a multiple of access size.", + "Load fault: Attempted to load from an address that does not exist, or lacks PMP read permissions, or is forbidden by ACCESSCTRL, or returned a fault from a peripheral.", + "Store/AMO alignment: Attempted to write to an address that was not a multiple of access size.", + "Store/AMO fault: Attempted to write to an address that does not exist, or lacks PMP write permissions, or is forbidden by ACCESSCTRL, or returned a fault from a peripheral. Also raised when attempting an AMO on an address that does not support AHB5 exclusives.", + "An ecall instruction was executed in U-mode.", + NULL, /* 0x9 - not defined */ + NULL, /* 0xa - not defined */ + "An ecall instruction was executed in M-mode." + }; + + uword_t cause_code = mcause & ~MCAUSE_INT; + if (cause_code <= 0xb && error_messages[cause_code] != NULL) { + printf("Error 0x%lx: %s\n", cause_code, error_messages[cause_code]); + } + } +#endif + /* Check for INT or TRAP */ if ((mcause & MCAUSE_INT) == MCAUSE_INT) { /* Cause is an interrupt - determine type */ switch (mcause & MCAUSE_CAUSE) { - #ifdef MODULE_PERIPH_CORETIMER case IRQ_M_TIMER: /* Handle timer interrupt */ @@ -108,6 +137,9 @@ static void handle_trap(uword_t mcause) if (IS_ACTIVE(MODULE_PERIPH_PLIC)) { plic_isr_handler(); } + if (IS_ACTIVE(MODULE_PERIPH_XH3IRQ)) { + xh3irq_handler(); + } break; default: @@ -123,8 +155,8 @@ static void handle_trap(uword_t mcause) } else { switch (trap) { - case CAUSE_USER_ECALL: /* ECALL from user mode */ - case CAUSE_MACHINE_ECALL: /* ECALL from machine mode */ + case CAUSE_USER_ECALL: /* ECALL from user mode */ + case CAUSE_MACHINE_ECALL: /* ECALL from machine mode */ { /* TODO: get the ecall arguments */ sched_context_switch_request = 1; @@ -197,7 +229,7 @@ static void __attribute__((interrupt)) trap_entry(void) "csrr a0, mcause \n" /* Call trap handler, a0 contains mcause before, and the return value after - * the call */ + * the call */ "call handle_trap \n" /* Load the sched_context_switch_request */ @@ -214,12 +246,12 @@ static void __attribute__((interrupt)) trap_entry(void) "no_sched: \n" /* Restore the thread stack pointer and check if a new thread must be - * scheduled */ + * scheduled */ "mv sp, s0 \n" /* No context switch required, shortcut to restore. a0 contains the return - * value of sched_run, or the sched_context_switch_request if the sched_run - * was skipped */ + * value of sched_run, or the sched_context_switch_request if the sched_run + * was skipped */ "beqz a0, no_switch \n" /* Skips the rest of the save if no active thread */ diff --git a/cpu/riscv_common/ldscripts/riscv_base.ld b/cpu/riscv_common/ldscripts/riscv_base.ld index c77f152a1963..0473f720aafe 100644 --- a/cpu/riscv_common/ldscripts/riscv_base.ld +++ b/cpu/riscv_common/ldscripts/riscv_base.ld @@ -41,6 +41,7 @@ SECTIONS .text : { + KEEP(*(SORT(.picobin_block*))) /* Keep picobin block used by RP2350 */ *(.text.unlikely .text.unlikely.*) *(.text.startup .text.startup.*) *(.text .text.*) diff --git a/cpu/riscv_common/periph/xh3irq.c b/cpu/riscv_common/periph/xh3irq.c new file mode 100644 index 000000000000..8bd4c2c7cf47 --- /dev/null +++ b/cpu/riscv_common/periph/xh3irq.c @@ -0,0 +1,57 @@ +/* + * SPDX-FileCopyrightText: 2025 Tom Hert + * SPDX-FileCopyrightText: 2025 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#include "xh3irq.h" +#include + +uint32_t xh3irq_has_pending(void) { + /* Get MEIP at 0x344 which is the external interrupt pending bit */ + uint32_t meip = (read_csr(0x344) >> 11) & 0x1; + + return (meip != 0); +} + +void xh3irq_handler(void) { + /* Get MEINEXT at 0xbe4 which is the next highest interrupt to handle (Bit 2-10). + * This will also automagically clear the interrupt (See 3.8.6.1.2.) */ + uint32_t meinext = (read_csr(0xbe4) >> 2) & 0x1ff; + + void (*isr)(void) = (void (*)(void))vector_cpu[meinext]; + printf("Calling isr %p for irq %ld\n", isr, meinext); + isr(); +} + +void _meiea_set_req_bit(uint32_t irq_no, uint32_t bit_val) { + uint32_t index = irq_no / 16; + uint32_t mask = bit_val << (irq_no % 16); + + __asm__ volatile( + "csrs 0xbe0, %0\n" + : : "r"(index | (mask << 16)) + + ); +} + +void xh3irq_enable_irq(uint32_t irq_no) +{ + _meiea_set_req_bit(irq_no, 1); +} + +void xh3irq_disable_irq(uint32_t irq_no) +{ + _meiea_set_req_bit(irq_no, 0); +} + +void xh3irq_force_irq(uint32_t irq_no) +{ + uint32_t index = irq_no / 16; + uint32_t mask = 1u << (irq_no % 16); + + __asm__ volatile( + "csrs 0xbe2, %0\n" + : : "r"(index | (mask << 16)) + ); +} diff --git a/cpu/rp2350_riscv/Makefile b/cpu/rp2350_riscv/Makefile new file mode 100644 index 000000000000..a0b6e87555a0 --- /dev/null +++ b/cpu/rp2350_riscv/Makefile @@ -0,0 +1,7 @@ +# define the module that is built +MODULE = cpu + +# add a list of subdirectories, that should also be built +DIRS = $(RIOTCPU)/riscv_common periph + +include $(RIOTBASE)/Makefile.base diff --git a/cpu/rp2350_riscv/Makefile.dep b/cpu/rp2350_riscv/Makefile.dep new file mode 100644 index 000000000000..9b356f1d4deb --- /dev/null +++ b/cpu/rp2350_riscv/Makefile.dep @@ -0,0 +1,5 @@ +USEMODULE += periph + +FEATURES_REQUIRED += periph_xh3irq + +include $(RIOTCPU)/riscv_common/Makefile.dep diff --git a/cpu/rp2350_riscv/Makefile.features b/cpu/rp2350_riscv/Makefile.features new file mode 100644 index 000000000000..8914a41a2f94 --- /dev/null +++ b/cpu/rp2350_riscv/Makefile.features @@ -0,0 +1,7 @@ +CPU_CORE := rv32imac + +FEATURES_PROVIDED += periph_gpio +FEATURES_PROVIDED += periph_uart +FEATURES_PROVIDED += periph_xh3irq + +include $(RIOTCPU)/riscv_common/Makefile.features diff --git a/cpu/rp2350_riscv/Makefile.include b/cpu/rp2350_riscv/Makefile.include new file mode 100644 index 000000000000..a903472fa12d --- /dev/null +++ b/cpu/rp2350_riscv/Makefile.include @@ -0,0 +1,33 @@ +CFLAGS += -Wno-pedantic + +ROM_LEN ?= 2097152 # = 2 MiB used in the RPi Pico +ROM_OFFSET := 0 # bootloader size +RAM_LEN := 0x82000 # 520kB = 532479 used in the RPi Pico 2350 +ROM_START_ADDR := 0x10000000 # XIP Non-Secure address for rp2350 +RAM_START_ADDR := 0x20000000 # Non-Secure RAM address for rp2350 + +INCLUDES += -I$(RIOTCPU)/rp2350_riscv/include +INCLUDES += -isystem$(RIOTBASE)/build/pkg/picosdk/src/rp2_common/cmsis/stub/CMSIS/Core/Include +INCLUDES += -isystem$(RIOTBASE)/build/pkg/picosdk/src/rp2_common/cmsis/stub/CMSIS/Device/RP2350/Include +INCLUDES += -isystem$(RIOTBASE)/build/pkg/picosdk/src/rp2350/hardware_regs/include/hardware + +# CPU and architecture specific flags +CFLAGS += -D$(CPU_MODEL) +CFLAGS += -DROM_START_ADDR=$(ROM_START_ADDR) +CFLAGS += -DRAM_START_ADDR=$(RAM_START_ADDR) +CFLAGS += -Wno-error +CFLAGS += -march=rv32imac_zicsr_zifencei_zba_zbb_zbkb_zbs +CFLAGS += -mabi=lp64 + +# Linker flags +LINKFLAGS += -mcpu=$(CPU_ARCH) +LINKFLAGS += -Wl,--gc-sections +LINKFLAGS += -Wl,--start-group -lc -lm -Wl,--end-group +LINKFLAGS += -march=rv32imac_zicsr_zifencei_zba_zbb_zbkb_zbs + +# Supported programmers and debuggers +PROGRAMMERS_SUPPORTED := picotool openocd jlink +PROGRAMMER ?= picotool +OPENOCD_DEBUG_ADAPTER ?= dap + +include $(RIOTCPU)/riscv_common/Makefile.include diff --git a/cpu/rp2350_riscv/clock.c b/cpu/rp2350_riscv/clock.c new file mode 100644 index 000000000000..af6047f88f02 --- /dev/null +++ b/cpu/rp2350_riscv/clock.c @@ -0,0 +1,71 @@ +/* + * SPDX-FileCopyrightText: 2025 Tom Hert + * SPDX-FileCopyrightText: 2025 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +/** + * @ingroup cpu_rp2350_riscv + * @{ + * + * @file + * @brief Clock configuration implementation for the RP2350 + * + * @author Tom Hert + */ + +#include "periph_cpu.h" + +void clock_reset(void) { + /* Reset the clock system */ + reset_component(RESET_PLL_SYS, RESET_PLL_SYS); +} + +/** + * @brief Configures the XOSC and then sets CLK_SYS, PLL_SYS and CLK_PERI to it + * @warning Make sure to call clock_reset() before this function to reset the + * clock system + * @see RP2350 Docs Chapter 8, mostly 8.2 for more details + */ +void cpu_clock_init(void) { + /* Enable the XOSC */ + xosc_start(); + + /* Setup the PLL using the XOSC as the reference clock. */ + PLL_SYS->FBDIV_INT = + PLL_FEEDBACK_DIVIDER_VALUE; /* Set the feedback divider */ + + /* Set the post-dividers for the PLL output.*/ + PLL_SYS->PRIM = PDIV; + /* Turn on PLL */ + atomic_clear(&PLL_SYS->PWR, + PLL_PWR_PD_BITS | PLL_PWR_VCOPD_BITS | PLL_PWR_POSTDIVPD_BITS); + + /* sleep 10ms to allow the PLL to stabilize */ + xosc_sleep(10); + + /* Based on the description in chapter 8 this is something that should be done + * However, it appears to cause issues and is not done by other examples on the + * internet. This needs to be investigated further. */ + + /* Wait for lock */ + /* while (!(PLL_SYS->CS & PLL_CS_LOCK_BITS)) { */ + /* Wait for the PLL to lock */ + /* } */ + + /* AUXSRC = 0x0 7:5 && SRC == 0x0 0 */ + CLOCKS->CLK_SYS_CTRL = CLK_SYS_PERI_CTRL_ENABLE_BIT; + + /* This register contains one decoded bit for each of the clock sources + * enumerated in the CTRL SRC field. The bit does not directly correlate with + * the value of the SRC field For example 0x0 is the first bit while 0x1 is + * the second bit. In some way this makes sense, in some way I lost too much + * time on this. */ + while (CLOCKS->CLK_SYS_SELECTED != CLK_SYS_SELECTED_PERI_FIELD_VALUE) { + } + + /* AUXSRC = 0x0 -> CLK_SYS Indirectly through lower line */ + CLOCKS->CLK_PERI_CTRL = CLK_PERI_CTRL_ENABLE_BIT; +} + +/** @} */ diff --git a/cpu/rp2350_riscv/cpu.c b/cpu/rp2350_riscv/cpu.c new file mode 100644 index 000000000000..5419f3463277 --- /dev/null +++ b/cpu/rp2350_riscv/cpu.c @@ -0,0 +1,59 @@ +/* + * SPDX-FileCopyrightText: 2025 Tom Hert + * SPDX-FileCopyrightText: 2025 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +/** + * @ingroup cpu_rp2350_riscv + * @{ + * + * @file cpu.c + * @brief Implementation of the CPU initialization for RP2350 + * + * @author Tom Hert + * @} + */ + +#include "board.h" +#include "cpu.h" +#include "clock_conf.h" +#include "kernel_init.h" +#include "periph/init.h" +#include "periph/uart.h" +#include "periph_conf.h" + +#include + +void gpio_reset(void) +{ + reset_component(RESET_PADS_BANK0, RESET_PADS_BANK0); + reset_component(RESET_IO_BANK0, RESET_IO_BANK0); +} + +/** + * @brief Initialize the CPU, set IRQ priorities, clocks, peripheral + */ +void cpu_init(void) +{ + /* Reset GPIO state */ + gpio_reset(); + + /* Reset clock to default state */ + clock_reset(); + + /* initialize the CPU clock */ + cpu_clock_init(); + + /* initialize the RISC-V core */ + riscv_init(); + + /* initialize the early peripherals */ + early_init(); + + /* trigger static peripheral initialization */ + periph_init(); + + /* initialize the board */ + board_init(); +} diff --git a/cpu/rp2350_riscv/doc.md b/cpu/rp2350_riscv/doc.md new file mode 100644 index 000000000000..f9d0b1664f2d --- /dev/null +++ b/cpu/rp2350_riscv/doc.md @@ -0,0 +1,5 @@ +@defgroup cpu_rp2350_riscv RP2350 RISCV MCUs +@ingroup cpu +@brief RP2350 RISCV MCU code and definitions + +This module contains the code and definitions for MCUs of the RP2350 RISCV family used by the Pi Pico 2. diff --git a/cpu/rp2350_riscv/include/clock_conf.h b/cpu/rp2350_riscv/include/clock_conf.h new file mode 100644 index 000000000000..e2609ff0c704 --- /dev/null +++ b/cpu/rp2350_riscv/include/clock_conf.h @@ -0,0 +1,159 @@ +/* + * SPDX-FileCopyrightText: 2025 Tom Hert + * SPDX-FileCopyrightText: 2025 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#pragma once + +/** + * @ingroup cpu_rp2350_riscv + * @{ + * + * @file + * @brief Clock configuration for the RP2350 + * + * @author Tom Hert + */ + +#include "RP2350.h" +#include "macros/units.h" + +/** 1-15 MHz range + * @see hardware/regs/xosc.h and chapter 8.2.8 + */ +#define XOSC_CTRL_FREQ_RANGE_VALUE_1_15MHZ 0xaa0u +/** 10-30 MHz range */ +#define XOSC_CTRL_FREQ_RANGE_VALUE_10_30MHZ 0xaa1u +/** 25-60 MHz range */ +#define XOSC_CTRL_FREQ_RANGE_VALUE_25_60MHZ 0xaa2u +/** 40-100 MHz range */ +#define XOSC_CTRL_FREQ_RANGE_VALUE_40_100MHZ 0xaa3u +/** Disable the XOSC */ +#define XOSC_CTRL_ENABLE_VALUE_DISABLE 0xd1eu +/** Enable the XOSC */ +#define XOSC_CTRL_ENABLE_VALUE_ENABLE 0xfabu +/** LSB of the enable bit */ +#define XOSC_CTRL_ENABLE_LSB 12u +/** Stable bit in the XOSC status register */ +#define XOSC_STATUS_STABLE_BITS 0x80000000u +/** Default crystal frequency is 12 MHz */ +#define XOSC_HZ MHZ(12u) +/** Reference divider for the PLL, set to 2 as per hardware manual */ +#define PLL_REF_DIV 2u +/** VCO frequency for the PLL, set to 750 MHz as per hardware manual */ +#define PLL_VCO_FREQ 750000000u +/** Post divider 1 for the PLL, set to 6 as per hardware manual */ +#define PLL_PD1 6u +/** Post divider 2 for the PLL, set to 2 as per hardware manual */ +#define PLL_PD2 2u +/** Power down bits for the PLL */ +#define PLL_PWR_PD_BITS 0x00000001u +/** VCO power down bits for the PLL */ +#define PLL_PWR_VCOPD_BITS 0x00000020u +/** Lock bit in the PLL control status register */ +#define PLL_CS_LOCK_BITS 0x80000000u +/** LSB of the post divider 1 in the PLL primary register */ +#define PLL_PRIM_POSTDIV1_LSB 16u +/** LSB of the post divider 2 in the PLL primary register */ +#define PLL_PRIM_POSTDIV2_LSB 12u +/** Post divider power down bits for the PLL */ +#define PLL_PWR_POSTDIVPD_BITS 0x00000008u +/** Enable bit for the peripheral clock control register */ +#define CLK_PERI_CTRL_ENABLE_BIT (1u << 11u) +/** Default CPU frequency in Hz, set to 125 MHz as per hardware manual */ +#define CPUFREQ 125000000u +/** Maximum crystal frequency */ +#define CLOCK_XOSC_MAX MHZ(15u) +/** Minimum crystal frequency */ +#define CLOCK_XOSC_MIN MHZ(5u) +/** Crystal frequency */ +#define CLOCK_XOSC (XOSC_HZ) +/** Minimum value of the post PLL clock divers */ +#define PLL_POSTDIV_MIN 1u +/** Maximum value of the post PLL clock divers */ +#define PLL_POSTDIV_MAX 7u +/** Minimum value of the PLL VCO feedback scaler */ +#define PLL_VCO_FEEDBACK_SCALE_MIN 16u +/** Maximum value of the PLL VCO feedback scaler */ +#define PLL_VCO_FEEDBACK_SCALE_MAX 320u +/** Minimum value of the clock divider applied before + * feeding in the reference clock into the PLL */ +#define PLL_REF_DIV_MIN 1u +/** Minimum value of the clock divider applied before feeding in + * the reference clock into the PLL */ +#define PLL_REF_DIV_MAX 1u +/** PLL feedback divider value, set to 125 as per hardware manual */ +#define PLL_FEEDBACK_DIVIDER_VALUE 125u +/** Enable bit for the system clock control register to select the peripheral + * clock */ +#define CLK_SYS_PERI_CTRL_ENABLE_BIT (1u << 0u) +/** Selected field value for the system clock control register + * to select the peripheral clock */ +#define CLK_SYS_SELECTED_PERI_FIELD_VALUE 2u +/** RIOT core clock frequency defined as the CPU frequency */ +#define CLOCK_CORECLOCK MHZ(12u) + +#if (PLL_VCO_FEEDBACK_SCALE_MIN < PLL_VCO_FEEDBACK_SCALE_MIN) || \ +(PLL_VCO_FEEDBACK_SCALE_MAX > PLL_VCO_FEEDBACK_SCALE_MAX) +# error "Value for PLL_VCO_FEEDBACK_SCALE out of range, check config" +#endif +#if (PLL_REF_DIV_MIN < PLL_REF_DIV_MIN) || (PLL_REF_DIV_MAX > PLL_REF_DIV_MAX) +# error "Value for PLLxosc_sleep_REF_DIV out of range, check config" +#endif +#if (PLL_POSTDIV_MIN < PLL_POSTDIV_MIN) || (PLL_POSTDIV_MAX > PLL_POSTDIV_MAX) +# error "Value for PLL_POSTDIV out of range, check config" +#endif +#if ((CLOCK_XOSC > CLOCK_XOSC_MAX) || (CLOCK_XOSC < CLOCK_XOSC_MIN)) +# error "Value for CLOCK_XOSC out of range, check config" +#endif + +/** Post divider for the PLL, calculated based on the post divider values */ +#define PDIV ((PLL_PD1 << PLL_PRIM_POSTDIV1_LSB) | (PLL_PD2 << PLL_PRIM_POSTDIV2_LSB)) +/** Feedback divider for the PLL, calculated based on the VCO frequency and +* reference clock frequency */ +#define FBDIV ((PLL_VCO_FREQ / XOSC_HZ) / PLL_REF_DIV) + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Configures the Crystal to run. + * @note The reference hardware manual suggests to use a 12 MHz crystal, which we + * use by default. + */ +void xosc_start(void); + +/** + * @brief Stop the crystal. + */ +void xosc_stop(void); + +/** + * @brief Sleep for a given time in milliseconds. + * @param milliseconds The time to sleep in milliseconds. + */ +void xosc_sleep(uint32_t milliseconds); + +/** + * @brief Reset the clock system. + * + * This function resets the clock system to a known state. + * It is recommended to call this function before configuring the clock system. + */ +void clock_reset(void); + +/** + * @brief Configures the XOSC and then sets CLK_SYS, PLL_SYS and CLK_PERI to it + * @pre Make sure to call clock_reset() before this function to reset the + * clock system + * @see RP2350 Docs Chapter 8, mostly 8.2 for more details + */ +void cpu_clock_init(void); + +#ifdef __cplusplus +} +#endif + +/** @} */ diff --git a/cpu/rp2350_riscv/include/core_cm33.h b/cpu/rp2350_riscv/include/core_cm33.h new file mode 100644 index 000000000000..6ae37e6c4c65 --- /dev/null +++ b/cpu/rp2350_riscv/include/core_cm33.h @@ -0,0 +1,19 @@ +/* + * SPDX-FileCopyrightText: 2025 Tom Hert + * SPDX-FileCopyrightText: 2025 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#define __IM volatile const /*! Defines 'read only' structure member permissions */ +#define __OM volatile /*! Defines 'write only' structure member permissions */ +#define __IOM volatile /*! Defines 'read / write' structure member permissions */ + +#ifdef __cplusplus +} +#endif diff --git a/cpu/rp2350_riscv/include/cpu.h b/cpu/rp2350_riscv/include/cpu.h new file mode 100644 index 000000000000..d995f19efcc7 --- /dev/null +++ b/cpu/rp2350_riscv/include/cpu.h @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: 2025 Tom Hert git@annsann.eu> + * SPDX-FileCopyrightText: 2025 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#pragma once + +/** + * @ingroup cpu_rp2350_riscv + * @{ + * + * @file + * @brief CPU specific definitions + */ + +#include "cpu_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +/** @} */ diff --git a/cpu/rp2350_riscv/include/cpu_conf.h b/cpu/rp2350_riscv/include/cpu_conf.h new file mode 100644 index 000000000000..28f799c0976e --- /dev/null +++ b/cpu/rp2350_riscv/include/cpu_conf.h @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2025 Tom Hert git@annsann.eu> + * SPDX-FileCopyrightText: 2025 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#pragma once + +/** + * @ingroup cpu_rp2350_riscv + * @{ + * @file + * @brief CPU configuration for the RP2350 + * + * @author Tom Hert + */ + +#include "cpu_conf_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define CPU_DEFAULT_IRQ_PRIO 1u +#define CPU_IRQ_NUMOF 52u + +#ifdef __cplusplus +} +#endif + +/** @} */ diff --git a/cpu/rp2350_riscv/include/gpio_conf.h b/cpu/rp2350_riscv/include/gpio_conf.h new file mode 100644 index 000000000000..4408392c00ad --- /dev/null +++ b/cpu/rp2350_riscv/include/gpio_conf.h @@ -0,0 +1,66 @@ +/* + * SPDX-FileCopyrightText: 2025 Tom Hert git@annsann.eu> + * SPDX-FileCopyrightText: 2025 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#pragma once + +/** + * @ingroup cpu_rp2350_riscv + * @{ + * + * @file + * @brief GPIO configuration for the RP2350 + * + * @author Tom Hert + */ + +/** The number of GPIO pins available on the RP2350 */ +#define GPIO_PIN_NUMOF 30u + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Possible function values for @ref gpio_io_ctrl_t::function_select + */ +typedef enum { + /** connect pin to the SPI peripheral (MISO/MOSI/SCK depends on pin) */ + FUNCTION_SELECT_SPI = 1, + + /** connect pin to the UART peripheral (TXD/RXD depends on pin) */ + FUNCTION_SELECT_UART = 2, + + /** connect pin to the I2C peripheral (SCL/SDA depends on pin) */ + FUNCTION_SELECT_I2C = 3, + + /** connect pin to the timer for PWM (channel depends on pin) */ + FUNCTION_SELECT_PWM = 4, + + /** use pin as vanilla GPIO */ + FUNCTION_SELECT_SIO = 5, + + /** connect pin to the first PIO peripheral */ + FUNCTION_SELECT_PIO0 = 6, + + /** connect pin to the second PIO peripheral */ + FUNCTION_SELECT_PIO1 = 7, + + /** connect pin to the timer (depending on pin: external clock, + * clock output, or not supported) */ + FUNCTION_SELECT_CLOCK = 8, + + /** connect pin to the USB peripheral (function depends on pin) */ + FUNCTION_SELECT_USB = 9, + + /** Reset value, pin unconnected */ + FUNCTION_SELECT_NONE = 31, +} gpio_function_select_t; + +#ifdef __cplusplus +} +#endif + +/** @} */ diff --git a/cpu/rp2350_riscv/include/helpers.h b/cpu/rp2350_riscv/include/helpers.h new file mode 100644 index 000000000000..f88b7d0950a3 --- /dev/null +++ b/cpu/rp2350_riscv/include/helpers.h @@ -0,0 +1,78 @@ +/* + * SPDX-FileCopyrightText: 2025 Tom Hert + * SPDX-FileCopyrightText: 2025 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#pragma once + +/** + * @ingroup cpu_rp2350_riscv + * @{ + * + * @file + * @brief Helper functions for atomic register operations + * + * @author Tom Hert + */ + +/** Bit to be set for an atomic XOR operation */ +#define ATOMIC_XOR_WRITE 0x1000u +/** Bit to be set for an atomic set operation */ +#define ATOMIC_BITMASK_SET_WRITE 0x2000u +/** Bits to be set for an atomic clear operation */ +#define ATOMIC_BITMASK_CLEAR_WRITE 0x3000u + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Perform an atomic XOR write to a register + * + * @param[in,out] reg Pointer to the target register + * @param[in] val Value to be XORed with the register + */ +static inline void atomic_xor(volatile uint32_t *reg, uint32_t val) { + *(volatile uint32_t *)((uintptr_t)reg | ATOMIC_XOR_WRITE) = val; +} + +/** + * @brief Set bits in a register atomically + * + * @param[in,out] reg Pointer to the target register + * @param[in] val Bit mask of bits to set + */ +static inline void atomic_set(volatile uint32_t *reg, uint32_t val) { + *(volatile uint32_t *)((uintptr_t)reg | ATOMIC_BITMASK_SET_WRITE) = val; +} + +/** + * @brief Clear bits in a register atomically + * + * @param[in,out] reg Pointer to the target register + * @param[in] val Bit mask of bits to clear + */ +static inline void atomic_clear(volatile uint32_t *reg, uint32_t val) { + *(volatile uint32_t *)((uintptr_t)reg | ATOMIC_BITMASK_CLEAR_WRITE) = val; +} + +/** + * @brief Reset a component by clearing its reset bits and waiting for the reset to complete + * + * @param reset_value Bit mask of the reset bits to clear + * @param reset_done_value Bit mask of the reset done bits to wait for + */ +static inline void reset_component(uint32_t reset_value, + uint32_t reset_done_value) { + atomic_clear(&RESETS->RESET, reset_value); + while (~RESETS->RESET_DONE & reset_done_value) { + /* Wait for the reset to complete */ + } +} + +#ifdef __cplusplus +} +#endif + +/** @} */ diff --git a/cpu/rp2350_riscv/include/periph_cpu.h b/cpu/rp2350_riscv/include/periph_cpu.h new file mode 100644 index 000000000000..4a0f3a3bf2df --- /dev/null +++ b/cpu/rp2350_riscv/include/periph_cpu.h @@ -0,0 +1,115 @@ +/* + * SPDX-FileCopyrightText: 2025 Tom Hert + * SPDX-FileCopyrightText: 2025 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#pragma once + +/** + * @ingroup cpu_rp2350_riscv + * @{ + * + * @file + * @brief Peripheral CPU definitions for the RP2350 + * + * @author Tom Hert + */ + +#include + +/** Overwrite the default GPIO type to use uint32_t */ +#define HAVE_GPIO_T +typedef uint32_t gpio_t; + +/** + * @brief Macro to create a GPIO pin identifier + * @param port The GPIO port (Currently only GPIO0) + * @param pin The GPIO pin number + */ +#define GPIO_PIN(port, pin) (((port) & 0) | (pin)) + +/** This is a define used throughout the pico sdk */ +#define _u(x) ((uint32_t)(x)) + +#include "periph_cpu_common.h" +#include "cpu.h" +#include "core_cm33.h" /* Trick RP2350 into believing the file exists on RISCV */ +#include "RP2350.h" +#include "helpers.h" +#include "gpio_conf.h" +#include "clock_conf.h" +#include "uart_conf.h" +#include "xh3irq.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** GPIO Pin ID for oscillator debugging */ +#define OSC_DEBUG_PIN_ID 15u + +/** Reset bit for the system PLL */ +#define RESET_PLL_SYS (1u << 14u) + +/** Reset bit for the pads bank 0 */ +#define RESET_PADS_BANK0 (1u << 9u) + +/** Reset bit for UART0 peripheral */ +#define RESET_UART0 (1u << 26u) + +/** Reset bit for UART1 peripheral */ +#define RESET_UART1 (1u << 27u) + +/** Reset bit for the IO bank 0 */ +#define RESET_IO_BANK0 (1u << 6u) + +/** Input enable bit for GPIO0 in PADS_BANK0 */ +#define PADS_BANK0_GPIO0_IE_BITS (1u << 6u) + +/** Isolation bits for PADS_BANK0 */ +#define PADS_BANK0_ISO_BITS (1u << 8u) + + +/** + * @brief Calculate the address of the GPIO pad register for a given pin + * @param pin The GPIO pin number + * @return The address of the GPIO pad register for the given pin + */ +static inline uint32_t calculate_gpio_pad_register_addr(gpio_t pin) { + /* Each pin has a 4 byte register, so we can calculate the address + * by adding 4 bytes for each pin, starting at the base address of PADS_BANK0 + * and adding 4 bytes to skip VOLTAGE_SELECT */ + return PADS_BANK0_BASE + 4 * (pin + 1); +} + +/** + * @brief Calculate the address of the GPIO IO status register for a given pin + * @param pin The GPIO pin number + * @return The address of the GPIO IO status register for the given pin + */ +static uint32_t calculate_gpio_io_status_register_addr(gpio_t pin) { + /* Each status register is followed by a ctrl register */ + return IO_BANK0_BASE + 8 * pin; +} + +/** + * @brief Calculate the address of the GPIO IO control register for a given + * pin + * @param pin The GPIO pin number + * @return The address of the GPIO IO control register for the given pin + */ +static inline uint32_t calculate_gpio_io_ctrl_register_addr(gpio_t pin) { + /* Each pin has a 8 byte register (4 Bytes of Status, 4 Bytes of CTRL), + * so we can calculate the address by adding 8 bytes for each pin, + * starting at the base address of IO_BANK0 */ + return calculate_gpio_io_status_register_addr(pin) + 4; +} + + + +#ifdef __cplusplus +} +#endif + +/** @} */ diff --git a/cpu/rp2350_riscv/include/uart_conf.h b/cpu/rp2350_riscv/include/uart_conf.h new file mode 100644 index 000000000000..f886e974bc3c --- /dev/null +++ b/cpu/rp2350_riscv/include/uart_conf.h @@ -0,0 +1,55 @@ +/* + * SPDX-FileCopyrightText: 2025 Tom Hert + * SPDX-FileCopyrightText: 2025 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +#pragma once + +/** + * @ingroup cpu_rp2350_riscv + * @{ + * + * @file + * @brief UART configuration for the RP2350 + * + * @author Tom Hert + */ + +#include "RP2350.h" +#include "macros/units.h" +#include "periph_cpu.h" + +/** UART baud rate in bits per second */ +#define BAUDRATE 115200u + +/** Integer baud rate divisor */ +#define IBRD ((((8u * CPUFREQ) + BAUDRATE) / (2u * BAUDRATE)) / 64u) + +/** Fractional baud rate divisor */ +#define FBRD ((((8u * CPUFREQ) + BAUDRATE) / (2u * BAUDRATE)) % 64u) + +/** UART enable bit in control register */ +#define UART_UARTCR_UARTEN_BITS (1u << 0u) + +/** UART receive enable bit in control register */ +#define UART_UARTCR_RXE_BITS (1u << 9u) + +/** UART transmit enable bit in control register */ +#define UART_UARTCR_TXE_BITS (1u << 8u) + +/** UART receive FIFO full flag bit in flag register */ +#define UART_UARTFR_RXFF_BITS (1u << 6u) + +/** UART transmit FIFO empty flag bit in flag register */ +#define UART_UARTFR_TXFE_BITS (1u << 7u) + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef __cplusplus +} +#endif + +/** @} */ diff --git a/cpu/rp2350_riscv/periph/Makefile b/cpu/rp2350_riscv/periph/Makefile new file mode 100644 index 000000000000..a36df249ac1d --- /dev/null +++ b/cpu/rp2350_riscv/periph/Makefile @@ -0,0 +1 @@ +include $(RIOTMAKE)/periph.mk diff --git a/cpu/rp2350_riscv/periph/gpio.c b/cpu/rp2350_riscv/periph/gpio.c new file mode 100644 index 000000000000..4ba74bbfa513 --- /dev/null +++ b/cpu/rp2350_riscv/periph/gpio.c @@ -0,0 +1,96 @@ +/* + * SPDX-FileCopyrightText: 2025 Tom Hert + * SPDX-FileCopyrightText: 2025 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +/** + * @ingroup cpu_rp2350_riscv + * @{ + * + * @file + * @brief GPIO implementation for the RP2350 + * + * @author Tom Hert + */ + +#include "periph/gpio.h" + +#include + +#include "board.h" +#include "irq.h" +#include "periph_conf.h" +#include "periph_cpu.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +int gpio_init(gpio_t pin, gpio_mode_t mode) { + /* Check if we exceed the maximum number of GPIO pins */ + assert(pin < GPIO_PIN_NUMOF); + + /* Clear the pin's output enable and output state */ + SIO->GPIO_OE_CLR = 1LU << pin; + SIO->GPIO_OUT_CLR = 1LU << pin; + + switch (mode) { + case GPIO_OUT: + *(uint32_t*)calculate_gpio_io_ctrl_register_addr(pin) = + FUNCTION_SELECT_SIO; + + volatile uint32_t* pad_reg = + (uint32_t*)calculate_gpio_pad_register_addr(pin); + + /* We clear all bits except the drive strength bit. + * We set that to the highest one possible (12mA) + * to mimic the behavior of the pico1 GPIO driver + * (Not too sure why we do this, but it seems to be the standard) */ + *pad_reg = 0x3 << 4; + + SIO->GPIO_OE_SET = 1 << pin; /* Set the pin as output */ + + break; + default: + /* Unsupported mode */ + return -ENOTSUP; + } + return 0; +} + +bool gpio_read(gpio_t pin) { + /* Check if we exceed the maximum number of GPIO pins */ + assert(pin < GPIO_PIN_NUMOF); + /* Read the pin state */ + return (SIO->GPIO_IN & (1 << pin)) != 0; /* Return true if the pin is HIGH */ +} + +void gpio_set(gpio_t pin) { + /* Check if we exceed the maximum number of GPIO pins */ + assert(pin < GPIO_PIN_NUMOF); + SIO->GPIO_OUT_SET = 1 << pin; /* Set the pin to HIGH */ +} + +void gpio_clear(gpio_t pin) { + /* Check if we exceed the maximum number of GPIO pins */ + assert(pin < GPIO_PIN_NUMOF); + SIO->GPIO_OUT_CLR = 1 << pin; /* Set the pin to LOW */ +} + +void gpio_toggle(gpio_t pin) { + /* Check if we exceed the maximum number of GPIO pins */ + assert(pin < GPIO_PIN_NUMOF); + SIO->GPIO_OUT_XOR = 1 << pin; /* Toggle the pin state (XOR) */ +} + +void gpio_write(gpio_t pin, bool value) { + /* Check if we exceed the maximum number of GPIO pins */ + assert(pin < GPIO_PIN_NUMOF); + if (value) { + gpio_set(pin); /* Set the pin to HIGH */ + } else { + gpio_clear(pin); /* Set the pin to LOW */ + } +} + +/** @} */ diff --git a/cpu/rp2350_riscv/periph/uart.c b/cpu/rp2350_riscv/periph/uart.c new file mode 100644 index 000000000000..ca79a626e59c --- /dev/null +++ b/cpu/rp2350_riscv/periph/uart.c @@ -0,0 +1,231 @@ +/* + * SPDX-FileCopyrightText: 2025 Tom Hert + * SPDX-FileCopyrightText: 2025 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +/** + * @ingroup cpu_rp2350 + * @{ + * + * @file + * @brief UART implementation for the RP2350 + * + * @author Tom Hert + */ + +#include "periph/uart.h" + +#include "periph_cpu.h" + +#include "regs/uart.h" +#include + +#define ENABLE_DEBUG 0 +#include "debug.h" + +#include "xh3irq.h" + +#include "board.h" + +static uart_isr_ctx_t ctx[UART_NUMOF]; + +/* back up values of registers used during uart_poweroff() / uart_poweron() */ +static uint32_t uartibrd; +static uint32_t uartfbrd; +static uint32_t uartlcr_h; +static uint32_t uartcr; + +/** Pico1 uart uses non-sdk conform defines + * @todo Change Pico1 defines if I ever get around to it + */ +#define UART0_UARTIMSC_RXIM_Msk (UART_UARTIMSC_RXIM_BITS) + +void _irq_enable(uart_t uart) { + UART0_Type *dev = uart_config[uart].dev; + /* We set the UART Receive Interrupt Mask (Bit 4) [See p979 UART 12.1]*/ + dev->UARTIMSC = UART0_UARTIMSC_RXIM_Msk; + /* Enable the IRQ in the NVIC */ + xh3irq_enable_irq(uart_config[uart].irqn); +} + +int uart_mode(uart_t uart, uart_data_bits_t data_bits, uart_parity_t parity, + uart_stop_bits_t stop_bits) { + assert((unsigned)uart < UART_NUMOF); + UART0_Type *dev = uart_config[uart].dev; + + (void)data_bits; + (void)stop_bits; + + /* Disable the UART before changing the mode */ + atomic_clear(&dev->UARTCR, UART_UARTCR_UARTEN_BITS | UART_UARTCR_RXE_BITS | + UART_UARTCR_TXE_BITS); + + /* Set the data bits, parity, and stop bits + * Set to 8 bits (0b11) based on Table 1035 page 976 + * @todo allow different data bits + */ + dev->UARTLCR_H = 0b11 << 5; + + switch (parity) { + case UART_PARITY_NONE: + break; + // case UART_PARITY_EVEN: + // io_reg_atomic_set(&dev->UARTLCR_H, UART0_UARTLCR_H_EPS_Msk | UART0_UARTLCR_H_PEN_Msk); + // break; + // case UART_PARITY_ODD: + // io_reg_atomic_set(&dev->UARTLCR_H, UART0_UARTLCR_H_PEN_Msk); + // break; + default: + return UART_NOMODE; + } + + dev->UARTCR = UART_UARTCR_TXE_BITS | UART_UARTCR_UARTEN_BITS | UART_UARTCR_RXE_BITS; + + return UART_OK; +} + +static void _reset_uart(uart_t uart) { + switch (uart) { + case 0: + /* We reset UART0 here, so we can be sure it is in a known state */ + reset_component(RESET_UART0, RESET_UART0); + break; + case 1: + /* We reset UART1 here, so we can be sure it is in a known state */ + reset_component(RESET_UART1, RESET_UART1); + break; + default: + break; + } +} + +void uart_init_pins(uart_t uart) { + assert((unsigned)uart < UART_NUMOF); + UART0_Type *dev = uart_config[uart].dev; + + /* Set the UART pins to the correct function */ + *(uint32_t *)calculate_gpio_io_ctrl_register_addr(uart_config[uart].tx_pin) = FUNCTION_SELECT_UART; + *(uint32_t *)calculate_gpio_io_ctrl_register_addr(uart_config[uart].rx_pin) = FUNCTION_SELECT_UART; + /* Clear the ISO bits */ + atomic_clear((uint32_t *)calculate_gpio_pad_register_addr(uart_config[uart].tx_pin), PADS_BANK0_ISO_BITS); + atomic_clear((uint32_t *)calculate_gpio_pad_register_addr(uart_config[uart].rx_pin), PADS_BANK0_ISO_BITS); + + /* Set Input Enable Flag */ + atomic_set((uint32_t *)calculate_gpio_pad_register_addr(uart_config[uart].rx_pin), PADS_BANK0_GPIO0_IE_BITS); + + /* We reset UART0 here, so we can be sure it is in a known state */ + _reset_uart(uart); + + dev->UARTIBRD = IBRD; + dev->UARTFBRD = FBRD; +} + +int uart_init(uart_t uart, uint32_t baud, uart_rx_cb_t rx_cb, void *arg) { + (void)baud; + + if (uart >= UART_NUMOF) { + return UART_NODEV; + } + + UART0_Type *dev = uart_config[uart].dev; + ctx[uart].rx_cb = rx_cb; + ctx[uart].arg = arg; + + uart_init_pins(uart); + + if (uart_mode(uart, UART_DATA_BITS_8, UART_PARITY_NONE, UART_STOP_BITS_1) != UART_OK) { + return UART_NOMODE; + } + + /* enable RX and IRQs, if needed */ + if (rx_cb != NULL) { + _irq_enable(uart); + /* clear any pending data and IRQ to avoid receiving a garbage char */ + uint32_t status = dev->UARTRIS; + dev->UARTICR = status; + (void)dev->UARTDR; + atomic_set(&dev->UARTCR, UART_UARTCR_RXE_BITS); + } + + return UART_OK; +} + +void uart_write(uart_t uart, const uint8_t *data, size_t len) { + UART0_Type *dev = uart_config[uart].dev; + for (size_t i = 0; i < len; i++) { + dev->UARTDR = data[i]; + /* Wait until the TX FIFO is empty before sending the next byte */ + while (!(dev->UARTFR & UART_UARTFR_TXFE_BITS)) { + } + } +} + +void uart_poweron(uart_t uart) { + assert((unsigned)uart < UART_NUMOF); + /* Get into a save state where we know whats up */ + _reset_uart(uart); + UART0_Type *dev = uart_config[uart].dev; + /* Restore config from registers */ + dev->UARTIBRD = uartibrd; + dev->UARTFBRD = uartfbrd; + dev->UARTLCR_H = uartlcr_h; + dev->UARTCR = uartcr; + /* restore IRQs, if needed */ + if (ctx[uart].rx_cb != NULL) { + _irq_enable(uart); + } + uart_init_pins(uart); +} + +void uart_deinit_pins(uart_t uart) { + assert((unsigned)uart < UART_NUMOF); + /* @TODO */ + /* gpio_reset_all_config(uart_config[uart].tx_pin); */ + SIO->GPIO_OE_CLR = 1LU << uart_config[uart].tx_pin; + if (ctx[uart].rx_cb) { + /* gpio_reset_all_config(uart_config[uart].rx_pin); */ + } +} + +void uart_poweroff(uart_t uart) { + assert((unsigned)uart < UART_NUMOF); + UART0_Type *dev = uart_config[uart].dev; + /* backup configuration registers */ + uartibrd = dev->UARTIBRD; + uartfbrd = dev->UARTFBRD; + uartlcr_h = dev->UARTLCR_H; + uartcr = dev->UARTCR; + /* disconnect GPIOs and power off peripheral */ + uart_deinit_pins(uart); + _reset_uart(uart); +} + +void isr_handler(uint8_t num) { + UART0_Type *dev = uart_config[num].dev; + + uint32_t status = dev->UARTMIS; + dev->UARTICR = status; + + if (status & UART_UARTMIS_RXMIS_BITS) { + uint32_t data = dev->UARTDR; + // if (data & (UART0_UARTDR_BE_Msk | UART0_UARTDR_PE_Msk | UART0_UARTDR_FE_Msk)) { + // DEBUG_PUTS("[rpx0xx] uart RX error (parity, break, or framing error"); + // } + // else { + printf("UART%d received: %c\n", num, (char)(data & 0xFF)); + ctx[num].rx_cb(ctx[num].arg, (uint8_t)data); + // } + } +} + +/** Overwrites the WEAK_DEFAULT isr_uart0 */ +void isr_uart0(void) { + isr_handler(0); +} + +void isr_uart1(void) { + isr_handler(1); +} + +/** @} */ diff --git a/cpu/rp2350_riscv/picobin_block.s b/cpu/rp2350_riscv/picobin_block.s new file mode 100644 index 000000000000..5f190452377a --- /dev/null +++ b/cpu/rp2350_riscv/picobin_block.s @@ -0,0 +1,37 @@ +/* + * SPDX-FileCopyrightText: 2025 Tom Hert + * SPDX-FileCopyrightText: 2025 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +/* Picobin block required for the binary */ +/* This defines the minimum viable image metadata to be recognized by the RP2350 bootloader */ +/* based on RP2350 Chapter 5.9.1 */ + +.section .picobin_block, "a" /* "a" means "allocatable" (can be moved by the linker) */ + +/* PICOBIN_BLOCK_MARKER_START */ +.word 0xffffded3 + /* ITEM 0 START based on 5.9.3.1 */ + .byte 0x42 /* (size_flag == 0, item_type == PICOBIN_BLOCK_ITEM_1BS_IMAGE_TYPE) */ + .byte 0x1 /* Block Size in words */ + /* image_type_flags (2 bytes) [See 5.9.3.1 / p419] */ + /* 15 -> 0 (1 for "Try before you buy" image [Wacky] */ + /* 12-14 -> 001 (RP2350 = 1) */ + /* 11 -> 0 (Reserved) */ + /* 8-10 -> 001 (EXE_CPU_ARM == 000) || (EXE_CPU_RISCV == 001^) */ + /* 6-7 -> 00 (Reserved) */ + /* 4-5 -> 10 (2) EXE Security + * (As far as I understand we cant run in EXE_SECURITY_NS on the RP2350) + * thus EXE_SECURITY_S = 2 + */ + /* 0-3 // 0001 IMAGE_TYPE_EXE */ + .hword 0b0001000100100001 + /* ITEM 0 END see 5.1.5.1 for explanation and 5.9.5.1 for the value / structure */ + .byte 0xff /* PICOBIN_BLOCK_ITEM_2BS_LAST */ + .hword 0x0001 /* Size of the item in words (predefined value) */ + .byte 0x00 /* Padding */ + /* Next Block Pointer */ + .word 0x00000000 /* Next block pointer (0 means no more blocks) */ +/* PICOBIN_BLOCK_MARKER_END */ +.word 0xab123579 /* Marker for the end of the picobin block */ diff --git a/cpu/rp2350_riscv/vectors.c b/cpu/rp2350_riscv/vectors.c new file mode 100644 index 000000000000..0f3df6495478 --- /dev/null +++ b/cpu/rp2350_riscv/vectors.c @@ -0,0 +1,141 @@ +/* + * SPDX-FileCopyrightText: 2025 Tom Hert + * SPDX-FileCopyrightText: 2025 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +/** + * @ingroup cpu_rp2350 + * @{ + * + * @file + * @brief Interrupt vector table for the RP2350 + * + * @author Tom Hert + */ + +#include "cpu_conf.h" +#include "xh3irq.h" +#include "panic.h" +#include + +#define WEAK_DEFAULT __attribute__((weak, alias("dummy_handler"))) + +/* define a local dummy handler as it needs to be in the same compilation unit + * as the alias definition */ +void dummy_handler(void) +{ + core_panic(PANIC_GENERAL_ERROR, "DUMMY HANDLER"); +} + +/* rp2350 specific interrupt vector */ +WEAK_DEFAULT void isr_timer0_0(void); +WEAK_DEFAULT void isr_timer0_1(void); +WEAK_DEFAULT void isr_timer0_2(void); +WEAK_DEFAULT void isr_timer0_3(void); +WEAK_DEFAULT void isr_timer1_0(void); +WEAK_DEFAULT void isr_timer1_1(void); +WEAK_DEFAULT void isr_timer1_2(void); +WEAK_DEFAULT void isr_timer1_3(void); +WEAK_DEFAULT void isr_pwm_wrap_0(void); +WEAK_DEFAULT void isr_pwm_wrap_1(void); +WEAK_DEFAULT void isr_dma_0(void); +WEAK_DEFAULT void isr_dma_1(void); +WEAK_DEFAULT void isr_dma_2(void); +WEAK_DEFAULT void isr_dma_3(void); +WEAK_DEFAULT void isr_usbctrl(void); +WEAK_DEFAULT void isr_pio0_0(void); +WEAK_DEFAULT void isr_pio0_1(void); +WEAK_DEFAULT void isr_pio1_0(void); +WEAK_DEFAULT void isr_pio1_1(void); +WEAK_DEFAULT void isr_pio2_0(void); +WEAK_DEFAULT void isr_pio2_1(void); +WEAK_DEFAULT void isr_io_bank0(void); +WEAK_DEFAULT void isr_io_bank0_ns(void); +WEAK_DEFAULT void isr_io_qspi(void); +WEAK_DEFAULT void isr_io_qspi_ns(void); +WEAK_DEFAULT void isr_sio_fifo(void); +WEAK_DEFAULT void isr_sio_bell(void); +WEAK_DEFAULT void isr_sio_fifo_ns(void); +WEAK_DEFAULT void isr_sio_bell_ns(void); +WEAK_DEFAULT void isr_sio_mtimecmp(void); +WEAK_DEFAULT void isr_clocks(void); +WEAK_DEFAULT void isr_spi0(void); +WEAK_DEFAULT void isr_spi1(void); +WEAK_DEFAULT void isr_uart0(void); +WEAK_DEFAULT void isr_uart1(void); +WEAK_DEFAULT void isr_adc_fifo(void); +WEAK_DEFAULT void isr_i2c0(void); +WEAK_DEFAULT void isr_i2c1(void); +WEAK_DEFAULT void isr_otp(void); +WEAK_DEFAULT void isr_trng(void); +WEAK_DEFAULT void isr_proc0_cti(void); +WEAK_DEFAULT void isr_proc1_cti(void); +WEAK_DEFAULT void isr_pll_sys(void); +WEAK_DEFAULT void isr_pll_usb(void); +WEAK_DEFAULT void isr_powman_pow(void); +WEAK_DEFAULT void isr_powman_timer(void); +WEAK_DEFAULT void isr_spareirq_0(void); +WEAK_DEFAULT void isr_spareirq_1(void); +WEAK_DEFAULT void isr_spareirq_2(void); +WEAK_DEFAULT void isr_spareirq_3(void); +WEAK_DEFAULT void isr_spareirq_4(void); +WEAK_DEFAULT void isr_spareirq_5(void); + +/** CPU specific interrupt vector table + * @see 3.2 Interrupts and IRQn_Type in RP2350.h + */ +const void* vector_cpu[CPU_IRQ_NUMOF] = { + (void*)isr_timer0_0, /* 0 TIMER0_IRQ_0 */ + (void*)isr_timer0_1, /* 1 TIMER0_IRQ_1 */ + (void*)isr_timer0_2, /* 2 TIMER0_IRQ_2 */ + (void*)isr_timer0_3, /* 3 TIMER0_IRQ_3 */ + (void*)isr_timer1_0, /* 4 TIMER1_IRQ_0 */ + (void*)isr_timer1_1, /* 5 TIMER1_IRQ_1 */ + (void*)isr_timer1_2, /* 6 TIMER1_IRQ_2 */ + (void*)isr_timer1_3, /* 7 TIMER1_IRQ_3 */ + (void*)isr_pwm_wrap_0, /* 8 PWM_IRQ_WRAP_0 */ + (void*)isr_pwm_wrap_1, /* 9 PWM_IRQ_WRAP_1 */ + (void*)isr_dma_0, /* 10 DMA_IRQ_0 */ + (void*)isr_dma_1, /* 11 DMA_IRQ_1 */ + (void*)isr_dma_2, /* 12 DMA_IRQ_2 */ + (void*)isr_dma_3, /* 13 DMA_IRQ_3 */ + (void*)isr_usbctrl, /* 14 USBCTRL_IRQ */ + (void*)isr_pio0_0, /* 15 PIO0_IRQ_0 */ + (void*)isr_pio0_1, /* 16 PIO0_IRQ_1 */ + (void*)isr_pio1_0, /* 17 PIO1_IRQ_0 */ + (void*)isr_pio1_1, /* 18 PIO1_IRQ_1 */ + (void*)isr_pio2_0, /* 19 PIO2_IRQ_0 */ + (void*)isr_pio2_1, /* 20 PIO2_IRQ_1 */ + (void*)isr_io_bank0, /* 21 IO_IRQ_BANK0 */ + (void*)isr_io_bank0_ns, /* 22 IO_IRQ_BANK0_NS */ + (void*)isr_io_qspi, /* 23 IO_IRQ_QSPI */ + (void*)isr_io_qspi_ns, /* 24 IO_IRQ_QSPI_NS */ + (void*)isr_sio_fifo, /* 25 SIO_IRQ_FIFO */ + (void*)isr_sio_bell, /* 26 SIO_IRQ_BELL */ + (void*)isr_sio_fifo_ns, /* 27 SIO_IRQ_FIFO_NS */ + (void*)isr_sio_bell_ns, /* 28 SIO_IRQ_BELL_NS */ + (void*)isr_sio_mtimecmp, /* 29 SIO_IRQ_MTIMECMP */ + (void*)isr_clocks, /* 30 CLOCKS_IRQ */ + (void*)isr_spi0, /* 31 SPI0_IRQ */ + (void*)isr_spi1, /* 32 SPI1_IRQ */ + (void*)isr_uart0, /* 33 UART0_IRQ */ + (void*)isr_uart1, /* 34 UART1_IRQ */ + (void*)isr_adc_fifo, /* 35 ADC_IRQ_FIFO */ + (void*)isr_i2c0, /* 36 I2C0_IRQ */ + (void*)isr_i2c1, /* 37 I2C1_IRQ */ + (void*)isr_otp, /* 38 OTP_IRQ */ + (void*)isr_trng, /* 39 TRNG_IRQ */ + (void*)isr_proc0_cti, /* 40 PROC0_IRQ_CTI */ + (void*)isr_proc1_cti, /* 41 PROC1_IRQ_CTI */ + (void*)isr_pll_sys, /* 42 PLL_SYS_IRQ */ + (void*)isr_pll_usb, /* 43 PLL_USB_IRQ */ + (void*)isr_powman_pow, /* 44 POWMAN_IRQ_POW */ + (void*)isr_powman_timer, /* 45 POWMAN_IRQ_TIMER */ + (void*)isr_spareirq_0, /* 46 SPAREIRQ_IRQ_0 */ + (void*)isr_spareirq_1, /* 47 SPAREIRQ_IRQ_1 */ + (void*)isr_spareirq_2, /* 48 SPAREIRQ_IRQ_2 */ + (void*)isr_spareirq_3, /* 49 SPAREIRQ_IRQ_3 */ + (void*)isr_spareirq_4, /* 50 SPAREIRQ_IRQ_4 */ + (void*)isr_spareirq_5, /* 51 SPAREIRQ_IRQ_5 */ +}; diff --git a/cpu/rp2350_riscv/xosc.c b/cpu/rp2350_riscv/xosc.c new file mode 100644 index 000000000000..c398e28ec9ee --- /dev/null +++ b/cpu/rp2350_riscv/xosc.c @@ -0,0 +1,55 @@ +/* + * SPDX-FileCopyrightText: 2025 Tom Hert + * SPDX-FileCopyrightText: 2025 HAW Hamburg + * SPDX-License-Identifier: LGPL-2.1-only + */ + +/** + * @ingroup cpu_rp2350 + * @{ + * + * @file + * @brief XOSC implementation for the RP2350 + * + * @author Tom Hert + */ + +#include + +#include "RP2350.h" +#include "board.h" +#include "macros/units.h" +#include "periph_cpu.h" + +/* Based on datasheet 8.2.4 (1ms wait time) */ +#define STARTUP_DELAY 47 +#define MAX_XOSC_COUNTER_SIZE 0xFFFF +#define SLEEP_100HZ_SPEED 12000000UL +#define CYCLES_PER_MS (SLEEP_100HZ_SPEED / 1000) + +void xosc_start(void) { + /* Set the FREQ_RANGE */ + XOSC->CTRL = XOSC_CTRL_FREQ_RANGE_VALUE_1_15MHZ; + /* Set the startup delay (default 1ms) */ + XOSC->STARTUP = STARTUP_DELAY; + /* set enable bit */ + atomic_set(&XOSC->CTRL, + XOSC_CTRL_ENABLE_VALUE_ENABLE << XOSC_CTRL_ENABLE_LSB); + + while (!(XOSC->STATUS & XOSC_STATUS_STABLE_BITS)) { + /* Wait for the crystal to stabilize */ + } +} + +void xosc_sleep(uint32_t milliseconds) { + for (uint32_t i = milliseconds; i > 0; i--) { + XOSC->COUNT = CYCLES_PER_MS; + while (XOSC->COUNT != 0) {}; + } +} + +void xosc_stop(void) { + /* @TODO */ +} + +/** @} */ diff --git a/features.yaml b/features.yaml index 7113af63e6c5..5c98b23e43c6 100644 --- a/features.yaml +++ b/features.yaml @@ -285,6 +285,8 @@ groups: help: The MCU is part of the Raspberry PI RPx0xx family. - name: cpu_rp2350 help: The MCU is a Raspberry Pi RP2350 + - name: cpu_rp2350_riscv + help: The MCU is a Raspberry Pi RP2350 RISCV - title: Silicon Laboratories EFM32 Grouping features: @@ -827,6 +829,9 @@ groups: help: A RISC-V Core-local Interrupt Controller (CLIC) peripheral is present. - name: periph_plic help: A RISC-V Platform-local Interrupt Controller (PLIC) peripheral is present. + - name: periph_xh3irq + help: An XH3IRQ peripheral is present. This is a special interrupt controller + for the Hazard3 used by the RP2350. - title: Cryptographic Features help: Hardware acceleration for cryptographic primitives, hardware random diff --git a/makefiles/arch/riscv.inc.mk b/makefiles/arch/riscv.inc.mk index 55ae154c581e..4d2736aa8743 100644 --- a/makefiles/arch/riscv.inc.mk +++ b/makefiles/arch/riscv.inc.mk @@ -52,6 +52,7 @@ endif GCC_DEFAULTS_TO_NEW_RISCV_ISA ?= 0 CFLAGS_CPU := -march=rv32imac -mabi=ilp32 +ASFLAGS := $(CFLAGS_CPU) # Since RISC-V ISA specifications 20191213 instructions previously included in # rv32imac have been moved to the ZICSR extension. See diff --git a/makefiles/features_existing.inc.mk b/makefiles/features_existing.inc.mk index 6420ccd089cd..3f73608ce82c 100644 --- a/makefiles/features_existing.inc.mk +++ b/makefiles/features_existing.inc.mk @@ -87,6 +87,7 @@ FEATURES_EXISTING := \ cpu_nrf9160 \ cpu_qn908x \ cpu_rp2350 \ + cpu_rp2350_riscv \ cpu_rpx0xx \ cpu_sam3 \ cpu_sam4s \ @@ -257,6 +258,7 @@ FEATURES_EXISTING := \ periph_wdt \ periph_wdt_cb \ periph_wdt_warning_period \ + periph_xh3irq \ picolibc \ pio_i2c \ puf_sram \ diff --git a/makefiles/features_modules.inc.mk b/makefiles/features_modules.inc.mk index 975cf003c0c2..38dc3faff6f8 100644 --- a/makefiles/features_modules.inc.mk +++ b/makefiles/features_modules.inc.mk @@ -57,6 +57,7 @@ PERIPH_IGNORE_MODULES := \ periph_uart_rxstart_irq \ periph_wdog \ periph_wdt_auto_start \ + periph_xh3irq \ # PERIPH_MODULES := $(filter-out $(PERIPH_IGNORE_MODULES),\ $(filter periph_%,$(USEMODULE)))