diff options
| author | Frédéric Desbiens <[email protected]> | 2026-06-08 10:01:32 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2026-06-08 10:01:32 +0200 |
| commit | 87ab09cce305eb9cd4aacac4e8c62af72f665bff (patch) | |
| tree | dd062ebbea5235d1921c64b396bf7e4ce79e9e15 /ports/risc-v32/gnu | |
| parent | 9ab0cf9683f832cdb48e2099533a5b06a3afcd64 (diff) | |
| parent | 730b61874bc5cf40768987605ae5187fde0fa1e2 (diff) | |
Merge pull request #544 from eclipse-threadx/devv6.5.1.202602_rel
Merging changes for the v.6.5.1.202602 release
Diffstat (limited to 'ports/risc-v32/gnu')
82 files changed, 7121 insertions, 952 deletions
diff --git a/ports/risc-v32/gnu/CMakeLists.txt b/ports/risc-v32/gnu/CMakeLists.txt index 9357c697..05000757 100644 --- a/ports/risc-v32/gnu/CMakeLists.txt +++ b/ports/risc-v32/gnu/CMakeLists.txt @@ -1,19 +1,7 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../../../cmake/threadx_riscv_port.cmake) -target_sources(${PROJECT_NAME} - PRIVATE - # {{BEGIN_TARGET_SOURCES}} - ${CMAKE_CURRENT_LIST_DIR}/src/tx_initialize_low_level.S - ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_restore.S - ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_save.S - ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_interrupt_control.S - ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_schedule.S - ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_stack_build.S - ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_system_return.S - ${CMAKE_CURRENT_LIST_DIR}/src/tx_timer_interrupt.S - # {{END_TARGET_SOURCES}} -) - -target_include_directories(${PROJECT_NAME} - PUBLIC - ${CMAKE_CURRENT_LIST_DIR}/inc +threadx_add_riscv_port( + SRC_DIR ${CMAKE_CURRENT_LIST_DIR}/src + INC_DIR ${CMAKE_CURRENT_LIST_DIR}/inc + EXAMPLE_DIR ${CMAKE_CURRENT_LIST_DIR}/example_build/qemu_virt ) diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/CMakeLists.txt b/ports/risc-v32/gnu/example_build/core_v_mcu/CMakeLists.txt new file mode 100644 index 00000000..662914d1 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/CMakeLists.txt @@ -0,0 +1,69 @@ +cmake_minimum_required(VERSION 3.15 FATAL_ERROR) + +project(demo_threadx_core_v_mcu LANGUAGES C ASM) +set(CMAKE_C_STANDARD 99) +set(CMAKE_C_STANDARD_REQUIRED ON) + +set(CORE_V_MCU_DIR ${CMAKE_CURRENT_LIST_DIR}) +set(THREADX_ROOT ${CMAKE_CURRENT_LIST_DIR}/../../../../..) +set(TX_USER_FILE ${CORE_V_MCU_DIR}/include/tx_user.h) + +set(THREADX_ARCH "risc-v32") +set(THREADX_TOOLCHAIN "gnu") + +message(STATUS "Toolchain file: ${CMAKE_TOOLCHAIN_FILE}") +message(STATUS "C compiler: ${CMAKE_C_COMPILER}") + +add_subdirectory(${THREADX_ROOT} threadx) +target_include_directories(threadx PRIVATE ${CORE_V_MCU_DIR}/include) + +set(SRCS + ${CORE_V_MCU_DIR}/crt0.S + ${CORE_V_MCU_DIR}/vectors.S + ${CORE_V_MCU_DIR}/tx_initialize_low_level.S + ${CORE_V_MCU_DIR}/bsp/irq.c + ${CORE_V_MCU_DIR}/bsp/timer_irq.c + ${CORE_V_MCU_DIR}/bsp/fll.c + ${CORE_V_MCU_DIR}/bsp/gpio.c + ${CORE_V_MCU_DIR}/bsp/uart_driver.c + ${CORE_V_MCU_DIR}/bsp/i2c_master.c + ${CORE_V_MCU_DIR}/bsp/temp_sensor.c + ${CORE_V_MCU_DIR}/bsp/system_core_v_mcu.c + ${CORE_V_MCU_DIR}/bsp/string.c + ${CORE_V_MCU_DIR}/demo_threadx.c +) + +add_executable(demo_threadx ${SRCS}) +set_target_properties(demo_threadx PROPERTIES OUTPUT_NAME "demo_threadx.elf") + +target_include_directories(demo_threadx PRIVATE + ${CORE_V_MCU_DIR}/include + ${THREADX_ROOT}/common/inc + ${THREADX_ROOT}/ports/risc-v32/gnu/inc +) + +target_link_libraries(demo_threadx PRIVATE threadx gcc) + +target_link_options(demo_threadx PRIVATE + -T${CORE_V_MCU_DIR}/link.ld + -nostartfiles + -nodefaultlibs + -Wl,--gc-sections + -Wl,-Map=${CMAKE_CURRENT_BINARY_DIR}/demo_threadx.map +) + +target_compile_options(demo_threadx PRIVATE + -Os + -g + -Wall + -Wextra + -ffreestanding + -ffunction-sections + -fdata-sections + -DTX_INCLUDE_USER_DEFINE_FILE +) + +add_custom_command(TARGET demo_threadx POST_BUILD + COMMAND ${OBJCOPY} -O binary $<TARGET_FILE:demo_threadx> ${CMAKE_CURRENT_BINARY_DIR}/demo_threadx.bin + COMMENT "Generating demo_threadx.bin" +) diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/README.md b/ports/risc-v32/gnu/example_build/core_v_mcu/README.md new file mode 100644 index 00000000..41f45dab --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/README.md @@ -0,0 +1,504 @@ +# ThreadX Port for CORE-V MCU + +This directory contains the Board Support Package (BSP) and demo application for +running Eclipse ThreadX on the +[CORE-V MCU](https://github.com/openhwgroup/core-v-mcu) — an open-source 32-bit +RISC-V SoC based on the CV32E40P core (RV32IMC). The reference hardware is a +[Digilent Nexys A7](https://digilent.com/reference/programmable-logic/nexys-a7/start) +FPGA board running the synthesised CORE-V MCU bitstream. + +--- + +## Table of Contents + +1. [Hardware overview](#1-hardware-overview) +2. [Software prerequisites](#2-software-prerequisites) +3. [Directory structure](#3-directory-structure) +4. [Architecture notes](#4-architecture-notes) +5. [Memory map](#5-memory-map) +6. [Building](#6-building) +7. [Flashing and debugging](#7-flashing-and-debugging) +8. [BSP API reference](#8-bsp-api-reference) +9. [Unit tests](#9-unit-tests) +10. [Adapting to a different board](#10-adapting-to-a-different-board) +11. [Setup and deployment scripts](#11-setup-and-deployment-scripts) + +--- + +## 1. Hardware overview + +| Item | Detail | +|------|--------| +| CPU | CV32E40P — RV32IMC, in-order, 4-stage pipeline | +| ISA | `rv32imc_zicsr` (integer, compressed, multiply/divide, CSR instructions) | +| L2 SRAM | 512 KB at `0x1c000000` (code + data + stack) | +| Interrupt controller | PULP APB interrupt controller (32 lines, **not** a standard CLINT) | +| Timer | PULP FC Timer at `0x1A10B000`, 32 kHz reference clock | +| UART | UDMA UART channel 0 at `0x1A102080` | +| GPIO | PULP GPIO controller at `0x1A101000` | +| FLL | Frequency Locked Loop — 5 MHz input (FPGA), 50 MHz output | +| Debug probe | [Ashling Opella LD](https://www.ashling.com/ashling-opella-ld/) via JTAG | + +The CV32E40P does **not** implement the standard RISC-V CLINT (`mip`/`mie` per-source +masking). Individual interrupt lines are enabled and cleared through PULP APB +registers, and `mstatus.MIE` (bit 3) acts as the single global enable. + +--- + +## 2. Software prerequisites + +Run `install_deps.sh` to install all dependencies in one step: + +```bash +bash install_deps.sh +``` + +Or install individually: + +| Tool | Minimum version | Notes | +|------|----------------|-------| +| `riscv-none-elf-gcc` | 15.x | xPack riscv-none-elf-gcc — run `bash install_deps.sh` | +| CMake | 3.15 | `apt install cmake` | +| Ninja | 1.10 | `apt install ninja-build` | +| OpenOCD | 0.12 | `apt install openocd` | +| GDB | any multiarch | `apt install gdb-multiarch` | + +> **Note:** Use the [xPack riscv-none-elf-gcc](https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack) +> toolchain — a cross-platform (Linux/macOS/Windows) multilib bare-metal +> toolchain analogous to `arm-none-eabi-gcc`. Its multilib includes +> rv32imc/ilp32 (soft-float) libgcc required for the CV32E40P. Do **not** +> use `riscv32-unknown-elf-gcc` (riscv-collab) — that release is built +> `--disable-multilib --with-abi=ilp32d` and cannot link soft-float objects. + +--- + +## 3. Directory structure + +``` +core_v_mcu/ +├── bsp/ Board Support Package +│ ├── fll.c Frequency Locked Loop driver +│ ├── gpio.c GPIO driver +│ ├── irq.c PULP APB interrupt controller driver +│ ├── string.c Freestanding memset/memcpy shim +│ ├── system_core_v_mcu.c Top-level BSP init and ISR dispatcher +│ ├── timer_irq.c PULP FC Timer driver +│ └── uart_driver.c UDMA UART driver +├── include/ BSP headers +│ ├── bits.h Bit-manipulation macros +│ ├── csr.h CSR access macros +│ ├── fll.h +│ ├── gpio.h +│ ├── io.h MMIO read/write inlines +│ ├── irq.h +│ ├── memory_map.h Peripheral base addresses +│ ├── properties.h Clock/frequency constants +│ ├── pulp_mem_map.h PULP peripheral register offsets +│ ├── string.h Freestanding string shim header +│ ├── system_core_v_mcu.h +│ ├── timer.h +│ ├── timer_irq.h +│ ├── tx_user.h ThreadX compile-time configuration +│ └── uart_driver.h +├── tests/ Host-compiled unit tests (no cross-compiler needed) +│ ├── mock/ +│ │ ├── mmio_mock.c Replaces io.h MMIO inlines with a software map +│ │ └── mmio_mock.h +│ ├── test_irq.c Tests for irq.c +│ ├── test_timer.c Tests for timer_irq.c +│ └── CMakeLists.txt +├── crt0.S C runtime startup (clear BSS, call main) +├── vectors.S 32-entry vectored interrupt table +├── tx_initialize_low_level.S ThreadX platform init (mtvec, stack pointer) +├── link.ld Linker script +├── demo_threadx.c Demo application (blinky + UART heartbeat) +├── CMakeLists.txt Build definition +├── build.sh One-shot build script +├── gdb_init GDB startup script +└── openocd-nexys-Ashling-Opella-LD.cfg OpenOCD configuration +``` + +The ThreadX kernel sources are **not** duplicated here; they are referenced from the +repository root via `THREADX_ROOT`. + +--- + +## 4. Architecture notes + +### 4.1 Interrupt model + +The CV32E40P uses a PULP/PULPissimo-style interrupt controller rather than the +standard RISC-V CLINT. Key differences: + +- **32 direct interrupt lines** connect to the core. `mcause` for an external + interrupt is `0x80000000 | N`, where `N` (bits 4–0) is the line number. +- **Per-line masking** is done through APB registers in the PULP IRQ controller + (`IRQ_REG_MASK_SET_OFFSET` / `IRQ_REG_MASK_CLEAR_OFFSET`) — not through the `mie` + CSR. +- **Global enable** is `mstatus.MIE` (bit 3). ThreadX manages this itself: the + scheduler sets MIE just before entering the WFI idle loop in + `_tx_thread_schedule`. + +ISR dispatch flow: + +``` +CPU takes interrupt + → PC jumps to mtvec base (vectored mode, bit 0 = 1) + → vectors.S entry for line N → tx_trap_entry + → tx_trap_entry saves registers, calls _tx_thread_context_save + → tx_trap_handler(mcause, mepc, mtval) dispatches via isr_table[mcause & 0x1f] + → ThreadX timer ISR (line 10) calls _tx_timer_interrupt + → _tx_thread_context_restore (never returns to tx_trap_entry) +``` + +### 4.2 `mtvec` alignment + +The CV32E40P requires the vector table base to be **256-byte aligned**. The linker +script places `.vectors` at `0x1c000800`, which satisfies this. `mtvec` is written +as `0x1c000801` (base + vectored-mode bit). + +### 4.3 ThreadX timer tick + +- Reference clock: 32 768 Hz (ARCHI_REF_CLOCK) +- Tick rate: 100 Hz → compare value 327 (~0.1 % drift) +- The timer auto-reloads on compare match (cycle mode) +- `_tx_timer_interrupt` is registered in `isr_table[10]` during `system_init()` + +### 4.4 FLL and clock + +On the Nexys A7 FPGA, the FLL input is 5 MHz (`ARCHI_FPGA_FREQUENCY`). After +`pi_fll_init()` the system clock is 50 MHz (`DEFAULT_SYSTEM_CLOCK`). The UART baud +rate divisor is computed from this 50 MHz clock. + +### 4.5 Freestanding build + +The port builds with `-ffreestanding -nodefaultlibs` — no newlib or libc. +`bsp/string.c` provides minimal `memset` and `memcpy` for ThreadX internal use. + +--- + +## 5. Memory map + +| Region | Start | Size | Contents | +|--------|-------|------|----------| +| Vector table | `0x1c000800` | 128 B | 32 × 4-byte jump instructions to `tx_trap_entry` | +| `.text` (boot address) | `0x1c000880` | — | Code + read-only data | +| `.data` | after `.text` | — | Initialised data | +| `.bss` | after `.data` | — | Zero-initialised data | +| ThreadX free memory | after `.bss` | — | Heap pools, thread stacks | +| Stack | top of L2 | 8 KB | Initial/idle stack grows downward from `0x1c080000` | + +L2 total: 512 KB (`0x1c000000`–`0x1c07ffff`). + +Peripheral base addresses (see `include/memory_map.h`): + +| Peripheral | Address | +|------------|---------| +| PULP IRQ controller | `0x1A109800` | +| PULP FC Timer | `0x1A10B000` | +| PULP GPIO | `0x1A101000` | +| UDMA UART ch0 | `0x1A102080` | +| FLL | `0x1A100000` | + +--- + +## 6. Building + +### Quick start + +```bash +cd ports/risc-v32/gnu/example_build/core_v_mcu +bash build.sh +``` + +The script configures and builds with Ninja. The output is: + +``` +build/demo_threadx.elf — ELF with debug symbols +build/demo_threadx.bin — raw binary for flashing +build/demo_threadx.map — linker map +``` + +### Manual CMake invocation + +```bash +THREADX_ROOT=$(realpath ../../../../..) +cmake -B build -G Ninja \ + -DCMAKE_TOOLCHAIN_FILE="${THREADX_ROOT}/cmake/riscv-none-elf-rv32imc.cmake" \ + . +cmake --build build +``` + +### Toolchain file + +`${THREADX_ROOT}/cmake/riscv-none-elf-rv32imc.cmake` sets: + +``` +CMAKE_C_COMPILER riscv-none-elf-gcc +CMAKE_ASM_COMPILER riscv-none-elf-gcc +CMAKE_OBJCOPY riscv-none-elf-objcopy +CMAKE_C_FLAGS -march=rv32imc_zicsr -mabi=ilp32 -mcmodel=medlow +``` + +--- + +## 7. Flashing and debugging + +### 7.1 Quick deploy (recommended) + +Use `deploy.sh` to automate the entire OpenOCD + GDB workflow in one command: + +```bash +# Flash and run (leave target running after GDB exits): +bash deploy.sh + +# Build first, then flash and run: +bash deploy.sh --build + +# Flash and stop at main for interactive debugging: +bash deploy.sh --debug + +# Override the ELF or the OpenOCD config: +bash deploy.sh --elf path/to/my.elf --openocd-cfg path/to/other.cfg +``` + +> **First time only:** run `bash ../../../../../samplex-fd/OpenHW/scripts/setup_opella.sh` +> (or the equivalent path) once to install the udev rule for the Opella LD. +> See [Section 11](#11-setup-and-deployment-scripts) for details. + +### 7.2 Manual procedure + +#### Connect hardware + +1. Connect the Ashling Opella LD to the Nexys A7 JTAG header. +2. Connect the USB-UART cable (CORE-V MCU UART0 mapped to USB micro-B on Nexys A7). +3. Power on the board. + +#### Start OpenOCD + +```bash +openocd -f openocd-nexys-Ashling-Opella-LD.cfg +``` + +Expected output ends with `Ready for Remote Connections`. OpenOCD listens on +port 3333 (GDB) and 4444 (telnet). + +#### Flash and run with GDB + +In a second terminal: + +```bash +riscv-none-elf-gdb --command=gdb_init +``` + +`gdb_init` connects to OpenOCD, loads the ELF, sets a breakpoint at `main`, and +continues. You should see the LED on pin 5 blinking at 1 Hz and the UART printing +`ThreadX on CORE-V MCU` followed by periodic `.` heartbeats at 115 200 baud. + +#### UART monitor + +```bash +minicom -b 115200 -D /dev/ttyUSBx # adjust device as needed +``` + +#### Useful GDB commands + +``` +(gdb) info threads — list ThreadX threads +(gdb) thread 2 — switch to a thread +(gdb) bt — backtrace +(gdb) monitor halt — halt via OpenOCD +(gdb) monitor resume — resume via OpenOCD +``` + +--- + +## 8. BSP API reference + +### `system_core_v_mcu.h` + +```c +void system_init(void); +``` +Initialises FLL, UART, IRQ controller, and FC Timer. Must be called once from +`main()` before `tx_kernel_enter()`. + +### `uart_driver.h` + +```c +int uart_init(uint8_t uart_id, uint32_t baudrate, uint32_t periph_freq); +void uart_write_byte(uint8_t uart_id, uint8_t c); +int uart_read_byte(uint8_t uart_id); /* returns -1 if no data */ +void uart_write_str(uint8_t uart_id, const char *str); +void uart_write_buf(uint8_t uart_id, const uint8_t *buf, uint32_t len); +``` + +`uart_id` 0 is the console UART. `uart_read_byte` is non-blocking. + +### `gpio.h` + +```c +void gpio_init(void); +void gpio_set_output(uint32_t pin_mask); /* configure pins as outputs */ +void gpio_set(uint32_t pin_mask); /* drive high */ +void gpio_clear(uint32_t pin_mask); /* drive low */ +void gpio_toggle(uint32_t pin_mask); +``` + +`pin_mask` is a bitmask where bit N corresponds to GPIO pin N. + +### `irq.h` + +```c +void pulp_irq_init(void); +void irq_enable(uint32_t irq_id); +void irq_disable(uint32_t irq_id); +void irq_clint_enable(void); /* set mstatus.MIE */ +void irq_clint_disable(void); /* clear mstatus.MIE */ +``` + +`irq_id` values are defined as `IRQ_FC_EVT_*` in `irq.h`. The timer uses +`IRQ_FC_EVT_TIMER0_LO` (= 10). + +### `timer_irq.h` + +```c +void timer_irq_init(uint32_t ticks); +void timer_irq_set_timeout(uint32_t ticks, bool enable); +uint32_t timer_irq_cycle_get_32(void); +``` + +### Registering a custom ISR + +Add a function pointer to `isr_table` before calling `tx_kernel_enter()`: + +```c +extern void (*isr_table[32])(void); + +static void my_irq_handler(void) { /* ... */ } + +/* In main() or tx_application_define(), before enabling the line: */ +isr_table[MY_IRQ_LINE] = my_irq_handler; +irq_enable(MY_IRQ_LINE); +``` + +--- + +## 9. Unit tests + +The tests compile for the **host** (no cross-compiler required) using a software +MMIO mock that backs the peripheral register space with a `uint32_t` array. + +```bash +cd tests +cmake -B build -G Ninja +cmake --build build +./build/tx_core_v_mcu_tests +``` + +Expected output: + +``` +irq_tests: PASS +timer_tests: PASS +``` + +The mock is enabled by defining `CORE_V_MCU_MMIO_MOCK` before including `io.h`. +`mmio_mock.c` provides `mock_reg_read` / `mock_reg_write` helpers for assertions. + +--- + +## 10. Adapting to a different board + +To port to a different CORE-V MCU carrier board: + +1. **Clock frequencies** — update `ARCHI_REF_CLOCK`, `ARCHI_FPGA_FREQUENCY`, and + `DEFAULT_SYSTEM_CLOCK` in `include/properties.h`. + +2. **Memory layout** — adjust `ORIGIN` and `LENGTH` for `L2` in `link.ld` and + update `__boot_address` if the boot ROM routes differently. + +3. **mtvec base** — if `.vectors` moves, update the address in `link.ld`. The + 256-byte alignment requirement is a hard CV32E40P constraint. + +4. **Debug probe** — replace `openocd-nexys-Ashling-Opella-LD.cfg` with a config + matching your probe (e.g., `interface jlink` for a J-Link). + +5. **GPIO pin** — `DEMO_LED_PIN` in `demo_threadx.c` selects the LED. Change to + match your board's LED assignment. + +--- + +## 11. Setup and deployment scripts + +### 11.1 `setup_opella.sh` — one-time host setup + +Located in `samplex-fd/OpenHW/scripts/setup_opella.sh`. Run **once per machine** +to grant user-space access to the Ashling Opella LD USB device. + +```bash +# Native Linux: +sudo bash samplex-fd/OpenHW/scripts/setup_opella.sh + +# WSL (also attempts usbipd-win USB forwarding): +bash samplex-fd/OpenHW/scripts/setup_opella.sh +``` + +What it does: + +| Step | Native Linux | WSL | +|------|-------------|-----| +| Write `/etc/udev/rules.d/99-ashling-opella.rules` | ✓ | ✓ | +| Add user to `plugdev` group | ✓ | ✓ | +| Reload udev | ✓ | ✓ | +| `usbipd.exe attach --wsl` (v3: `wsl attach`) | — | automatic | + +> **WSL — one-time bind (Windows admin required):** +> Before the first attach, run this once in an **elevated (Run as Administrator) PowerShell** on the Windows host: +> ```powershell +> usbipd bind --hardware-id 0b6b:0040 +> ``` +> After binding, `setup_opella.sh` handles subsequent attaches automatically. + +> **WSL — if attach fails with "not USBIP capable" (usbipd-win v3 + Ubuntu 22.04+):** +> Ubuntu 22.04 and 24.04 enable systemd by default. usbipd-win **v3** has a +> known bug where it incorrectly reports the kernel as not USBIP capable when +> systemd is running. **Upgrade usbipd-win to v4 or later:** +> <https://github.com/dorssel/usbipd-win/releases> +> +> If you are on an older WSL2 kernel without USBIP support, first run: +> ```powershell +> wsl --update +> wsl --shutdown +> ``` + +After running, **re-plug the probe**. On WSL, re-run the script after each WSL restart. + +### 11.2 `deploy.sh` — flash and run + +```bash +# Flash and run (leave target running): +bash deploy.sh + +# Build first, then flash: +bash deploy.sh --build + +# Flash and stop at main for interactive debug session: +bash deploy.sh --debug +``` + +The script starts OpenOCD in the background, waits for it to become ready, +then drives `riscv-none-elf-gdb` to load the ELF. OpenOCD is always +stopped on exit (including Ctrl-C). + +--- + +## Licence + +New files in this directory are © 2026 Eclipse ThreadX contributors, MIT. + +Files derived from +[core-v-freertos](https://github.com/openhwgroup/core-v-freertos) retain their +original Apache-2.0 copyright notices (ETH Zurich, GreenWaves Technologies, Wind +River Systems, SiFive Inc.) and are dual-licensed `Apache-2.0 AND MIT` (or +`(Apache-2.0 OR BSD-2-Clause) AND MIT` for `crt0.S`). See individual file headers. diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/fll.c b/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/fll.c new file mode 100644 index 00000000..46dce857 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/fll.c @@ -0,0 +1,209 @@ +/***************************************************************************/ +/* Copyright 2020 GreenWaves Technologies + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * Derived from core-v-freertos (https://github.com/openhwgroup/core-v-freertos) + * Original work licensed under the Apache License, Version 2.0. + * See https://www.apache.org/licenses/LICENSE-2.0 + * Modifications licensed under MIT (https://opensource.org/licenses/MIT). + * + * AI Disclosure: Some portions generated or modified by Copilot (Sonnet 4.6). + * + * SPDX-License-Identifier: Apache-2.0 AND MIT + ***************************************************************************/ + +#include <stddef.h> +#include <stdint.h> + +#include "bits.h" +#include "csr.h" +#include "fll.h" +#include "irq.h" +#include "properties.h" +#include "system_core_v_mcu.h" + +#define FLL_LOG2_MAXDCO 29U +#define FLL_LOG2_REFCLK ARCHI_REF_CLOCK_LOG2 +#define FLL_LOG2_MAXM (FLL_LOG2_MAXDCO - FLL_LOG2_REFCLK) + +static volatile uint32_t flls_frequency[FLL_NUM]; + +static uint32_t fll_max_u32(uint32_t a, uint32_t b) +{ + return (a > b) ? a : b; +} + +static uint32_t fll_fl1(uint32_t value) +{ + return (uint32_t)(31U - (uint32_t)__builtin_clz(value)); +} + +static uint32_t fll_get_mult_div_from_frequency(uint32_t freq, uint32_t *mult, + uint32_t *div) +{ + uint32_t fref = FLL_REF_CLK; + uint32_t log2_m; + uint32_t d; + uint32_t m; + uint32_t fres; + + if ((freq == 0U) || (mult == NULL) || (div == NULL)) + { + if (mult != NULL) + { + *mult = 0U; + } + if (div != NULL) + { + *div = 0U; + } + return 0U; + } + + if (freq > fref) + { + log2_m = fll_fl1(freq) - fll_fl1(fref); + } + else + { + log2_m = 0U; + } + + if (FLL_LOG2_MAXM > log2_m) + { + d = fll_max_u32(1U, (FLL_LOG2_MAXM - log2_m) >> 1U); + } + else + { + d = 1U; + } + + m = (freq << d) / fref; + fres = (fref * m + (1UL << (d - 1U))) >> d; + + *mult = m; + *div = d + 1U; + return fres; +} + +static uint32_t fll_get_frequency_from_mult_div(uint32_t mult, uint32_t div) +{ + uint32_t fref = FLL_REF_CLK; + + if (div == 0U) + { + return fref * mult; + } + + return (fref * mult) >> (div - 1U); +} + +int pi_fll_set_frequency(fll_type_t which_fll, uint32_t frequency, int check) +{ + uint32_t mult; + uint32_t div; + uint32_t reg1; + uint32_t saved_irq; + + (void)check; + + if ((uint32_t)which_fll >= FLL_NUM) + { + return -1; + } + + if (frequency == 0U) + { + return -1; + } + + saved_irq = irq_clint_disable(); + (void)fll_get_mult_div_from_frequency(frequency, &mult, &div); + + reg1 = FLL_CTRL[which_fll].FLL_CONF1; + reg1 &= ~FLL_CTRL_CONF1_MULTI_FACTOR_MASK; + reg1 |= REG_SET(FLL_CTRL_CONF1_MULTI_FACTOR, mult); + reg1 &= ~FLL_CTRL_CONF1_CLK_OUT_DIV_MASK; + reg1 |= REG_SET(FLL_CTRL_CONF1_CLK_OUT_DIV, div); + FLL_CTRL[which_fll].FLL_CONF1 = reg1; + + flls_frequency[which_fll] = frequency; + if (which_fll == FLL_SOC) + { + system_core_clock = frequency; + } + + if ((saved_irq & MSTATUS_IE) != 0U) + { + (void)irq_clint_enable(); + } + + return (int)frequency; +} + +void pi_fll_init(fll_type_t which_fll, uint32_t ret_state) +{ + uint32_t reg1; + + if ((uint32_t)which_fll >= FLL_NUM) + { + return; + } + + if (ret_state != 0U) + { + (void)pi_fll_get_frequency(which_fll, 1U); + return; + } + + reg1 = FLL_CTRL[which_fll].FLL_CONF1; + if (REG_GET(FLL_CTRL_CONF1_MODE, reg1) == 0U) + { + uint32_t reg2 = FLL_CTRL[which_fll].FLL_CONF2; + uint32_t regint = FLL_CTRL[which_fll].FLL_INTEGRATOR; + + reg2 &= ~FLL_CTRL_CONF2_ASSERT_CYCLES_MASK; + reg2 |= REG_SET(FLL_CTRL_CONF2_ASSERT_CYCLES, 0x6U); + reg2 &= ~FLL_CTRL_CONF2_LOCK_TOLERANCE_MASK; + reg2 |= REG_SET(FLL_CTRL_CONF2_LOCK_TOLERANCE, 0x50U); + FLL_CTRL[which_fll].FLL_CONF2 = reg2; + + regint &= ~FLL_CTRL_INTEGRATOR_INT_PART_MASK; + regint |= REG_SET(FLL_CTRL_INTEGRATOR_INT_PART, 332U); + FLL_CTRL[which_fll].FLL_INTEGRATOR = regint; + + reg1 &= ~FLL_CTRL_CONF1_OUTPUT_LOCK_EN_MASK; + reg1 |= REG_SET(FLL_CTRL_CONF1_OUTPUT_LOCK_EN, 1U); + reg1 &= ~FLL_CTRL_CONF1_MODE_MASK; + reg1 |= REG_SET(FLL_CTRL_CONF1_MODE, 1U); + FLL_CTRL[which_fll].FLL_CONF1 = reg1; + } + + if (flls_frequency[which_fll] != 0U) + { + (void)pi_fll_set_frequency(which_fll, flls_frequency[which_fll], 0); + } + else + { + flls_frequency[which_fll] = fll_get_frequency_from_mult_div( + REG_GET(FLL_CTRL_CONF1_MULTI_FACTOR, reg1), + REG_GET(FLL_CTRL_CONF1_CLK_OUT_DIV, reg1)); + } +} + +int pi_fll_get_frequency(fll_type_t which_fll, uint8_t real) +{ + if ((uint32_t)which_fll >= FLL_NUM) + { + return -1; + } + + if (real != 0U) + { + flls_frequency[which_fll] = fll_get_frequency_from_mult_div( + FLL_CTRL[which_fll].FLL_STATUS, + REG_GET(FLL_CTRL_CONF1_CLK_OUT_DIV, FLL_CTRL[which_fll].FLL_CONF1)); + } + + return (int)flls_frequency[which_fll]; +} diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/gpio.c b/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/gpio.c new file mode 100644 index 00000000..ddeba5b5 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/gpio.c @@ -0,0 +1,128 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +#include <stdint.h> + +#include "pulp_mem_map.h" +#include "io.h" +#include "gpio.h" + +/* APB SOC CTRL pad-mux interface (apb_soc_ctrl v1.0.0): + * Each IO pad has a dedicated 32-bit register at base + 0x400 + pad_index*4. + * PWDATA[1:0] = mux function (0=sys, 1=perio, 2=apbio/GPIO, 3=fpgaio) + * PWDATA[13:8] = pad config (drive/pull, optional) + * + * FUNC_C (2) routes the pad to the GPIO (apbio) controller. + * For the standard GPIO pins: io_pad = gpio_pin + GPIO_IO_PAD_OFFSET (=7). + * Verified in pad_control.sv: apbio[N] ↔ io_pad[N+7] when pad_mux[N+7]==2. */ +#define APB_SOC_PADMUX_BASE 0x400U +#define PINMUX_FUNC_C 2U +#define GPIO_IO_PAD_OFFSET 7U + +static void gpio_pinmux_set_func_c(uint32_t pin_mask) +{ + for (uint32_t gpio_pin = 0U; gpio_pin < 32U; gpio_pin++) + { + if ((pin_mask & (1UL << gpio_pin)) == 0U) + { + continue; + } + uint32_t io_pad = gpio_pin + GPIO_IO_PAD_OFFSET; + uintptr_t reg = PULP_APB_SOC_CTRL_ADDR + APB_SOC_PADMUX_BASE + + io_pad * 4U; + writew(PINMUX_FUNC_C, reg); + } +} + +/* Iterate over every set bit in pin_mask and call writew(pin_number, addr). + * The apb_gpiov2 SETGPIO/CLRGPIO/TOGGPIO/SETDIR registers are all + * pin-indexed: the written word encodes a pin number, not a bitmask. */ +static void gpio_foreach_pin(uint32_t pin_mask, uintptr_t reg_addr, uint32_t extra_bits) +{ + for (uint32_t pin = 0U; pin < 32U; pin++) + { + if ((pin_mask & (1UL << pin)) != 0U) + { + writew(pin | extra_bits, reg_addr); + } + } +} + +void gpio_init(void) +{ + /* GPIO clocking is handled by platform boot code on CORE-V MCU. */ +} + +void gpio_set_output(uint32_t pin_mask) +{ + /* Route physical pads to the GPIO controller via the pad-mux (FUNC_C). */ + gpio_pinmux_set_func_c(pin_mask); + + /* Configure each pin as push-pull output via SETDIR. */ + gpio_foreach_pin(pin_mask, + (uintptr_t)(PULP_GPIO_ADDR + GPIO_SETDIR_OFFSET), + GPIO_DIR_OUTPUT); +} + +void gpio_toggle(uint32_t pin_mask) +{ + gpio_foreach_pin(pin_mask, + (uintptr_t)(PULP_GPIO_ADDR + GPIO_TOGGPIO_OFFSET), + 0U); +} + +void gpio_set(uint32_t pin_mask) +{ + gpio_foreach_pin(pin_mask, + (uintptr_t)(PULP_GPIO_ADDR + GPIO_SETGPIO_OFFSET), + 0U); +} + +void gpio_clear(uint32_t pin_mask) +{ + gpio_foreach_pin(pin_mask, + (uintptr_t)(PULP_GPIO_ADDR + GPIO_CLRGPIO_OFFSET), + 0U); +} + +void gpio_setpinmux(uint8_t io_pad, uint8_t mux) +{ + uintptr_t reg = PULP_APB_SOC_CTRL_ADDR + APB_SOC_PADMUX_BASE + + (uintptr_t)io_pad * 4U; + writew((uint32_t)mux & 0x3U, reg); +} + +uint8_t gpio_getpinmux(uint8_t io_pad) +{ + uintptr_t reg = PULP_APB_SOC_CTRL_ADDR + APB_SOC_PADMUX_BASE + + (uintptr_t)io_pad * 4U; + return (uint8_t)(readw(reg) & 0x3U); +} + +void gpio_pin_set_dir(uint8_t pin, uint8_t dir) +{ + uint32_t val = (uint32_t)pin; + if (dir != 0U) + { + val |= GPIO_DIR_OUTPUT; + } + writew(val, (uintptr_t)(PULP_GPIO_ADDR + GPIO_SETDIR_OFFSET)); +} + +uint32_t gpio_pin_read_status(uint8_t pin) +{ + writew((uint32_t)pin, (uintptr_t)(PULP_GPIO_ADDR + GPIO_SETSEL_OFFSET)); + return readw((uintptr_t)(PULP_GPIO_ADDR + GPIO_RDSTAT_OFFSET)); +} diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/i2c_master.c b/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/i2c_master.c new file mode 100644 index 00000000..eb1461b9 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/i2c_master.c @@ -0,0 +1,153 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +#include <stddef.h> +#include <stdint.h> + +#include "bits.h" +#include "i2c_master.h" +#include "io.h" +#include "pulp_mem_map.h" + +#define UDMA_I2C_RX_SADDR_OFFSET 0x00U +#define UDMA_I2C_RX_SIZE_OFFSET 0x04U +#define UDMA_I2C_RX_CFG_OFFSET 0x08U +#define UDMA_I2C_TX_SADDR_OFFSET 0x10U +#define UDMA_I2C_TX_SIZE_OFFSET 0x14U +#define UDMA_I2C_TX_CFG_OFFSET 0x18U +#define UDMA_I2C_STATUS_OFFSET 0x20U +#define UDMA_I2C_SETUP_OFFSET 0x24U + +#define UDMA_CTRL_CG_OFFSET 0x00U +#define UDMA_CFG_EN_BIT BIT(4) +#define UDMA_CFG_PENDING_BIT BIT(5) +#define UDMA_I2C_STATUS_AL_BIT BIT(1) +#define UDMA_I2C_SETUP_RESET_BIT BIT(0) +#define I2C_MASTER_COUNT 2U +#define I2C_TIMEOUT_ITERATIONS 1000000U + +#define I2C_CMD_CFG 0xE0U +#define I2C_CMD_START 0x00U +#define I2C_CMD_STOP 0x20U +#define I2C_CMD_RD_ACK 0x40U +#define I2C_CMD_RD_NACK 0x60U +#define I2C_CMD_WR 0x80U +#define I2C_CMD_WAIT 0xA0U +#define I2C_CMD_RPT 0xC0U + +static uint8_t s_cmd_buf[32]; +static uint8_t s_clkdiv[I2C_MASTER_COUNT]; + +static uintptr_t i2c_master_base(uint8_t id) +{ + return (id == 0U) ? (uintptr_t)UDMA_CH_ADDR_I2CM0 : (uintptr_t)UDMA_CH_ADDR_I2CM1; +} + +int i2c_master_init(uint8_t id, uint32_t i2c_freq, uint32_t periph_freq) +{ + uint32_t cg; + uint32_t clk_div; + uintptr_t base; + + if ((id >= I2C_MASTER_COUNT) || (i2c_freq == 0U) || (periph_freq == 0U)) + { + return -1; + } + + cg = readw((uintptr_t)(UDMA_CH_ADDR_CTRL + UDMA_CTRL_CG_OFFSET)); + cg |= (id == 0U) ? UDMA_CTRL_I2CM0_CLKEN : UDMA_CTRL_I2CM1_CLKEN; + writew(cg, (uintptr_t)(UDMA_CH_ADDR_CTRL + UDMA_CTRL_CG_OFFSET)); + + clk_div = periph_freq / i2c_freq; + if (clk_div == 0U) + { + clk_div = 1U; + } + else if (clk_div > 255U) + { + clk_div = 255U; + } + s_clkdiv[id] = (uint8_t)clk_div; + + base = i2c_master_base(id); + writew(UDMA_I2C_SETUP_RESET_BIT, base + UDMA_I2C_SETUP_OFFSET); + writew(0U, base + UDMA_I2C_SETUP_OFFSET); + writew(0U, base + UDMA_I2C_RX_CFG_OFFSET); + writew(0U, base + UDMA_I2C_TX_CFG_OFFSET); + + return 0; +} + +int i2c_master_read_reg(uint8_t id, uint8_t dev_addr7, uint8_t reg_addr, + uint8_t *buf, uint8_t len) +{ + uintptr_t base; + uint32_t timeout; + uint32_t cmd_len; + uint32_t status; + + if ((id >= I2C_MASTER_COUNT) || (dev_addr7 > 0x7FU) || (buf == NULL) || (len == 0U)) + { + return -1; + } + + cmd_len = 0U; + s_cmd_buf[cmd_len++] = I2C_CMD_CFG; + s_cmd_buf[cmd_len++] = 0U; + s_cmd_buf[cmd_len++] = s_clkdiv[id]; + s_cmd_buf[cmd_len++] = I2C_CMD_START; + s_cmd_buf[cmd_len++] = I2C_CMD_WR; + s_cmd_buf[cmd_len++] = (uint8_t)(dev_addr7 << 1U); + s_cmd_buf[cmd_len++] = I2C_CMD_WR; + s_cmd_buf[cmd_len++] = reg_addr; + s_cmd_buf[cmd_len++] = I2C_CMD_START; + s_cmd_buf[cmd_len++] = I2C_CMD_WR; + s_cmd_buf[cmd_len++] = (uint8_t)((dev_addr7 << 1U) | 1U); + if (len > 1U) + { + s_cmd_buf[cmd_len++] = I2C_CMD_RPT; + s_cmd_buf[cmd_len++] = (uint8_t)(len - 1U); + s_cmd_buf[cmd_len++] = I2C_CMD_RD_ACK; + } + s_cmd_buf[cmd_len++] = I2C_CMD_RD_NACK; + s_cmd_buf[cmd_len++] = I2C_CMD_STOP; + s_cmd_buf[cmd_len++] = I2C_CMD_WAIT; + s_cmd_buf[cmd_len++] = 0U; + + base = i2c_master_base(id); + writew((uint32_t)(uintptr_t)buf, base + UDMA_I2C_RX_SADDR_OFFSET); + writew((uint32_t)len, base + UDMA_I2C_RX_SIZE_OFFSET); + writew(UDMA_CFG_EN_BIT, base + UDMA_I2C_RX_CFG_OFFSET); + + writew((uint32_t)(uintptr_t)s_cmd_buf, base + UDMA_I2C_TX_SADDR_OFFSET); + writew(cmd_len, base + UDMA_I2C_TX_SIZE_OFFSET); + writew(UDMA_CFG_EN_BIT, base + UDMA_I2C_TX_CFG_OFFSET); + + for (timeout = 0U; timeout < I2C_TIMEOUT_ITERATIONS; ++timeout) + { + status = readw(base + UDMA_I2C_STATUS_OFFSET); + if ((status & UDMA_I2C_STATUS_AL_BIT) != 0U) + { + return -1; + } + if (((readw(base + UDMA_I2C_TX_CFG_OFFSET) & UDMA_CFG_PENDING_BIT) == 0U) && + ((readw(base + UDMA_I2C_RX_CFG_OFFSET) & UDMA_CFG_PENDING_BIT) == 0U)) + { + return 0; + } + } + + return -1; +} diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/irq.c b/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/irq.c new file mode 100644 index 00000000..cb79cc06 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/irq.c @@ -0,0 +1,50 @@ +/***************************************************************************/ +/* Copyright (C) 2019 ETH Zurich and University of Bologna + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * Derived from core-v-freertos (https://github.com/openhwgroup/core-v-freertos) + * Original work licensed under the Apache License, Version 2.0. + * See https://www.apache.org/licenses/LICENSE-2.0 + * Modifications licensed under MIT (https://opensource.org/licenses/MIT). + * + * AI Disclosure: Some portions generated or modified by Copilot (Sonnet 4.6). + * + * SPDX-License-Identifier: Apache-2.0 AND MIT + ***************************************************************************/ + +#include <stdint.h> + +#include "pulp_mem_map.h" +#include "io.h" +#include "irq.h" +#include "csr.h" + +void irq_mask(uint32_t mask) +{ + writew(mask, (uintptr_t)(PULP_FC_IRQ_ADDR + IRQ_REG_MASK_OFFSET)); +} + +void irq_enable(uint32_t mask) +{ + writew(mask, (uintptr_t)(PULP_FC_IRQ_ADDR + IRQ_REG_MASK_SET_OFFSET)); +} + +void irq_disable(uint32_t mask) +{ + writew(mask, (uintptr_t)(PULP_FC_IRQ_ADDR + IRQ_REG_MASK_CLEAR_OFFSET)); +} + +uint32_t irq_clint_disable(void) +{ + return (uint32_t)csr_read_clear(CSR_MSTATUS, MSTATUS_IE); +} + +uint32_t irq_clint_enable(void) +{ + return (uint32_t)csr_read_set(CSR_MSTATUS, MSTATUS_IE); +} + +void pulp_irq_init(void) +{ + irq_disable(0xFFFFFFFFUL); +} diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/string.c b/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/string.c new file mode 100644 index 00000000..7ff699e7 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/string.c @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +#include <stddef.h> +#include <stdint.h> + +#include "string.h" + +void *memcpy(void *dest, const void *src, size_t n) +{ + size_t index; + uint8_t *dst = (uint8_t *)dest; + const uint8_t *source = (const uint8_t *)src; + + for (index = 0U; index < n; ++index) + { + dst[index] = source[index]; + } + + return dest; +} + +void *memset(void *dest, int c, size_t n) +{ + size_t index; + uint8_t *dst = (uint8_t *)dest; + uint8_t value = (uint8_t)c; + + for (index = 0U; index < n; ++index) + { + dst[index] = value; + } + + return dest; +} + +int memcmp(const void *lhs, const void *rhs, size_t n) +{ + size_t index; + const uint8_t *left = (const uint8_t *)lhs; + const uint8_t *right = (const uint8_t *)rhs; + + for (index = 0U; index < n; ++index) + { + if (left[index] != right[index]) + { + return (left[index] < right[index]) ? -1 : 1; + } + } + + return 0; +} diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/system_core_v_mcu.c b/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/system_core_v_mcu.c new file mode 100644 index 00000000..f142c95d --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/system_core_v_mcu.c @@ -0,0 +1,132 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +#include <stddef.h> +#include <stdint.h> + +#include "csr.h" +#include "fll.h" +#include "gpio.h" +#include "irq.h" +#include "properties.h" +#include "system_core_v_mcu.h" +#include "timer_irq.h" +#include "tx_api.h" +#include "uart_driver.h" + +extern void _tx_timer_interrupt(void); + +volatile uint32_t system_core_clock = DEFAULT_SYSTEM_CLOCK; +volatile uint32_t last_trap_mcause; +void (*isr_table[32])(void); + +static uint32_t uart_console_ready; + +static void uart_write_hex32(uint32_t value) +{ + static const char hex[] = "0123456789ABCDEF"; + int shift; + + uart_write_str(0U, "0x"); + for (shift = 28; shift >= 0; shift -= 4) + { + uart_write_byte(0U, (uint8_t)hex[(value >> (uint32_t)shift) & 0xFU]); + } +} + +void tx_undefined_irq_handler(void) +{ + if (uart_console_ready != 0U) + { + uart_write_str(0U, "\r\nUndefined IRQ\r\n"); + } + + /* MISRA deviation: intentional infinite loop used as a fault sink. */ + for (;;) + { + } +} + +void tx_timer_irq_handler(void) +{ + _tx_timer_interrupt(); +} + +void system_init(void) +{ + uint32_t i; + + for (i = 0U; i < 32U; ++i) + { + isr_table[i] = tx_undefined_irq_handler; + } + + isr_table[7U] = tx_timer_irq_handler; + + for (i = 0U; i < ARCHI_NB_FLL; ++i) + { + pi_fll_init((fll_type_t)i, 0U); + } + + pulp_irq_init(); + (void)timer_irq_init(ARCHI_SOC_FREQUENCY / (uint32_t)TX_TIMER_TICKS_PER_SECOND); + /* CV32E40P routes the FC Timer LO to irq_i[7] (MTI, bit 7), which maps + * to mip[7]. IRQ_MASK in cv32e40p_cs_registers forces mie[10] to zero, + * so bit 7 is the correct enable bit. mcause will be 0x80000007. */ + (void)csr_read_set(CSR_MIE, BIT(7)); + + gpio_init(); + if (uart_init(0U, 115200U, ARCHI_FPGA_FREQUENCY) == 0) + { + uart_console_ready = 1U; + } +} + +void tx_trap_handler(uint32_t mcause, uint32_t mepc, uint32_t mtval) +{ + last_trap_mcause = mcause; + + if ((mcause & 0x80000000UL) != 0UL) + { + uint32_t irq_id = mcause & 0x1FUL; + + if (isr_table[irq_id] != NULL) + { + isr_table[irq_id](); + } + else + { + tx_undefined_irq_handler(); + } + } + else + { + if (uart_console_ready != 0U) + { + uart_write_str(0U, "\r\nTrap mcause="); + uart_write_hex32(mcause); + uart_write_str(0U, " mepc="); + uart_write_hex32(mepc); + uart_write_str(0U, " mtval="); + uart_write_hex32(mtval); + uart_write_str(0U, "\r\n"); + } + + /* MISRA deviation: intentional infinite loop used as a fault sink. */ + for (;;) + { + } + } +} diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/temp_sensor.c b/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/temp_sensor.c new file mode 100644 index 00000000..6d70e943 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/temp_sensor.c @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +#include <stdint.h> + +#include "i2c_master.h" +#include "properties.h" +#include "temp_sensor.h" + +#define ADT7420_I2C_ID 1U +#define ADT7420_ADDR7 0x4BU +#define ADT7420_TEMP_REG 0x00U +#define ADT7420_I2C_FREQ_HZ 200000U + +void temp_sensor_init(void) +{ + (void)i2c_master_init(ADT7420_I2C_ID, ADT7420_I2C_FREQ_HZ, ARCHI_FPGA_FREQUENCY); +} + +int16_t temp_read_celsius_x10(void) +{ + uint8_t buf[2]; + int16_t raw; + int16_t value; + + if (i2c_master_read_reg(ADT7420_I2C_ID, ADT7420_ADDR7, ADT7420_TEMP_REG, &buf[0], 2U) != 0) + { + return INT16_MIN; + } + + raw = (int16_t)(((uint16_t)buf[0] << 8U) | (uint16_t)buf[1]); + value = (int16_t)(raw >> 3); + return (int16_t)(((int32_t)value * 5) / 8); +} diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/timer_irq.c b/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/timer_irq.c new file mode 100644 index 00000000..9a085d9f --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/timer_irq.c @@ -0,0 +1,58 @@ +/***************************************************************************/ +/* Copyright (C) 2019 ETH Zurich and University of Bologna + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * Derived from core-v-freertos (https://github.com/openhwgroup/core-v-freertos) + * Original work licensed under the Apache License, Version 2.0. + * See https://www.apache.org/licenses/LICENSE-2.0 + * Modifications licensed under MIT (https://opensource.org/licenses/MIT). + * + * AI Disclosure: Some portions generated or modified by Copilot (Sonnet 4.6). + * + * SPDX-License-Identifier: Apache-2.0 AND MIT + ***************************************************************************/ + +#include <stdint.h> + +#include "pulp_mem_map.h" +#include "io.h" +#include "timer.h" +#include "timer_irq.h" + +static uint32_t last_count; + +int timer_irq_init(uint32_t ticks) +{ + (void)timer_irq_set_timeout(ticks); + + writew(TIMER_CFG_LO_ENABLE_MASK | + TIMER_CFG_LO_RESET_MASK | + TIMER_CFG_LO_MODE_MASK | + TIMER_CFG_LO_IRQEN_MASK, + (uintptr_t)(PULP_FC_TIMER_ADDR + TIMER_CFG_LO_OFFSET)); + + last_count = 0U; + return 0; +} + +int timer_irq_set_timeout(uint32_t ticks) +{ + writew(1U, (uintptr_t)(PULP_FC_TIMER_ADDR + TIMER_RESET_LO_OFFSET)); + writew(ticks, (uintptr_t)(PULP_FC_TIMER_ADDR + TIMER_CMP_LO_OFFSET)); + last_count = 0U; + return 0; +} + +uint32_t timer_irq_clock_elapsed(void) +{ + uint32_t current = timer_irq_cycle_get_32(); + uint32_t elapsed = current - last_count; + + last_count = current; + return elapsed; +} + +uint32_t timer_irq_cycle_get_32(void) +{ + return readw((uintptr_t)(PULP_FC_TIMER_ADDR + TIMER_CNT_LO_OFFSET)); +} diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/uart_driver.c b/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/uart_driver.c new file mode 100644 index 00000000..33b1ff15 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/uart_driver.c @@ -0,0 +1,169 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +#include <stddef.h> +#include <stdint.h> + +#include "bits.h" +#include "io.h" +#include "pulp_mem_map.h" +#include "uart_driver.h" + +#define UDMA_CTRL_CG_OFFSET 0x00U +#define UDMA_CTRL_UART0_CLKEN BIT(0) + +#define UDMA_UART_RX_SADDR_OFFSET 0x00U +#define UDMA_UART_RX_SIZE_OFFSET 0x04U +#define UDMA_UART_RX_CFG_OFFSET 0x08U +#define UDMA_UART_TX_SADDR_OFFSET 0x10U +#define UDMA_UART_TX_SIZE_OFFSET 0x14U +#define UDMA_UART_TX_CFG_OFFSET 0x18U +#define UDMA_UART_STATUS_OFFSET 0x20U +#define UDMA_UART_SETUP_OFFSET 0x24U +#define UDMA_UART_VALID_OFFSET 0x30U +#define UDMA_UART_DATA_OFFSET 0x34U + +#define UDMA_CFG_EN_BIT BIT(4) +/* EN (bit 4) only; DATASIZE=0 for 8-bit transfers; 0x12 would set DATASIZE=1 + * (16-bit) and cause the EN bit to never clear for single-byte buffers. */ +#define UDMA_UART_TX_CFG_ENABLE 0x10U +#define UDMA_UART_WORDLEN_8_BITS 3U +#define UDMA_UART_TX_ENABLE_BIT BIT(8) +#define UDMA_UART_RX_ENABLE_BIT BIT(9) +#define UDMA_UART_RX_POLLING_BIT BIT(4) +#define UDMA_UART_RX_CLEAN_BIT BIT(5) +#define UDMA_UART_DIV_SHIFT 16U +#define UDMA_UART_VALID_DATA_BIT BIT(0) +#define UDMA_UART_STATUS_TX_BUSY BIT(0) +#define UDMA_UART_MAX_TRANSFER 0xFFFFU + +static uint8_t uart_tx_byte[N_UART]; + +static uintptr_t uart_base(uint8_t uart_id) +{ + return (uintptr_t)(UDMA_CH_ADDR_UART + ((uintptr_t)uart_id * (uintptr_t)UDMA_CH_SIZE)); +} + +int uart_init(uint8_t uart_id, uint32_t baudrate, uint32_t periph_freq) +{ + uint32_t clk_div; + uint32_t setup; + uint32_t cg; + + if ((uart_id >= N_UART) || (baudrate == 0U) || (periph_freq == 0U)) + { + return -1; + } + + cg = readw((uintptr_t)(UDMA_CH_ADDR_CTRL + UDMA_CTRL_CG_OFFSET)); + cg |= (uint32_t)(UDMA_CTRL_UART0_CLKEN << uart_id); + writew(cg, (uintptr_t)(UDMA_CH_ADDR_CTRL + UDMA_CTRL_CG_OFFSET)); + + clk_div = periph_freq / baudrate; + if (clk_div == 0U) + { + clk_div = 1U; + } + + setup = (clk_div << UDMA_UART_DIV_SHIFT) | + UDMA_UART_TX_ENABLE_BIT | + UDMA_UART_RX_ENABLE_BIT | + UDMA_UART_RX_POLLING_BIT | + UDMA_UART_RX_CLEAN_BIT | + (UDMA_UART_WORDLEN_8_BITS << 1U); + writew(setup, uart_base(uart_id) + UDMA_UART_SETUP_OFFSET); + + writew(0U, uart_base(uart_id) + UDMA_UART_RX_SADDR_OFFSET); + writew(0U, uart_base(uart_id) + UDMA_UART_RX_SIZE_OFFSET); + writew(0U, uart_base(uart_id) + UDMA_UART_RX_CFG_OFFSET); + + return 0; +} + +void uart_write_byte(uint8_t uart_id, uint8_t c) +{ + if (uart_id >= N_UART) + { + return; + } + + uart_tx_byte[uart_id] = c; + uart_write_buf(uart_id, &uart_tx_byte[uart_id], 1U); +} + +int uart_read_byte(uint8_t uart_id) +{ + if (uart_id >= N_UART) + { + return -1; + } + + if ((readw(uart_base(uart_id) + UDMA_UART_VALID_OFFSET) & UDMA_UART_VALID_DATA_BIT) == 0U) + { + return -1; + } + + return (int)(readw(uart_base(uart_id) + UDMA_UART_DATA_OFFSET) & 0xFFU); +} + +void uart_write_str(uint8_t uart_id, const char *str) +{ + if (str == NULL) + { + return; + } + + while (*str != '\0') + { + uart_write_byte(uart_id, (uint8_t)*str); + ++str; + } +} + +void uart_write_buf(uint8_t uart_id, const uint8_t *buf, uint32_t len) +{ + uintptr_t base_addr; + const uint8_t *current; + uint32_t remaining; + + if ((uart_id >= N_UART) || (buf == NULL) || (len == 0U)) + { + return; + } + + base_addr = uart_base(uart_id); + current = buf; + remaining = len; + + while (remaining > 0U) + { + uint32_t chunk = (remaining > UDMA_UART_MAX_TRANSFER) ? UDMA_UART_MAX_TRANSFER : remaining; + + while ((readw(base_addr + UDMA_UART_STATUS_OFFSET) & UDMA_UART_STATUS_TX_BUSY) != 0U) + { + } + + writew((uint32_t)(uintptr_t)current, base_addr + UDMA_UART_TX_SADDR_OFFSET); + writew(chunk, base_addr + UDMA_UART_TX_SIZE_OFFSET); + writew(UDMA_UART_TX_CFG_ENABLE, base_addr + UDMA_UART_TX_CFG_OFFSET); + + while ((readw(base_addr + UDMA_UART_TX_CFG_OFFSET) & UDMA_CFG_EN_BIT) != 0U) + { + } + + current += chunk; + remaining -= chunk; + } +} diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/build.sh b/ports/risc-v32/gnu/example_build/core_v_mcu/build.sh new file mode 100755 index 00000000..b610ac42 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/build.sh @@ -0,0 +1,26 @@ +#!/bin/bash +############################################################################## +# Copyright (c) 2024 Microsoft Corporation +# Copyright (c) 2026 Eclipse ThreadX contributors +# +# This program and the accompanying materials are made available under the +# terms of the MIT License which is available at +# https://opensource.org/licenses/MIT. +# +# SPDX-License-Identifier: MIT +############################################################################## + +set -e + +XPACK_GCC_DIR=/opt/xpack-riscv-none-elf-gcc-15.2.0-1 +if [ -d "${XPACK_GCC_DIR}/bin" ]; then + export PATH="${XPACK_GCC_DIR}/bin:${PATH}" +fi + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +THREADX_ROOT="${SCRIPT_DIR}/../../../../.." +cmake -B "${SCRIPT_DIR}/build" \ + -G Ninja \ + -DCMAKE_TOOLCHAIN_FILE="${THREADX_ROOT}/cmake/riscv-none-elf-rv32imc.cmake" \ + "${SCRIPT_DIR}" +cmake --build "${SCRIPT_DIR}/build" diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/crt0.S b/ports/risc-v32/gnu/example_build/core_v_mcu/crt0.S new file mode 100644 index 00000000..3c761bbe --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/crt0.S @@ -0,0 +1,48 @@ +/***************************************************************************/ +/* Copyright 2020 ETH Zurich + * Copyright (c) 2017 SiFive Inc. All rights reserved. + * Copyright (c) 2019 ETH Zürich and University of Bologna + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * Derived from core-v-freertos (https://github.com/openhwgroup/core-v-freertos) + * Original work licensed under the Apache License, Version 2.0 (Apache-2.0) + * or the BSD 2-Clause License (BSD-2-Clause) at your option. + * See https://www.apache.org/licenses/LICENSE-2.0 + * Modifications licensed under MIT (https://opensource.org/licenses/MIT). + * + * AI Disclosure: Some portions generated or modified by Copilot (Sonnet 4.6). + * + * SPDX-License-Identifier: (Apache-2.0 OR BSD-2-Clause) AND MIT + ***************************************************************************/ + + .section .text.start + .global _start + .type _start, @function + +_start: + .option push + .option norelax +1: auipc gp, %pcrel_hi(__global_pointer$) + addi gp, gp, %pcrel_lo(1b) + .option pop + + la sp, __stack_top + + la a0, __vector_start + ori a0, a0, 1 + csrw mtvec, a0 + + la t0, __bss_start + la t1, __bss_end +2: + bgeu t0, t1, 3f + sw zero, 0(t0) + addi t0, t0, 4 + j 2b +3: + call main +4: + wfi + j 4b + + .size _start, .-_start diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/demo_threadx.c b/ports/risc-v32/gnu/example_build/core_v_mcu/demo_threadx.c new file mode 100644 index 00000000..c7daac3c --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/demo_threadx.c @@ -0,0 +1,134 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +#include <stdint.h> + +#include "gpio.h" +#include "system_core_v_mcu.h" +#include "tx_api.h" +#include "uart_driver.h" + +#define DEMO_STACK_SIZE 1024U +#define DEMO_BYTE_POOL_SIZE 4096U +/* + * LED[0] on Nexys A7 = IO pad 11 = GPIO/apbio pin 4 (MUX=2). + * Verified in the quick-start guide and the 2022 nexys-pin-table.csv used to + * synthesise the pre-built bitfile (io_pad = gpio_pin + 7). + */ +#define DEMO_LED_PIN 4U +#define DEMO_LED_MASK (1UL << DEMO_LED_PIN) + +/* Assemble the version banner string at compile time from tx_api.h constants. */ +#define _TX_STR(x) #x +#define TX_STR(x) _TX_STR(x) +#define TX_VERSION_STRING \ + "v" TX_STR(THREADX_MAJOR_VERSION) \ + "." TX_STR(THREADX_MINOR_VERSION) \ + "." TX_STR(THREADX_PATCH_VERSION) \ + "." TX_STR(THREADX_BUILD_VERSION) + +static const char banner[] = + "\r\nEclipse ThreadX for OpenHW CORE-V MCU " TX_VERSION_STRING "\r\n" + "Copyright (c) 2026 Eclipse ThreadX Contributors\r\n\r\n"; + +static TX_THREAD blinky_thread; +static TX_THREAD uart_thread; +static TX_BYTE_POOL byte_pool; +static uint8_t memory_area[DEMO_BYTE_POOL_SIZE]; + +static void demo_error_loop(void) +{ + /* MISRA deviation: intentional infinite loop used as a fault sink. */ + for (;;) + { + } +} + +static void blinky_thread_entry(ULONG arg) +{ + TX_PARAMETER_NOT_USED(arg); + + /* Configure LED[0] as push-pull output (sets pad-mux to FUNC_C = 2). */ + gpio_set_output((uint32_t)DEMO_LED_MASK); + + for (;;) + { + gpio_set((uint32_t)DEMO_LED_MASK); + tx_thread_sleep(100U); /* 1 s on (100 ticks × 10 ms) */ + gpio_clear((uint32_t)DEMO_LED_MASK); + tx_thread_sleep(100U); /* 1 s off */ + } +} + +static void uart_thread_entry(ULONG arg) +{ + TX_PARAMETER_NOT_USED(arg); + + uart_write_str(0U, banner); + for (;;) + { + uart_write_str(0U, "."); + tx_thread_sleep(100U); + } +} + +void tx_application_define(void *first_unused_memory) +{ + CHAR *stack_ptr = TX_NULL; + UINT status; + + TX_PARAMETER_NOT_USED(first_unused_memory); + + status = tx_byte_pool_create(&byte_pool, "demo pool", memory_area, DEMO_BYTE_POOL_SIZE); + if (status != TX_SUCCESS) + { + demo_error_loop(); + } + + status = tx_byte_allocate(&byte_pool, (VOID **)&stack_ptr, DEMO_STACK_SIZE, TX_NO_WAIT); + if (status != TX_SUCCESS) + { + demo_error_loop(); + } + + status = tx_thread_create(&blinky_thread, "blinky", blinky_thread_entry, 0U, + stack_ptr, DEMO_STACK_SIZE, 1U, 1U, + TX_NO_TIME_SLICE, TX_AUTO_START); + if (status != TX_SUCCESS) + { + demo_error_loop(); + } + + status = tx_byte_allocate(&byte_pool, (VOID **)&stack_ptr, DEMO_STACK_SIZE, TX_NO_WAIT); + if (status != TX_SUCCESS) + { + demo_error_loop(); + } + + status = tx_thread_create(&uart_thread, "uart", uart_thread_entry, 0U, + stack_ptr, DEMO_STACK_SIZE, 2U, 2U, + TX_NO_TIME_SLICE, TX_AUTO_START); + if (status != TX_SUCCESS) + { + demo_error_loop(); + } +} + +int main(void) +{ + system_init(); + tx_kernel_enter(); + return 0; +} diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/deploy.sh b/ports/risc-v32/gnu/example_build/core_v_mcu/deploy.sh new file mode 100755 index 00000000..2e22ec1e --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/deploy.sh @@ -0,0 +1,142 @@ +#!/usr/bin/env bash +# /***************************************************************************/ +# /* Copyright (C) 2026 Eclipse ThreadX contributors +# * +# * This program and the accompanying materials are made available under the +# * terms of the MIT License which is available at +# * https://opensource.org/licenses/MIT. +# * +# * AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6). +# * The AI-generated portions may be considered public domain (CC0-1.0) +# * and not subject to the project's licence. +# * +# * SPDX-License-Identifier: MIT AND CC0-1.0 +# ***************************************************************************/ + +# deploy.sh -- build (optionally), flash, and run/debug the CORE-V MCU target +# via the Ashling Opella LD debug probe. +# +# Usage: +# bash deploy.sh [options] +# +# Options: +# --build Run build.sh before flashing (default: skip). +# --debug After loading, stop at main and leave GDB attached +# (default: run free and exit GDB). +# --elf <path> Override the ELF to flash +# (default: build/demo_threadx.elf). +# --openocd-cfg <p> Override the OpenOCD config file. +# +# Prerequisites: +# * riscv64-unknown-elf-gdb and openocd must be on PATH. +# * Run setup_opella.sh once to configure udev rules. +# * The Nexys A7 board must be powered on. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# Prefer a bare-metal RISC-V GDB; fall back to gdb-multiarch (Ubuntu apt default) +if command -v riscv64-unknown-elf-gdb >/dev/null 2>&1; then + GDB="riscv64-unknown-elf-gdb" +elif command -v gdb-multiarch >/dev/null 2>&1; then + GDB="gdb-multiarch" +else + GDB="riscv64-unknown-elf-gdb" # will produce a clear error below +fi +OPENOCD_LOG="/tmp/openocd_demo_threadx.log" +OPENOCD_PID="" +OPENOCD_TIMEOUT=10 + +DEFAULT_ELF="${SCRIPT_DIR}/build/demo_threadx.elf" +DEFAULT_CFG="${SCRIPT_DIR}/openocd-nexys-Ashling-Opella-LD.cfg" + +OPT_BUILD=0 +OPT_DEBUG=0 +OPT_ELF="" +OPT_CFG="" + +usage() { grep '^# ' "$0" | sed 's/^# //' | head -30; exit 0; } + +while [[ $# -gt 0 ]]; do + case "$1" in + --build) OPT_BUILD=1; shift ;; + --debug) OPT_DEBUG=1; shift ;; + --elf) OPT_ELF="$2"; shift 2 ;; + --openocd-cfg) OPT_CFG="$2"; shift 2 ;; + -h|--help) usage ;; + *) echo "[ERROR] Unknown option: $1" >&2; usage ;; + esac +done + +ELF="${OPT_ELF:-${DEFAULT_ELF}}" +CFG="${OPT_CFG:-${DEFAULT_CFG}}" + +info() { echo "[INFO] $*"; } +die() { echo "[ERROR] $*" >&2; exit 1; } + +stop_openocd() { + if [ -n "${OPENOCD_PID}" ] && kill -0 "${OPENOCD_PID}" 2>/dev/null; then + info "Stopping OpenOCD (PID ${OPENOCD_PID}) ..." + kill "${OPENOCD_PID}" 2>/dev/null || true + wait "${OPENOCD_PID}" 2>/dev/null || true + fi +} + +trap stop_openocd EXIT INT TERM + +if [ "${OPT_BUILD}" -eq 1 ]; then + info "Running build.sh ..." + bash "${SCRIPT_DIR}/build.sh" +fi + +[ -f "${ELF}" ] || die "ELF not found: ${ELF} (run with --build to build first)" +[ -f "${CFG}" ] || die "OpenOCD config not found: ${CFG}" +command -v openocd >/dev/null 2>&1 || die "openocd not found on PATH" +command -v "${GDB}" >/dev/null 2>&1 || die "${GDB} not found on PATH" + +info "Starting OpenOCD with config: ${CFG}" +info "Log: ${OPENOCD_LOG}" +openocd -f "${CFG}" >"${OPENOCD_LOG}" 2>&1 & +OPENOCD_PID=$! + +info "Waiting for OpenOCD to be ready (up to ${OPENOCD_TIMEOUT}s) ..." +ELAPSED=0 +while ! grep -q "Ready for Remote Connections" "${OPENOCD_LOG}" 2>/dev/null; do + sleep 1 + ELAPSED=$((ELAPSED + 1)) + if [ "${ELAPSED}" -ge "${OPENOCD_TIMEOUT}" ]; then + echo "--- OpenOCD log ---" && cat "${OPENOCD_LOG}" + die "OpenOCD did not become ready within ${OPENOCD_TIMEOUT}s" + fi + if ! kill -0 "${OPENOCD_PID}" 2>/dev/null; then + echo "--- OpenOCD log ---" && cat "${OPENOCD_LOG}" + die "OpenOCD exited unexpectedly" + fi +done +info "OpenOCD is ready." + +GDB_INIT_FILE="/tmp/gdb_deploy_demo_threadx.gdb" +printf 'set confirm off +set remotetimeout 60 +file %s +target extended-remote localhost:3333 +load +' "${ELF}" > "${GDB_INIT_FILE}" + +if [ "${OPT_DEBUG}" -eq 0 ]; then + info "Flashing and running (detached) ..." + printf 'monitor resume\ndisconnect\nquit\n' >> "${GDB_INIT_FILE}" + info "ELF: ${ELF}" + riscv64-unknown-elf-size "${ELF}" + "${GDB}" --batch --command="${GDB_INIT_FILE}" 2>&1 \ + | grep -v "keep_alive() was not invoked" \ + | grep -v "GDB alive packet not sent" + info "Flash complete. Application is running on the target." + info "Serial console: minicom -b 115200 -D /dev/ttyUSBx" +else + info "Flashing and stopping at main for interactive debug ..." + printf 'break main\ncontinue\n' >> "${GDB_INIT_FILE}" + info "ELF: ${ELF}" + riscv64-unknown-elf-size "${ELF}" + "${GDB}" --command="${GDB_INIT_FILE}" +fi diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/gdb_init b/ports/risc-v32/gnu/example_build/core_v_mcu/gdb_init new file mode 100644 index 00000000..718a20f1 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/gdb_init @@ -0,0 +1,6 @@ +set arch riscv:rv32 +target extended-remote localhost:3333 +file build/demo_threadx.elf +load +break main +continue diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/include/bits.h b/ports/risc-v32/gnu/example_build/core_v_mcu/include/bits.h new file mode 100644 index 00000000..08871387 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/include/bits.h @@ -0,0 +1,59 @@ +/***************************************************************************/ +/* Copyright (c) 2011-2014, Wind River Systems, Inc. + * Copyright 2020 ETH Zurich + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * Derived from core-v-freertos (https://github.com/openhwgroup/core-v-freertos) + * Original work licensed under the Apache License, Version 2.0. + * See https://www.apache.org/licenses/LICENSE-2.0 + * Modifications licensed under MIT (https://opensource.org/licenses/MIT). + * + * AI Disclosure: Some portions generated or modified by Copilot (Sonnet 4.6). + * + * SPDX-License-Identifier: Apache-2.0 AND MIT + ***************************************************************************/ + +#ifndef BITS_H +#define BITS_H + +#include <stdbool.h> +#include <stdint.h> + +/* Helper to pass an integer as a pointer or vice versa. */ +#define POINTER_TO_UINT(x) ((uintptr_t)(x)) +#define UINT_TO_POINTER(x) ((void *)(uintptr_t)(x)) +#define POINTER_TO_INT(x) ((intptr_t)(x)) +#define INT_TO_POINTER(x) ((void *)(intptr_t)(x)) + +#if !(defined(__CHAR_BIT__) && defined(__SIZEOF_LONG__)) +#error Missing required predefined macros for BITS_PER_LONG calculation +#endif + +#define BITS_PER_LONG (__CHAR_BIT__ * __SIZEOF_LONG__) +#define GENMASK(h, l) \ + (((~0UL) - (1UL << (l)) + 1UL) & (~0UL >> (BITS_PER_LONG - 1UL - (h)))) + +#define KB(x) ((x) << 10) +#define MB(x) (KB(x) << 10) +#define GB(x) (MB(x) << 10) + +#define KHZ(x) ((x) * 1000) +#define MHZ(x) (KHZ(x) * 1000) + +#ifndef BIT +#ifdef _ASMLANGUAGE +#define BIT(n) (1 << (n)) +#else +#define BIT(n) (1UL << (n)) +#endif +#endif + +#define WRITE_BIT(var, bit, set) \ + ((var) = ((set) != 0) ? ((var) | BIT(bit)) : ((var) & ~BIT(bit))) + +#define BIT_MASK(n) (BIT(n) - 1UL) + +#define REG_SET(FIELD, v) (((uint32_t)(v) << FIELD##_SHIFT) & FIELD##_MASK) +#define REG_GET(FIELD, v) (((uint32_t)(v) & FIELD##_MASK) >> FIELD##_SHIFT) + +#endif /* BITS_H */ diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/include/csr.h b/ports/risc-v32/gnu/example_build/core_v_mcu/include/csr.h new file mode 100644 index 00000000..69c7763e --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/include/csr.h @@ -0,0 +1,75 @@ +/***************************************************************************/ +/* Copyright (C) 2020 ETH Zurich and University of Bologna + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * Derived from core-v-freertos (https://github.com/openhwgroup/core-v-freertos) + * Original work licensed under the Apache License, Version 2.0. + * See https://www.apache.org/licenses/LICENSE-2.0 + * Modifications licensed under MIT (https://opensource.org/licenses/MIT). + * + * AI Disclosure: Some portions generated or modified by Copilot (Sonnet 4.6). + * + * SPDX-License-Identifier: Apache-2.0 AND MIT + ***************************************************************************/ + +#ifndef CSR_H +#define CSR_H + +#include "bits.h" + +#define CSR_MSTATUS 0x300 +#define CSR_MISA 0x301 +#define CSR_MIE 0x304 +#define CSR_MTVEC 0x305 +#define CSR_MSCRATCH 0x340 +#define CSR_MEPC 0x341 +#define CSR_MCAUSE 0x342 +#define CSR_MTVAL 0x343 +#define CSR_MIP 0x344 +#define CSR_PMPCFG0 0x3A0 +#define CSR_PMPADDR0 0x3B0 +#define CSR_MVENDORID 0xF11 +#define CSR_MARCHID 0xF12 +#define CSR_MIMPID 0xF13 +#define CSR_MHARTID 0xF14 + +#define MSTATUS_IE BIT(3) + +#define __CSR_EXPAND(x) #x + +#ifndef csr_read +#define csr_read(csr) \ + ({ \ + register unsigned long __val; \ + __asm__ volatile("csrr %0, " __CSR_EXPAND(csr) : "=r"(__val) : : "memory"); \ + __val; \ + }) +#endif + +#ifndef csr_write +#define csr_write(csr, val) \ + ({ \ + unsigned long __val = (unsigned long)(val); \ + __asm__ volatile("csrw " __CSR_EXPAND(csr) ", %0" : : "rK"(__val) : "memory"); \ + }) +#endif + +#ifndef csr_read_clear +#define csr_read_clear(csr, val) \ + ({ \ + unsigned long __val = (unsigned long)(val); \ + __asm__ volatile("csrrc %0, " __CSR_EXPAND(csr) ", %1" : "=r"(__val) : "rK"(__val) : "memory"); \ + __val; \ + }) +#endif + +#ifndef csr_read_set +#define csr_read_set(csr, val) \ + ({ \ + unsigned long __val = (unsigned long)(val); \ + __asm__ volatile("csrrs %0, " __CSR_EXPAND(csr) ", %1" : "=r"(__val) : "rK"(__val) : "memory"); \ + __val; \ + }) +#endif + +#endif /* CSR_H */ diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/include/fll.h b/ports/risc-v32/gnu/example_build/core_v_mcu/include/fll.h new file mode 100644 index 00000000..011268df --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/include/fll.h @@ -0,0 +1,93 @@ +/***************************************************************************/ +/* Copyright 2020 GreenWaves Technologies + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * Derived from core-v-freertos (https://github.com/openhwgroup/core-v-freertos) + * Original work licensed under the Apache License, Version 2.0. + * See https://www.apache.org/licenses/LICENSE-2.0 + * Modifications licensed under MIT (https://opensource.org/licenses/MIT). + * + * AI Disclosure: Some portions generated or modified by Copilot (Sonnet 4.6). + * + * SPDX-License-Identifier: Apache-2.0 AND MIT + ***************************************************************************/ + +#ifndef FLL_H +#define FLL_H + +#include <stdint.h> + +#include "properties.h" +#include "pulp_mem_map.h" + +#define FLL_STATUS_OFFSET 0x000U +#define FLL_CONF1_OFFSET 0x004U +#define FLL_CONF2_OFFSET 0x008U +#define FLL_INTEGRATOR_OFFSET 0x00CU + +typedef struct +{ + volatile uint32_t FLL_STATUS; + volatile uint32_t FLL_CONF1; + volatile uint32_t FLL_CONF2; + volatile uint32_t FLL_INTEGRATOR; +} fll_ctrl_t; + +typedef enum _fll_type +{ + FLL_SOC = 0, + FLL_PERI = 1, + FLL_CLUSTER = 2 +} fll_type_t; + +typedef enum +{ + PI_FREQ_DOMAIN_FC = 0, + PI_FREQ_DOMAIN_CL = 1, + PI_FREQ_DOMAIN_PERIPH = 2 +} pi_freq_domain_e; + +#define FLL_CTRL_STATUS_MULTI_FACTOR_MASK 0xFFFFU +#define FLL_CTRL_STATUS_MULTI_FACTOR_SHIFT 0U +#define FLL_CTRL_CONF1_MULTI_FACTOR_MASK 0xFFFFU +#define FLL_CTRL_CONF1_MULTI_FACTOR_SHIFT 0U +#define FLL_CTRL_CONF1_DCO_INPUT_MASK 0x03FF0000U +#define FLL_CTRL_CONF1_DCO_INPUT_SHIFT 16U +#define FLL_CTRL_CONF1_CLK_OUT_DIV_MASK 0x3C000000U +#define FLL_CTRL_CONF1_CLK_OUT_DIV_SHIFT 26U +#define FLL_CTRL_CONF1_OUTPUT_LOCK_EN_MASK 0x40000000U +#define FLL_CTRL_CONF1_OUTPUT_LOCK_EN_SHIFT 30U +#define FLL_CTRL_CONF1_MODE_MASK 0x80000000U +#define FLL_CTRL_CONF1_MODE_SHIFT 31U +#define FLL_CTRL_CONF2_LOOPGAIN_MASK 0xFU +#define FLL_CTRL_CONF2_LOOPGAIN_SHIFT 0U +#define FLL_CTRL_CONF2_DEASSERT_CYCLES_MASK 0x3F0U +#define FLL_CTRL_CONF2_DEASSERT_CYCLES_SHIFT 4U +#define FLL_CTRL_CONF2_ASSERT_CYCLES_MASK 0xFC00U +#define FLL_CTRL_CONF2_ASSERT_CYCLES_SHIFT 10U +#define FLL_CTRL_CONF2_LOCK_TOLERANCE_MASK 0x0FFF0000U +#define FLL_CTRL_CONF2_LOCK_TOLERANCE_SHIFT 16U +#define FLL_CTRL_CONF2_CONF_CLK_SEL_MASK 0x20000000U +#define FLL_CTRL_CONF2_CONF_CLK_SEL_SHIFT 29U +#define FLL_CTRL_CONF2_OPEN_LOOP_MASK 0x40000000U +#define FLL_CTRL_CONF2_OPEN_LOOP_SHIFT 30U +#define FLL_CTRL_CONF2_DITHERING_MASK 0x80000000U +#define FLL_CTRL_CONF2_DITHERING_SHIFT 31U +#define FLL_CTRL_INTEGRATOR_FRACT_PART_MASK 0x0000FFC0U +#define FLL_CTRL_INTEGRATOR_FRACT_PART_SHIFT 6U +#define FLL_CTRL_INTEGRATOR_INT_PART_MASK 0x03FF0000U +#define FLL_CTRL_INTEGRATOR_INT_PART_SHIFT 16U +#define FLL_CTRL_SOC_FLL_CONV_MASK 0x1U +#define FLL_CTRL_SOC_FLL_CONV_SHIFT 0U +#define FLL_CTRL_CLUSTER_FLL_CONV_MASK 0x2U +#define FLL_CTRL_CLUSTER_FLL_CONV_SHIFT 1U + +#define FLL_NUM ARCHI_NB_FLL +#define FLL_REF_CLK ARCHI_REF_CLOCK +#define FLL_CTRL ((volatile fll_ctrl_t *)PULP_FLL_ADDR) + +void pi_fll_init(fll_type_t which_fll, uint32_t ret_state); +int pi_fll_set_frequency(fll_type_t which_fll, uint32_t frequency, int check); +int pi_fll_get_frequency(fll_type_t which_fll, uint8_t real); + +#endif /* FLL_H */ diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/include/gpio.h b/ports/risc-v32/gnu/example_build/core_v_mcu/include/gpio.h new file mode 100644 index 00000000..da5f6008 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/include/gpio.h @@ -0,0 +1,68 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +#ifndef GPIO_H +#define GPIO_H + +#include <stdint.h> + +/* apb_gpiov2 register map (pin-indexed interface) + * + * Offset Name Access Description + * 0x000 SETGPIO W PWDATA[6:0] = pin number → drive pin high + * 0x004 CLRGPIO W PWDATA[6:0] = pin number → drive pin low + * 0x008 TOGGPIO W PWDATA[6:0] = pin number → toggle pin + * 0x010 PIN0 R bits [31:0] = sampled input state of pins 0-31 + * 0x020 OUT0 R/W bits [31:0] = output state bitmask, pins 0-31 + * 0x030 SETSEL W select pin for RDSTAT + * 0x034 RDSTAT R status of selected pin + * 0x038 SETDIR W PWDATA[6:0]=pin, PWDATA[25:24]=dir + * dir[0]=1 → push-pull output; dir[1]=1 → open-drain + * 0x03C SETINT W interrupt configuration + * 0x040 INTACK W interrupt acknowledge + */ +#define GPIO_SETGPIO_OFFSET 0x000U +#define GPIO_CLRGPIO_OFFSET 0x004U +#define GPIO_TOGGPIO_OFFSET 0x008U +#define GPIO_PIN0_OFFSET 0x010U +#define GPIO_OUT0_OFFSET 0x020U +#define GPIO_SETSEL_OFFSET 0x030U +#define GPIO_RDSTAT_OFFSET 0x034U +#define GPIO_SETDIR_OFFSET 0x038U +#define GPIO_SETINT_OFFSET 0x03CU +#define GPIO_INTACK_OFFSET 0x040U + +/* Direction field in SETDIR: bits [25:24] of the written word. + * 0b01 = push-pull output enable; 0b10 = open-drain; 0b00 = input */ +#define GPIO_DIR_OUTPUT (1U << 24U) + +void gpio_init(void); +void gpio_set_output(uint32_t pin_mask); +void gpio_toggle(uint32_t pin_mask); +void gpio_set(uint32_t pin_mask); +void gpio_clear(uint32_t pin_mask); + +/* Pad-mux control (APB SOC CTRL v1.0.0). + * Each IO pad has a 32-bit register at SOC_CTRL_BASE + 0x400 + pad*4. + * bits[1:0] = mux function: 0=sys, 1=perio, 2=apbio/GPIO, 3=fpgaio. */ +void gpio_setpinmux(uint8_t io_pad, uint8_t mux); +uint8_t gpio_getpinmux(uint8_t io_pad); + +/* Single-pin direction and status (for CLI use). + * dir: 0 = input, 1 = push-pull output. */ +void gpio_pin_set_dir(uint8_t pin, uint8_t dir); +uint32_t gpio_pin_read_status(uint8_t pin); + +#endif /* GPIO_H */ diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/include/i2c_master.h b/ports/risc-v32/gnu/example_build/core_v_mcu/include/i2c_master.h new file mode 100644 index 00000000..4a478b30 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/include/i2c_master.h @@ -0,0 +1,38 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +#ifndef I2C_MASTER_H +#define I2C_MASTER_H + +#include <stdint.h> + +/* i2c_master_init: enable I2C channel clock gate, configure clock divider. + * id: 0 or 1 (I2CM0 or I2CM1) + * i2c_freq: desired I2C clock in Hz (e.g. 200000) + * periph_freq: UDMA peripheral clock in Hz (5000000 on CORE-V FPGA) + * Returns 0 on success, -1 on bad params. */ +int i2c_master_init(uint8_t id, uint32_t i2c_freq, uint32_t periph_freq); + +/* i2c_master_read_reg: read len bytes from register reg_addr of 7-bit addressed device. + * id: I2CM channel (0 or 1) + * dev_addr7: 7-bit I2C device address + * reg_addr: register address to read from + * buf: output buffer (must be in RAM, not stack if possible) + * len: number of bytes to read (1..255) + * Returns 0 on success, -1 on timeout or arbitration loss. */ +int i2c_master_read_reg(uint8_t id, uint8_t dev_addr7, uint8_t reg_addr, + uint8_t *buf, uint8_t len); + +#endif /* I2C_MASTER_H */ diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/include/io.h b/ports/risc-v32/gnu/example_build/core_v_mcu/include/io.h new file mode 100644 index 00000000..02364544 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/include/io.h @@ -0,0 +1,61 @@ +/***************************************************************************/ +/* Copyright 2020 ETH Zurich + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * Derived from core-v-freertos (https://github.com/openhwgroup/core-v-freertos) + * Original work licensed under the Apache License, Version 2.0. + * See https://www.apache.org/licenses/LICENSE-2.0 + * Modifications licensed under MIT (https://opensource.org/licenses/MIT). + * + * AI Disclosure: Some portions generated or modified by Copilot (Sonnet 4.6). + * + * SPDX-License-Identifier: Apache-2.0 AND MIT + ***************************************************************************/ + +#ifndef IO_H +#define IO_H + +#include <stdint.h> + +#ifndef CORE_V_MCU_MMIO_MOCK +static inline void writeb(uint8_t val, uintptr_t addr) +{ + __asm__ volatile("sb %0, 0(%1)" : : "r"(val), "r"((volatile uint8_t *)addr)); +} + +static inline void writeh(uint16_t val, uintptr_t addr) +{ + __asm__ volatile("sh %0, 0(%1)" : : "r"(val), "r"((volatile uint16_t *)addr)); +} + +static inline void writew(uint32_t val, uintptr_t addr) +{ + __asm__ volatile("sw %0, 0(%1)" : : "r"(val), "r"((volatile uint32_t *)addr)); +} + +static inline uint8_t readb(const uintptr_t addr) +{ + uint8_t val; + + __asm__ volatile("lb %0, 0(%1)" : "=r"(val) : "r"((const volatile uint8_t *)addr)); + return val; +} + +static inline uint16_t readh(const uintptr_t addr) +{ + uint16_t val; + + __asm__ volatile("lh %0, 0(%1)" : "=r"(val) : "r"((const volatile uint16_t *)addr)); + return val; +} + +static inline uint32_t readw(const uintptr_t addr) +{ + uint32_t val; + + __asm__ volatile("lw %0, 0(%1)" : "=r"(val) : "r"((const volatile uint32_t *)addr)); + return val; +} +#endif /* CORE_V_MCU_MMIO_MOCK */ + +#endif /* IO_H */ diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/include/irq.h b/ports/risc-v32/gnu/example_build/core_v_mcu/include/irq.h new file mode 100644 index 00000000..aafe6226 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/include/irq.h @@ -0,0 +1,64 @@ +/***************************************************************************/ +/* Copyright (C) 2019 ETH Zurich and University of Bologna + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * Derived from core-v-freertos (https://github.com/openhwgroup/core-v-freertos) + * Original work licensed under the Apache License, Version 2.0. + * See https://www.apache.org/licenses/LICENSE-2.0 + * Modifications licensed under MIT (https://opensource.org/licenses/MIT). + * + * AI Disclosure: Some portions generated or modified by Copilot (Sonnet 4.6). + * + * SPDX-License-Identifier: Apache-2.0 AND MIT + ***************************************************************************/ + +#ifndef IRQ_H +#define IRQ_H + +#include "pulp_mem_map.h" +#include "io.h" +#include "bits.h" + +#define IRQ_REG_MASK_OFFSET 0x000U +#define IRQ_REG_MASK_SET_OFFSET 0x004U +#define IRQ_REG_MASK_CLEAR_OFFSET 0x008U +#define IRQ_REG_INT_OFFSET 0x00CU +#define IRQ_REG_INT_SET_OFFSET 0x010U +#define IRQ_REG_INT_CLEAR_OFFSET 0x014U +#define IRQ_REG_ACK_OFFSET 0x018U +#define IRQ_REG_ACK_SET_OFFSET 0x01CU +#define IRQ_REG_ACK_CLEAR_OFFSET 0x020U +#define IRQ_REG_FIFO_OFFSET 0x024U + +#define IRQ_FC_EVT_SW0 BIT(0) +#define IRQ_FC_EVT_SW1 BIT(1) +#define IRQ_FC_EVT_SW2 BIT(2) +#define IRQ_FC_EVT_SW3 BIT(3) +#define IRQ_FC_EVT_SW4 BIT(4) +#define IRQ_FC_EVT_SW5 BIT(5) +#define IRQ_FC_EVT_SW6 BIT(6) +#define IRQ_FC_EVT_SW7 BIT(7) +#define IRQ_FC_EVT_DMA_PE_EVT BIT(8) +#define IRQ_FC_EVT_DMA_PE_IRQ BIT(9) +#define IRQ_FC_EVT_TIMER0_LO BIT(10) +#define IRQ_FC_EVT_TIMER0_HI BIT(11) +#define IRQ_FC_EVT_PF BIT(12) +#define IRQ_FC_EVT_CLK_REF BIT(14) +#define IRQ_FC_EVT_GPIO BIT(15) +#define IRQ_FC_EVT_ADV_TIMER0 BIT(17) +#define IRQ_FC_EVT_ADV_TIMER1 BIT(18) +#define IRQ_FC_EVT_ADV_TIMER2 BIT(19) +#define IRQ_FC_EVT_ADV_TIMER3 BIT(20) +#define IRQ_FC_EVT_SOC_EVT BIT(26) +#define IRQ_FC_EVT_QUIRQE_ERROR BIT(29) +#define IRQ_FC_EVT_PERIPH0 BIT(30) +#define IRQ_FC_EVT_PERIPH1 BIT(31) + +void irq_mask(uint32_t mask); +void irq_enable(uint32_t mask); +void irq_disable(uint32_t mask); +uint32_t irq_clint_disable(void); +uint32_t irq_clint_enable(void); +void pulp_irq_init(void); + +#endif /* IRQ_H */ diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/include/memory_map.h b/ports/risc-v32/gnu/example_build/core_v_mcu/include/memory_map.h new file mode 100644 index 00000000..f3048367 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/include/memory_map.h @@ -0,0 +1,39 @@ +/***************************************************************************/ +/* Copyright (C) 2019 ETH Zurich, University of Bologna and GreenWaves Technologies + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * Derived from core-v-freertos (https://github.com/openhwgroup/core-v-freertos) + * Original work licensed under the Apache License, Version 2.0. + * See https://www.apache.org/licenses/LICENSE-2.0 + * Modifications licensed under MIT (https://opensource.org/licenses/MIT). + * + * AI Disclosure: Some portions generated or modified by Copilot (Sonnet 4.6). + * + * SPDX-License-Identifier: Apache-2.0 AND MIT + ***************************************************************************/ + +#ifndef MEMORY_MAP_H +#define MEMORY_MAP_H + +#include "pulp_mem_map.h" + +#define L2_BASE 0x1C000000UL +#define L2_SIZE 0x80000UL +#define L2_SHARED_ADDR L2_BASE +#define L2_SHARED_SIZE L2_SIZE +#define ROM_ADDR 0x1A000000UL +#define ROM_SIZE 0x00002000UL + +#define SOC_PERIPHERALS_ADDR PULP_SOC_PERIPHERALS_ADDR +#define SOC_FLL_ADDR PULP_FLL_ADDR +#define GPIO_ADDR PULP_GPIO_ADDR +#define UDMA_CTRL_ADDR PULP_UDMA_ADDR +#define APB_SOC_CTRL_ADDR PULP_APB_SOC_CTRL_ADDR +#define ADV_TIMER_ADDR PULP_ADV_TIMER_ADDR +#define SOC_EU_ADDR PULP_SOC_EU_ADDR +#define FC_IRQ_ADDR PULP_FC_IRQ_ADDR +#define FC_TIMER_ADDR PULP_FC_TIMER_ADDR +#define FC_HWPE_ADDR PULP_FC_HWPE_ADDR +#define STDOUT_ADDR PULP_STDOUT_ADDR + +#endif /* MEMORY_MAP_H */ diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/include/properties.h b/ports/risc-v32/gnu/example_build/core_v_mcu/include/properties.h new file mode 100644 index 00000000..98473696 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/include/properties.h @@ -0,0 +1,43 @@ +/***************************************************************************/ +/* Copyright (C) 2019 ETH Zurich, University of Bologna and GreenWaves Technologies + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * Derived from core-v-freertos (https://github.com/openhwgroup/core-v-freertos) + * Original work licensed under the Apache License, Version 2.0. + * See https://www.apache.org/licenses/LICENSE-2.0 + * Modifications licensed under MIT (https://opensource.org/licenses/MIT). + * + * AI Disclosure: Some portions generated or modified by Copilot (Sonnet 4.6). + * + * SPDX-License-Identifier: Apache-2.0 AND MIT + ***************************************************************************/ + +#ifndef PROPERTIES_H +#define PROPERTIES_H + +#define PULP + +/* Peripheral bus clock (clk_per): PER_CLK_PERIOD_NS=200 → 5 MHz */ +#define ARCHI_FPGA_FREQUENCY 5000000U +/* FC/SOC clock (clk_soc): FC_CLK_PERIOD_NS=100 → 10 MHz */ +#define ARCHI_SOC_FREQUENCY 10000000U +#define ARCHI_NUM_TIMER 1U +#define ARCHI_NUM_FLL 2U + +#define ARCHI_REF_CLOCK_LOG2 15U +#define ARCHI_REF_CLOCK (1U << ARCHI_REF_CLOCK_LOG2) + +#define ARCHI_NB_FLL 3U + +#define __RT_FLL_CL 2U +#define __RT_FLL_PERIPH 1U +#define __RT_FLL_FC 0U + +#define __RT_FREQ_DOMAIN_FC 0U +#define __RT_FREQ_DOMAIN_CL 2U +#define __RT_FREQ_DOMAIN_PERIPH 1U +#define RT_FREQ_NB_DOMAIN 3U + +#define DEFAULT_SYSTEM_CLOCK 50000000U + +#endif /* PROPERTIES_H */ diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/include/pulp_mem_map.h b/ports/risc-v32/gnu/example_build/core_v_mcu/include/pulp_mem_map.h new file mode 100644 index 00000000..32757d62 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/include/pulp_mem_map.h @@ -0,0 +1,103 @@ +/***************************************************************************/ +/* Copyright (C) 2019 ETH Zurich and University of Bologna + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * Derived from core-v-freertos (https://github.com/openhwgroup/core-v-freertos) + * Original work licensed under the Apache License, Version 2.0. + * See https://www.apache.org/licenses/LICENSE-2.0 + * Modifications licensed under MIT (https://opensource.org/licenses/MIT). + * + * AI Disclosure: Some portions generated or modified by Copilot (Sonnet 4.6). + * + * SPDX-License-Identifier: Apache-2.0 AND MIT + ***************************************************************************/ + +#ifndef PULP_MEM_MAP_H +#define PULP_MEM_MAP_H + +#ifndef PULP_SOC_PERIPHERALS_ADDR +#define PULP_SOC_PERIPHERALS_ADDR 0x1A100000UL +#endif + +#ifndef PULP_FC_TIMER_SIZE +#define PULP_FC_TIMER_SIZE 0x00000800UL +#endif + +#ifndef PULP_FLL_OFFSET +#define PULP_FLL_OFFSET 0x00000000UL +#endif +#ifndef PULP_GPIO_OFFSET +#define PULP_GPIO_OFFSET 0x00001000UL +#endif +#ifndef PULP_UDMA_OFFSET +#define PULP_UDMA_OFFSET 0x00002000UL +#endif +#ifndef PULP_APB_SOC_CTRL_OFFSET +#define PULP_APB_SOC_CTRL_OFFSET 0x00004000UL +#endif +#ifndef PULP_ADV_TIMER_OFFSET +#define PULP_ADV_TIMER_OFFSET 0x00005000UL +#endif +#ifndef PULP_SOC_EU_OFFSET +#define PULP_SOC_EU_OFFSET 0x00006000UL +#endif +#ifndef PULP_FC_IRQ_OFFSET +#define PULP_FC_IRQ_OFFSET 0x00009800UL +#endif +#ifndef PULP_FC_TIMER_OFFSET +#define PULP_FC_TIMER_OFFSET 0x0000B000UL +#endif +#ifndef PULP_FC_HWPE_OFFSET +#define PULP_FC_HWPE_OFFSET 0x0000C000UL +#endif +#ifndef PULP_STDOUT_OFFSET +#define PULP_STDOUT_OFFSET 0x0000F000UL +#endif +#ifndef PULP_DEBUG_OFFSET +#define PULP_DEBUG_OFFSET 0x00010000UL +#endif + +#ifndef PULP_FLL_ADDR +#define PULP_FLL_ADDR (PULP_SOC_PERIPHERALS_ADDR + PULP_FLL_OFFSET) +#endif +#ifndef PULP_GPIO_ADDR +#define PULP_GPIO_ADDR (PULP_SOC_PERIPHERALS_ADDR + PULP_GPIO_OFFSET) +#endif +#ifndef PULP_UDMA_ADDR +#define PULP_UDMA_ADDR (PULP_SOC_PERIPHERALS_ADDR + PULP_UDMA_OFFSET) +#endif +#ifndef PULP_APB_SOC_CTRL_ADDR +#define PULP_APB_SOC_CTRL_ADDR (PULP_SOC_PERIPHERALS_ADDR + PULP_APB_SOC_CTRL_OFFSET) +#endif +#ifndef PULP_ADV_TIMER_ADDR +#define PULP_ADV_TIMER_ADDR (PULP_SOC_PERIPHERALS_ADDR + PULP_ADV_TIMER_OFFSET) +#endif +#ifndef PULP_SOC_EU_ADDR +#define PULP_SOC_EU_ADDR (PULP_SOC_PERIPHERALS_ADDR + PULP_SOC_EU_OFFSET) +#endif +#ifndef PULP_FC_IRQ_ADDR +#define PULP_FC_IRQ_ADDR (PULP_SOC_PERIPHERALS_ADDR + PULP_FC_IRQ_OFFSET) +#endif +#ifndef PULP_FC_TIMER_ADDR +#define PULP_FC_TIMER_ADDR (PULP_SOC_PERIPHERALS_ADDR + PULP_FC_TIMER_OFFSET) +#endif +#ifndef PULP_FC_HWPE_ADDR +#define PULP_FC_HWPE_ADDR (PULP_SOC_PERIPHERALS_ADDR + PULP_FC_HWPE_OFFSET) +#endif +#ifndef PULP_STDOUT_ADDR +#define PULP_STDOUT_ADDR (PULP_SOC_PERIPHERALS_ADDR + PULP_STDOUT_OFFSET) +#endif + +#define PULP_FLL_AREA_SIZE 0x00000010UL + +/* UDMA */ +#define UDMA_CH_ADDR_CTRL (PULP_UDMA_ADDR) +#define UDMA_CH_ADDR_UART (PULP_UDMA_ADDR + 0x80U) +#define UDMA_CH_ADDR_I2CM0 (PULP_UDMA_ADDR + 5U * UDMA_CH_SIZE) +#define UDMA_CH_ADDR_I2CM1 (PULP_UDMA_ADDR + 6U * UDMA_CH_SIZE) +#define UDMA_CTRL_I2CM0_CLKEN BIT(4) +#define UDMA_CTRL_I2CM1_CLKEN BIT(5) +#define UDMA_CH_SIZE (0x80U) +#define N_UART (2U) + +#endif /* PULP_MEM_MAP_H */ diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/include/string.h b/ports/risc-v32/gnu/example_build/core_v_mcu/include/string.h new file mode 100644 index 00000000..b5600000 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/include/string.h @@ -0,0 +1,25 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +#ifndef STRING_H +#define STRING_H + +#include <stddef.h> + +void *memcpy(void *dest, const void *src, size_t n); +void *memset(void *dest, int c, size_t n); +int memcmp(const void *lhs, const void *rhs, size_t n); + +#endif /* STRING_H */ diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/include/system_core_v_mcu.h b/ports/risc-v32/gnu/example_build/core_v_mcu/include/system_core_v_mcu.h new file mode 100644 index 00000000..4b445e31 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/include/system_core_v_mcu.h @@ -0,0 +1,29 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +#ifndef SYSTEM_CORE_V_MCU_H +#define SYSTEM_CORE_V_MCU_H + +#include <stdint.h> + +extern void (*isr_table[32])(void); +extern volatile uint32_t system_core_clock; + +void system_init(void); +void tx_trap_handler(uint32_t mcause, uint32_t mepc, uint32_t mtval); +void tx_timer_irq_handler(void); +void tx_undefined_irq_handler(void); + +#endif /* SYSTEM_CORE_V_MCU_H */ diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/include/temp_sensor.h b/ports/risc-v32/gnu/example_build/core_v_mcu/include/temp_sensor.h new file mode 100644 index 00000000..e94c589a --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/include/temp_sensor.h @@ -0,0 +1,29 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +#ifndef TEMP_SENSOR_H +#define TEMP_SENSOR_H + +#include <stdint.h> + +/* Read ADT7420 temperature sensor on I2CM1 (Nexys A7 on-board sensor). + * Returns temperature in tenths of degrees Celsius (e.g. 213 = 21.3°C). + * Returns INT16_MIN on I2C error. */ +int16_t temp_read_celsius_x10(void); + +/* Initialize the temperature sensor I2C channel. Call once at startup. */ +void temp_sensor_init(void); + +#endif /* TEMP_SENSOR_H */ diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/include/timer.h b/ports/risc-v32/gnu/example_build/core_v_mcu/include/timer.h new file mode 100644 index 00000000..ecfc0480 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/include/timer.h @@ -0,0 +1,120 @@ +/***************************************************************************/ +/* Copyright (C) 2019 ETH Zurich and University of Bologna + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * Derived from core-v-freertos (https://github.com/openhwgroup/core-v-freertos) + * Original work licensed under the Apache License, Version 2.0. + * See https://www.apache.org/licenses/LICENSE-2.0 + * Modifications licensed under MIT (https://opensource.org/licenses/MIT). + * + * AI Disclosure: Some portions generated or modified by Copilot (Sonnet 4.6). + * + * SPDX-License-Identifier: Apache-2.0 AND MIT + ***************************************************************************/ + +#ifndef TIMER_H +#define TIMER_H + +#include "bits.h" + +#define TIMER_CFG_LO_OFFSET 0x0U +#define TIMER_CFG_HI_OFFSET 0x4U +#define TIMER_CNT_LO_OFFSET 0x8U +#define TIMER_CNT_HI_OFFSET 0xCU +#define TIMER_CMP_LO_OFFSET 0x10U +#define TIMER_CMP_HI_OFFSET 0x14U +#define TIMER_START_LO_OFFSET 0x18U +#define TIMER_START_HI_OFFSET 0x1CU +#define TIMER_RESET_LO_OFFSET 0x20U +#define TIMER_RESET_HI_OFFSET 0x24U + +#define TIMER_CFG_LO_ENABLE_BIT 0U +#define TIMER_CFG_LO_ENABLE_WIDTH 1U +#define TIMER_CFG_LO_ENABLE_MASK 0x1U +#define TIMER_CFG_LO_RESET_BIT 1U +#define TIMER_CFG_LO_RESET_WIDTH 1U +#define TIMER_CFG_LO_RESET_MASK 0x2U +#define TIMER_CFG_LO_IRQEN_BIT 2U +#define TIMER_CFG_LO_IRQEN_WIDTH 1U +#define TIMER_CFG_LO_IRQEN_MASK 0x4U +#define TIMER_CFG_LO_IEM_BIT 3U +#define TIMER_CFG_LO_IEM_WIDTH 1U +#define TIMER_CFG_LO_IEM_MASK 0x8U +#define TIMER_CFG_LO_MODE_BIT 4U +#define TIMER_CFG_LO_MODE_WIDTH 1U +#define TIMER_CFG_LO_MODE_MASK 0x10U +#define TIMER_CFG_LO_ONE_S_BIT 5U +#define TIMER_CFG_LO_ONE_S_WIDTH 1U +#define TIMER_CFG_LO_ONE_S_MASK 0x20U +#define TIMER_CFG_LO_PEN_BIT 6U +#define TIMER_CFG_LO_PEN_WIDTH 1U +#define TIMER_CFG_LO_PEN_MASK 0x40U +#define TIMER_CFG_LO_CCFG_BIT 7U +#define TIMER_CFG_LO_CCFG_WIDTH 1U +#define TIMER_CFG_LO_CCFG_MASK 0x80U +#define TIMER_CFG_LO_PVAL_BIT 8U +#define TIMER_CFG_LO_PVAL_WIDTH 8U +#define TIMER_CFG_LO_PVAL_MASK 0xFF00U +#define TIMER_CFG_LO_CASC_BIT 31U +#define TIMER_CFG_LO_CASC_WIDTH 1U +#define TIMER_CFG_LO_CASC_MASK 0x80000000UL + +#define TIMER_CFG_HI_ENABLE_BIT 0U +#define TIMER_CFG_HI_ENABLE_WIDTH 1U +#define TIMER_CFG_HI_ENABLE_MASK 0x1U +#define TIMER_CFG_HI_RESET_BIT 1U +#define TIMER_CFG_HI_RESET_WIDTH 1U +#define TIMER_CFG_HI_RESET_MASK 0x2U +#define TIMER_CFG_HI_IRQEN_BIT 2U +#define TIMER_CFG_HI_IRQEN_WIDTH 1U +#define TIMER_CFG_HI_IRQEN_MASK 0x4U +#define TIMER_CFG_HI_IEM_BIT 3U +#define TIMER_CFG_HI_IEM_WIDTH 1U +#define TIMER_CFG_HI_IEM_MASK 0x8U +#define TIMER_CFG_HI_MODE_BIT 4U +#define TIMER_CFG_HI_MODE_WIDTH 1U +#define TIMER_CFG_HI_MODE_MASK 0x10U +#define TIMER_CFG_HI_ONE_S_BIT 5U +#define TIMER_CFG_HI_ONE_S_WIDTH 1U +#define TIMER_CFG_HI_ONE_S_MASK 0x20U +#define TIMER_CFG_HI_PEN_BIT 6U +#define TIMER_CFG_HI_PEN_WIDTH 1U +#define TIMER_CFG_HI_PEN_MASK 0x40U +#define TIMER_CFG_HI_CLKCFG_BIT 7U +#define TIMER_CFG_HI_CLKCFG_WIDTH 1U +#define TIMER_CFG_HI_CLKCFG_MASK 0x80U + +#define TIMER_CNT_LO_CNT_LO_BIT 0U +#define TIMER_CNT_LO_CNT_LO_WIDTH 32U +#define TIMER_CNT_LO_CNT_LO_MASK 0xFFFFFFFFUL +#define TIMER_CNT_HI_CNT_HI_BIT 0U +#define TIMER_CNT_HI_CNT_HI_WIDTH 32U +#define TIMER_CNT_HI_CNT_HI_MASK 0xFFFFFFFFUL +#define TIMER_CMP_LO_CMP_LO_BIT 0U +#define TIMER_CMP_LO_CMP_LO_WIDTH 32U +#define TIMER_CMP_LO_CMP_LO_MASK 0xFFFFFFFFUL +#define TIMER_CMP_HI_CMP_HI_BIT 0U +#define TIMER_CMP_HI_CMP_HI_WIDTH 32U +#define TIMER_CMP_HI_CMP_HI_MASK 0xFFFFFFFFUL + +#define TIMER_START_LO_STRT_LO_BIT 0U +#define TIMER_START_LO_STRT_LO_WIDTH 1U +#define TIMER_START_LO_STRT_LO_MASK 0x1U +#define TIMER_START_HI_STRT_HI_BIT 0U +#define TIMER_START_HI_STRT_HI_WIDTH 1U +#define TIMER_START_HI_STRT_HI_MASK 0x1U +#define TIMER_RESET_LO_RST_LO_BIT 0U +#define TIMER_RESET_LO_RST_LO_WIDTH 1U +#define TIMER_RESET_LO_RST_LO_MASK 0x1U +#define TIMER_RESET_HI_RST_HI_BIT 0U +#define TIMER_RESET_HI_RST_HI_WIDTH 1U +#define TIMER_RESET_HI_RST_HI_MASK 0x1U + +struct pulp_timer +{ + unsigned int current_time; + unsigned int flags; + void *base; +}; + +#endif /* TIMER_H */ diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/include/timer_irq.h b/ports/risc-v32/gnu/example_build/core_v_mcu/include/timer_irq.h new file mode 100644 index 00000000..11e0d76a --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/include/timer_irq.h @@ -0,0 +1,26 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +#ifndef TIMER_IRQ_H +#define TIMER_IRQ_H + +#include <stdint.h> + +int timer_irq_init(uint32_t ticks); +int timer_irq_set_timeout(uint32_t ticks); +uint32_t timer_irq_clock_elapsed(void); +uint32_t timer_irq_cycle_get_32(void); + +#endif /* TIMER_IRQ_H */ diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/include/tx_user.h b/ports/risc-v32/gnu/example_build/core_v_mcu/include/tx_user.h new file mode 100644 index 00000000..cc104032 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/include/tx_user.h @@ -0,0 +1,22 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +#ifndef TX_USER_H +#define TX_USER_H + +#define TX_TIMER_TICKS_PER_SECOND 100U +#define TX_INCLUDE_USER_DEFINE_FILE + +#endif /* TX_USER_H */ diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/include/uart_driver.h b/ports/risc-v32/gnu/example_build/core_v_mcu/include/uart_driver.h new file mode 100644 index 00000000..c1771093 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/include/uart_driver.h @@ -0,0 +1,27 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +#ifndef UART_DRIVER_H +#define UART_DRIVER_H + +#include <stdint.h> + +int uart_init(uint8_t uart_id, uint32_t baudrate, uint32_t periph_freq); +void uart_write_byte(uint8_t uart_id, uint8_t c); +int uart_read_byte(uint8_t uart_id); +void uart_write_str(uint8_t uart_id, const char *str); +void uart_write_buf(uint8_t uart_id, const uint8_t *buf, uint32_t len); + +#endif /* UART_DRIVER_H */ diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/install_deps.sh b/ports/risc-v32/gnu/example_build/core_v_mcu/install_deps.sh new file mode 100755 index 00000000..d0192665 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/install_deps.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +# /***************************************************************************/ +# /* Copyright (C) 2026 Eclipse ThreadX contributors +# * +# * This program and the accompanying materials are made available under the +# * terms of the MIT License which is available at +# * https://opensource.org/licenses/MIT. +# * +# * SPDX-License-Identifier: MIT +# ***************************************************************************/ + +# install_deps.sh -- Install Linux build/debug dependencies for the CORE-V MCU ThreadX port. +# +# Supported: Ubuntu 20.04 / 22.04 / 24.04 and Debian derivatives. +# Run as a regular user; sudo is invoked internally where needed. +# +# Installs: +# - xPack riscv-none-elf-gcc (multilib bare-metal toolchain, rv32imc/ilp32 capable) +# - cmake, ninja-build, gdb-multiarch, openocd, usbutils +# +# Usage: +# bash install_deps.sh + +set -euo pipefail + +die() { echo "[ERROR] $*" >&2; exit 1; } +info() { echo "[INFO] $*"; } + +command -v apt-get >/dev/null 2>&1 || die "This script requires apt-get (Ubuntu/Debian)." + +# --------------------------------------------------------------------------- +# xPack riscv-none-elf-gcc +# Cross-platform multilib toolchain (Linux/macOS/Windows). +# Includes rv32imc/ilp32 (soft-float) libgcc — required for CV32E40P. +# See: https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack +# --------------------------------------------------------------------------- +XPACK_VERSION="15.2.0-1" +XPACK_TARBALL="xpack-riscv-none-elf-gcc-${XPACK_VERSION}-linux-x64.tar.gz" +XPACK_URL="https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v${XPACK_VERSION}/${XPACK_TARBALL}" +XPACK_INSTALL_DIR="/opt/xpack-riscv-none-elf-gcc-${XPACK_VERSION}" +XPACK_BIN="${XPACK_INSTALL_DIR}/bin" + +APT_PACKAGES=( + cmake + ninja-build + gdb-multiarch + openocd + usbutils + git +) + +info "Updating package lists ..." +sudo apt-get update -qq + +info "Installing apt packages: ${APT_PACKAGES[*]}" +sudo apt-get install -y "${APT_PACKAGES[@]}" + +if [ -x "${XPACK_BIN}/riscv-none-elf-gcc" ]; then + info "xPack riscv-none-elf-gcc ${XPACK_VERSION} already installed at ${XPACK_INSTALL_DIR}" +else + info "Downloading xPack riscv-none-elf-gcc ${XPACK_VERSION} ..." + wget --no-verbose "${XPACK_URL}" -O "/tmp/${XPACK_TARBALL}" + sudo mkdir -p "${XPACK_INSTALL_DIR}" + sudo tar xzf "/tmp/${XPACK_TARBALL}" -C /opt + rm "/tmp/${XPACK_TARBALL}" + info "Installed to ${XPACK_INSTALL_DIR}" +fi + +info "" +info "Add the toolchain to your PATH:" +info " export PATH=${XPACK_BIN}:\$PATH" +info "" +info "All dependencies installed." +info "" +info "Next steps:" +info " 1. Run scripts/setup_opella.sh to configure udev rules for the Ashling Opella LD." +info " 2. (WSL only) Follow the usbipd-win instructions printed by setup_opella.sh." + diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/link.ld b/ports/risc-v32/gnu/example_build/core_v_mcu/link.ld new file mode 100644 index 00000000..8ffb174e --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/link.ld @@ -0,0 +1,65 @@ +OUTPUT_ARCH(riscv) +ENTRY(_start) + +MEMORY +{ + L2 (rwx) : ORIGIN = 0x1c000000, LENGTH = 0x80000 +} + +SECTIONS +{ + PROVIDE(__boot_address = 0x1c000880); + __stack_size = DEFINED(__stack_size) ? __stack_size : 0x2000; + PROVIDE(__stack_size = __stack_size); + + .vectors 0x1c000800 : + { + __vector_start = .; + KEEP(*(.vectors)) + . = __vector_start + 0x80; + __vector_end = .; + } > L2 + + .text __boot_address : + { + *(.text.start) + *(.text .text.*) + *(.rodata .rodata.*) + . = ALIGN(4); + } > L2 + + .data : + { + __data_begin = .; + __sdata_begin = .; + *(.sdata .sdata.*) + *(.data .data.*) + . = ALIGN(4); + __data_end = .; + } > L2 + + .bss (NOLOAD) : + { + __bss_start = .; + *(.sbss .sbss.*) + *(.bss .bss.*) + *(COMMON) + . = ALIGN(4); + __bss_end = .; + _end = .; + } > L2 + + PROVIDE(__tx_free_memory_start = __bss_end); + PROVIDE(__global_pointer$ = MIN(__sdata_begin + 0x800, + MAX(__data_begin + 0x800, __bss_end - 0x800))); + + __stack_top = ORIGIN(L2) + LENGTH(L2); + + ASSERT(SIZEOF(.vectors) <= 128, "Vector table exceeds reserved 128 bytes") + + /DISCARD/ : + { + *(.eh_frame) + *(.note.*) + } +} diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/openocd-nexys-Ashling-Opella-LD.cfg b/ports/risc-v32/gnu/example_build/core_v_mcu/openocd-nexys-Ashling-Opella-LD.cfg new file mode 100644 index 00000000..db43eae5 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/openocd-nexys-Ashling-Opella-LD.cfg @@ -0,0 +1,41 @@ +interface ftdi +ftdi_device_desc "Opella-LD Debug Probe" +ftdi_vid_pid 0x0B6B 0x0040 +ftdi_tdo_sample_edge falling +ftdi_layout_init 0x0A68 0xFF7B +ftdi_channel 0 +ftdi_layout_signal JTAGOE -ndata 0x0010 +ftdi_layout_signal nTRST -data 0x0020 +ftdi_layout_signal nSRST -data 0x0040 +ftdi_layout_signal SWD_EN -data 0x0100 +ftdi_layout_signal SWDIO_OE -data 0x0200 +ftdi_layout_signal LED -ndata 0x0800 +transport select jtag + + +reset_config none + +adapter_khz 1000 + +set _CHIPNAME riscv + +jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x10001C05 + +set _TARGETNAME $_CHIPNAME.cpu +target create $_TARGETNAME riscv -chain-position $_TARGETNAME + +gdb_report_data_abort enable +gdb_report_register_access_error enable + +riscv set_reset_timeout_sec 120 +riscv set_command_timeout_sec 120 + +# prefer to use sba for system bus access +riscv set_prefer_sba on + +# dump jtag chain +scan_chain + +init +halt +echo "Ready for Remote Connections"
\ No newline at end of file diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/tests/CMakeLists.txt b/ports/risc-v32/gnu/example_build/core_v_mcu/tests/CMakeLists.txt new file mode 100644 index 00000000..6726981f --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/tests/CMakeLists.txt @@ -0,0 +1,28 @@ +cmake_minimum_required(VERSION 3.15 FATAL_ERROR) +project(core_v_mcu_tests C) +set(CMAKE_C_STANDARD 99) +set(CMAKE_C_STANDARD_REQUIRED ON) + +set(CORE_V_MCU_DIR ${CMAKE_CURRENT_LIST_DIR}/..) + +add_executable(test_irq + ${CMAKE_CURRENT_LIST_DIR}/test_irq.c + ${CMAKE_CURRENT_LIST_DIR}/mock/mmio_mock.c +) +target_include_directories(test_irq PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/mock + ${CORE_V_MCU_DIR}/include +) + +add_executable(test_timer + ${CMAKE_CURRENT_LIST_DIR}/test_timer.c + ${CMAKE_CURRENT_LIST_DIR}/mock/mmio_mock.c +) +target_include_directories(test_timer PRIVATE + ${CMAKE_CURRENT_LIST_DIR}/mock + ${CORE_V_MCU_DIR}/include +) + +enable_testing() +add_test(NAME irq_tests COMMAND test_irq) +add_test(NAME timer_tests COMMAND test_timer) diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/tests/mock/mmio_mock.c b/ports/risc-v32/gnu/example_build/core_v_mcu/tests/mock/mmio_mock.c new file mode 100644 index 00000000..7852d5f1 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/tests/mock/mmio_mock.c @@ -0,0 +1,18 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +#include "mmio_mock.h" + +uint32_t mock_mmio[MOCK_MMIO_SIZE]; diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/tests/mock/mmio_mock.h b/ports/risc-v32/gnu/example_build/core_v_mcu/tests/mock/mmio_mock.h new file mode 100644 index 00000000..c8839e1e --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/tests/mock/mmio_mock.h @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +#ifndef MMIO_MOCK_H +#define MMIO_MOCK_H + +#include <stdint.h> +#include <string.h> + +#define CORE_V_MCU_MMIO_MOCK 1 +#define MOCK_MMIO_SIZE 1024U + +extern uint32_t mock_mmio[MOCK_MMIO_SIZE]; + +static inline void writew(uint32_t val, uintptr_t addr) +{ + if (addr < (uintptr_t)(MOCK_MMIO_SIZE * 4U)) + { + mock_mmio[addr / 4U] = val; + } +} + +static inline uint32_t readw(const uintptr_t addr) +{ + if (addr < (uintptr_t)(MOCK_MMIO_SIZE * 4U)) + { + return mock_mmio[addr / 4U]; + } + + return 0U; +} + +#endif /* MMIO_MOCK_H */ diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/tests/test_irq.c b/ports/risc-v32/gnu/example_build/core_v_mcu/tests/test_irq.c new file mode 100644 index 00000000..72c17f49 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/tests/test_irq.c @@ -0,0 +1,66 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +#include <assert.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include "mmio_mock.h" + +static uint32_t mock_mstatus = 0U; +#define csr_read(csr) (mock_mstatus) +#define csr_write(csr, val) do { mock_mstatus = (uint32_t)(val); } while (0) +#define csr_read_clear(csr, val) ({ uint32_t _v = mock_mstatus; mock_mstatus &= ~(uint32_t)(val); _v; }) +#define csr_read_set(csr, val) ({ uint32_t _v = mock_mstatus; mock_mstatus |= (uint32_t)(val); _v; }) + +#include "bits.h" +#define PULP_SOC_PERIPHERALS_ADDR 0U +#define PULP_FC_IRQ_OFFSET 0U +#include "irq.h" +#include "../../bsp/irq.c" + +static void test_irq_enable(void) +{ + memset(mock_mmio, 0, sizeof(mock_mmio)); + irq_enable(BIT(10)); + assert(mock_mmio[IRQ_REG_MASK_SET_OFFSET / 4U] == BIT(10)); + printf("test_irq_enable: PASS\n"); +} + +static void test_irq_disable(void) +{ + memset(mock_mmio, 0, sizeof(mock_mmio)); + irq_disable(BIT(10)); + assert(mock_mmio[IRQ_REG_MASK_CLEAR_OFFSET / 4U] == BIT(10)); + printf("test_irq_disable: PASS\n"); +} + +static void test_irq_clint_enable(void) +{ + mock_mstatus = 0U; + (void)irq_clint_enable(); + assert((mock_mstatus & 0x8U) != 0U); + printf("test_irq_clint_enable: PASS\n"); +} + +int main(void) +{ + test_irq_enable(); + test_irq_disable(); + test_irq_clint_enable(); + printf("All IRQ tests passed.\n"); + return 0; +} diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/tests/test_timer.c b/ports/risc-v32/gnu/example_build/core_v_mcu/tests/test_timer.c new file mode 100644 index 00000000..3e1499e7 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/tests/test_timer.c @@ -0,0 +1,53 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + +#include <assert.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include "mmio_mock.h" +#define PULP_SOC_PERIPHERALS_ADDR 0U +#define PULP_FC_TIMER_OFFSET 0U + +#include "bits.h" +#include "timer.h" +#include "timer_irq.h" +#include "../../bsp/timer_irq.c" + +static void test_timer_irq_init(void) +{ + uint32_t cfg; + int ret; + + memset(mock_mmio, 0, sizeof(mock_mmio)); + ret = timer_irq_init(327U); + assert(ret == 0); + assert(mock_mmio[TIMER_CMP_LO_OFFSET / 4U] == 327U); + + cfg = mock_mmio[TIMER_CFG_LO_OFFSET / 4U]; + assert((cfg & TIMER_CFG_LO_ENABLE_MASK) != 0U); + assert((cfg & TIMER_CFG_LO_IRQEN_MASK) != 0U); + assert((cfg & TIMER_CFG_LO_CCFG_MASK) != 0U); + assert((cfg & TIMER_CFG_LO_MODE_MASK) != 0U); + printf("test_timer_irq_init: PASS\n"); +} + +int main(void) +{ + test_timer_irq_init(); + printf("All timer tests passed.\n"); + return 0; +} diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/tx_initialize_low_level.S b/ports/risc-v32/gnu/example_build/core_v_mcu/tx_initialize_low_level.S new file mode 100644 index 00000000..3c5a3888 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/tx_initialize_low_level.S @@ -0,0 +1,63 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + + .section .data + .global __tx_free_memory_start +__tx_free_memory_start: + + .section .text.trap_entry + .align 4 + .global tx_trap_entry + .extern _tx_thread_context_save + .extern _tx_thread_context_restore + .extern tx_trap_handler + +tx_trap_entry: + addi sp, sp, -128 + sw ra, 112(sp) + call _tx_thread_context_save + csrr a0, mcause + csrr a1, mepc + csrr a2, mtval + addi sp, sp, -4 + sw ra, 0(sp) + call tx_trap_handler + lw ra, 0(sp) + addi sp, sp, 4 + call _tx_thread_context_restore +1: + wfi + j 1b + + .section .text + .global _tx_initialize_low_level + .weak _tx_initialize_low_level + .extern _tx_thread_system_stack_ptr + .extern _tx_initialize_unused_memory + .extern __vector_start + +_tx_initialize_low_level: + la t0, _tx_thread_system_stack_ptr + sw sp, 0(t0) + + la t0, __tx_free_memory_start + la t1, _tx_initialize_unused_memory + sw t0, 0(t1) + + la t0, __vector_start + ori t0, t0, 1 + csrw mtvec, t0 + + ret diff --git a/ports/risc-v32/gnu/example_build/core_v_mcu/vectors.S b/ports/risc-v32/gnu/example_build/core_v_mcu/vectors.S new file mode 100644 index 00000000..f6a57d7a --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/vectors.S @@ -0,0 +1,25 @@ +/*************************************************************************** + * Copyright (C) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6). + * The AI-generated portions may be considered public domain (CC0-1.0) + * and not subject to the project's licence. The human contributor has + * reviewed and verified that the code is correct. + * + * SPDX-License-Identifier: MIT and CC0-1.0 + **************************************************************************/ + + .section .vectors, "ax" + .option norvc + .align 2 + .extern tx_trap_entry + .global __vector_start + +__vector_start: + .rept 32 + jal zero, tx_trap_entry + .endr diff --git a/ports/risc-v32/gnu/example_build/cva6_ariane/board.c b/ports/risc-v32/gnu/example_build/cva6_ariane/board.c new file mode 100644 index 00000000..b1c7e5c5 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/cva6_ariane/board.c @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright (c) 2026 Quintauris + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#include "plic.h" +#include "hwtimer.h" +#include "uart.h" +#include <stdint.h> +#include <stddef.h> + +void *memset(const void *des, int c,size_t n) +{ + if((des == NULL) || n <=0) + return (void*)des; + char* t = (char*)des; + int i; + for(i=0;i<n;i++) + t[i]=c; + return t; +} + + +int board_init(void) +{ + int ret; + ret = plic_init(); + if(ret) + return ret; + ret = uart_init(); + if(ret) + return ret; + ret = hwtimer_init(); + if(ret) + return ret; + return 0; +} diff --git a/ports/risc-v32/gnu/example_build/cva6_ariane/build_libthreadx.sh b/ports/risc-v32/gnu/example_build/cva6_ariane/build_libthreadx.sh new file mode 100644 index 00000000..23f707dc --- /dev/null +++ b/ports/risc-v32/gnu/example_build/cva6_ariane/build_libthreadx.sh @@ -0,0 +1,17 @@ +#!/bin/bash +############################################################################## +# Copyright (c) 2024 Microsoft Corporation +# Copyright (c) 2026 Eclipse ThreadX contributors +# +# This program and the accompanying materials are made available under the +# terms of the MIT License which is available at +# https://opensource.org/licenses/MIT. +# +# SPDX-License-Identifier: MIT +############################################################################## + + +pushd ../../../../../ +cmake -Bbuild -GNinja -DCMAKE_TOOLCHAIN_FILE=cmake/riscv32_gnu.cmake -DSOFT_FLOAT=1 . +cmake --build ./build/ +popd diff --git a/ports/risc-v32/gnu/example_build/cva6_ariane/csr.h b/ports/risc-v32/gnu/example_build/cva6_ariane/csr.h new file mode 100644 index 00000000..1bffcf39 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/cva6_ariane/csr.h @@ -0,0 +1,360 @@ +/*************************************************************************** + * Copyright (c) 2026 Quintauris + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +#ifndef RISCV_CSR_H +#define RISCV_CSR_H + + +// Machine Status Register, mstatus +#define MSTATUS_MPP_MASK (3L << 11) // previous mode. +#define MSTATUS_MPP_M (3L << 11) +#define MSTATUS_MPP_S (1L << 11) +#define MSTATUS_MPP_U (0L << 11) +#define MSTATUS_MIE (1L << 3) // machine-mode interrupt enable. +#define MSTATUS_MPIE (1L << 7) +#define MSTATUS_FS (1L << 13) + +// Machine-mode Interrupt Enable +#define MIE_MTIE (1L << 7) +#define MIE_MSIE (1L << 3) +#define MIE_MEIE (1L << 11) +#define MIE_STIE (1L << 5) // supervisor timer +#define MIE_SSIE (1L << 1) +#define MIE_SEIE (1L << 9) + +// Supervisor Status Register, sstatus +#define SSTATUS_SPP (1L << 8) // Previous mode, 1=Supervisor, 0=User +#define SSTATUS_SPIE (1L << 5) // Supervisor Previous Interrupt Enable +#define SSTATUS_UPIE (1L << 4) // User Previous Interrupt Enable +#define SSTATUS_SIE (1L << 1) // Supervisor Interrupt Enable +#define SSTATUS_UIE (1L << 0) // User Interrupt Enable +#define SSTATUS_SPIE (1L << 5) +#define SSTATUS_UPIE (1L << 4) + +// Supervisor Interrupt Enable +#define SIE_SEIE (1L << 9) // external +#define SIE_STIE (1L << 5) // timer +#define SIE_SSIE (1L << 1) // software + +#ifndef __ASSEMBLER__ + +#include <stdint.h> + +static inline uint32_t riscv_get_core() +{ + uint32_t x; + asm volatile("csrr %0, mhartid" : "=r" (x) ); + return x; +} + +static inline uint64_t riscv_get_mstatus() +{ + uint32_t x; + uint32_t xh; + asm volatile("csrr %0, mstatus" : "=r" (x) ); + asm volatile("csrr %0, mstatush" : "=r" (xh) ); + return ((uint64_t)(x)) + ((uint64_t)(xh)) << 32; +} + +static inline void riscv_writ_mstatus(uint64_t x) +{ + asm volatile("csrw mstatus, %0" : : "r" ((uint32_t)x)); + asm volatile("csrw mstatush, %0" : : "r" ((uint32_t)(x >> 32))); +} + +// machine exception program counter, holds the +// instruction address to which a return from +// exception will go. +static inline void riscv_writ_mepc(uint32_t x) +{ + asm volatile("csrw mepc, %0" : : "r" (x)); +} + +static inline uint32_t riscv_get_sstatus() +{ + uint32_t x; + asm volatile("csrr %0, sstatus" : "=r" (x) ); + return x; +} + +static inline void riscv_writ_sstatus(uint32_t x) +{ + asm volatile("csrw sstatus, %0" : : "r" (x)); +} + +// Supervisor Interrupt Pending +static inline uint32_t riscv_get_sip() +{ + uint32_t x; + asm volatile("csrr %0, sip" : "=r" (x) ); + return x; +} + +static inline void riscv_writ_sip(uint32_t x) +{ + asm volatile("csrw sip, %0" : : "r" (x)); +} + +static inline uint32_t riscv_get_sie() +{ + uint32_t x; + asm volatile("csrr %0, sie" : "=r" (x) ); + return x; +} + +static inline void riscv_writ_sie(uint32_t x) +{ + asm volatile("csrw sie, %0" : : "r" (x)); +} + +static inline uint32_t riscv_get_mie() +{ + uint32_t x; + asm volatile("csrr %0, mie" : "=r" (x) ); + return x; +} + +static inline void riscv_writ_mie(uint32_t x) +{ + asm volatile("csrw mie, %0" : : "r" (x)); +} + +// supervisor exception program counter, holds the +// instruction address to which a return from +// exception will go. +static inline void riscv_writ_sepc(uint32_t x) +{ + asm volatile("csrw sepc, %0" : : "r" (x)); +} + +static inline uint32_t riscv_get_sepc() +{ + uint32_t x; + asm volatile("csrr %0, sepc" : "=r" (x) ); + return x; +} + +// Machine Exception Delegation +static inline uint32_t riscv_get_medeleg() +{ + uint32_t x; + asm volatile("csrr %0, medeleg" : "=r" (x) ); + return x; +} + +static inline void riscv_writ_medeleg(uint32_t x) +{ + asm volatile("csrw medeleg, %0" : : "r" (x)); +} + +// Machine Interrupt Delegation +static inline uint32_t riscv_get_mideleg() +{ + uint32_t x; + asm volatile("csrr %0, mideleg" : "=r" (x) ); + return x; +} + +static inline void riscv_writ_mideleg(uint32_t x) +{ + asm volatile("csrw mideleg, %0" : : "r" (x)); +} + +// Supervisor Trap-Vector Base Address +// low two bits are mode. +static inline void riscv_writ_stvec(uint32_t x) +{ + asm volatile("csrw stvec, %0" : : "r" (x)); +} + +static inline uint32_t riscv_get_stvec() +{ + uint32_t x; + asm volatile("csrr %0, stvec" : "=r" (x) ); + return x; +} + +static inline uint32_t riscv_get_stimecmp() +{ + uint32_t x; + asm volatile("csrr %0, 0x14d" : "=r" (x) ); + return x; +} + +static inline void riscv_writ_stimecmp(uint32_t x) +{ + asm volatile("csrw 0x14d, %0" : : "r" (x)); +} + +static inline uint32_t riscv_get_menvcfg() +{ + uint32_t x; + asm volatile("csrr %0, 0x30a" : "=r" (x) ); + return x; +} + +static inline void riscv_writ_menvcfg(uint32_t x) +{ + asm volatile("csrw 0x30a, %0" : : "r" (x)); +} + +static inline void riscv_writ_pmpcfg0(uint32_t x) +{ + asm volatile("csrw pmpcfg0, %0" : : "r" (x)); +} + +static inline void riscv_writ_pmpaddr0(uint32_t x) +{ + asm volatile("csrw pmpaddr0, %0" : : "r" (x)); +} + +static inline void riscv_writ_satp(uint32_t x) +{ + asm volatile("csrw satp, %0" : : "r" (x)); +} + +static inline uint32_t riscv_get_satp() +{ + uint32_t x; + asm volatile("csrr %0, satp" : "=r" (x) ); + return x; +} + +static inline uint32_t riscv_get_scause() +{ + uint32_t x; + asm volatile("csrr %0, scause" : "=r" (x) ); + return x; +} + +static inline uint32_t riscv_get_stval() +{ + uint32_t x; + asm volatile("csrr %0, stval" : "=r" (x) ); + return x; +} + +static inline void riscv_writ_mcounteren(uint32_t x) +{ + asm volatile("csrw mcounteren, %0" : : "r" (x)); +} + +static inline uint32_t riscv_get_mcounteren() +{ + uint32_t x; + asm volatile("csrr %0, mcounteren" : "=r" (x) ); + return x; +} + +// machine-mode cycle counter +static inline uint64_t riscv_get_time() +{ + uint32_t x; + uint32_t xh; + asm volatile("csrr %0, time" : "=r" (x) ); + asm volatile("csrr %0, timeh" : "=r" (xh) ); + return ((uint64_t)(x)) + ((uint64_t)(xh)) << 32; +} + +static inline void riscv_sintr_on() +{ + uint32_t sstatus = riscv_get_sstatus(); + sstatus |= SSTATUS_SIE; + riscv_writ_sstatus(sstatus); +} + +static inline void riscv_sintr_off() +{ + uint32_t sstatus = riscv_get_sstatus(); + sstatus &= (~SSTATUS_SIE); + riscv_writ_sstatus(sstatus); +} + +static inline int riscv_sintr_get() +{ + uint32_t x = riscv_get_sstatus(); + return (x & SSTATUS_SIE) != 0; +} + +static inline void riscv_sintr_restore(int x) +{ + if(x) + riscv_sintr_on(); + else + riscv_sintr_off(); +} + +static inline void riscv_mintr_on() +{ + uint32_t mstatus = riscv_get_mstatus(); + mstatus |= MSTATUS_MIE; + riscv_writ_mstatus(mstatus); +} + +static inline void riscv_mintr_off() +{ + uint32_t mstatus = riscv_get_mstatus(); + mstatus &= (~MSTATUS_MIE); + riscv_writ_mstatus(mstatus); +} + +static inline int riscv_mintr_get() +{ + uint32_t x = riscv_get_mstatus(); + return (x & MSTATUS_MIE) != 0; +} + +static inline void riscv_mintr_restore(int x) +{ + if(x) + riscv_mintr_on(); + else + riscv_mintr_off(); +} + +static inline uint32_t riscv_get_sp() +{ + uint32_t x; + asm volatile("mv %0, sp" : "=r" (x) ); + return x; +} + +// read and write tp, the thread pointer, which xv6 uses to hold +// this core's hartid (core number), the index into cpus[]. +static inline uint32_t riscv_get_tp() +{ + uint32_t x; + asm volatile("mv %0, tp" : "=r" (x) ); + return x; +} + +static inline void riscv_writ_tp(uint32_t x) +{ + asm volatile("mv tp, %0" : : "r" (x)); +} + +static inline uint32_t riscv_get_ra() +{ + uint32_t x; + asm volatile("mv %0, ra" : "=r" (x) ); + return x; +} + +// flush the TLB. +static inline void sfence_vma() +{ + // the zero, zero means flush all TLB entries. + asm volatile("sfence.vma zero, zero"); +} + +#endif // __ASSEMBLER__ + +#endif diff --git a/ports/risc-v32/gnu/example_build/cva6_ariane/demo_threadx.c b/ports/risc-v32/gnu/example_build/cva6_ariane/demo_threadx.c new file mode 100644 index 00000000..adc56f4a --- /dev/null +++ b/ports/risc-v32/gnu/example_build/cva6_ariane/demo_threadx.c @@ -0,0 +1,404 @@ +/***************************************************************************/ +/* Copyright (c) 2024 Microsoft Corporation */ +/* Copyright (c) 2026 Eclipse ThreadX contributors */ +/* */ +/* This program and the accompanying materials are made available under */ +/* the terms of the MIT License which is available at */ +/* https://opensource.org/licenses/MIT. */ +/* */ +/* SPDX-License-Identifier: MIT */ +/***************************************************************************/ + +/* This is a small demo of the high-performance ThreadX kernel. It includes examples of eight + threads of different priorities, using a message queue, semaphore, mutex, event flags group, + byte pool, and block pool. */ + +#include "tx_api.h" +#include "uart.h" + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + +char *_to_str(ULONG val) +{ + static char buf[11]; /* 10 digits max + '\0' */ + char *p = buf + sizeof(buf) - 1; + + *p = '\0'; + do { + *--p = '0' + (val % 10); + val /= 10; + } while (val); + + return p; +} + +/* Define the ThreadX object control blocks... */ + +TX_THREAD thread_0; +TX_THREAD thread_1; +TX_THREAD thread_2; +TX_THREAD thread_3; +TX_THREAD thread_4; +TX_THREAD thread_5; +TX_THREAD thread_6; +TX_THREAD thread_7; +TX_QUEUE queue_0; +TX_SEMAPHORE semaphore_0; +TX_MUTEX mutex_0; +TX_EVENT_FLAGS_GROUP event_flags_0; +TX_BYTE_POOL byte_pool_0; +TX_BLOCK_POOL block_pool_0; + + +/* Define the counters used in the demo application... */ + +ULONG thread_0_counter; +ULONG thread_1_counter; +ULONG thread_1_messages_sent; +ULONG thread_2_counter; +ULONG thread_2_messages_received; +ULONG thread_3_counter; +ULONG thread_4_counter; +ULONG thread_5_counter; +ULONG thread_6_counter; +ULONG thread_7_counter; + + +/* Define thread prototypes. */ + +void thread_0_entry(ULONG thread_input); +void thread_1_entry(ULONG thread_input); +void thread_2_entry(ULONG thread_input); +void thread_3_and_4_entry(ULONG thread_input); +void thread_5_entry(ULONG thread_input); +void thread_6_and_7_entry(ULONG thread_input); + + +/* Define main entry point. */ + +int main() +{ + + /* Enter the ThreadX kernel. */ + tx_kernel_enter(); +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + CHAR *pointer = TX_NULL; + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", first_unused_memory, DEMO_BYTE_POOL_SIZE); + + /* Put system definition stuff in here, e.g. thread creates and other assorted + create information. */ + + /* Allocate the stack for thread 0. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create the main thread. */ + tx_thread_create(&thread_0, "thread 0", thread_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 1, 1, TX_NO_TIME_SLICE, TX_AUTO_START); + + + /* Allocate the stack for thread 1. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create threads 1 and 2. These threads pass information through a ThreadX + message queue. It is also interesting to note that these threads have a time + slice. */ + tx_thread_create(&thread_1, "thread 1", thread_1_entry, 1, + pointer, DEMO_STACK_SIZE, + 16, 16, 4, TX_AUTO_START); + + /* Allocate the stack for thread 2. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + tx_thread_create(&thread_2, "thread 2", thread_2_entry, 2, + pointer, DEMO_STACK_SIZE, + 16, 16, 4, TX_AUTO_START); + + /* Allocate the stack for thread 3. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create threads 3 and 4. These threads compete for a ThreadX counting semaphore. + An interesting thing here is that both threads share the same instruction area. */ + tx_thread_create(&thread_3, "thread 3", thread_3_and_4_entry, 3, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 4. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + tx_thread_create(&thread_4, "thread 4", thread_3_and_4_entry, 4, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 5. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create thread 5. This thread simply pends on an event flag which will be set + by thread_0. */ + tx_thread_create(&thread_5, "thread 5", thread_5_entry, 5, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 6. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create threads 6 and 7. These threads compete for a ThreadX mutex. */ + tx_thread_create(&thread_6, "thread 6", thread_6_and_7_entry, 6, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 7. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + tx_thread_create(&thread_7, "thread 7", thread_6_and_7_entry, 7, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the message queue. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_QUEUE_SIZE*sizeof(ULONG), TX_NO_WAIT); + + /* Create the message queue shared by threads 1 and 2. */ + tx_queue_create(&queue_0, "queue 0", TX_1_ULONG, pointer, DEMO_QUEUE_SIZE*sizeof(ULONG)); + + /* Create the semaphore used by threads 3 and 4. */ + tx_semaphore_create(&semaphore_0, "semaphore 0", 1); + + /* Create the event flags group used by threads 1 and 5. */ + tx_event_flags_create(&event_flags_0, "event flags 0"); + + /* Create the mutex used by thread 6 and 7 without priority inheritance. */ + tx_mutex_create(&mutex_0, "mutex 0", TX_NO_INHERIT); + + /* Allocate the memory for a small block pool. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_BLOCK_POOL_SIZE, TX_NO_WAIT); + + /* Create a block memory pool to allocate a message buffer from. */ + tx_block_pool_create(&block_pool_0, "block pool 0", sizeof(ULONG), pointer, DEMO_BLOCK_POOL_SIZE); + + /* Allocate a block and release the block memory. */ + tx_block_allocate(&block_pool_0, (VOID **) &pointer, TX_NO_WAIT); + + /* Release the block back to the pool. */ + tx_block_release(pointer); +} + + + +/* Define the test threads. */ + +void thread_0_entry(ULONG thread_input) +{ + +UINT status; + + + /* This thread simply sits in while-forever-sleep loop. */ + while(1) + { + puts("[Thread] : thread_0_entry is here!"); + + /* Increment the thread counter. */ + thread_0_counter++; + + /* Sleep for 10 ticks. */ + tx_thread_sleep(10); + + /* Set event flag 0 to wakeup thread 5. */ + status = tx_event_flags_set(&event_flags_0, 0x1, TX_OR); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + } +} + + +void thread_1_entry(ULONG thread_input) +{ + +UINT status; + + /* This thread simply sends messages to a queue shared by thread 2. */ + while(1) + { + puts("[Thread] : thread_1_entry is here!"); + /* Increment the thread counter. */ + thread_1_counter++; + + /* Send message to queue 0. */ + status = tx_queue_send(&queue_0, &thread_1_messages_sent, TX_WAIT_FOREVER); + + /* Check completion status. */ + if (status != TX_SUCCESS) { + puts("[Thread 1] ERROR: Failed to send message!"); + break; + } + + /* Increment the message sent. */ + thread_1_messages_sent++; + } +} + + + +void thread_2_entry(ULONG thread_input) +{ + +ULONG received_message; +UINT status; + + /* This thread retrieves messages placed on the queue by thread 1. */ + while(1) + { + puts("[Thread] : thread_2_entry is here!"); + /* Increment the thread counter. */ + thread_2_counter++; + + /* Retrieve a message from the queue. */ + status = tx_queue_receive(&queue_0, &received_message, TX_WAIT_FOREVER); + + /* Check completion status and make sure the message is what we + expected. */ + if ((status != TX_SUCCESS) || (received_message != thread_2_messages_received)){ + puts("[Thread 2] ERROR: Failed to receive message ! Expected # "); + uart_puts(_to_str(thread_2_messages_received)); + puts(", but got # "); + uart_puts(_to_str(received_message)); + break; + } + + /* Otherwise, all is okay. Increment the received message count. */ + thread_2_messages_received++; + } +} + + +void thread_3_and_4_entry(ULONG thread_input) +{ + +UINT status; + + + /* This function is executed from thread 3 and thread 4. As the loop + below shows, these function compete for ownership of semaphore_0. */ + while(1) + { + puts("[Thread] : thread_3_and_4_entry is here!"); + + + /* Increment the thread counter. */ + if (thread_input == 3) + thread_3_counter++; + else + thread_4_counter++; + + /* Get the semaphore with suspension. */ + status = tx_semaphore_get(&semaphore_0, TX_WAIT_FOREVER); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Sleep for 2 ticks to hold the semaphore. */ + tx_thread_sleep(2); + + /* Release the semaphore. */ + status = tx_semaphore_put(&semaphore_0); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + } +} + + +void thread_5_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual_flags; + + + /* This thread simply waits for an event in a forever loop. */ + while(1) + { + puts("[Thread] : thread_5_entry is here!"); + + /* Increment the thread counter. */ + thread_5_counter++; + + /* Wait for event flag 0. */ + status = tx_event_flags_get(&event_flags_0, 0x1, TX_OR_CLEAR, + &actual_flags, TX_WAIT_FOREVER); + + /* Check status. */ + if ((status != TX_SUCCESS) || (actual_flags != 0x1)) + break; + } +} + + +void thread_6_and_7_entry(ULONG thread_input) +{ + +UINT status; + + + /* This function is executed from thread 6 and thread 7. As the loop + below shows, these function compete for ownership of mutex_0. */ + while(1) + { + puts("[Thread] : thread_6_and_7_entry is here!"); + + /* Increment the thread counter. */ + if (thread_input == 6) + thread_6_counter++; + else + thread_7_counter++; + + /* Get the mutex with suspension. */ + status = tx_mutex_get(&mutex_0, TX_WAIT_FOREVER); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Get the mutex again with suspension. This shows + that an owning thread may retrieve the mutex it + owns multiple times. */ + status = tx_mutex_get(&mutex_0, TX_WAIT_FOREVER); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Sleep for 2 ticks to hold the mutex. */ + tx_thread_sleep(2); + + /* Release the mutex. */ + status = tx_mutex_put(&mutex_0); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Release the mutex again. This will actually + release ownership since it was obtained twice. */ + status = tx_mutex_put(&mutex_0); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + } +} diff --git a/ports/risc-v32/gnu/example_build/cva6_ariane/entry.S b/ports/risc-v32/gnu/example_build/cva6_ariane/entry.S new file mode 100644 index 00000000..f9645bc7 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/cva6_ariane/entry.S @@ -0,0 +1,69 @@ +/***************************************************************************/ +/* Copyright (c) 2024 Microsoft Corporation */ +/* Copyright (c) 2026 Eclipse ThreadX contributors */ +/* */ +/* This program and the accompanying materials are made available under */ +/* the terms of the MIT License which is available at */ +/* https://opensource.org/licenses/MIT. */ +/* */ +/* SPDX-License-Identifier: MIT */ +/***************************************************************************/ + + +.section .text +.align 4 +.global _start +.extern main +.extern _sysstack_start +.extern _bss_start +.extern _bss_end +_start: + csrr t0, mhartid + bne t0, zero, 1f + li x1, 0 + li x2, 0 + li x3, 0 + li x4, 0 + li x5, 0 + li x6, 0 + li x7, 0 + li x8, 0 + li x9, 0 + li x10, 0 + li x11, 0 + li x12, 0 + li x13, 0 + li x14, 0 + li x15, 0 + li x16, 0 + li x17, 0 + li x18, 0 + li x19, 0 + li x20, 0 + li x21, 0 + li x22, 0 + li x23, 0 + li x24, 0 + li x25, 0 + li x26, 0 + li x27, 0 + li x28, 0 + li x29, 0 + li x30, 0 + li x31, 0 + la t0, _sysstack_start + li t1, 0x1000 + add sp, t0, t1 + la t0, _bss_start + la t1, _bss_end +_bss_clean_start: + bgeu t0, t1, _bss_clean_end + sb zero, 0(t0) + addi t0, t0, 1 + j _bss_clean_start +_bss_clean_end: + call main +1: + /* todo smp */ + wfi + j 1b diff --git a/ports/risc-v32/gnu/example_build/cva6_ariane/hwtimer.c b/ports/risc-v32/gnu/example_build/cva6_ariane/hwtimer.c new file mode 100644 index 00000000..b5335cf3 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/cva6_ariane/hwtimer.c @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright (c) 2026 Quintauris + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#include "tx_port.h" +#include "csr.h" +#include "hwtimer.h" + +#define CLINT (0x02000000L) +#define CLINT_TIME (CLINT+0xBFF8) +#define CLINT_TIMECMP(hart_id) (CLINT+0x4000+8*(hart_id)) + + +int hwtimer_init(void) +{ + int hart = riscv_get_core(); + uint64_t time = *((uint64_t*)CLINT_TIME); + *((uint64_t*)CLINT_TIMECMP(hart)) = time + TICKNUM_PER_TIMER; + return 0; +} + +int hwtimer_handler(void) +{ + int hart = riscv_get_core(); + uint64_t time = *((uint64_t*)CLINT_TIME); + *((uint64_t*)CLINT_TIMECMP(hart)) = time + TICKNUM_PER_TIMER; + return 0; +} + diff --git a/ports/risc-v32/gnu/example_build/cva6_ariane/hwtimer.h b/ports/risc-v32/gnu/example_build/cva6_ariane/hwtimer.h new file mode 100644 index 00000000..9e4a066d --- /dev/null +++ b/ports/risc-v32/gnu/example_build/cva6_ariane/hwtimer.h @@ -0,0 +1,24 @@ + +/*************************************************************************** + * Copyright (c) 2026 Quintauris + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#ifndef RISCV_HWTIMER_H +#define RISCV_HWTIMER_H + +#include <stdint.h> + +#define TICKNUM_PER_SECOND 50000000 +#define TICKNUM_PER_TIMER (TICKNUM_PER_SECOND / 100) + +int hwtimer_init(void); + +int hwtimer_handler(void); + +#endif diff --git a/ports/risc-v32/gnu/example_build/cva6_ariane/link.lds b/ports/risc-v32/gnu/example_build/cva6_ariane/link.lds new file mode 100644 index 00000000..619080e7 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/cva6_ariane/link.lds @@ -0,0 +1,49 @@ +OUTPUT_ARCH( "riscv" ) +ENTRY( _start ) + +SECTIONS +{ + /* + * ensure that entry.S / _entry is at 0x80000000, + * where arianes's -kernel jumps. + */ + . = 0x80000000; + + .text : { + *(.text .text.*) + . = ALIGN(0x1000); + PROVIDE(etext = .); + } + + .rodata : { + . = ALIGN(16); + *(.srodata .srodata.*) /* do not need to distinguish this from .rodata */ + . = ALIGN(16); + *(.rodata .rodata.*) + } + + .data : { + . = ALIGN(16); + *(.sdata .sdata.*) /* do not need to distinguish this from .data */ + . = ALIGN(16); + *(.data .data.*) + } + + .bss : { + . = ALIGN(16); + _bss_start = .; + *(.sbss .sbss.*) /* do not need to distinguish this from .bss */ + . = ALIGN(16); + *(.bss .bss.*) + _bss_end = .; + } + + .stack : { + . = ALIGN(4096); + _sysstack_start = .; + . += 0x1000; + _sysstack_end = .; + } + + PROVIDE(_end = .); +} diff --git a/ports/risc-v32/gnu/example_build/cva6_ariane/plic.c b/ports/risc-v32/gnu/example_build/cva6_ariane/plic.c new file mode 120000 index 00000000..b247f4fe --- /dev/null +++ b/ports/risc-v32/gnu/example_build/cva6_ariane/plic.c @@ -0,0 +1 @@ +../../../../risc-v_common/example_build/plic/plic.c
\ No newline at end of file diff --git a/ports/risc-v32/gnu/example_build/cva6_ariane/plic.h b/ports/risc-v32/gnu/example_build/cva6_ariane/plic.h new file mode 120000 index 00000000..f776a3d2 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/cva6_ariane/plic.h @@ -0,0 +1 @@ +../../../../risc-v_common/example_build/plic/plic.h
\ No newline at end of file diff --git a/ports/risc-v32/gnu/example_build/cva6_ariane/trap.c b/ports/risc-v32/gnu/example_build/cva6_ariane/trap.c new file mode 100644 index 00000000..520b74a5 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/cva6_ariane/trap.c @@ -0,0 +1,77 @@ +/***************************************************************************/ +/* Copyright (c) 2024 Microsoft Corporation */ +/* Copyright (c) 2026 Eclipse ThreadX contributors */ +/* */ +/* This program and the accompanying materials are made available under */ +/* the terms of the MIT License which is available at */ +/* https://opensource.org/licenses/MIT. */ +/* */ +/* SPDX-License-Identifier: MIT */ +/***************************************************************************/ + +#include "csr.h" +#include <stdint.h> +#include "uart.h" +#include "hwtimer.h" +#include "plic.h" +#include <tx_port.h> +#include <tx_api.h> + +#define OS_IS_INTERUPT(mcause) (mcause & 0x80000000u) +#define OS_IS_EXCEPTION(mcause) (~(OS_IS_INTERUPT)) +#define OS_IS_TICK_INT(mcause) (mcause == 0x80000007u) +#define OS_IS_SOFT_INT(mcause) (mcause == 0x80000003u) +#define OS_IS_EXT_INT(mcause) (mcause == 0x8000000bu) +#define OS_IS_TRAP_USER(mcause) (mcause == 0x0000000bu) +extern void _tx_timer_interrupt(void); + +extern int uart_putc(int ch); + +static void print_hex(uintptr_t val) +{ + char digits[] = "0123456789ABCDEF"; + uart_putc('0'); + uart_putc('x'); + for(int i = (sizeof(uintptr_t)*2) - 1; i >= 0; i--) { + int d = (val >> (i*4)) & 0xF; + uart_putc(digits[d]); + } + uart_putc('\n'); +} + +void trap_handler(uintptr_t mcause, uintptr_t mepc, uintptr_t mtval) +{ + if(OS_IS_INTERUPT(mcause)) + { + if(OS_IS_TICK_INT(mcause)) + { + hwtimer_handler(); + _tx_timer_interrupt(); + } + else if(OS_IS_EXT_INT(mcause)) + { + int ret = plic_irq_intr(); + if(ret) + { + puts("[INTERRUPT]: handler irq error!"); + while(1) ; + } + } + else + { + puts("[INTERRUPT]: now can't deal with the interrupt!"); + while(1) ; + } + } + else + { + puts("[EXCEPTION] : Unkown Error!!"); + puts("mcause:"); + print_hex(mcause); + puts("mepc:"); + print_hex(mepc); + puts("mtval:"); + print_hex(mtval); + while(1) ; + } +} diff --git a/ports/risc-v32/gnu/example_build/cva6_ariane/tx_initialize_low_level.S b/ports/risc-v32/gnu/example_build/cva6_ariane/tx_initialize_low_level.S new file mode 100644 index 00000000..16910f76 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/cva6_ariane/tx_initialize_low_level.S @@ -0,0 +1,163 @@ +/*************************************************************************** + * Copyright (c) 2026 Quintauris + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#include "csr.h" +#include "tx_port.h" + + .section .text + .align 4 +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* trap_entry RISC-V32/GNU */ +/* 6.4.x */ +/* AUTHOR */ +/* */ +/* Francisco Merino, Quintauris */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for riscv processor trap handle */ +/* It will do the contex save and call c trap_handler and do contex */ +/* load */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* trap_handler */ +/* */ +/* CALLED BY */ +/* */ +/* hardware exception */ +/* */ +/**************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + .global trap_entry + .extern trap_handler + .extern _tx_thread_context_restore + trap_entry: +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + addi sp, sp, -65*4 // Allocate space for all registers - with floating point enabled +#else + addi sp, sp, -32*4 // Allocate space for all registers - without floating point enabled +#endif + + sw x1, 28*4(sp) // Store RA, 28*4(because call will override ra [ra is a calle register in riscv]) + + call _tx_thread_context_save + + csrr a0, mcause + csrr a1, mepc + csrr a2, mtval + addi sp, sp, -4 + sw ra, 0(sp) + call trap_handler + lw ra, 0(sp) + addi sp, sp, 4 + call _tx_thread_context_restore + // it will nerver return +_err: + wfi + j _err + .section .text +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level RISC-V32/GNU */ +/* 6.4.x */ +/* AUTHOR */ +/* */ +/* Francisco Merino, Quintauris */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for any low-level processor */ +/* initialization, including setting up interrupt vectors, setting */ +/* up a periodic timer interrupt source, saving the system stack */ +/* pointer for use in ISR processing later, and finding the first */ +/* available RAM memory address for tx_application_define. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 11-03-2026 Francisco Merino Adapted for RV32 from RV64 port */ +/* */ +/**************************************************************************/ +/* VOID _tx_initialize_low_level(VOID) +{ */ + .global _tx_initialize_low_level + .weak _tx_initialize_low_level + .extern _end + .extern board_init +_tx_initialize_low_level: + + .section .text + + la t0, _tx_thread_system_stack_ptr + sw sp, 0(t0) // Save system stack pointer + + la t0, _end // Pickup first free address + la t1, _tx_initialize_unused_memory + sw t0, 0(t1) // Save unused memory address + li t0, MSTATUS_MIE + csrrc zero, mstatus, t0 // clear MSTATUS_MIE bit + li t0, (MSTATUS_MPP_M | MSTATUS_MPIE ) + csrrs zero, mstatus, t0 // set MSTATUS_MPP, MPIE bit + li t0, (MIE_MTIE | MIE_MSIE | MIE_MEIE) + csrrs zero, mie, t0 // set mie +#ifdef __riscv_flen + li t0, MSTATUS_FS + csrrs zero, mstatus, t0 // set MSTATUS_FS bit to open f/d isa in riscv + fscsr x0 +#endif + addi sp, sp, -4 + sw ra, 0(sp) + call board_init + lw ra, 0(sp) + addi sp, sp, 4 + la t0, trap_entry + csrw mtvec, t0 + ret diff --git a/ports/risc-v32/gnu/example_build/cva6_ariane/uart.c b/ports/risc-v32/gnu/example_build/cva6_ariane/uart.c new file mode 100644 index 00000000..c92c67e6 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/cva6_ariane/uart.c @@ -0,0 +1,893 @@ +/******************************************************************************* + * Copyright (c) 2020 Thales. + * Copyright 2019-2020 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + */ +// Additional contributions by: +// Sebastien Jacq - sjthales on github.com +// Anjali Gedam - anjaliigedam on github.com +// +// Description: Driver for UART Ip of the CVA6 platform +// +// =========================================================================== // +// Revisions : +// Date Version Author Description +// 2020-10-06 0.1 S.Jacq modification of the Test for CVA6 softcore +// 2021-07-12 0.2 Anjali G modification for the CVA6 FPGA +// =========================================================================== // + + +#include "uart.h" + +//#include "../plic/plic.h" +//#include "../hw_platform.h" +#define FPGA_UART_0_FREQUENCY 50000000 +#define FPGA_UART_0_BASE (UART_TypeDef *)0x10000000 +#define STDIO_BAUD_RATE UART_115200_BAUD + +/******************************************************************************* + * Defines + */ +#define TX_COMPLETE 0u +#define TX_FIFO_SIZE 16u + +#define FCR_TRIG_LEVEL_MASK 0xC0u + +#define IIRF_MASK 0x0Fu + +#define INVALID_INTERRUPT 0u +#define INVALID_IRQ_HANDLER ((uart_irq_handler_t) 0) +#define NULL_HANDLER ((uart_irq_handler_t) 0) + +#define UART_DATA_READY ((uint8_t) 0x01) + + +/******************************************************************************* + * Possible values for Interrupt Identification Register Field. + */ +#define IIRF_MODEM_STATUS 0x00u +#define IIRF_THRE 0x02u +//#define IIRF_MMI 0x03u +#define IIRF_RX_DATA 0x04u +#define IIRF_RX_LINE_STATUS 0x06u +#define IIRF_DATA_TIMEOUT 0x0Cu + + +//#define PLIC_DEMO 1 +// Source/portable/GCC/RISC-V/portASM.S:290:freertos_risc_v_application_interrupt_handler (weak function : User application can add there own override) + +uart_instance_t g_uart_0 = { .hw_reg = FPGA_UART_0_BASE }; + +/******************************************************************************* + * Global initialization for all modes + */ +static void global_init +( + uart_instance_t * this_uart, + uint32_t baud_rate, + uint8_t line_config +) +{ + + /* disable interrupts */ + this_uart->hw_reg->IER = 0u; + + /* FIFO configuration */ + this_uart->hw_reg->FCR = 0u; + + /* clear receiver FIFO */ + this_uart->hw_reg->FCR = FIFO_RX_TRIGGER_LEVEL_14_MASK | CLEAR_RX_FIFO_MASK | CLEAR_TX_FIFO_MASK | RXRDY_TXRDYN_EN_MASK; + + /* clear transmitter FIFO */ + this_uart->hw_reg->FCR |= CLEAR_TX_FIFO_MASK; + + /* set default READY mode : Mode 0*/ + /* enable RXRDYN and TXRDYN pins. The earlier FCR write to set the TX FIFO + * trigger level inadvertently disabled the FCR_RXRDY_TXRDYN_EN bit. */ + this_uart->hw_reg->FCR |= RXRDY_TXRDYN_EN_MASK; + + this_uart->hw_reg->MCR = 0u; + + + + /* + * Configure baud rate divisors. This uses the fractional baud rate divisor + * where possible to provide the most accurate baud rat possible. + */ + config_baud_divisors(this_uart, baud_rate); + + /* set the line control register (bit length, stop bits, parity) */ + this_uart->hw_reg->LCR = line_config; + + /* Instance setup */ + this_uart->baudrate = baud_rate; + this_uart->lineconfig = line_config; + this_uart->tx_buff_size = TX_COMPLETE; + this_uart->tx_buffer = (const uint8_t*)0; + this_uart->tx_idx = 0u; + + /* Default handlers for MSS UART interrupts */ + this_uart->rx_handler = NULL_HANDLER; + this_uart->tx_handler = NULL_HANDLER; + this_uart->linests_handler = NULL_HANDLER; + this_uart->modemsts_handler = NULL_HANDLER; + + /* Initialize the sticky status */ + this_uart->status = 0u; +} + +/******************************************************************************* + * Public Functions + *******************************************************************************/ +/***************************************************************************//** + * See uart.h for details of how to use this function. + */ +void +UART_init +( + uart_instance_t* this_uart, + uint32_t baud_rate, + uint8_t line_config +) +{ + /* Perform generic initialization */ + global_init(this_uart, baud_rate, line_config); + + + /* set default tx handler for automated TX using interrupt in USART mode */ + this_uart->tx_handler = default_tx_handler; +} + + + +/***************************************************************************//** + * See uart.h for details of how to use this function. + */ +void +UART_polled_tx +( + uart_instance_t * this_uart, + const uint8_t * pbuff, + uint32_t tx_size +) +{ + uint32_t char_idx = 0u; + // uint32_t size_sent; + uint8_t status; + //uint32_t temp_tx_size = tx_size; + + //ASSERT(pbuff != ( (uint8_t*)0)); + //ASSERT(tx_size > 0u); + + if ((pbuff != ((uint8_t*)0)) && (tx_size > 0u)) + { + /* Remain in this loop until the entire input buffer + * has been transferred to the UART. + */ + do + { + /* Wait until TX FIFO is empty. */ + do + { + status = this_uart->hw_reg->LSR; + // this_uart->status |= status; + }while (0u == (status & UART_THRE)); + + + /* Check if TX FIFO is empty. */ + // if (status & UART_THRE) + //{ + // uint32_t fill_size = TX_FIFO_SIZE; + + /* Calculate the number of bytes to transmit. */ + //if (temp_tx_size < TX_FIFO_SIZE) + //{ + // fill_size = temp_tx_size; + //} + + /* Fill the TX FIFO with the calculated the number of bytes. */ + //for (size_sent = 0u; size_sent < fill_size; ++size_sent) + //{ + /* Send next character in the buffer. */ + this_uart->hw_reg->THR = pbuff[char_idx]; + char_idx++; + //} + + /* Calculate the number of bytes remaining(not transmitted yet)*/ + //temp_tx_size -= size_sent; + //} + }while (char_idx < tx_size); + } +} + + +/***************************************************************************//** + * See uart.h for details of how to use this function. + */ +void +UART_polled_tx_string +( + uart_instance_t * this_uart, + const uint8_t * p_sz_string +) +{ + uint32_t char_idx = 0u; + //uint32_t fill_size; + uint8_t data_byte; + volatile uint8_t status; + + //ASSERT(p_sz_string != ((uint8_t*)0)); + + if (p_sz_string != ((uint8_t*)0)) + { + /* Get the first data byte from the input buffer */ + data_byte = p_sz_string[char_idx]; + + /* First check for the NULL terminator byte. + * Then remain in this loop until the entire string in the input buffer + * has been transferred to the UART. + */ + while (0u != data_byte) + { + /* Wait until TX FIFO is empty. */ + do + { + status = this_uart->hw_reg->LSR; + // this_uart->status |= status; + }while (0u == (status & UART_THRE)); + + + /* Send bytes from the input buffer until the TX FIFO is full + * or we reach the NULL terminator byte. + */ + //fill_size = 0u; + + // while ((0u != data_byte) && (fill_size < TX_FIFO_SIZE)) + //{ + /* Send the data byte */ + this_uart->hw_reg->THR = data_byte; + //++fill_size; + char_idx++; + /* Get the next data byte from the input buffer */ + data_byte = p_sz_string[char_idx]; + //} + } + } +} + +/***************************************************************************//** + * See uart.h for details of how to use this function. + */ +void +UART_irq_tx +( + uart_instance_t * this_uart, + const uint8_t * pbuff, + uint32_t tx_size +) +{ + //ASSERT(pbuff != ((uint8_t*)0)); + //ASSERT(tx_size > 0u); + + if ((tx_size > 0u) && (pbuff != ((uint8_t*)0))) + { + /*Initialize the transmit info for the UART instance with the arguments*/ + this_uart->tx_buffer = pbuff; + this_uart->tx_buff_size = tx_size; + this_uart->tx_idx = 0u; + + /* assign default handler for data transfer */ + this_uart->tx_handler = default_tx_handler; + + /* enables TX interrupt */ + this_uart->hw_reg->IER |= ETBEI_MASK; + enable_irq(this_uart); + } +} + +/***************************************************************************//** + * See uart.h for details of how to use this function. + */ +int8_t +UART_tx_complete +( + uart_instance_t * this_uart +) +{ + int8_t ret_value = 0; + uint8_t status = 0u; + + /* Read the Line Status Register and update the sticky record. */ + status = this_uart->hw_reg->LSR; + this_uart->status |= status; + + if ((TX_COMPLETE == this_uart->tx_buff_size) && + ((status & UART_TEMT) != 0u)) + { + ret_value = (int8_t)1; + } + + return ret_value; +} + + +/***************************************************************************//** + * See uart.h for details of how to use this function. + */ +size_t +UART_get_rx +( + uart_instance_t * this_uart, + uint8_t * rx_buff, + size_t buff_size +) +{ + size_t rx_size = 0u; + uint8_t status = 0u; + + //ASSERT(rx_buff != ((uint8_t*)0)); + //ASSERT(buff_size > 0u); + //printf("UART_get_rx() called buff_size = %d\n", buff_size); + + if ((rx_buff != (uint8_t*)0) && (buff_size > 0u)) + { + status = this_uart->hw_reg->LSR; + this_uart->status |= status; + //printf("UART_get_rx() LSR status = %d\n", status); + + while (((status & UART_DATA_READY) != 0u) && (rx_size < buff_size)) + { + rx_buff[rx_size] = this_uart->hw_reg->RBR; + ++rx_size; + status = this_uart->hw_reg->LSR; + //printf("1 UART_get_rx() LSR status = %d\n", status); + this_uart->status |= status; + //printf("2 UART_get_rx() LSR status = %d\n", status); + + } + } + + return rx_size; +} + +/***************************************************************************//** + * See uart.h for details of how to use this function. + */ +void +UART_enable_irq +( + uart_instance_t * this_uart, + uart_irq_t irq_mask +) +{ + //ASSERT(UART_INVALID_IRQ > irq_mask); + + enable_irq(this_uart); + + if (UART_INVALID_IRQ > irq_mask) + { + /* irq_mask encoding: 1- enable + * bit 0 - Receive Data Available Interrupt + * bit 1 - Transmitter Holding Register Empty Interrupt + * bit 2 - Receiver Line Status Interrupt + * bit 3 - Modem Status Interrupt + */ + this_uart->hw_reg->IER |= ((uint8_t)(((uint32_t)irq_mask & + (uint32_t)IIRF_MASK))); + + } +} + + +/***************************************************************************//** + * See uart.h for details of how to use this function. + */ +void +UART_set_rx_handler +( + uart_instance_t * this_uart, + uart_irq_handler_t handler, + uart_rx_trig_level_t trigger_level +) +{ + //ASSERT(handler != INVALID_IRQ_HANDLER ); + //ASSERT(trigger_level < UART_FIFO_INVALID_TRIG_LEVEL); +#ifdef PLIC_DEMO + PLIC_init(); +#endif + + if ((handler != INVALID_IRQ_HANDLER) && + (trigger_level < UART_FIFO_INVALID_TRIG_LEVEL)) + { + //printf("UART DEBUG1\n"); + this_uart->rx_handler = handler; + + /* Set the receive interrupt trigger level. */ + this_uart->hw_reg->FCR = (this_uart->hw_reg->FCR & + (uint8_t)(~((uint8_t)FCR_TRIG_LEVEL_MASK))) | + (uint8_t)trigger_level; + + /* Enable receive interrupt. */ + this_uart->hw_reg->IER |= ERBFI_MASK; + //printf("UART Enable IRQ\n"); + enable_irq(this_uart); + } +} + +/***************************************************************************//** + * See uart.h for details of how to use this function. + */ +void +UART_set_tx_handler +( + uart_instance_t * this_uart, + uart_irq_handler_t handler +) +{ + //ASSERT(handler != INVALID_IRQ_HANDLER); + + if (handler != INVALID_IRQ_HANDLER) + { + this_uart->tx_handler = handler; + + /* Make TX buffer info invalid */ + this_uart->tx_buffer = (const uint8_t*)0; + this_uart->tx_buff_size = 0u; + + /* Enable transmitter holding register Empty interrupt. */ + this_uart->hw_reg->IER |= ETBEI_MASK; + enable_irq(this_uart); + } +} + +/***************************************************************************//** + * See uart.h for details of how to use this function. + */ +void +UART_set_modemstatus_handler +( + uart_instance_t * this_uart, + uart_irq_handler_t handler +) +{ + //ASSERT(handler != INVALID_IRQ_HANDLER); + + if (handler != INVALID_IRQ_HANDLER) + { + this_uart->modemsts_handler = handler; + + /* Enable modem status interrupt. */ + this_uart->hw_reg->IER |= EDSSI_MASK; + enable_irq(this_uart); + } +} + + +/***************************************************************************//** + * See uart.h for details of how to use this function. + */ +size_t +UART_fill_tx_fifo +( + uart_instance_t * this_uart, + const uint8_t * tx_buffer, + size_t tx_size +) +{ + uint8_t status = 0u; + uint32_t size_sent = 0u; + + //ASSERT(tx_buffer != ( (uint8_t*)0)); + //ASSERT(tx_size > 0); + + /* Fill the UART's Tx FIFO until the FIFO is full or the complete input + * buffer has been written. */ + if ((tx_buffer != ((uint8_t*)0)) && (tx_size > 0u)) + { + status = this_uart->hw_reg->LSR; + this_uart->status |= status; + + if (status & UART_THRE) + { + uint32_t fill_size = TX_FIFO_SIZE; + + if (tx_size < TX_FIFO_SIZE) + { + fill_size = tx_size; + } + + /* Fill up FIFO */ + for (size_sent = 0u; size_sent < fill_size; size_sent++) + { + /* Send next character in the buffer. */ + this_uart->hw_reg->THR = tx_buffer[size_sent]; + } + } + } + + return size_sent; +} + +/***************************************************************************//** + * See uart.h for details of how to use this function. + */ +uint8_t +UART_get_rx_status +( + uart_instance_t * this_uart +) +{ + uint8_t status = UART_INVALID_PARAM; + + /* + * Extract UART receive error status. + * Bit 1 - Overflow error status + * Bit 2 - Parity error status + * Bit 3 - Frame error status + * Bit 4 - Break interrupt indicator + * Bit 7 - FIFO data error status + */ + this_uart->status |= (this_uart->hw_reg->LSR); + status = (this_uart->status & STATUS_ERROR_MASK); + + //printf("UART_get_rx_status = %d", status); + //printf("UART_get_rx_LSR = %d", this_uart->hw_reg->LSR); + + + /* Clear the sticky status after reading */ + this_uart->status = 0u; + + return status; +} + +/***************************************************************************//** + * See uart.h for details of how to use this function. + */ +uint8_t +UART_get_modem_status +( + const uart_instance_t * this_uart +) +{ + uint8_t status = UART_INVALID_PARAM; + + /* + * Extract UART modem status and place in lower bits of "status". + * Bit 0 - Delta Clear to Send Indicator + * Bit 1 - Delta Clear to Receive Indicator + * Bit 2 - Trailing edge of Ring Indicator detector + * Bit 3 - Delta Data Carrier Detect indicator + * Bit 4 - Clear To Send + * Bit 5 - Data Set Ready + * Bit 6 - Ring Indicator + * Bit 7 - Data Carrier Detect + */ + status = this_uart->hw_reg->MSR; + //printf("UART_get_modem_status = %d", status); + + return status; +} + + +/***************************************************************************//** + * UART_get_tx_status. + * See uart.h for details of how to use this function. + */ +uint8_t +UART_get_tx_status +( + uart_instance_t * this_uart +) +{ + uint8_t status = UART_TX_BUSY; + + /* Read the Line Status Register and update the sticky record. */ + status = this_uart->hw_reg->LSR; + this_uart->status |= status; + + /* + * Extract the transmit status bits from the UART's Line Status Register. + * Bit 5 - Transmitter Holding Register/FIFO Empty (THRE) status. + (If = 1, TX FIFO is empty) + * Bit 6 - Transmitter Empty (TEMT) status. + (If = 1, both TX FIFO and shift register are empty) + */ + status &= (UART_THRE | UART_TEMT); + + return status; +} + +/***************************************************************************//** + * See uart.h for details of how to use this function. + */ +void +UART_set_break +( + uart_instance_t * this_uart +) +{ + /* set break character on Tx line */ + this_uart->hw_reg->LCR |= SB_MASK; +} + + +/***************************************************************************//** + * See uart.h for details of how to use this function. + */ +void +UART_clear_break +( + uart_instance_t * this_uart +) +{ + /* remove break character from Tx line */ + this_uart->hw_reg->LCR &= ~SB_MASK; +} + +/***************************************************************************//** + * Configure baud divisors using fractional baud rate if possible. + */ +static void +config_baud_divisors +( + uart_instance_t * this_uart, + uint32_t baudrate +) +{ + uint32_t baud_value; + uint32_t baud_value_by_64; + uint32_t baud_value_by_128; +// uint32_t fractional_baud_value; + /* Note that we may need to set this as 64 bits for high frequencies */ + uint32_t pclk_freq; + + this_uart->baudrate = baudrate; + + /* Use the system clock value from hw_platform.h */ + pclk_freq = FPGA_UART_0_FREQUENCY; + + /* + * Compute baud value based on requested baud rate and PCLK frequency. + * The baud value is computed using the following equation: + * baud_value = PCLK_Frequency / (baud_rate * 16) + */ + baud_value_by_128 = (uint32_t)((8UL * pclk_freq) / baudrate); + baud_value_by_64 = baud_value_by_128 / 2u; + baud_value = baud_value_by_64 / 64u; +// fractional_baud_value = baud_value_by_64 - (baud_value * 64u); +// fractional_baud_value += (baud_value_by_128 - (baud_value * 128u)) + // - (fractional_baud_value * 2u); + + /* //ASSERT if integer baud value fits in 16-bit. */ + //ASSERT(baud_value <= UINT16_MAX); + + if (baud_value <= (uint32_t)UINT16_MAX) + { + + /* + * Use Fractional baud rate divisors + */ + /* set divisor latch */ + this_uart->hw_reg->LCR = DLAB_MASK; + + /* msb of baud value */ + this_uart->hw_reg->DLM = (uint8_t)(baud_value >> 8); + /* lsb of baud value */ + this_uart->hw_reg->DLL = (uint8_t)baud_value; + + /* reset divisor latch */ + this_uart->hw_reg->LCR = 0; + } +} + +/***************************************************************************//** + * Interrupt service routine triggered by any MSS UART interrupt. This routine + * will call the handler function appropriate to the interrupt from the + * handlers previously registered with the driver through calls to the + * UART_set_*_handler() functions, or it will call the default_tx_handler() + * function in response to transmit interrupts if UART_irq_tx() is used to + * transmit data. + */ +static void __attribute__ ((unused)) +uart_isr +( + uart_instance_t * this_uart +) +{ + //printf("uart_isr called\n"); + uint8_t iirf; + + iirf = this_uart->hw_reg->IIR & IIRF_MASK; + + switch (iirf) + { + case IIRF_MODEM_STATUS: /* Modem status interrupt */ + { + //ASSERT(NULL_HANDLER != this_uart->modemsts_handler); + if (NULL_HANDLER != this_uart->modemsts_handler) + { + (*(this_uart->modemsts_handler))(this_uart); + } + } + break; + + case IIRF_THRE: /* Transmitter Holding Register Empty */ + { + //ASSERT(NULL_HANDLER != this_uart->tx_handler); + if (NULL_HANDLER != this_uart->tx_handler) + { + (*(this_uart->tx_handler))(this_uart); + } + } + break; + + case IIRF_RX_DATA: /* Received Data Available */ + case IIRF_DATA_TIMEOUT: /* Received Data Timed-out */ + { + //ASSERT(NULL_HANDLER != this_uart->rx_handler); + if (NULL_HANDLER != this_uart->rx_handler) + { + (*(this_uart->rx_handler))(this_uart); + } + } + break; + + case IIRF_RX_LINE_STATUS: /* Line Status Interrupt */ + { + //ASSERT(NULL_HANDLER != this_uart->linests_handler); + if (NULL_HANDLER != this_uart->linests_handler) + { + (*(this_uart->linests_handler))(this_uart); + } + } + break; + + default: + { + //ASSERT(INVALID_INTERRUPT); /*Alternative case has been considered*/ + } + break; + } +} + + + +/***************************************************************************//** + * See uart.h for details of how to use this function. + */ +static void +default_tx_handler +( + uart_instance_t * this_uart +) +{ + uint8_t status; + + //ASSERT(( (uint8_t*)0 ) != this_uart->tx_buffer); + //ASSERT(0u < this_uart->tx_buff_size); + + if ((((uint8_t*)0 ) != this_uart->tx_buffer) && + (0u < this_uart->tx_buff_size)) + { + /* Read the Line Status Register and update the sticky record. */ + status = this_uart->hw_reg->LSR; + this_uart->status |= status; + + /* + * This function should only be called as a result of a THRE interrupt. + * Verify that this is true before proceeding to transmit data. + */ + if (status & UART_THRE) + { + uint32_t cnt; + uint32_t fill_size = TX_FIFO_SIZE; + uint32_t tx_remain = this_uart->tx_buff_size - this_uart->tx_idx; + + /* Calculate the number of bytes to transmit. */ + if (tx_remain < TX_FIFO_SIZE) + { + fill_size = tx_remain; + } + + /* Fill the TX FIFO with the calculated the number of bytes. */ + for (cnt = 0u; cnt < fill_size; ++cnt) + { + /* Send next character in the buffer. */ + this_uart->hw_reg->THR = this_uart->tx_buffer[this_uart->tx_idx]; + ++this_uart->tx_idx; + } + } + + /* Flag Tx as complete if all data has been pushed into the Tx FIFO. */ + if (this_uart->tx_idx == this_uart->tx_buff_size) + { + this_uart->tx_buff_size = TX_COMPLETE; + + /* disables TX interrupt */ + this_uart->hw_reg->IER &= ~ETBEI_MASK; + } + } +} + + +static void __attribute__ ((unused)) +enable_irq +( + const uart_instance_t * this_uart +) +{ + +#ifdef PLIC_DEMO + PLIC_IRQn_Type plic_num = 0; +#endif + + if (&g_uart_0 == this_uart ) + { + //printf("enable_irq() UART assign plic_num = %d\n",plic_num); +#ifdef PLIC_DEMO + plic_num = UART_0_PLIC_IRQHandler; +#endif + + } + else + { + //printf("ASSERT(0) called\n"); + //ASSERT(0); /*Alternative case has been considered*/ + } + +#ifdef PLIC_DEMO + /* Enable UART instance interrupt in PLIC. */ + PLIC_EnableIRQ(plic_num); +#endif +} + +static void __attribute__ ((unused)) +disable_irq +( + const uart_instance_t * this_uart +) +{ +#ifdef PLIC_DEMO + PLIC_IRQn_Type plic_num = 0; +#endif + + if (&g_uart_0 == this_uart ) + { +#ifdef PLIC_DEMO + plic_num = UART_0_PLIC_IRQHandler; +#endif + } + else + { + //ASSERT(0); /*Alternative case has been considered*/ + } + + /* Disable UART instance interrupt in PLIC. */ +#ifdef PLIC_DEMO + PLIC_DisableIRQ(plic_num); +#endif +} + + +/////////////////////////////////////////////////////////////////////////////////// + +static uart_instance_t * const gp_my_uart = &g_uart_0; + +int uart_init(void) +{ + UART_init(gp_my_uart, STDIO_BAUD_RATE, ( UART_DATA_8_BITS | UART_NO_PARITY | UART_ONE_STOP_BIT ) ); + + return 0; +} + +int uart_putc(int ch) +{ + UART_polled_tx(gp_my_uart, (const uint8_t *)&ch, 1); +} + +int uart_puts(const char* str) +{ + UART_polled_tx_string(gp_my_uart, str); +}
\ No newline at end of file diff --git a/ports/risc-v32/gnu/example_build/cva6_ariane/uart.h b/ports/risc-v32/gnu/example_build/cva6_ariane/uart.h new file mode 100644 index 00000000..0a835727 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/cva6_ariane/uart.h @@ -0,0 +1,961 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * Copyright (c) 2026 Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + +#ifndef RISCV_UART_H +#define RISCV_UART_H + +#define UART0 0x10000000L +#define UART0_IRQ 10 + +#define puts(str) uart_puts(str"\n\r") +int uart_init(void); +int uart_putc(int ch); +int uart_puts(const char* str); +#endif + + +/******************************************************************************* + * Copyright (c) 2020 Thales. + * Copyright 2019-2020 Microchip FPGA Embedded Systems Solutions. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * + */ +// Additional contributions by: +// Sebastien Jacq - sjthales on github.com +// Anjali Gedam - anjaliigedam on github.com +// +// Description: Driver header for UART Ip of the CVA6 platform +// +// =========================================================================== // +// Revisions : +// Date Version Author Description +// 2020-10-06 0.1 S.Jacq modification of the Test for CVA6 softcore +// 2021-07-12 0.2 Anjali G modification for the CVA6 FPGA +// =========================================================================== // + +#ifndef __UART1_H_ +#define __UART1_H_ 1 + +#include <stddef.h> +#include <stdint.h> + + +/***************************************************************************//** + Baud rates + ========== + The following definitions are used to specify standard baud rates as a + parameter to the UART_init() function. + + | Constant | Description | + |----------------------|------------------| + | UART_110_BAUD | 110 baud rate | + | UART_300_BAUD | 300 baud rate | + | UART_600_BAUD | 600 baud rate | + | UART_1200_BAUD | 1200 baud rate | + | UART_2400_BAUD | 2400 baud rate | + | UART_4800_BAUD | 4800 baud rate | + | UART_9600_BAUD | 9600 baud rate | + | UART_19200_BAUD | 19200 baud rate | + | UART_38400_BAUD | 38400 baud rate | + | UART_57600_BAUD | 57600 baud rate | + | UART_115200_BAUD | 115200 baud rate | + | UART_230400_BAUD | 230400 baud rate | + | UART_460800_BAUD | 460800 baud rate | + | UART_921600_BAUD | 921600 baud rate | + + */ +#define UART_110_BAUD 110U +#define UART_300_BAUD 300U +#define UART_600_BAUD 600U +#define UART_1200_BAUD 1200U +#define UART_2400_BAUD 2400U +#define UART_4800_BAUD 4800U +#define UART_9600_BAUD 9600U +#define UART_19200_BAUD 19200U +#define UART_38400_BAUD 38400U +#define UART_57600_BAUD 57600U +#define UART_115200_BAUD 115200U +#define UART_230400_BAUD 230400U +#define UART_460800_BAUD 460800U +#define UART_921600_BAUD 921600U + +/***************************************************************************//** + Data Bits Length + ================ + The following defines are used to build the value of the UART_init() + function line_config parameter. + + | Constant | Description | + |----------------------|----------------------------| + | UART_DATA_5_BITS | 5 bits of data transmitted | + | UART_DATA_6_BITS | 6 bits of data transmitted | + | UART_DATA_7_BITS | 7 bits of data transmitted | + | UART_DATA_8_BITS | 8 bits of data transmitted | + + */ +#define UART_DATA_5_BITS ((uint8_t) 0x00) +#define UART_DATA_6_BITS ((uint8_t) 0x01) +#define UART_DATA_7_BITS ((uint8_t) 0x02) +#define UART_DATA_8_BITS ((uint8_t) 0x03) + +/***************************************************************************//** + Parity + ====== + The following defines are used to build the value of the UART_init() + function line_config parameter. + + | Constant | Description | + |-------------------------|--------------------------| + | UART_NO_PARITY | No parity | + | UART_ODD_PARITY | Odd Parity | + | UART_EVEN_PARITY | Even parity | + | UART_STICK_PARITY_0 | Stick parity bit to zero | + | UART_STICK_PARITY_1 | Stick parity bit to one | + */ +#define UART_NO_PARITY ((uint8_t) 0x00) +#define UART_ODD_PARITY ((uint8_t) 0x08) +#define UART_EVEN_PARITY ((uint8_t) 0x18) +#define UART_STICK_PARITY_0 ((uint8_t) 0x38) +#define UART_STICK_PARITY_1 ((uint8_t) 0x28) + +/***************************************************************************//** + Number of Stop Bits + =================== + The following defines are used to build the value of the UART_init() + function line_config parameter. + + | Constant | Description | + |---------------------------|--------------------------| + | UART_ONE_STOP_BIT | One stop bit | + | UART_ONEHALF_STOP_BIT | One and a half stop bits | + | UART_TWO_STOP_BITS | Two stop bits | + + */ +#define UART_ONE_STOP_BIT ((uint8_t) 0x00) +#define UART_ONEHALF_STOP_BIT ((uint8_t) 0x04) +#define UART_TWO_STOP_BITS ((uint8_t) 0x04) + +/***************************************************************************//** + Receiver Error Status + ===================== + The following defines are used to determine the UART receiver error type. + These bit mask constants are used with the return value of the + UART_get_rx_status() function to find out if any errors occurred while + receiving data. + + + | Constant | Description | + |------------------------|--------------------------------------------| + | UART_NO_ERROR | No error bit mask (0x00) | + | UART_OVERUN_ERROR | Overrun error bit mask (0x02) | + | UART_PARITY_ERROR | Parity error bit mask (0x04) | + | UART_FRAMING_ERROR | Framing error bit mask (0x08) | + | UART_BREAK_ERROR | Break error bit mask (0x10) | + | UART_FIFO_ERROR | FIFO error bit mask (0x80) | + | UART_INVALID_PARAM | Invalid function parameter bit mask (0xFF) | + */ +#define UART_INVALID_PARAM ((uint8_t)0xFF) +#define UART_NO_ERROR ((uint8_t)0x00 ) +#define UART_OVERUN_ERROR ((uint8_t)0x02) +#define UART_PARITY_ERROR ((uint8_t)0x04) +#define UART_FRAMING_ERROR ((uint8_t)0x08) +#define UART_BREAK_ERROR ((uint8_t)0x10) +#define UART_FIFO_ERROR ((uint8_t)0x80) + + +/******************************************************************************* + * Receiver error status mask. + */ +#define STATUS_ERROR_MASK ( UART_OVERUN_ERROR | UART_PARITY_ERROR | \ + UART_FRAMING_ERROR | UART_BREAK_ERROR | \ + UART_FIFO_ERROR) + +/***************************************************************************//** + Transmitter Status + ================== + The following definitions are used to determine the UART transmitter status. + These bit mask constants are used with the return value of the + UART_get_tx_status() function to find out the status of the transmitter. + + | Constant | Description | + |------------------|----------------------------------------------------| + | UART_TX_BUSY | Transmitter busy (0x00) | + | UART_THRE | Transmitter holding register empty bit mask (0x20) | + | UART_TEMT | Transmitter empty bit mask (0x40) | + + */ +#define UART_TX_BUSY ((uint8_t) 0x00) +#define UART_THRE ((uint8_t) 0x20) +#define UART_TEMT ((uint8_t) 0x40) + +/***************************************************************************//** + Modem Status + ============ + The following defines are used to determine the modem status. These bit + mask constants are used with the return value of the + UART_get_modem_status() function to find out the modem status of + the UART. + + | Constant | Description | + |---------------|-------------------------------------------------| + | UART_DCTS | Delta clear to send bit mask (0x01) | + | UART_DDSR | Delta data set ready bit mask (0x02) | + | UART_TERI | Trailing edge of ring indicator bit mask (0x04) | + | UART_DDCD | Delta data carrier detect bit mask (0x08) | + | UART_CTS | Clear to send bit mask (0x10) | + | UART_DSR | Data set ready bit mask (0x20) | + | UART_RI | Ring indicator bit mask (0x40) | + | UART_DCD | Data carrier detect bit mask (0x80) | + */ +#define UART_DCTS ((uint8_t) 0x01) +#define UART_DDSR ((uint8_t) 0x02) +#define UART_TERI ((uint8_t) 0x04) +#define UART_DDCD ((uint8_t) 0x08) +#define UART_CTS ((uint8_t) 0x10) +#define UART_DSR ((uint8_t) 0x20) +#define UART_RI ((uint8_t) 0x40) +#define UART_DCD ((uint8_t) 0x80) + +/***************************************************************************//** + This typedef specifies the irq_mask parameter for the UART_enable_irq() + and UART_disable_irq() functions. The driver defines a set of bit masks + that are used to build the value of the irq_mask parameter. A bitwise OR of + these bit masks is used to enable or disable multipleUART interrupts. + */ +typedef uint16_t uart_irq_t; + +/***************************************************************************//** + UART Interrupts + ===================== + The following defines specify the interrupt masks to enable and disable + UART interrupts. They are used to build the value of the irq_mask parameter + for the UART_enable_irq() and UART_disable_irq() functions. A bitwise + OR of these constants is used to enable or disable multiple interrupts. + + + | Constant | Description | + |--------------------|---------------------------------------------------------------| + | UART_RBF_IRQ | Receive Data Available Interrupt bit mask (0x001) | + | UART_TBE_IRQ | Transmitter Holding Register Empty interrupt bit mask (0x002) | + | UART_LS_IRQ | Receiver Line Status interrupt bit mask (0x004) | + | UART_MS_IRQ | Modem Status interrupt bit mask (0x008) | + + */ +#define UART_RBF_IRQ 0x001 +#define UART_TBE_IRQ 0x002 +#define UART_LS_IRQ 0x004 +#define UART_MS_IRQ 0x008 +//#define UART_RTO_IRQ 0x010 +//#define UART_NACK_IRQ 0x020 +//#define UART_PIDPE_IRQ 0x040 +//#define UART_LINB_IRQ 0x080 +//#define UART_LINS_IRQ 0x100 +#define UART_INVALID_IRQ UINT16_MAX + +/***************************************************************************//** + This enumeration specifies the receiver FIFO trigger level. This is the number + of bytes that must be received before the UART generates a receive data + available interrupt. It provides the allowed values for the + UART_set_rx_handler() function trigger_level parameter. + */ +typedef enum { + UART_FIFO_SINGLE_BYTE = 0x00, + UART_FIFO_FOUR_BYTES = 0x40, + UART_FIFO_EIGHT_BYTES = 0x80, + UART_FIFO_FOURTEEN_BYTES = 0xC0, + UART_FIFO_INVALID_TRIG_LEVEL + +} uart_rx_trig_level_t; + + + + +/***************************************************************************//** + UART instance type. + This is type definition for UART instance. You need to create and + maintain a record of this type. This holds all data regarding the UART + instance + */ +typedef struct uart_instance uart_instance_t; + +/***************************************************************************//** + Interrupt handler prototype. + This typedef specifies the function prototype for UART interrupt handlers. + All interrupt handlers registered with the UART driver must be of this type. + The interrupt handlers are registered with the driver through the + UART_set_rx_handler(), UART_set_tx_handler(), + UART_set_rxstatus_handler(), and UART_set_modemstatus_handler() + functions. + The this_uart parameter is a pointer to either g_uart0 or g_uart1 to + identify the UART to associate with the handler function. + */ +typedef void (*uart_irq_handler_t)( uart_instance_t * this_uart ); + + +/******************************************************************************* + Register Bit definitions + */ + +/* Line Control register bit definitions */ +#define SB 6u /* Set break */ +#define DLAB 7u /* Divisor latch access bit */ + +/* Line Control register bit masks */ +#define SB_MASK (0x01u << SB) /* Set break */ +#define DLAB_MASK (0x01u << DLAB) /* Divisor latch access bit */ + +/* FIFO Control register bit definitions */ +#define ENABLE_FIFO 0u /* Enable FIFO */ +#define CLEAR_RX_FIFO 1u /* Clear receiver FIFO */ +#define CLEAR_TX_FIFO 2u /* Clear transmitter FIFO */ +#define DMA_MODE 3u /* DMA mode */ +#define FIFO64_ENABLE 5u /* FIFO64 enable */ +#define RDYMODE 3u /* Mode 0 or Mode 1 for TXRDY and RXRDY */ +#define FIFO_RX_TRIGGER 6u /* FIFO RX trigger */ + +/* FIFO Control register bit MASKS */ +#define RXRDY_TXRDYN_EN_MASK (0x01u << 0u) /* Enable TXRDY and RXRDY signals */ +#define CLEAR_RX_FIFO_MASK (0x01u << 1u) /* Clear receiver FIFO */ +#define CLEAR_TX_FIFO_MASK (0x01u << 2u) /* Clear transmitter FIFO */ +#define DMA_MODE_MASK (0x01u << 3u) /* DMA mode */ +#define FIFO64_ENABLE_MASK (0x01u << 5u) /* FIFO64 enable */ +#define RDYMODE_MASK (0x01u << 3u) /* Mode 0 or Mode 1 for TXRDY and RXRDY */ + +#define FIFO_RX_TRIGGER_LEVEL_4_MASK (0x01u << 6u) +#define FIFO_RX_TRIGGER_LEVEL_8_MASK (0x02u << 6u) +#define FIFO_RX_TRIGGER_LEVEL_14_MASK (0x03u << 6u) + +#define FIFO64_RX_TRIGGER_LEVEL_16_MASK (0x01u << 6u) +#define FIFO64_RX_TRIGGER_LEVEL_32_MASK (0x02u << 6u) +#define FIFO64_RX_TRIGGER_LEVEL_56_MASK (0x03u << 6u) + +/* Modem Control register bit definitions */ +//#define LOOP 4u /* Local loopback */ +//#define RLOOP 5u /* Remote loopback */ +//#define ECHO 6u /* Automatic echo */ + +/* Modem Control register bit MASKS */ +//#define LOOP_MASK (0x01u << 4u) /* Local loopback */ +//#define RLOOP_MASK (0x01u << 5u) /* Remote loopback & Automatic echo*/ +//#define ECHO_MASK (0x01u << 6u) /* Automatic echo */ + +/* Line Status register bit definitions */ +#define DR 0u /* Data ready */ +#define THRE 5u /* Transmitter holding register empty */ +#define TEMT 6u /* Transmitter empty */ + +/* Line Status register bit MASKS */ +#define DR_MASK (0x01u << 0u) /* Data ready */ +#define THRE_MASK (0x01u << 5u) /* Transmitter holding register empty */ +#define TEMT_MASK (0x01u << 6u) /* Transmitter empty */ + +/* Interrupt Enable register bit definitions */ +#define ERBFI 0u /* Enable receiver buffer full interrupt */ +#define ETBEI 1u /* Enable transmitter buffer empty interrupt */ +#define ELSI 2u /* Enable line status interrupt */ +#define EDSSI 3u /* Enable modem status interrupt */ + +/* Interrupt Enable register bit MASKS */ +#define ERBFI_MASK (0x01u << 0u) /* Enable receiver buffer full interrupt */ +#define ETBEI_MASK (0x01u << 1u) /* Enable transmitter buffer empty interrupt */ +#define ELSI_MASK (0x01u << 2u) /* Enable line status interrupt */ +#define EDSSI_MASK (0x01u << 3u) /* Enable modem status interrupt */ + +/* Multimode register 0 bit definitions */ +#define ELIN 3u /* Enable LIN header detection */ +#define ETTG 5u /* Enable transmitter time guard */ +#define ERTO 6u /* Enable receiver time-out */ +#define EFBR 7u /* Enable fractional baud rate mode */ + +/* Multimode register 0 bit MASKS */ +#define ELIN_MASK (0x01u << 3u) /* Enable LIN header detection */ +#define ETTG_MASK (0x01u << 5u) /* Enable transmitter time guard */ +#define ERTO_MASK (0x01u << 6u) /* Enable receiver time-out */ +#define EFBR_MASK (0x01u << 7u) /* Enable fractional baud rate mode */ + +/* Multimode register 1 bit definitions */ +#define E_MSB_RX 0u /* MSB / LSB first for receiver */ +#define E_MSB_TX 1u /* MSB / LSB first for transmitter */ +#define EIRD 2u /* Enable IrDA modem */ +#define EIRX 3u /* Input polarity for IrDA modem */ +#define EITX 4u /* Output polarity for IrDA modem */ +#define EITP 5u /* Output pulse width for IrDA modem */ + +/* Multimode register 1 bit MASKS */ +#define E_MSB_RX_MASK (0x01u << 0u) /* MSB / LSB first for receiver */ +#define E_MSB_TX_MASK (0x01u << 1u) /* MSB / LSB first for transmitter */ +#define EIRD_MASK (0x01u << 2u) /* Enable IrDA modem */ +#define EIRX_MASK (0x01u << 3u) /* Input polarity for IrDA modem */ +#define EITX_MASK (0x01u << 4u) /* Output polarity for IrDA modem */ +#define EITP_MASK (0x01u << 5u) /* Output pulse width for IrDA modem */ + +/* Multimode register 2 bit definitions */ +//#define EERR 0u /* Enable ERR / NACK during stop time */ +//#define EAFM 1u /* Enable 9-bit address flag mode */ +//#define EAFC 2u /* Enable address flag clear */ +//#define ESWM 3u /* Enable single wire half-duplex mode */ + +/* Multimode register 2 bit MASKS */ +//#define EERR_MASK (0x01u << 0u) /* Enable ERR / NACK during stop time */ +//#define EAFM_MASK (0x01u << 1u) /* Enable 9-bit address flag mode */ +//#define EAFC_MASK (0x01u << 2u) /* Enable address flag clear */ +//#define ESWM_MASK (0x01u << 3u) /* Enable single wire half-duplex mode */ + +/* Multimode Interrupt Enable register and + Multimode Interrupt Identification register definitions */ +//#define ERTOI 0u /* Enable receiver timeout interrupt */ +//#define ENACKI 1u /* Enable NACK / ERR interrupt */ +//#define EPID_PEI 2u /* Enable PID parity error interrupt */ +//#define ELINBI 3u /* Enable LIN break interrupt */ +//#define ELINSI 4u /* Enable LIN sync detection interrupt */ + +/* Multimode Interrupt Enable register and + Multimode Interrupt Identification register MASKS */ +//#define ERTOI_MASK (0x01u << 0u) /* Enable receiver timeout interrupt */ +//#define ENACKI_MASK (0x01u << 1u) /* Enable NACK / ERR interrupt */ +//#define EPID_PEI_MASK (0x01u << 2u) /* Enable PID parity error interrupt */ +//#define ELINBI_MASK (0x01u << 3u) /* Enable LIN break interrupt */ +//#define ELINSI_MASK (0x01u << 4u) /* Enable LIN sync detection interrupt */ + +typedef struct +{ + union + { + volatile const uint8_t RBR; + volatile uint8_t THR; + volatile uint8_t DLL; + uint32_t RESERVED0; + }; + + union + { + volatile uint8_t DLM; + volatile uint8_t IER; + uint32_t RESERVED1; + }; + + union + { + volatile uint8_t IIR; + volatile uint8_t FCR; + uint32_t RESERVED2; + }; + + volatile uint8_t LCR; + uint8_t RESERVED3[3]; + + volatile uint8_t MCR; + uint8_t RESERVED4[3]; + + volatile const uint8_t LSR; + uint8_t RESERVED5[3]; + + volatile const uint8_t MSR; + uint8_t RESERVED6[3]; + + volatile uint8_t SR; + uint8_t RESERVED7[7]; + +} UART_TypeDef; + + +/***************************************************************************//** + uart_instance. + There is one instance of this structure for each instance of the + microprocessor subsystem's UARTs. Instances of this structure are used to + identify a specific UART. A pointer to an initialized instance of the + uart_instance_t structure is passed as the first parameter to + UART driver functions to identify which UART should perform the + requested operation. + */ +struct uart_instance{ + /* CMSIS related defines identifying the UART hardware. */ + UART_TypeDef * hw_reg; /*!< Pointer to UART registers. */ + uint32_t baudrate; /*!< Operating baud rate. */ + uint8_t lineconfig; /*!< Line configuration parameters. */ + uint8_t status; /*!< Sticky line status. */ + + /* transmit related info (used with interrupt driven transmit): */ + const uint8_t * tx_buffer; /*!< Pointer to transmit buffer. */ + uint32_t tx_buff_size; /*!< Transmit buffer size. */ + uint32_t tx_idx; /*!< Index within transmit buffer of next byte to transmit.*/ + + /* line status interrupt handler:*/ + uart_irq_handler_t linests_handler; /*!< Pointer to user registered line status handler. */ + /* receive interrupt handler:*/ + uart_irq_handler_t rx_handler; /*!< Pointer to user registered receiver handler. */ + /* transmit interrupt handler:*/ + uart_irq_handler_t tx_handler; /*!< Pointer to user registered transmit handler. */ + /* modem status interrupt handler:*/ + uart_irq_handler_t modemsts_handler; /*!< Pointer to user registered modem status handler. */ + + + +}; + +extern uart_instance_t g_uart_0; + +static void default_tx_handler(uart_instance_t * this_uart); +static void enable_irq(const uart_instance_t * this_uart); + +static void disable_irq(const uart_instance_t * this_uart); + +static void config_baud_divisors +( + uart_instance_t * this_uart, + uint32_t baudrate +); + +/***************************************************************************//** + The UART_init() function initializes and configures the + UART with the configuration passed as a parameter. The configuration + parameters are the baud_rate which is used to generate the baud value and the + line_config which is used to specify the line configuration (bit length, + stop bits and parity). + @param this_uart + The this_uart parameter is a pointer to an uart_instance_t + structure identifying the UART hardware block that will perform + the requested function. + @param baud_rate + The baud_rate parameter specifies the baud rate. It can be specified for + common baud rates using the following defines: + - UART_110_BAUD + - UART_300_BAUD + - UART_600_BAUD + - UART_1200_BAUD + - UART_2400_BAUD + - UART_4800_BAUD + - UART_9600_BAUD + - UART_19200_BAUD + - UART_38400_BAUD + - UART_57600_BAUD + - UART_115200_BAUD + - UART_230400_BAUD + - UART_460800_BAUD + - UART_921600_BAUD + + Alternatively, any nonstandard baud rate can be specified by simply passing + the actual required baud rate as the value for this parameter. + @param line_config + The line_config parameter is the line configuration specifying the bit length, + number of stop bits and parity settings. + + This is a bitwise OR of one value from each of the following groups of + allowed values: + + One of the following to specify the transmit/receive data bit length: + - UART_DATA_5_BITS + - UART_DATA_6_BITS, + - UART_DATA_7_BITS + - UART_DATA_8_BITS + + One of the following to specify the parity setting: + - UART_NO_PARITY + - UART_EVEN_PARITY + - UART_ODD_PARITY + - UART_STICK_PARITY_0 + - UART_STICK_PARITY_1 + + One of the following to specify the number of stop bits: + - UART_ONE_STOP_BIT + - UART_ONEHALF_STOP_BIT + - UART_TWO_STOP_BITS + @return + This function does not return a value. + Example: + @code + #include "uart.h" + int main(void) + { + UART_init(&g_uart0_lo, + UART_57600_BAUD, + UART_DATA_8_BITS | UART_NO_PARITY | UART_ONE_STOP_BIT); + + return(0); + } + @endcode + */ +void +UART_init_ariane +( + uart_instance_t* this_uart, + uint32_t baud_rate, + uint8_t line_config +); + +/***************************************************************************//** + The function UART_polled_tx() is used to transmit data. It transfers the + contents of the transmitter data buffer, passed as a function parameter, into + the UART's hardware transmitter FIFO. It returns when the full content of the + transmit data buffer has been transferred to the UART's transmit FIFO. It is + safe to release or reuse the memory used as the transmitter data buffer once + this function returns. + Note: This function reads the UART's line status register (LSR) to poll + for the active state of the transmitter holding register empty (THRE) bit + before transferring data from the data buffer to the transmitter FIFO. It + transfers data to the transmitter FIFO in blocks of 16 bytes or less and + allows the FIFO to empty before transferring the next block of data. + Note: The actual transmission over the serial connection will still be + in progress when this function returns. Use the UART_get_tx_status() + function if you need to know when the transmitter is empty. + @param this_uart + The this_uart parameter is a pointer to an uart_instance_t + structure identifying the UART hardware block that will perform + the requested function. + @param pbuff + The pbuff parameter is a pointer to a buffer containing the data to + be transmitted. + @param tx_size + The tx_size parameter specifies the size, in bytes, of the data to + be transmitted. + @return + This function does not return a value. + Example: + @code + #include "uart.h" + int main(void) + { + uint8_t message[12] = "Hello World"; + UART_init(&g_uart0_lo, + UART_57600_BAUD, + SS_UART_DATA_8_BITS | UART_NO_PARITY | UART_ONE_STOP_BIT); + UART_polled_tx(&g_uart0_lo, message, sizeof(message)); + + return(0); + } + @endcode + */ +void +UART_polled_tx +( + uart_instance_t * this_uart, + const uint8_t * pbuff, + uint32_t tx_size +); + +/***************************************************************************//** + The function UART_polled_tx_string() is used to transmit a NULL ('\0') + terminated string. It transfers the text string, from the buffer starting at + the address pointed to by p_sz_string into the UART's hardware transmitter + FIFO. It returns when the complete string has been transferred to the UART's + transmit FIFO. It is safe to release or reuse the memory used as the string + buffer once this function returns. + Note: This function reads the UART's line status register (LSR) to poll + for the active state of the transmitter holding register empty (THRE) bit + before transferring data from the data buffer to the transmitter FIFO. It + transfers data to the transmitter FIFO in blocks of 16 bytes or less and + allows the FIFO to empty before transferring the next block of data. + Note: The actual transmission over the serial connection will still be + in progress when this function returns. Use the UART_get_tx_status() + function if you need to know when the transmitter is empty. + @param this_uart + The this_uart parameter is a pointer to an uart_instance_t + structure identifying the UART hardware block that will perform + the requested function. + @param p_sz_string + The p_sz_string parameter is a pointer to a buffer containing the NULL + ('\0') terminated string to be transmitted. + @return + This function does not return a value. + Example: + @code + #include "uart.h" + int main(void) + { + uint8_t message[12] = "Hello World"; + UART_init(&g_uart0_lo, + UART_57600_BAUD, + UART_DATA_8_BITS | UART_NO_PARITY | UART_ONE_STOP_BIT); + + UART_polled_tx_string(&g_uart0_lo, message); + + return(0); + } + @endcode + */ +void +UART_polled_tx_string +( + uart_instance_t * this_uart, + const uint8_t * p_sz_string +); + +/***************************************************************************//** + The function UART_irq_tx() is used to initiate an interrupt-driven + transmit. It returns immediately after making a note of the transmit buffer + location and enabling transmit interrupts both at the UART and the PolarFire + SoC Core Complex PLIC level. This function takes a pointer via the pbuff + parameter to a memory buffer containing the data to transmit. The memory + buffer specified through this pointer must remain allocated and contain the + data to transmit until the transmit completion has been detected through calls + to function UART_tx_complete(). The actual transmission over the serial + connection is still in progress until calls to the UART_tx_complete() + function indicate transmit completion. + Note: The UART_irq_tx() function enables both the transmit holding + register empty (THRE) interrupt in the UART and the UART instance + interrupt in the PolarFire SoC Core Complex PLIC as part of its implementation. + Note: The UART_irq_tx() function assigns an internal default transmit + interrupt handler function to the UART's THRE interrupt. This interrupt + handler overrides any custom interrupt handler that you may have previously + registered using the UART_set_tx_handler() function. + Note: The UART_irq_tx() function's default transmit interrupt + handler disables the UART's THRE interrupt when all of the data has + been transferred to the UART's transmit FIFO. + @param this_uart + The this_uart parameter is a pointer to an uart_instance_t + structure identifying the UART hardware block that will perform + the requested function. + @param pbuff + The pbuff parameter is a pointer to a buffer containing the data + to be transmitted. + @param tx_size + The tx_size parameter specifies the size, in bytes, of the data + to be transmitted. + @return + This function does not return a value. + Example: + @code + #include "uart.h" + int main(void) + { + uint8_t tx_buff[10] = "abcdefghi"; + + UART_init(&g_uart0_lo, + UART_57600_BAUD, + UART_DATA_8_BITS | UART_NO_PARITY | UART_ONE_STOP_BIT); + + UART_irq_tx(&g_uart0_lo, tx_buff, sizeof(tx_buff)); + + while(0 == UART_tx_complete(&g_uart0_lo)) + { + ; + } + return(0); + } + @endcode + */ +void +UART_irq_tx +( + uart_instance_t * this_uart, + const uint8_t * pbuff, + uint32_t tx_size +); + +/***************************************************************************//** + The UART_tx_complete() function is used to find out if the + interrupt-driven transmit previously initiated through a call to + UART_irq_tx() is complete. This is typically used to find out when it is + safe to reuse or release the memory buffer holding transmit data. + Note: The transfer of all of the data from the memory buffer to the UART's + transmit FIFO and the actual transmission over the serial connection are both + complete when a call to the UART_tx_complete() function indicates transmit + completion. + @param this_uart + The this_uart parameter is a pointer to an uart_instance_t + structure identifying the UART hardware block that will perform + the requested function. + @return + This function return a non-zero value if transmit has completed, otherwise + it returns zero. + Example: + See the UART_irq_tx() function for an example that uses the + UART_tx_complete() function. + */ +int8_t +UART_tx_complete +( + uart_instance_t * this_uart +); + +/***************************************************************************//** + The UART_get_rx() function reads the content of the UART receiver's FIFO + and stores it in the receive buffer that is passed via the rx_buff function + parameter. It copies either the full contents of the FIFO into the receive + buffer, or just enough data from the FIFO to fill the receive buffer, + dependent upon the size of the receive buffer passed by the buff_size + parameter. The UART_get_rx() function returns the number of bytes copied + into the receive buffer .This function is non-blocking and will return 0 + immediately if no data has been received. + Note: The UART_get_rx() function reads and accumulates the receiver + status of the UART instance before reading each byte from the receiver's + data register/FIFO. This allows the driver to maintain a sticky record of any + receiver errors that occur as the UART receives each data byte; receiver + errors would otherwise be lost after each read from the receiver's data + register. A call to the UART_get_rx_status() function returns any receiver + errors accumulated during the execution of the UART_get_rx() function. + Note: If you need to read the error status for each byte received, set + the buff_size to 1 and read the receive line error status for each byte + using the UART_get_rx_status() function. + The UART_get_rx() function can be used in polled mode, where it is called + at regular intervals to find out if any data has been received, or in + interrupt driven-mode, where it is called as part of a receive handler that is + called by the driver as a result of data being received. + Note: In interrupt driven mode you should call the UART_get_rx() + function as part of the receive handler function that you register with + the UART driver through a call to UART_set_rx_handler(). + @param this_uart + The this_uart parameter is a pointer to an uart_instance_t + structure identifying the UART hardware block that will perform + the requested function. + @param rx_buff + The rx_buff parameter is a pointer to a buffer where the received + data is copied. + @param buff_size + The buff_size parameter specifies the size of the receive buffer in bytes. + @return + This function returns the number of bytes that were copied into the + rx_buff buffer. It returns 0 if no data has been received. + Polled mode example: + @code + int main( void ) + { + uint8_t rx_buff[RX_BUFF_SIZE]; + uint32_t rx_idx = 0; + UART_init(&g_uart0_lo, + UART_57600_BAUD, + UART_DATA_8_BITS | UART_NO_PARITY | UART_ONE_STOP_BIT); + + while(1) + { + rx_size = UART_get_rx(&g_uart0_lo, rx_buff, sizeof(rx_buff)); + if(rx_size > 0) + { + process_rx_data(rx_buff, rx_size); + } + task_a(); + task_b(); + } + return 0; + } + @endcode + Interrupt driven example: + @code + int main( void ) + { + UART_init(&g_uart1, + UART_57600_BAUD, + UART_DATA_8_BITS | UART_NO_PARITY | UART_ONE_STOP_BIT); + + UART_set_rx_handler(&g_uart1, + uart1_rx_handler, + UART_FIFO_SINGLE_BYTE); + + while(1) + { + task_a(); + task_b(); + } + return 0; + } + void uart1_rx_handler(uart_instance_t * this_uart) + { + uint8_t rx_buff[RX_BUFF_SIZE]; + uint32_t rx_idx = 0; + rx_size = UART_get_rx(this_uart, rx_buff, sizeof(rx_buff)); + process_rx_data(rx_buff, rx_size); + } + @endcode + */ +size_t +UART_get_rx +( + uart_instance_t * this_uart, + uint8_t * rx_buff, + size_t buff_size +); + +/***************************************************************************//** + The UART_set_rx_handler() function is used to register a receive handler + function that is called by the driver when a UART receive data available (RDA) + interrupt occurs. You must create and register the receive handler function + to suit your application and it must include a call to the UART_get_rx() + function to actually read the received data. + Note: The UART_set_rx_handler() function enables both the RDA + interrupt in the UART instance. It also enables the corresponding + UART instance interrupt in the PolarFire SoC Core Complex PLIC as part + of its implementation. + Note: You can disable the RDA interrupt when required by calling the + UART_disable_irq() function. This is your choice and is dependent upon + your application. + + @param this_uart + The this_uart parameter is a pointer to an uart_instance_t + structure identifying the UART hardware block that will perform + the requested function. + @param handler + The handler parameter is a pointer to a receive interrupt handler function + provided by your application that will be called as a result of a UART RDA + interrupt. This handler function must be of type uart_irq_handler_t. + @param trigger_level + The trigger_level parameter is the receive FIFO trigger level. This + specifies the number of bytes that must be received before the UART + triggers an RDA interrupt. + @return + This function does not return a value. + Example: + @code + #include "uart.h" + #define RX_BUFF_SIZE 64 + uint8_t g_rx_buff[RX_BUFF_SIZE]; + void uart0_rx_handler(uart_instance_t * this_uart) + { + UART_get_rx(this_uart, &g_rx_buff[g_rx_idx], sizeof(g_rx_buff)); + } + int main(void) + { + UART_init(&g_uart0_lo, + UART_57600_BAUD, + UART_DATA_8_BITS | UART_NO_PARITY | UART_ONE_STOP_BIT); + + UART_set_rx_handler(&g_uart0_lo, + uart0_rx_handler, + UART_FIFO_SINGLE_BYTE); + + while(1) + { + ; + } + return(0); + } + @endcode + */ +void +UART_set_rx_handler +( + uart_instance_t * this_uart, + uart_irq_handler_t handler, + uart_rx_trig_level_t trigger_level +); + + +#endif /*__UART_H_*/ + diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/CMakeLists.txt b/ports/risc-v32/gnu/example_build/qemu_virt/CMakeLists.txt new file mode 100644 index 00000000..43e757ac --- /dev/null +++ b/ports/risc-v32/gnu/example_build/qemu_virt/CMakeLists.txt @@ -0,0 +1,45 @@ +set(QEMU_DEMO_DIR ${CMAKE_CURRENT_LIST_DIR}) + +add_executable(kernel.elf EXCLUDE_FROM_ALL + ${QEMU_DEMO_DIR}/demo_threadx.c + ${QEMU_DEMO_DIR}/entry.S + ${QEMU_DEMO_DIR}/uart.c + ${QEMU_DEMO_DIR}/plic.c + ${QEMU_DEMO_DIR}/hwtimer.c + ${QEMU_DEMO_DIR}/trap.c + ${QEMU_DEMO_DIR}/board.c + ${QEMU_DEMO_DIR}/tx_initialize_low_level.S +) + +target_link_libraries(kernel.elf PRIVATE threadx) + +target_include_directories(kernel.elf PRIVATE + ${CMAKE_SOURCE_DIR}/common/inc + ${CMAKE_SOURCE_DIR}/ports/${THREADX_ARCH}/${THREADX_TOOLCHAIN}/inc + ${QEMU_DEMO_DIR} +) + +target_link_options(kernel.elf PRIVATE + -T${QEMU_DEMO_DIR}/link.lds + -nostartfiles + -Wl,-Map=kernel.map +) + +# QEMU/GDB functional test runner. Optional: skipped silently if the +# host has no Python 3 interpreter on PATH. +find_package(Python3 COMPONENTS Interpreter) +if(Python3_FOUND) + add_custom_target(check-functional-riscv32 + COMMAND ${Python3_EXECUTABLE} + ${QEMU_DEMO_DIR}/test/threadx_test_tx_gnu_riscv32_qemu.py + --elf $<TARGET_FILE:kernel.elf> + --qemu qemu-system-riscv32 + --gdb gdb + DEPENDS kernel.elf + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Running RISC-V32 QEMU/GDB functional test runner..." + ) +else() + message(STATUS + "Python3 not found; check-functional-riscv32 target unavailable.") +endif() diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/build_libthreadx.sh b/ports/risc-v32/gnu/example_build/qemu_virt/build_libthreadx.sh index ba6b4022..d0e092da 100755 --- a/ports/risc-v32/gnu/example_build/qemu_virt/build_libthreadx.sh +++ b/ports/risc-v32/gnu/example_build/qemu_virt/build_libthreadx.sh @@ -1,5 +1,35 @@ #!/bin/bash +############################################################################## +# Copyright (c) 2024 Microsoft Corporation +# Copyright (c) 2026 Eclipse ThreadX contributors +# +# This program and the accompanying materials are made available under the +# terms of the MIT License which is available at +# https://opensource.org/licenses/MIT. +# +# SPDX-License-Identifier: MIT +############################################################################## + +printf "y\n" | rm -rf ../../../../../build/ +rm -f kernel.elf + pushd ../../../../../ cmake -Bbuild -GNinja -DCMAKE_TOOLCHAIN_FILE=cmake/riscv32_gnu.cmake . cmake --build ./build/ -popd
\ No newline at end of file +popd + +riscv32-unknown-elf-gcc \ + -march=rv32gc -mabi=ilp32d \ + -mcmodel=medany -O0 -g3 -Wall \ + -ffunction-sections -fdata-sections \ + -I../../../../../common/inc \ + -I../../inc \ + entry.S \ + tx_initialize_low_level.S \ + board.c uart.c hwtimer.c plic.c trap.c demo_threadx.c \ + -L../../../../../build -lthreadx \ + -T link.lds -nostartfiles \ + -o kernel.elf + + +qemu-system-riscv32 -nographic -smp 1 -bios none -m 128M -machine virt -kernel kernel.elf diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/csr.h b/ports/risc-v32/gnu/example_build/qemu_virt/csr.h index 9f986bac..b5df6f12 100644..120000 --- a/ports/risc-v32/gnu/example_build/qemu_virt/csr.h +++ b/ports/risc-v32/gnu/example_build/qemu_virt/csr.h @@ -1,343 +1 @@ -/*************************************************************************** - * Copyright (c) 2025 10xEngineers - * - * This program and the accompanying materials are made available under the - * terms of the MIT License which is available at - * https://opensource.org/licenses/MIT. - * - * SPDX-License-Identifier: MIT - **************************************************************************/ - - -#ifndef RISCV_CSR_H -#define RISCV_CSR_H - - -// Machine Status Register, mstatus -#define MSTATUS_MPP_MASK (3L << 11) // previous mode. -#define MSTATUS_MPP_M (3L << 11) -#define MSTATUS_MPP_S (1L << 11) -#define MSTATUS_MPP_U (0L << 11) -#define MSTATUS_MIE (1L << 3) // machine-mode interrupt enable. -#define MSTATUS_MPIE (1L << 7) -#define MSTATUS_FS (1L << 13) - -// Machine-mode Interrupt Enable -#define MIE_MTIE (1L << 7) -#define MIE_MSIE (1L << 3) -#define MIE_MEIE (1L << 11) -#define MIE_STIE (1L << 5) // supervisor timer -#define MIE_SSIE (1L << 1) -#define MIE_SEIE (1L << 9) - -// Supervisor Status Register, sstatus -#define SSTATUS_SPP (1L << 8) // Previous mode, 1=Supervisor, 0=User -#define SSTATUS_SPIE (1L << 5) // Supervisor Previous Interrupt Enable -#define SSTATUS_UPIE (1L << 4) // User Previous Interrupt Enable -#define SSTATUS_SIE (1L << 1) // Supervisor Interrupt Enable -#define SSTATUS_UIE (1L << 0) // User Interrupt Enable -#define SSTATUS_SPIE (1L << 5) -#define SSTATUS_UPIE (1L << 4) - -// Supervisor Interrupt Enable -#define SIE_SEIE (1L << 9) // external -#define SIE_STIE (1L << 5) // timer -#define SIE_SSIE (1L << 1) // software - -#ifndef __ASSEMBLER__ - -#include <stdint.h> - -static inline uint32_t riscv_get_core() -{ - uint32_t x; - asm volatile("csrr %0, mhartid" : "=r" (x) ); - return x; -} - -static inline uint32_t riscv_get_mstatus() -{ - uint32_t x; - asm volatile("csrr %0, mstatus" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_mstatus(uint32_t x) -{ - asm volatile("csrw mstatus, %0" : : "r" (x)); -} - -static inline void riscv_writ_mepc(uint32_t x) -{ - asm volatile("csrw mepc, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_sstatus() -{ - uint32_t x; - asm volatile("csrr %0, sstatus" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_sstatus(uint32_t x) -{ - asm volatile("csrw sstatus, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_sip() -{ - uint32_t x; - asm volatile("csrr %0, sip" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_sip(uint32_t x) -{ - asm volatile("csrw sip, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_sie() -{ - uint32_t x; - asm volatile("csrr %0, sie" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_sie(uint32_t x) -{ - asm volatile("csrw sie, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_mie() -{ - uint32_t x; - asm volatile("csrr %0, mie" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_mie(uint32_t x) -{ - asm volatile("csrw mie, %0" : : "r" (x)); -} - -static inline void riscv_writ_sepc(uint32_t x) -{ - asm volatile("csrw sepc, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_sepc() -{ - uint32_t x; - asm volatile("csrr %0, sepc" : "=r" (x) ); - return x; -} - -static inline uint32_t riscv_get_medeleg() -{ - uint32_t x; - asm volatile("csrr %0, medeleg" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_medeleg(uint32_t x) -{ - asm volatile("csrw medeleg, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_mideleg() -{ - uint32_t x; - asm volatile("csrr %0, mideleg" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_mideleg(uint32_t x) -{ - asm volatile("csrw mideleg, %0" : : "r" (x)); -} - -static inline void riscv_writ_stvec(uint32_t x) -{ - asm volatile("csrw stvec, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_stvec() -{ - uint32_t x; - asm volatile("csrr %0, stvec" : "=r" (x) ); - return x; -} - -static inline uint32_t riscv_get_stimecmp() -{ - uint32_t x; - asm volatile("csrr %0, 0x14d" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_stimecmp(uint32_t x) -{ - asm volatile("csrw 0x14d, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_menvcfg() -{ - uint32_t x; - asm volatile("csrr %0, 0x30a" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_menvcfg(uint32_t x) -{ - asm volatile("csrw 0x30a, %0" : : "r" (x)); -} - -static inline void riscv_writ_pmpcfg0(uint32_t x) -{ - asm volatile("csrw pmpcfg0, %0" : : "r" (x)); -} - -static inline void riscv_writ_pmpaddr0(uint32_t x) -{ - asm volatile("csrw pmpaddr0, %0" : : "r" (x)); -} - -static inline void riscv_writ_satp(uint32_t x) -{ - asm volatile("csrw satp, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_satp() -{ - uint32_t x; - asm volatile("csrr %0, satp" : "=r" (x) ); - return x; -} - -static inline uint32_t riscv_get_scause() -{ - uint32_t x; - asm volatile("csrr %0, scause" : "=r" (x) ); - return x; -} - -static inline uint32_t riscv_get_stval() -{ - uint32_t x; - asm volatile("csrr %0, stval" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_mcounteren(uint32_t x) -{ - asm volatile("csrw mcounteren, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_mcounteren() -{ - uint32_t x; - asm volatile("csrr %0, mcounteren" : "=r" (x) ); - return x; -} - -static inline uint32_t riscv_get_time() -{ - uint32_t x; - asm volatile("csrr %0, time" : "=r" (x) ); - return x; -} - -static inline void riscv_sintr_on() -{ - uint32_t sstatus = riscv_get_sstatus(); - sstatus |= SSTATUS_SIE; - riscv_writ_sstatus(sstatus); -} - -static inline void riscv_sintr_off() -{ - uint32_t sstatus = riscv_get_sstatus(); - sstatus &= (~SSTATUS_SIE); - riscv_writ_sstatus(sstatus); -} - -static inline int riscv_sintr_get() -{ - uint32_t x = riscv_get_sstatus(); - return (x & SSTATUS_SIE) != 0; -} - -static inline void riscv_sintr_restore(int x) -{ - if(x) - riscv_sintr_on(); - else - riscv_sintr_off(); -} - -static inline void riscv_mintr_on() -{ - uint32_t mstatus = riscv_get_mstatus(); - mstatus |= MSTATUS_MIE; - riscv_writ_mstatus(mstatus); -} - -static inline void riscv_mintr_off() -{ - uint32_t mstatus = riscv_get_mstatus(); - mstatus &= (~MSTATUS_MIE); - riscv_writ_mstatus(mstatus); -} - -static inline int riscv_mintr_get() -{ - uint32_t x = riscv_get_mstatus(); - return (x & MSTATUS_MIE) != 0; -} - -static inline void riscv_mintr_restore(int x) -{ - if(x) - riscv_mintr_on(); - else - riscv_mintr_off(); -} - -static inline uint32_t riscv_get_sp() -{ - uint32_t x; - asm volatile("mv %0, sp" : "=r" (x) ); - return x; -} - -// read and write tp, the thread pointer, which xv6 uses to hold -// this core's hartid (core number), the index into cpus[]. -static inline uint32_t riscv_get_tp() -{ - uint32_t x; - asm volatile("mv %0, tp" : "=r" (x) ); - return x; -} - -static inline void riscv_writ_tp(uint32_t x) -{ - asm volatile("mv tp, %0" : : "r" (x)); -} - -static inline uint32_t riscv_get_ra() -{ - uint32_t x; - asm volatile("mv %0, ra" : "=r" (x) ); - return x; -} - -// flush the TLB. -static inline void sfence_vma() -{ - // the zero, zero means flush all TLB entries. - asm volatile("sfence.vma zero, zero"); -} - -#endif // __ASSEMBLER__ - -#endif +../../../../risc-v_common/inc/csr.h
\ No newline at end of file diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/demo_threadx.c b/ports/risc-v32/gnu/example_build/qemu_virt/demo_threadx.c index f21dbb26..8288e749 100644 --- a/ports/risc-v32/gnu/example_build/qemu_virt/demo_threadx.c +++ b/ports/risc-v32/gnu/example_build/qemu_virt/demo_threadx.c @@ -1,3 +1,14 @@ +/***************************************************************************/ +/* Copyright (c) 2024 Microsoft Corporation */ +/* Copyright (c) 2026 Eclipse ThreadX contributors */ +/* */ +/* This program and the accompanying materials are made available under */ +/* the terms of the MIT License which is available at */ +/* https://opensource.org/licenses/MIT. */ +/* */ +/* SPDX-License-Identifier: MIT */ +/***************************************************************************/ + /* This is a small demo of the high-performance ThreadX kernel. It includes examples of eight threads of different priorities, using a message queue, semaphore, mutex, event flags group, byte pool, and block pool. */ @@ -9,6 +20,7 @@ #define DEMO_BYTE_POOL_SIZE 9120 #define DEMO_BLOCK_POOL_SIZE 100 #define DEMO_QUEUE_SIZE 100 +float fpu_test_val = 0.0f; char *_to_str(ULONG val) { @@ -201,7 +213,7 @@ UINT status; thread_0_counter++; /* Sleep for 10 ticks. */ - tx_thread_sleep(10); + tx_thread_sleep(1); /* Set event flag 0 to wakeup thread 5. */ status = tx_event_flags_set(&event_flags_0, 0x1, TX_OR); @@ -363,6 +375,8 @@ UINT status; if (status != TX_SUCCESS) break; + /* FPU Test */ + fpu_test_val += 1.1f; /* Get the mutex again with suspension. This shows that an owning thread may retrieve the mutex it owns multiple times. */ diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/entry.S b/ports/risc-v32/gnu/example_build/qemu_virt/entry.S new file mode 100644 index 00000000..598a072e --- /dev/null +++ b/ports/risc-v32/gnu/example_build/qemu_virt/entry.S @@ -0,0 +1,72 @@ +/***************************************************************************/ +/* Copyright (c) 2024 Microsoft Corporation */ +/* Copyright (c) 2026 Eclipse ThreadX contributors */ +/* */ +/* This program and the accompanying materials are made available under */ +/* the terms of the MIT License which is available at */ +/* https://opensource.org/licenses/MIT. */ +/* */ +/* SPDX-License-Identifier: MIT */ +/***************************************************************************/ + + +.section .text.boot, "ax" +.align 4 +.global _start +.extern main +.extern _sysstack_start +.extern _bss_start +.extern _bss_end +_start: + csrr t0, mhartid + bne t0, zero, 1f + li x1, 0 + li x2, 0 +.option push +.option norelax + la gp, __global_pointer$ /* x3 = gp; norelax keeps this load absolute */ +.option pop + li x4, 0 + li x5, 0 + li x6, 0 + li x7, 0 + li x8, 0 + li x9, 0 + li x10, 0 + li x11, 0 + li x12, 0 + li x13, 0 + li x14, 0 + li x15, 0 + li x16, 0 + li x17, 0 + li x18, 0 + li x19, 0 + li x20, 0 + li x21, 0 + li x22, 0 + li x23, 0 + li x24, 0 + li x25, 0 + li x26, 0 + li x27, 0 + li x28, 0 + li x29, 0 + li x30, 0 + li x31, 0 + la t0, _sysstack_start + li t1, 0x1000 + add sp, t0, t1 + la t0, _bss_start + la t1, _bss_end +_bss_clean_start: + bgeu t0, t1, _bss_clean_end + sb zero, 0(t0) + addi t0, t0, 1 + j _bss_clean_start +_bss_clean_end: + call main +1: + /* todo smp */ + wfi + j 1b diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/entry.s b/ports/risc-v32/gnu/example_build/qemu_virt/entry.s deleted file mode 100644 index 9b202ca1..00000000 --- a/ports/risc-v32/gnu/example_build/qemu_virt/entry.s +++ /dev/null @@ -1,58 +0,0 @@ - -.section .text -.align 4 -.global _start -.extern main -.extern _sysstack_start -.extern _bss_start -.extern _bss_end -_start: - csrr t0, mhartid - bne t0, zero, 1f - li x1, 0 - li x2, 0 - li x3, 0 - li x4, 0 - li x5, 0 - li x6, 0 - li x7, 0 - li x8, 0 - li x9, 0 - li x10, 0 - li x11, 0 - li x12, 0 - li x13, 0 - li x14, 0 - li x15, 0 - li x16, 0 - li x17, 0 - li x18, 0 - li x19, 0 - li x20, 0 - li x21, 0 - li x22, 0 - li x23, 0 - li x24, 0 - li x25, 0 - li x26, 0 - li x27, 0 - li x28, 0 - li x29, 0 - li x30, 0 - li x31, 0 - la t0, _sysstack_start - li t1, 0x1000 - add sp, t0, t1 - la t0, _bss_start - la t1, _bss_end -_bss_clean_start: - bgeu t0, t1, _bss_clean_end - sb zero, 0(t0) - addi t0, t0, 1 - j _bss_clean_start -_bss_clean_end: - call main -1: - /* todo smp */ - wfi - j 1b diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/link.lds b/ports/risc-v32/gnu/example_build/qemu_virt/link.lds index 522f90d9..d2ef5f28 100644 --- a/ports/risc-v32/gnu/example_build/qemu_virt/link.lds +++ b/ports/risc-v32/gnu/example_build/qemu_virt/link.lds @@ -10,6 +10,7 @@ SECTIONS . = 0x80000000; .text : { + KEEP(*(.text.boot)) /* entry.s _start — must be first at 0x80000000 */ *(.text .text.*) . = ALIGN(0x1000); PROVIDE(etext = .); @@ -24,6 +25,9 @@ SECTIONS .data : { . = ALIGN(16); + /* Centre __global_pointer$ in the small-data window so the +/-2 KiB + reach of GP-relative addressing covers the .sdata/.sbss area. */ + PROVIDE( __global_pointer$ = . + 0x800 ); *(.sdata .sdata.*) /* do not need to distinguish this from .data */ . = ALIGN(16); *(.data .data.*) diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/plic.c b/ports/risc-v32/gnu/example_build/qemu_virt/plic.c index 01e5c71a..b247f4fe 100644..120000 --- a/ports/risc-v32/gnu/example_build/qemu_virt/plic.c +++ b/ports/risc-v32/gnu/example_build/qemu_virt/plic.c @@ -1,72 +1 @@ -#include "plic.h" -#include <stddef.h> -irq_callback callbacks[MAX_CALLBACK_NUM]; - -void plic_irq_enable(int irqno) -{ - int hart = riscv_get_core(); - *(uint32_t*)PLIC_MENABLE(hart) = (*(uint32_t*)PLIC_MENABLE(hart) | (1 << irqno)); - return; -} - -void plic_irq_disable(int irqno) -{ - int hart = riscv_get_core(); - *(uint32_t*)PLIC_MENABLE(hart) = (*(uint32_t*)PLIC_MENABLE(hart) & (~(1 << irqno))); - return; -} - -void plic_prio_set(int irqno, int prio) -{ - PLIC_SET_PRIO(irqno, prio); -} - -int plic_prio_get(int irqno) -{ - return PLIC_GET_PRIO(irqno); -} - -int plic_register_callback(int irqno, irq_callback callback) -{ - if(!(irqno >=0 && irqno < MAX_CALLBACK_NUM)) - return -1; - callbacks[irqno] = callback; - return 0; -} - -int plic_unregister_callback(int irqno) -{ - return plic_register_callback(irqno, NULL); -} - -int plic_init(void) -{ - for(int i=0;i<MAX_CALLBACK_NUM;i++) - { - callbacks[i] = NULL; - } - return 0; -} - -int plic_claim(void) -{ - int hart = riscv_get_core(); - return (*(uint32_t*)PLIC_MCLAIM(hart)); -} - -void plic_complete(int irqno) -{ - int hart = riscv_get_core(); - *(uint32_t*)(PLIC_MCOMPLETE(hart)) = (uint32_t)irqno; - return; -} - -int plic_irq_intr(void) -{ - int ret = -1; - int irqno = plic_claim(); - if(callbacks[irqno] != NULL) - ret = (callbacks[irqno])(irqno); - plic_complete(irqno); - return ret; -} +../../../../risc-v_common/example_build/plic/plic.c
\ No newline at end of file diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/plic.h b/ports/risc-v32/gnu/example_build/qemu_virt/plic.h index f505e608..f776a3d2 100644..120000 --- a/ports/risc-v32/gnu/example_build/qemu_virt/plic.h +++ b/ports/risc-v32/gnu/example_build/qemu_virt/plic.h @@ -1,49 +1 @@ -/*************************************************************************** - * Copyright (c) 2025 10xEngineers - * - * This program and the accompanying materials are made available under the - * terms of the MIT License which is available at - * https://opensource.org/licenses/MIT. - * - * SPDX-License-Identifier: MIT - **************************************************************************/ - -#ifndef RISCV_PLIC_H -#define RISCV_PLIC_H - -#include "csr.h" -#include <stdint.h> - -#define PLIC 0x0c000000L -#define PLIC_PRIORITY (PLIC + 0x0) -#define PLIC_PENDING (PLIC + 0x1000) -#define PLIC_MENABLE(hart) (PLIC + 0x2000 + (hart)*0x100) -#define PLIC_SENABLE(hart) (PLIC + 0x2080 + (hart)*0x100) -#define PLIC_MPRIORITY(hart) (PLIC + 0x200000 + (hart)*0x2000) -#define PLIC_SPRIORITY(hart) (PLIC + 0x201000 + (hart)*0x2000) -#define PLIC_MCLAIM(hart) (PLIC + 0x200004 + (hart)*0x2000) -#define PLIC_SCLAIM(hart) (PLIC + 0x201004 + (hart)*0x2000) -#define PLIC_MCOMPLETE(hart) (PLIC + 0x200004 + (hart)*0x2000) -#define PLIC_SCOMPLETE(hart) (PLIC + 0x201004 + (hart)*0x2000) - - -#define PLIC_GET_PRIO(irqno) (*(uint32_t *)(PLIC_PRIORITY + (irqno)*4)) -#define PLIC_SET_PRIO(irqno, prio) (*(uint32_t *)(PLIC_PRIORITY + (irqno)*4) = (prio)) - -#define MAX_CALLBACK_NUM 128 -typedef int (*irq_callback)(int irqno); - -void plic_irq_enable(int irqno); -void plic_irq_disable(int irqno); -int plic_prio_get(int irqno); -void plic_prio_set(int irqno, int prio); -int plic_register_callback(int irqno, irq_callback callback); -int plic_unregister_callback(int irqno); -int plic_init(void); -int plic_claim(void); -void plic_complete(int irqno); - -int plic_irq_intr(void); - -#endif - +../../../../risc-v_common/example_build/plic/plic.h
\ No newline at end of file diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/test/threadx_test_tx_gnu_riscv32_qemu.py b/ports/risc-v32/gnu/example_build/qemu_virt/test/threadx_test_tx_gnu_riscv32_qemu.py new file mode 100644 index 00000000..6123cd73 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/qemu_virt/test/threadx_test_tx_gnu_riscv32_qemu.py @@ -0,0 +1,295 @@ +############################################################################## +# Copyright (c) 2024 Microsoft Corporation +# Copyright (c) 2026 Eclipse ThreadX contributors +# +# This program and the accompanying materials are made available under the +# terms of the MIT License which is available at +# https://opensource.org/licenses/MIT. +# +# SPDX-License-Identifier: MIT +############################################################################## + +import subprocess +import sys +import os +import argparse +import socket +import select + +def print_content(content): + """Prints content using os.write to handle non-blocking stdout robustly.""" + try: + msg = f"{content}\n".encode('utf-8') + total_len = len(msg) + written = 0 + fd = sys.stdout.fileno() + while written < total_len: + try: + n = os.write(fd, msg[written:]) + written += n + except BlockingIOError: + select.select([], [fd], []) + except Exception: + pass + +def get_free_port(): + """Finds a free TCP port.""" + with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: + s.bind(('', 0)) + return s.getsockname()[1] + +def run_qemu_test(elf_path, qemu_bin, gdb_bin): + """ + Runs a test cycle using QEMU and GDB. + """ + print(f"Testing ELF: {elf_path}") + print(f"QEMU: {qemu_bin}") + print(f"GDB: {gdb_bin}") + + # Find a free port for GDB connection + gdb_port = get_free_port() + print(f"Using GDB port: {gdb_port}") + + # 1. Start QEMU in the background + qemu_cmd = [ + qemu_bin, + "-M", "virt", + "-nographic", + "-bios", "none", # Disable default OpenSBI to avoid overlap at 0x80000000 + "-kernel", elf_path, + "-gdb", f"tcp::{gdb_port}", "-S", + "-monitor", "none", # Disable monitor to avoid clutter + "-serial", "stdio" # Redirect serial output to stdio so we can see it + ] + + print(f"Starting QEMU: {' '.join(qemu_cmd)}") + qemu_process = subprocess.Popen( + qemu_cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) + + if qemu_process.poll() is not None: + print("QEMU failed to start.") + print(qemu_process.stderr.read()) + return False + + # 2. Create a GDB command file + # We use a defined command for the timer interrupt to perform the check automatically + gdb_cmds = """ +file {elf} +target remote :{port} +set pagination off +set confirm off + +# Setup Breakpoints +break tx_application_define +break thread_0_entry +break thread_6_and_7_entry +break _tx_timer_interrupt + +# Execute to Application Definition +continue + +# Inspect mstatus once thread_0 has started (FS bits should be observable; +# kept as a smoke check, the lazy-save logic itself is targeted by a +# follow-up PR). +continue +print/x $mstatus + +# Verify FPU Logic and Register State exercised by thread_6/7 +continue +finish +step +step +step +print/x $mstatus +info registers float +print fpu_test_val + +# Await Timer Interrupt +continue +print "Hit Timer Interrupt" + +# Verify MEPC Integrity - Save State +print/x $mepc +set $saved_pc = $mepc + +# Verify System Timer Before ISR +set $clock_before = _tx_timer_system_clock +print $clock_before + +# Configure Time-Slice Test Conditions +set _tx_timer_time_slice = 1 +set _tx_timer_expired_time_slice = 0 +set $ts_handler_called = 0 + +# Set Breakpoint at Time-Slice Handler with Auto-Continue +tbreak _tx_thread_time_slice +commands + set $ts_handler_called = 1 + continue +end + +# Set Breakpoint at ISR Return Address +set $ret_addr = $ra +tbreak *$ret_addr +continue + +# Verify Time-Slice Handler Was Called +if $ts_handler_called == 1 + print "SUCCESS: Time-slice handler called." +else + print "FAILURE: Time-slice handler NOT called." +end + +# Verify System Timer Increment (Monotonicity) +set $clock_after = _tx_timer_system_clock +print $clock_after + +if $clock_after > $clock_before + print "SUCCESS: System timer incremented." +else + print "FAILURE: System timer did not increment." +end + +# Verify Preemption Logic (Thread Priority) +# +# We are now stopped at the return address from _tx_timer_interrupt, +# after _tx_thread_time_slice has had a chance to update +# _tx_thread_execute_ptr but before trap_handler returns into +# _tx_thread_context_restore. At this point, a pending preemption is +# observable directly by comparing current_ptr (interrupted thread) +# and execute_ptr (thread chosen by the scheduler). +set $curr_ptr = _tx_thread_current_ptr +set $exec_ptr = _tx_thread_execute_ptr +if $curr_ptr != 0 && $exec_ptr != 0 + set $curr_prio = $curr_ptr->tx_thread_priority + set $exec_prio = $exec_ptr->tx_thread_priority + printf "PREEMPT_CHECK current_prio=%d execute_prio=%d\\n", $curr_prio, $exec_prio + if $exec_prio < $curr_prio + printf "PREEMPT_VERIFIED_OK\\n" + else + printf "PREEMPT_VERIFIED_FAIL_NOT_OBSERVED\\n" + end +else + printf "PREEMPT_VERIFIED_FAIL_NULL\\n" +end + +quit +""".format(port=gdb_port, elf=elf_path) + + gdb_cmd_file = "test_cmds.gdb" + with open(gdb_cmd_file, "w") as f: + f.write(gdb_cmds) + + # 3. Run GDB + gdb_cmd = [ + gdb_bin, + "--batch", + "-x", gdb_cmd_file + ] + + print_content(f"Starting GDB: {' '.join(gdb_cmd)}") + + # Cap the GDB session to 30 s so a wedged batch script (e.g. a + # `continue` that never hits its breakpoint) cannot hang CI. + GDB_TIMEOUT_S = 30 + + try: + gdb_process = subprocess.run( + gdb_cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + timeout=GDB_TIMEOUT_S, + ) + + print_content("GDB Output:") + print_content(gdb_process.stdout) + if gdb_process.stderr: + print_content("GDB Error Output:") + print_content(gdb_process.stderr) + + except subprocess.TimeoutExpired as e: + print_content( + f"FAILURE: GDB session exceeded {GDB_TIMEOUT_S}s timeout; " + "likely stuck on a `continue` that never matched a breakpoint." + ) + if e.stdout: + print_content("GDB Output (partial):") + print_content(e.stdout if isinstance(e.stdout, str) + else e.stdout.decode(errors='replace')) + if e.stderr: + print_content("GDB Error Output (partial):") + print_content(e.stderr if isinstance(e.stderr, str) + else e.stderr.decode(errors='replace')) + return False + + except Exception as e: + print_content(f"An error occurred during test execution: {e}") + return False + + finally: + # 4. Clean up + print_content("Stopping QEMU...") + qemu_process.terminate() + try: + qemu_process.wait(timeout=2) + except subprocess.TimeoutExpired: + print_content("QEMU did not terminate gracefully, killing it forcefullly.") + qemu_process.kill() + + # Verify results + stdout = gdb_process.stdout + timer_hit = "Breakpoint 4, _tx_timer_interrupt" in stdout + fpu_verified = False + preemption_verified = "PREEMPT_VERIFIED_OK" in stdout + + if "Breakpoint 3, thread_6_and_7_entry" in stdout: + if "1.10" in stdout or "fpu_test_val" in stdout: + print_content("SUCCESS: FPU instructions executed and registers inspected.") + fpu_verified = True + else: + print_content("FAILURE: Hit thread, but failed to inspect FPU. Output does not contain expected value.") + + if timer_hit: + print_content("SUCCESS: Timer Interrupt verified! Hit _tx_timer_interrupt.") + else: + print_content("FAILURE: Did not hit timer interrupt.") + + if preemption_verified: + print_content("SUCCESS: Preemption verified (higher-priority thread " + "preempted a lower-priority one).") + else: + if "PREEMPT_VERIFIED_FAIL_INVERTED" in stdout: + print_content("FAILURE: Preemption inverted -- lower priority " + "thread scheduled over higher priority one.") + elif "PREEMPT_VERIFIED_FAIL_NULL" in stdout: + print_content("FAILURE: Preemption check saw NULL thread pointers.") + elif "PREEMPT_VERIFIED_FAIL_NOT_OBSERVED" in stdout: + print_content("FAILURE: Preemption was not observed within the " + "loop budget.") + else: + print_content("FAILURE: Preemption check did not run to completion.") + + if timer_hit and fpu_verified and preemption_verified: + return True + else: + return False + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Run ThreadX QEMU/GDB Test") + parser.add_argument("--elf", required=True, help="Path to the kernel ELF file") + parser.add_argument("--qemu", default="qemu-system-riscv32", help="Path to QEMU binary") + parser.add_argument("--gdb", default="riscv-none-elf-gdb", help="Path to GDB binary") + + args = parser.parse_args() + + success = run_qemu_test(args.elf, args.qemu, args.gdb) + + if success: + sys.exit(0) + else: + sys.exit(1) diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/trap.c b/ports/risc-v32/gnu/example_build/qemu_virt/trap.c index a2733e02..bdce3305 100644..120000 --- a/ports/risc-v32/gnu/example_build/qemu_virt/trap.c +++ b/ports/risc-v32/gnu/example_build/qemu_virt/trap.c @@ -1,67 +1 @@ -#include "csr.h" -#include <stdint.h> -#include "uart.h" -#include "hwtimer.h" -#include "plic.h" -#include <tx_port.h> -#include <tx_api.h> - -#define OS_IS_INTERUPT(mcause) (mcause & 0x80000000u) -#define OS_IS_EXCEPTION(mcause) (~(OS_IS_INTERUPT)) -#define OS_IS_TICK_INT(mcause) (mcause == 0x80000007u) -#define OS_IS_SOFT_INT(mcause) (mcause == 0x80000003u) -#define OS_IS_EXT_INT(mcause) (mcause == 0x8000000bu) -#define OS_IS_TRAP_USER(mcause) (mcause == 0x0000000bu) -extern void _tx_timer_interrupt(void); - -extern int uart_putc(int ch); - -static void print_hex(uintptr_t val) -{ - char digits[] = "0123456789ABCDEF"; - uart_putc('0'); - uart_putc('x'); - for(int i = (sizeof(uintptr_t)*2) - 1; i >= 0; i--) { - int d = (val >> (i*4)) & 0xF; - uart_putc(digits[d]); - } - uart_putc('\n'); -} - -void trap_handler(uintptr_t mcause, uintptr_t mepc, uintptr_t mtval) -{ - // uart_puts("DEBUG : threadx/ports/risc-v32/gnu/example_build/qemu_virt/trap.c, trap_handler\n"); - if(OS_IS_INTERUPT(mcause)) - { - if(OS_IS_TICK_INT(mcause)) - { - hwtimer_handler(); - _tx_timer_interrupt(); - } - else if(OS_IS_EXT_INT(mcause)) - { - int ret = plic_irq_intr(); - if(ret) - { - puts("[INTERRUPT]: handler irq error!"); - while(1) ; - } - } - else - { - puts("[INTERRUPT]: now can't deal with the interrupt!"); - while(1) ; - } - } - else - { - puts("[EXCEPTION] : Unkown Error!!"); - puts("mcause:"); - print_hex(mcause); - puts("mepc:"); - print_hex(mepc); - puts("mtval:"); - print_hex(mtval); - while(1) ; - } -} +../../../../risc-v_common/example_build/trap/trap_qemu.c
\ No newline at end of file diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/uart.c b/ports/risc-v32/gnu/example_build/qemu_virt/uart.c index a175b7d2..1666c8c8 100644..120000 --- a/ports/risc-v32/gnu/example_build/qemu_virt/uart.c +++ b/ports/risc-v32/gnu/example_build/qemu_virt/uart.c @@ -1,102 +1 @@ -#include "uart.h" -#include "csr.h" -#include "plic.h" -#include <stdint.h> - -// the UART control registers are memory-mapped -// at address UART0. this macro returns the -// address of one of the registers. -#define Reg(reg) ((volatile unsigned char *)(UART0 + (reg))) - -// the UART control registers. -// some have different meanings for -// read vs write. -// see http://byterunner.com/16550.html -#define RHR 0 // receive holding register (for input bytes) -#define THR 0 // transmit holding register (for output bytes) -#define IER 1 // interrupt enable register -#define IER_RX_ENABLE (1<<0) -#define IER_TX_ENABLE (1<<1) -#define FCR 2 // FIFO control register -#define FCR_FIFO_ENABLE (1<<0) -#define FCR_FIFO_CLEAR (3<<1) // clear the content of the two FIFOs -#define ISR 2 // interrupt status register -#define LCR 3 // line control register -#define LCR_EIGHT_BITS (3<<0) -#define LCR_BAUD_LATCH (1<<7) // special mode to set baud rate -#define LSR 5 // line status register -#define LSR_RX_READY (1<<0) // input is waiting to be read from RHR -#define LSR_TX_IDLE (1<<5) // THR can accept another character to send - -#define ReadReg(reg) (*(Reg(reg))) -#define WriteReg(reg, v) (*(Reg(reg)) = (v)) - -int uart_init(void) -{ - // disable interrupts. - WriteReg(IER, 0x00); - - // special mode to set baud rate. - WriteReg(LCR, LCR_BAUD_LATCH); - - // LSB for baud rate of 38.4K. - WriteReg(0, 0x03); - - // MSB for baud rate of 38.4K. - WriteReg(1, 0x00); - - // leave set-baud mode, - // and set word length to 8 bits, no parity. - WriteReg(LCR, LCR_EIGHT_BITS); - - // reset and enable FIFOs. - WriteReg(FCR, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR); - - // enable transmit and receive interrupts. - // WriteReg(IER, IER_TX_ENABLE | IER_RX_ENABLE); - - //enable UART0 in PLIC - plic_irq_enable(UART0_IRQ); - - //set UART0 priority in PLIC - plic_prio_set(UART0_IRQ, 1); - - //register callback for UART0 - //plic_register_callback(UART0_IRQ, uart_intr); - puts("[UART0] : Uart Init Done, this is Test output!"); - return 0; -} - -void uart_putc_nolock(int ch) -{ - // wait for Transmit Holding Empty to be set in LSR. - while((ReadReg(LSR) & LSR_TX_IDLE) == 0) - ; - WriteReg(THR, ch); - return; -} - -int uart_putc(int ch) -{ - int intr_enable = riscv_mintr_get(); - riscv_mintr_off(); - uart_putc_nolock(ch); - riscv_mintr_restore(intr_enable); - return 1; -} - -int uart_puts(const char* str) -{ - int i; - int intr_enable = riscv_mintr_get(); - riscv_mintr_off(); - for(i=0;str[i]!=0;i++) - { - uart_putc_nolock(str[i]); - } - uart_putc_nolock('\n'); - riscv_mintr_restore(intr_enable); - return i; -} - - +../../../../risc-v_common/example_build/uart/uart_qemu_ns16550.c
\ No newline at end of file diff --git a/ports/risc-v32/gnu/example_build/qemu_virt/uart.h b/ports/risc-v32/gnu/example_build/qemu_virt/uart.h index debfd9df..aba5ae7b 100644..120000 --- a/ports/risc-v32/gnu/example_build/qemu_virt/uart.h +++ b/ports/risc-v32/gnu/example_build/qemu_virt/uart.h @@ -1,23 +1 @@ -/*************************************************************************** - * Copyright (c) 2024 Microsoft Corporation - * Copyright (c) 2026-present Eclipse ThreadX contributors - * - * This program and the accompanying materials are made available under the - * terms of the MIT License which is available at - * https://opensource.org/licenses/MIT. - * - * SPDX-License-Identifier: MIT - **************************************************************************/ - -#ifndef RISCV_UART_H -#define RISCV_UART_H - -#define UART0 0x10000000L -#define UART0_IRQ 10 - -#define puts uart_puts -int uart_init(void); -int uart_putc(int ch); -void uart_putc_nolock(int ch); -int uart_puts(const char* str); -#endif +../../../../risc-v_common/example_build/uart/uart_qemu_ns16550.h
\ No newline at end of file diff --git a/ports/risc-v32/gnu/example_build/xuantie_smartl_fpga/build_libthreadx.sh b/ports/risc-v32/gnu/example_build/xuantie_smartl_fpga/build_libthreadx.sh index ff9bb930..ff983efe 100755 --- a/ports/risc-v32/gnu/example_build/xuantie_smartl_fpga/build_libthreadx.sh +++ b/ports/risc-v32/gnu/example_build/xuantie_smartl_fpga/build_libthreadx.sh @@ -1,4 +1,15 @@ #!/bin/bash +############################################################################## +# Copyright (c) 2024 Microsoft Corporation +# Copyright (c) 2026 Eclipse ThreadX contributors +# +# This program and the accompanying materials are made available under the +# terms of the MIT License which is available at +# https://opensource.org/licenses/MIT. +# +# SPDX-License-Identifier: MIT +############################################################################## + pushd ../../../../../ rm -rf build diff --git a/ports/risc-v32/gnu/example_build/xuantie_smartl_fpga/build_threadx_sample.sh b/ports/risc-v32/gnu/example_build/xuantie_smartl_fpga/build_threadx_sample.sh index ec91e36a..cc0b6a67 100755 --- a/ports/risc-v32/gnu/example_build/xuantie_smartl_fpga/build_threadx_sample.sh +++ b/ports/risc-v32/gnu/example_build/xuantie_smartl_fpga/build_threadx_sample.sh @@ -1,4 +1,15 @@ #!/bin/bash +############################################################################## +# Copyright (c) 2024 Microsoft Corporation +# Copyright (c) 2026 Eclipse ThreadX contributors +# +# This program and the accompanying materials are made available under the +# terms of the MIT License which is available at +# https://opensource.org/licenses/MIT. +# +# SPDX-License-Identifier: MIT +############################################################################## + bash build_libthreadx.sh diff --git a/ports/risc-v32/gnu/example_build/xuantie_smartl_fpga/components/chip_riscv_dummy/include/dw_uart.h b/ports/risc-v32/gnu/example_build/xuantie_smartl_fpga/components/chip_riscv_dummy/include/dw_uart.h index e69de29b..054d0844 100644 --- a/ports/risc-v32/gnu/example_build/xuantie_smartl_fpga/components/chip_riscv_dummy/include/dw_uart.h +++ b/ports/risc-v32/gnu/example_build/xuantie_smartl_fpga/components/chip_riscv_dummy/include/dw_uart.h @@ -0,0 +1,11 @@ +/***************************************************************************/ +/* Copyright (c) 2024 Microsoft Corporation */ +/* Copyright (c) 2026 Eclipse ThreadX contributors */ +/* */ +/* This program and the accompanying materials are made available under */ +/* the terms of the MIT License which is available at */ +/* https://opensource.org/licenses/MIT. */ +/* */ +/* SPDX-License-Identifier: MIT */ +/***************************************************************************/ + diff --git a/ports/risc-v32/gnu/example_build/xuantie_smartl_fpga/components/csi/csi2/include/drv/list.h b/ports/risc-v32/gnu/example_build/xuantie_smartl_fpga/components/csi/csi2/include/drv/list.h index 9f65050c..5a09070c 100755 --- a/ports/risc-v32/gnu/example_build/xuantie_smartl_fpga/components/csi/csi2/include/drv/list.h +++ b/ports/risc-v32/gnu/example_build/xuantie_smartl_fpga/components/csi/csi2/include/drv/list.h @@ -1,3 +1,14 @@ +/***************************************************************************/ +/* Copyright (c) 2024 Microsoft Corporation */ +/* Copyright (c) 2026 Eclipse ThreadX contributors */ +/* */ +/* This program and the accompanying materials are made available under */ +/* the terms of the MIT License which is available at */ +/* https://opensource.org/licenses/MIT. */ +/* */ +/* SPDX-License-Identifier: MIT */ +/***************************************************************************/ + #ifndef AOS_LIST_H #define AOS_LIST_H diff --git a/ports/risc-v32/gnu/example_build/xuantie_smartl_fpga/demo_threadx.c b/ports/risc-v32/gnu/example_build/xuantie_smartl_fpga/demo_threadx.c index 3df2c251..f8a6d82c 100644 --- a/ports/risc-v32/gnu/example_build/xuantie_smartl_fpga/demo_threadx.c +++ b/ports/risc-v32/gnu/example_build/xuantie_smartl_fpga/demo_threadx.c @@ -1,3 +1,14 @@ +/***************************************************************************/ +/* Copyright (c) 2024 Microsoft Corporation */ +/* Copyright (c) 2026 Eclipse ThreadX contributors */ +/* */ +/* This program and the accompanying materials are made available under */ +/* the terms of the MIT License which is available at */ +/* https://opensource.org/licenses/MIT. */ +/* */ +/* SPDX-License-Identifier: MIT */ +/***************************************************************************/ + /* This is a small demo of the high-performance ThreadX kernel. It includes examples of eight threads of different priorities, using a message queue, semaphore, mutex, event flags group, byte pool, and block pool. */ diff --git a/ports/risc-v32/gnu/inc/tx_port.h b/ports/risc-v32/gnu/inc/tx_port.h index ed014675..baf14be9 100644 --- a/ports/risc-v32/gnu/inc/tx_port.h +++ b/ports/risc-v32/gnu/inc/tx_port.h @@ -47,228 +47,109 @@ #ifndef TX_PORT_H #define TX_PORT_H -#ifndef __ASSEMBLER__ - -/* Include for memset. */ -#include <string.h> - +/* Include shared RISC-V32 port definitions common to all toolchain ports. */ +#include "../../common/tx_port_riscv32_common.h" -/* Determine if the optional ThreadX user define file should be used. */ -#ifdef TX_INCLUDE_USER_DEFINE_FILE -/* Yes, include the user defines in tx_user.h. The defines in this file may - alternately be defined on the command line. */ -#include "tx_user.h" -#endif /* TX_INCLUDE_USER_DEFINE_FILE */ - -#endif /* __ASSEMBLER__ */ - - -/* Define ThreadX basic types for this port. */ - -#define VOID void +/* Define automated coverage test extensions for the ThreadX regression test. */ #ifndef __ASSEMBLER__ -typedef char CHAR; -typedef unsigned char UCHAR; -typedef int INT; -typedef unsigned int UINT; -typedef long LONG; -typedef unsigned long ULONG; -typedef unsigned long long ULONG64; -typedef short SHORT; -typedef unsigned short USHORT; -#define ULONG64_DEFINED -#endif /* __ASSEMBLER__ */ - - - -/* Define the priority levels for ThreadX. Legal values range - from 32 to 1024 and MUST be evenly divisible by 32. */ - -#ifndef TX_MAX_PRIORITIES -#define TX_MAX_PRIORITIES 32 +typedef unsigned int TEST_FLAG; +extern TEST_FLAG threadx_byte_allocate_loop_test; +extern TEST_FLAG threadx_byte_release_loop_test; +extern TEST_FLAG threadx_mutex_suspension_put_test; +extern TEST_FLAG threadx_mutex_suspension_priority_test; +#ifndef TX_TIMER_PROCESS_IN_ISR +extern TEST_FLAG threadx_delete_timer_thread; #endif - -/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during - thread creation is less than this value, the thread create call will return an error. */ - -#ifndef TX_MINIMUM_STACK -#define TX_MINIMUM_STACK 1024 /* Minimum stack size for this port */ +extern void abort_and_resume_byte_allocating_thread(void); +extern void abort_all_threads_suspended_on_mutex(void); +extern void suspend_lowest_priority(void); +#ifndef TX_TIMER_PROCESS_IN_ISR +extern void delete_timer_thread(void); #endif +extern TEST_FLAG test_stack_analyze_flag; +extern TEST_FLAG test_initialize_flag; +extern TEST_FLAG test_forced_mutex_timeout; -/* Define the system timer thread's default stack size and priority. These are only applicable - if TX_TIMER_PROCESS_IN_ISR is not defined. */ - -#ifndef TX_TIMER_THREAD_STACK_SIZE -#define TX_TIMER_THREAD_STACK_SIZE 1024 /* Default timer thread stack size */ -#endif - -#ifndef TX_TIMER_THREAD_PRIORITY -#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ -#endif - - -/* Define various constants for the ThreadX RISC-V port. */ - -#define TX_INT_DISABLE 0x00000000 /* Disable interrupts value */ -#define TX_INT_ENABLE 0x00000008 /* Enable interrupt value */ - - -/* Define the clock source for trace event entry time stamp. The following two item are port specific. - For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock - source constants would be: - -#define TX_TRACE_TIME_SOURCE *((ULONG *) 0x0a800024) -#define TX_TRACE_TIME_MASK 0x0000FFFFUL - -*/ - -#ifndef TX_TRACE_TIME_SOURCE -#define TX_TRACE_TIME_SOURCE ++_tx_trace_simulated_time -#endif -#ifndef TX_TRACE_TIME_MASK -#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL -#endif - - -/* Define the port specific options for the _tx_build_options variable. This variable indicates - how the ThreadX library was built. */ - -#define TX_PORT_SPECIFIC_BUILD_OPTIONS 0 - - -/* Define the in-line initialization constant so that modules with in-line - initialization capabilities can prevent their initialization from being - a function call. */ - -#define TX_INLINE_INITIALIZATION +#ifdef TX_REGRESSION_TEST +#define TX_BYTE_ALLOCATE_EXTENSION if (threadx_byte_allocate_loop_test == ((TEST_FLAG) 1)) \ + { \ + pool_ptr -> tx_byte_pool_owner = TX_NULL; \ + threadx_byte_allocate_loop_test = ((TEST_FLAG) 0); \ + } -/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is - disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack - checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING - define is negated, thereby forcing the stack fill which is necessary for the stack checking - logic. */ - -#ifdef TX_ENABLE_STACK_CHECKING -#undef TX_DISABLE_STACK_FILLING -#endif - - -/* Define the TX_THREAD control block extensions for this port. The main reason - for the multiple macros is so that backward compatibility can be maintained with - existing ThreadX kernel awareness modules. */ - -#define TX_THREAD_EXTENSION_0 -#define TX_THREAD_EXTENSION_1 -#define TX_THREAD_EXTENSION_2 -#define TX_THREAD_EXTENSION_3 - - -/* Define the port extensions of the remaining ThreadX objects. */ - -#define TX_BLOCK_POOL_EXTENSION -#define TX_BYTE_POOL_EXTENSION -#define TX_EVENT_FLAGS_GROUP_EXTENSION -#define TX_MUTEX_EXTENSION -#define TX_QUEUE_EXTENSION -#define TX_SEMAPHORE_EXTENSION -#define TX_TIMER_EXTENSION - - -/* Define the user extension field of the thread control block. Nothing - additional is needed for this port so it is defined as white space. */ - -#ifndef TX_THREAD_USER_EXTENSION -#define TX_THREAD_USER_EXTENSION -#endif - - -/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, - tx_thread_shell_entry, and tx_thread_terminate. */ - -#define TX_THREAD_CREATE_EXTENSION(thread_ptr) -#define TX_THREAD_DELETE_EXTENSION(thread_ptr) -#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) -#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) - - -/* Define the ThreadX object creation extensions for the remaining objects. */ - -#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) -#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) -#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) -#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) -#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) -#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) -#define TX_TIMER_CREATE_EXTENSION(timer_ptr) +#define TX_BYTE_RELEASE_EXTENSION if (threadx_byte_release_loop_test == ((TEST_FLAG) 1)) \ + { \ + threadx_byte_release_loop_test = ((TEST_FLAG) 0); \ + abort_and_resume_byte_allocating_thread(); \ + } +#define TX_MUTEX_PUT_EXTENSION_1 if (threadx_mutex_suspension_put_test == ((TEST_FLAG) 1)) \ + { \ + threadx_mutex_suspension_put_test = ((TEST_FLAG) 0); \ + abort_all_threads_suspended_on_mutex(); \ + } -/* Define the ThreadX object deletion extensions for the remaining objects. */ +#define TX_MUTEX_PUT_EXTENSION_2 if (test_forced_mutex_timeout == ((TEST_FLAG) 1)) \ + { \ + test_forced_mutex_timeout = ((TEST_FLAG) 0); \ + _tx_thread_wait_abort(mutex_ptr -> tx_mutex_suspension_list); \ + } -#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) -#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) -#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) -#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) -#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) -#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) -#define TX_TIMER_DELETE_EXTENSION(timer_ptr) +#define TX_MUTEX_PRIORITY_CHANGE_EXTENSION if (threadx_mutex_suspension_priority_test == ((TEST_FLAG) 1)) \ + { \ + threadx_mutex_suspension_priority_test = ((TEST_FLAG) 0); \ + suspend_lowest_priority(); \ + } +#ifndef TX_TIMER_PROCESS_IN_ISR -/* Define ThreadX interrupt lockout and restore macros for protection on - access of critical kernel information. The restore interrupt macro must - restore the interrupt posture of the running thread prior to the value - present prior to the disable macro. In most cases, the save area macro - is used to define a local function save area for the disable and restore - macros. */ +#define TX_TIMER_INITIALIZE_EXTENSION(a) if (threadx_delete_timer_thread == ((TEST_FLAG) 1)) \ + { \ + threadx_delete_timer_thread = ((TEST_FLAG) 0); \ + delete_timer_thread(); \ + (a) = ((UINT) 1); \ + } -/* Expose helper used to perform an atomic read/modify/write of mstatus. - The helper composes and returns the posture per ThreadX contract. */ -#ifndef __ASSEMBLER__ -UINT _tx_thread_interrupt_control(UINT new_posture); #endif -#ifdef TX_DISABLE_INLINE - -#define TX_INTERRUPT_SAVE_AREA register UINT interrupt_save; - -#define TX_DISABLE __asm__ volatile("csrrci %0, mstatus, 8" : "=r" (interrupt_save) :: "memory"); -#define TX_RESTORE { \ - unsigned long _temp_mstatus; \ - __asm__ volatile( \ - "csrc mstatus, 8\n" \ - "andi %0, %1, 8\n" \ - "csrs mstatus, %0" \ - : "=&r" (_temp_mstatus) \ - : "r" (interrupt_save) \ - : "memory"); \ +#define TX_THREAD_STACK_ANALYZE_EXTENSION if (test_stack_analyze_flag == ((TEST_FLAG) 1)) \ + { \ + thread_ptr -> tx_thread_id = ((TEST_FLAG) 0); \ + test_stack_analyze_flag = ((TEST_FLAG) 0); \ + } \ + else if (test_stack_analyze_flag == ((TEST_FLAG) 2)) \ + { \ + stack_ptr = thread_ptr -> tx_thread_stack_start; \ + test_stack_analyze_flag = ((TEST_FLAG) 0); \ + } \ + else if (test_stack_analyze_flag == ((TEST_FLAG) 3)) \ + { \ + *stack_ptr = TX_STACK_FILL; \ + test_stack_analyze_flag = ((TEST_FLAG) 0); \ + } \ + else \ + { \ + test_stack_analyze_flag = ((TEST_FLAG) 0); \ } -#else - -#define TX_INTERRUPT_SAVE_AREA register UINT interrupt_save; - -#define TX_DISABLE interrupt_save = _tx_thread_interrupt_control(TX_INT_DISABLE); -#define TX_RESTORE _tx_thread_interrupt_control(interrupt_save); - -#endif /* TX_DISABLE_INLINE */ - +#define TX_INITIALIZE_KERNEL_ENTER_EXTENSION if (test_initialize_flag == ((TEST_FLAG) 1)) \ + { \ + test_initialize_flag = ((TEST_FLAG) 0); \ + return; \ + } -/* Define the interrupt lockout macros for each ThreadX object. */ +#endif /* TX_REGRESSION_TEST */ -#define TX_BLOCK_POOL_DISABLE TX_DISABLE -#define TX_BYTE_POOL_DISABLE TX_DISABLE -#define TX_EVENT_FLAGS_GROUP_DISABLE TX_DISABLE -#define TX_MUTEX_DISABLE TX_DISABLE -#define TX_QUEUE_DISABLE TX_DISABLE -#define TX_SEMAPHORE_DISABLE TX_DISABLE +#endif /* __ASSEMBLER__ */ /* Define the version ID of ThreadX. This may be utilized by the application. */ @@ -276,7 +157,7 @@ UINT _tx_thread_interrupt_control(UIN #ifndef __ASSEMBLER__ #ifdef TX_THREAD_INIT CHAR _tx_version_id[] = - "(c) 2024 Microsoft Corp. (c) 2026-present Eclipse ThreadX contributors. * ThreadX RISC-V32/GNU Version 6.5.0.202601 *"; + "(c) 2024 Microsoft Corp. (c) 2026-present Eclipse ThreadX contributors. * ThreadX RISC-V32/GNU Version 6.5.1.202602 *"; #else extern CHAR _tx_version_id[]; #endif /* TX_THREAD_INIT */ diff --git a/ports/risc-v32/gnu/src/tx_initialize_low_level.S b/ports/risc-v32/gnu/src/tx_initialize_low_level.S index 703466bd..2940f215 100644 --- a/ports/risc-v32/gnu/src/tx_initialize_low_level.S +++ b/ports/risc-v32/gnu/src/tx_initialize_low_level.S @@ -62,7 +62,7 @@ __tx_free_memory_start: /**************************************************************************/ /* VOID _tx_initialize_low_level(VOID) { */ - .global _tx_initialize_low_level + .weak _tx_initialize_low_level .weak _tx_initialize_low_level _tx_initialize_low_level: diff --git a/ports/risc-v32/gnu/src/tx_thread_context_restore.S b/ports/risc-v32/gnu/src/tx_thread_context_restore.S index 73a07f61..d3d449e4 100644 --- a/ports/risc-v32/gnu/src/tx_thread_context_restore.S +++ b/ports/risc-v32/gnu/src/tx_thread_context_restore.S @@ -29,6 +29,7 @@ /* AUTHOR */ /* */ /* Akif Ejaz, 10xEngineers */ +/* Wei-Chen Lai, National Cheng Kung University */ /* */ /* DESCRIPTION */ /* */ @@ -82,7 +83,13 @@ _tx_thread_context_restore: /* Just recover the saved registers and return to the point of interrupt. */ - /* Recover floating point registers. */ + /* Recover floating point registers only if saved mstatus.FS was not Off. */ +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + lw t1, 29*4(sp) // Pickup saved mstatus + srli t1, t1, 13 + andi t1, t1, 0x3 + beqz t1, _tx_thread_skip_fp_restore // Skip if FS was Off +#endif #if defined(__riscv_float_abi_single) flw f0, 31*4(sp) // Recover ft0 flw f1, 32*4(sp) // Recover ft1 @@ -130,6 +137,7 @@ _tx_thread_context_restore: lw t0, 63*4(sp) // Recover fcsr csrw fcsr, t0 // Restore fcsr #endif +_tx_thread_skip_fp_restore: /* Recover standard registers. */ @@ -149,7 +157,7 @@ _tx_thread_context_restore: /* Clear MPP/MPIE/MIE bits in t1 then set desired values. */ li t2, 0x1888 // MPP(0x1800) | MPIE(0x80) | MIE(0x08) - li t3, 0x1800 // Set MPP to Machine mode (bits 12:11) + li t3, 0x1880 // Set MPP=Machine (0x1800) + MPIE=1 (0x80); mret sets MIE=MPIE /* Construct new mstatus in t1: clear mask bits, set MPP/MPIE and optionally FP bit, preserve everything except the bits we will modify. */ @@ -222,7 +230,13 @@ _tx_thread_no_preempt_restore: lw sp, 8(t1) // Switch back to thread's stack - /* Recover floating point registers. */ + /* Recover floating point registers only if saved mstatus.FS was not Off. */ +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + lw t3, 29*4(sp) // Pickup saved mstatus + srli t3, t3, 13 + andi t3, t3, 0x3 + beqz t3, _tx_thread_no_preempt_skip_fp_restore // Skip if FS was Off +#endif #if defined(__riscv_float_abi_single) flw f0, 31*4(sp) // Recover ft0 flw f1, 32*4(sp) // Recover ft1 @@ -270,6 +284,7 @@ _tx_thread_no_preempt_restore: lw t0, 63*4(sp) // Recover fcsr csrw fcsr, t0 // Restore fcsr #endif +_tx_thread_no_preempt_skip_fp_restore: /* Recover the saved context and return to the point of interrupt. */ @@ -284,7 +299,7 @@ _tx_thread_no_preempt_restore: csrr t1, mstatus li t2, 0x1888 // MPP(0x1800) | MPIE(0x80) | MIE(0x08) - li t3, 0x1800 // Set MPP to Machine mode + li t3, 0x1880 // Set MPP=Machine (0x1800) + MPIE=1 (0x80); mret sets MIE=MPIE li t4, ~0x1888 // Clear mask for MPP/MPIE/MIE and t1, t1, t4 or t1, t1, t3 @@ -331,7 +346,13 @@ _tx_thread_preempt_restore: ori t3, zero, 1 // Build interrupt stack type sw t3, 0(t0) // Store stack type - /* Store floating point preserved registers. */ + /* Store floating point preserved registers only if saved mstatus.FS was not Off. */ +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + lw t3, 29*4(t0) // Pickup saved mstatus + srli t3, t3, 13 + andi t3, t3, 0x3 + beqz t3, _tx_thread_preempt_skip_fp_restore // Skip if FS was Off +#endif #ifdef __riscv_float_abi_single fsw f8, 39*4(t0) // Store fs0 fsw f9, 40*4(t0) // Store fs1 @@ -359,6 +380,7 @@ _tx_thread_preempt_restore: fsd f26, 57*4(t0) // Store fs10 fsd f27, 58*4(t0) // Store fs11 #endif +_tx_thread_preempt_skip_fp_restore: /* Store standard preserved registers. */ diff --git a/ports/risc-v32/gnu/src/tx_thread_context_save.S b/ports/risc-v32/gnu/src/tx_thread_context_save.S index 66402934..0a0c0c15 100644 --- a/ports/risc-v32/gnu/src/tx_thread_context_save.S +++ b/ports/risc-v32/gnu/src/tx_thread_context_save.S @@ -29,6 +29,7 @@ /* AUTHOR */ /* */ /* Akif Ejaz, 10xEngineers */ +/* Wei-Chen Lai, National Cheng Kung University */ /* */ /* DESCRIPTION */ /* */ @@ -96,6 +97,15 @@ _tx_thread_context_save: sw t5, 14*4(sp) // Store t5 sw t6, 13*4(sp) // Store t6 + /* Save mstatus and skip FP state if FS is Off. */ + csrr t0, mstatus + sw t0, 29*4(sp) +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + srli t1, t0, 13 + andi t1, t1, 0x3 + beqz t1, _tx_thread_skip_fpu_save +#endif + /* Save floating point registers. */ #if defined(__riscv_float_abi_single) fsw f0, 31*4(sp) // Store ft0 @@ -144,14 +154,11 @@ _tx_thread_context_save: csrr t0, fcsr sw t0, 63*4(sp) // Store fcsr #endif +_tx_thread_skip_fpu_save: csrr t0, mepc sw t0, 30*4(sp) // Save it on the stack - /* Save mstatus. */ - csrr t0, mstatus - sw t0, 29*4(sp) - la t1, _tx_thread_current_ptr // Pickup address of current thread ptr lw t2, 0(t1) // Pickup current thread pointer beqz t2, _tx_thread_idle_system_save // If NULL, idle system was interrupted @@ -190,6 +197,15 @@ _tx_thread_nested_save: sw t5, 14*4(sp) // Store t5 sw t6, 13*4(sp) // Store t6 + /* Save mstatus and skip FP state if FS is Off. */ + csrr t0, mstatus + sw t0, 29*4(sp) +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + srli t1, t0, 13 + andi t1, t1, 0x3 + beqz t1, _tx_thread_skip_nested_fpu_save +#endif + /* Save floating point registers. */ #if defined(__riscv_float_abi_single) fsw f0, 31*4(sp) // Store ft0 @@ -238,13 +254,11 @@ _tx_thread_nested_save: csrr t0, fcsr sw t0, 63*4(sp) // Store fcsr #endif +_tx_thread_skip_nested_fpu_save: csrr t0, mepc sw t0, 30*4(sp) // Save it on stack - csrr t0, mstatus - sw t0, 29*4(sp) - /* Call the ISR execution exit function if enabled. */ #ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY call _tx_execution_isr_enter // Call the ISR execution enter function diff --git a/ports/risc-v32/gnu/src/tx_thread_schedule.S b/ports/risc-v32/gnu/src/tx_thread_schedule.S index 1c235a2d..79bfd51a 100644 --- a/ports/risc-v32/gnu/src/tx_thread_schedule.S +++ b/ports/risc-v32/gnu/src/tx_thread_schedule.S @@ -133,7 +133,12 @@ _tx_thread_ready_to_run: lw t0, 0(sp) // Pickup stack type beqz t0, _tx_thread_solicited_return // If 0, solicited return - /* Recover floating point registers. */ + /* Recover floating point registers only if saved mstatus.FS was not Off. */ +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + lw t1, 29*4(sp) // Pickup saved mstatus + srli t1, t1, 13 + andi t1, t1, 0x3 + beqz t1, _tx_thread_schedule_skip_fp_restore // Skip if FS was Off #if defined(__riscv_float_abi_single) flw f0, 31*4(sp) // Recover ft0 flw f1, 32*4(sp) // Recover ft1 @@ -205,9 +210,10 @@ _tx_thread_ready_to_run: lw t0, 63*4(sp) // Recover fcsr csrw fcsr, t0 // Restore fcsr #endif +_tx_thread_schedule_skip_fp_restore: +#endif /* Recover standard registers. */ - lw t0, 30*4(sp) // Recover mepc csrw mepc, t0 // Setup mepc @@ -256,7 +262,12 @@ _tx_thread_ready_to_run: _tx_thread_solicited_return: - /* Recover floating point registers. */ + /* Recover floating point registers only if saved mstatus.FS was not Off. */ +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + lw t1, 14*4(sp) // Pickup saved mstatus + srli t1, t1, 13 + andi t1, t1, 0x3 + beqz t1, _tx_thread_schedule_solicited_skip_fp_restore // Skip if FS was Off #if defined(__riscv_float_abi_single) flw f8, 15*4(sp) // Recover fs0 flw f9, 16*4(sp) // Recover fs1 @@ -288,10 +299,11 @@ _tx_thread_solicited_return: lw t0, 27*4(sp) // Recover fcsr csrw fcsr, t0 // Restore fcsr #endif +_tx_thread_schedule_solicited_skip_fp_restore: +#endif /* Recover standard registers. */ - - lw t0, 14*4(sp) // Recover mstatus + lw t0, 14*4(sp) // Reload saved mstatus (t0 may hold fcsr if FP was restored) csrw mstatus, t0 // Restore mstatus lw ra, 13*4(sp) // Recover return address diff --git a/ports/risc-v32/gnu/src/tx_thread_system_return.S b/ports/risc-v32/gnu/src/tx_thread_system_return.S index 4090e7b2..20ff25f2 100644 --- a/ports/risc-v32/gnu/src/tx_thread_system_return.S +++ b/ports/risc-v32/gnu/src/tx_thread_system_return.S @@ -69,7 +69,17 @@ _tx_thread_system_return: addi sp, sp, -16*4 // Allocate space on the stack - without floating point enabled #endif - /* Store floating point preserved registers. */ + sw zero, 0(sp) // Solicited stack type (store early so slot is always written) + + /* Read and save mstatus first; use it to guard FP register save. */ + csrr t0, mstatus // Pickup current mstatus + sw t0, 14*4(sp) // Save mstatus + + /* Store floating point preserved registers — only if FS != Off. */ +#if defined(__riscv_float_abi_single) || defined(__riscv_float_abi_double) + srli t1, t0, 13 // Shift FS field to bits [1:0] + andi t1, t1, 0x3 // Isolate FS bits + beqz t1, _tx_thread_system_return_skip_fp // Skip FP save if FS == Off #if defined(__riscv_float_abi_single) fsw f8, 15*4(sp) // Store fs0 fsw f9, 16*4(sp) // Store fs1 @@ -101,8 +111,9 @@ _tx_thread_system_return: csrr t0, fcsr sw t0, 27*4(sp) // Store fcsr #endif +_tx_thread_system_return_skip_fp: +#endif - sw zero, 0(sp) // Solicited stack type sw ra, 13*4(sp) // Save return address sw s0, 12*4(sp) // Save s0 sw s1, 11*4(sp) // Save s1 @@ -116,9 +127,6 @@ _tx_thread_system_return: sw s9, 3*4(sp) // Save s9 sw s10, 2*4(sp) // Save s10 sw s11, 1*4(sp) // Save s11 - csrr t0, mstatus // Pickup mstatus - sw t0, 14*4(sp) // Save mstatus - /* Lockout interrupts. will be enabled in _tx_thread_schedule */ |
