From a7bde40a363a9f5c4d749795b3983412d3d65bb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Desbiens?= Date: Thu, 14 May 2026 22:53:42 -0400 Subject: [PATCH 1/8] Added OpenHW CORE-V MCU (CV32E40P RV32IMC) BSP and demo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a complete ThreadX port for the OpenHW CORE-V MCU SoC, targeting the Digilent Nexys A7 FPGA board with an Ashling Opella LD debug probe. New files --------- cmake/riscv64-gcc-rv32imc.cmake CMake toolchain file for riscv64-unknown-elf-gcc targeting rv32imc_zicsr/ilp32. ports/risc-v32/gnu/example_build/core_v_mcu/ Full BSP + demo application: Assembly crt0.S C runtime startup (BSS clear, GP/SP init, call main) vectors.S 32-entry vectored interrupt table at 0x1c000800 tx_initialize_low_level.S mtvec setup (vectored mode), stack/free-mem pointers BSP drivers (bsp/) system_core_v_mcu.c Top-level init and ISR dispatcher (isr_table[32]) irq.c PULP APB interrupt controller (enable/disable/mask) timer_irq.c PULP FC Timer — 100 Hz tick from 32 kHz ref clock fll.c Frequency Locked Loop — 5 MHz → 50 MHz (FPGA) uart_driver.c UDMA UART channel 0 — polled TX, non-blocking RX gpio.c PULP GPIO — set/clear/toggle by pin mask string.c Freestanding memset/memcpy shim (no newlib) Headers (include/) Peripheral register maps, MMIO inlines, BSP API declarations, tx_user.h Application demo_threadx.c Two threads: blinky (LED pin 5, 1 Hz) + UART heartbeat link.ld Linker script: .vectors@0x1c000800, .text@0x1c000880 CMakeLists.txt Build definition (references THREADX_ROOT) build.sh One-shot CMake+Ninja build script Debug openocd-nexys-Ashling-Opella-LD.cfg OpenOCD config for Opella LD over JTAG gdb_init GDB startup script (connect, load, break main) Tests (tests/) test_irq.c / test_timer.c Host-compiled unit tests (2/2 pass) mock/mmio_mock.* Software MMIO register map for host testing Documentation README.md Hardware overview, build, flash/debug, BSP API reference Architecture notes ------------------ - CV32E40P uses PULP/PULPissimo interrupt controller (not CLINT); individual IRQ lines are masked via APB registers, not mie CSR. - mtvec must be 256-byte aligned; vectors placed at 0x1c000800 (vectored mode). - Timer IRQ = line 10; dispatch via isr_table[mcause & 0x1f]. - Build: -march=rv32imc_zicsr -mabi=ilp32, -ffreestanding, -nodefaultlibs. - Verified: ELF 11 KB text, sections at correct addresses, unit tests pass. Third-party attributions ------------------------ BSP files derived from core-v-freertos (Apache-2.0): © 2019-2020 ETH Zurich and University of Bologna © 2020 GreenWaves Technologies © 2011-2014 Wind River Systems, Inc. © 2017 SiFive Inc. (crt0.S, BSD-2-Clause portions) All original copyright notices retained; see individual file headers. SPDX: Apache-2.0 AND MIT (crt0.S: (Apache-2.0 OR BSD-2-Clause) AND MIT). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- cmake/riscv64-gcc-rv32imc.cmake | 64 +++ .../example_build/core_v_mcu/CMakeLists.txt | 63 +++ .../gnu/example_build/core_v_mcu/README.md | 404 ++++++++++++++++++ .../gnu/example_build/core_v_mcu/bsp/fll.c | 209 +++++++++ .../gnu/example_build/core_v_mcu/bsp/gpio.c | 49 +++ .../gnu/example_build/core_v_mcu/bsp/irq.c | 50 +++ .../gnu/example_build/core_v_mcu/bsp/string.c | 64 +++ .../core_v_mcu/bsp/system_core_v_mcu.c | 125 ++++++ .../example_build/core_v_mcu/bsp/timer_irq.c | 59 +++ .../core_v_mcu/bsp/uart_driver.c | 174 ++++++++ .../gnu/example_build/core_v_mcu/build.sh | 9 + .../gnu/example_build/core_v_mcu/crt0.S | 48 +++ .../example_build/core_v_mcu/demo_threadx.c | 113 +++++ .../gnu/example_build/core_v_mcu/gdb_init | 6 + .../example_build/core_v_mcu/include/bits.h | 59 +++ .../example_build/core_v_mcu/include/csr.h | 72 ++++ .../example_build/core_v_mcu/include/fll.h | 93 ++++ .../example_build/core_v_mcu/include/gpio.h | 36 ++ .../gnu/example_build/core_v_mcu/include/io.h | 61 +++ .../example_build/core_v_mcu/include/irq.h | 64 +++ .../core_v_mcu/include/memory_map.h | 39 ++ .../core_v_mcu/include/properties.h | 40 ++ .../core_v_mcu/include/pulp_mem_map.h | 99 +++++ .../example_build/core_v_mcu/include/string.h | 25 ++ .../core_v_mcu/include/system_core_v_mcu.h | 29 ++ .../example_build/core_v_mcu/include/timer.h | 120 ++++++ .../core_v_mcu/include/timer_irq.h | 26 ++ .../core_v_mcu/include/tx_user.h | 22 + .../core_v_mcu/include/uart_driver.h | 27 ++ .../gnu/example_build/core_v_mcu/link.ld | 65 +++ .../openocd-nexys-Ashling-Opella-LD.cfg | 41 ++ .../core_v_mcu/tests/CMakeLists.txt | 28 ++ .../core_v_mcu/tests/mock/mmio_mock.c | 18 + .../core_v_mcu/tests/mock/mmio_mock.h | 45 ++ .../example_build/core_v_mcu/tests/test_irq.c | 66 +++ .../core_v_mcu/tests/test_timer.c | 53 +++ .../core_v_mcu/tx_initialize_low_level.S | 63 +++ .../gnu/example_build/core_v_mcu/vectors.S | 25 ++ 38 files changed, 2653 insertions(+) create mode 100644 cmake/riscv64-gcc-rv32imc.cmake create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/CMakeLists.txt create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/README.md create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/bsp/fll.c create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/bsp/gpio.c create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/bsp/irq.c create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/bsp/string.c create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/bsp/system_core_v_mcu.c create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/bsp/timer_irq.c create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/bsp/uart_driver.c create mode 100755 ports/risc-v32/gnu/example_build/core_v_mcu/build.sh create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/crt0.S create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/demo_threadx.c create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/gdb_init create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/include/bits.h create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/include/csr.h create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/include/fll.h create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/include/gpio.h create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/include/io.h create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/include/irq.h create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/include/memory_map.h create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/include/properties.h create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/include/pulp_mem_map.h create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/include/string.h create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/include/system_core_v_mcu.h create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/include/timer.h create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/include/timer_irq.h create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/include/tx_user.h create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/include/uart_driver.h create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/link.ld create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/openocd-nexys-Ashling-Opella-LD.cfg create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/tests/CMakeLists.txt create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/tests/mock/mmio_mock.c create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/tests/mock/mmio_mock.h create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/tests/test_irq.c create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/tests/test_timer.c create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/tx_initialize_low_level.S create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/vectors.S diff --git a/cmake/riscv64-gcc-rv32imc.cmake b/cmake/riscv64-gcc-rv32imc.cmake new file mode 100644 index 000000000..836ed1ce3 --- /dev/null +++ b/cmake/riscv64-gcc-rv32imc.cmake @@ -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 +# ***************************************************************************/ + +# CMake toolchain file for CORE-V MCU (CV32E40P, RV32IMC) +# +# Uses the riscv64-unknown-elf-gcc multi-lib toolchain (Ubuntu package +# gcc-riscv64-unknown-elf) to cross-compile for a 32-bit RISC-V target. +# +# Target ISA : rv32imc_zicsr (integer, multiply, compressed, Zicsr) +# ABI : ilp32 (32-bit int/long/ptr, no hardware FP) +# Code model : medlow (addresses in [0, 2 GiB)) + +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_PROCESSOR riscv) + +set(THREADX_ARCH "risc-v32") +set(THREADX_TOOLCHAIN "gnu") + +set(ARCH_FLAGS "-march=rv32imc_zicsr -mabi=ilp32 -mcmodel=medlow") +set(CFLAGS "${ARCH_FLAGS}") +set(ASFLAGS "${ARCH_FLAGS}") +set(LDFLAGS "${ARCH_FLAGS}") + +# Toolchain binaries (riscv64-unknown-elf can target rv32 via multilib) +set(CMAKE_C_COMPILER riscv64-unknown-elf-gcc) +set(CMAKE_CXX_COMPILER riscv64-unknown-elf-g++) +set(AS riscv64-unknown-elf-as) +set(AR riscv64-unknown-elf-ar) +set(OBJCOPY riscv64-unknown-elf-objcopy) +set(OBJDUMP riscv64-unknown-elf-objdump) +set(SIZE riscv64-unknown-elf-size) + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +# Use static library for compiler feature probing (no linker script yet) +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) + +set(CMAKE_C_FLAGS "${CFLAGS}" CACHE INTERNAL "c compiler flags") +set(CMAKE_CXX_FLAGS "${CFLAGS}" CACHE INTERNAL "cxx compiler flags") +set(CMAKE_ASM_FLAGS "${ASFLAGS} -D__ASSEMBLER__" CACHE INTERNAL "asm compiler flags") +set(CMAKE_EXE_LINKER_FLAGS "${LDFLAGS}" CACHE INTERNAL "exe link flags") + +set(CMAKE_C_FLAGS_DEBUG "-Og -g -ggdb3" CACHE INTERNAL "c debug flags") +set(CMAKE_CXX_FLAGS_DEBUG "-Og -g -ggdb3" CACHE INTERNAL "cxx debug flags") +set(CMAKE_ASM_FLAGS_DEBUG "-g -ggdb3" CACHE INTERNAL "asm debug flags") + +set(CMAKE_C_FLAGS_RELEASE "-Os" CACHE INTERNAL "c release flags") +set(CMAKE_CXX_FLAGS_RELEASE "-Os" CACHE INTERNAL "cxx release flags") +set(CMAKE_ASM_FLAGS_RELEASE "" CACHE INTERNAL "asm release flags") 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 000000000..a432bdc22 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/CMakeLists.txt @@ -0,0 +1,63 @@ +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") +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/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 $ ${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 000000000..b7c0216d9 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/README.md @@ -0,0 +1,404 @@ +# 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) + +--- + +## 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 + +| Tool | Minimum version | Notes | +|------|----------------|-------| +| `riscv64-unknown-elf-gcc` | 13.x | Ubuntu: `apt install gcc-riscv64-unknown-elf` | +| CMake | 3.15 | `apt install cmake` | +| Ninja | 1.10 | `apt install ninja-build` | +| OpenOCD | 0.11 with RISC-V support | Build from source or use the [RISC-V OpenOCD fork](https://github.com/riscv-collab/riscv-openocd) | +| GDB (RISC-V) | Any recent multiarch build | Included in most `riscv64-unknown-elf` toolchain packages | + +> **Note:** There is no `riscv32-unknown-elf-gcc` package required. The +> `riscv64-unknown-elf-gcc` compiler generates RV32 code when passed +> `-march=rv32imc_zicsr -mabi=ilp32`, as configured by the toolchain file. + +--- + +## 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/riscv64-gcc-rv32imc.cmake" \ + . +cmake --build build +``` + +### Toolchain file + +`${THREADX_ROOT}/cmake/riscv64-gcc-rv32imc.cmake` sets: + +``` +CMAKE_C_COMPILER riscv64-unknown-elf-gcc +CMAKE_ASM_COMPILER riscv64-unknown-elf-gcc +CMAKE_OBJCOPY riscv64-unknown-elf-objcopy +CMAKE_C_FLAGS -march=rv32imc_zicsr -mabi=ilp32 -mcmodel=medlow +``` + +--- + +## 7. Flashing and debugging + +### 7.1 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. + +### 7.2 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). + +### 7.3 Flash and run with GDB + +In a second terminal: + +```bash +riscv64-unknown-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. + +### 7.4 UART monitor + +```bash +minicom -b 115200 -D /dev/ttyUSBx # adjust device as needed +``` + +### 7.5 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. + +--- + +## 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 000000000..46dce8576 --- /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 +#include + +#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 000000000..e51bdb527 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/gpio.c @@ -0,0 +1,49 @@ +/*************************************************************************** + * 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 + +#include "pulp_mem_map.h" +#include "io.h" +#include "gpio.h" + +void gpio_init(void) +{ + /* GPIO clocking is handled by platform boot code on CORE-V MCU. */ +} + +void gpio_set_output(uint32_t pin_mask) +{ + uint32_t value = readw((uintptr_t)(PULP_GPIO_ADDR + GPIO_DIR_OFFSET)); + + writew(value | pin_mask, (uintptr_t)(PULP_GPIO_ADDR + GPIO_DIR_OFFSET)); +} + +void gpio_toggle(uint32_t pin_mask) +{ + uint32_t value = readw((uintptr_t)(PULP_GPIO_ADDR + GPIO_OUT_OFFSET)); + + writew(value ^ pin_mask, (uintptr_t)(PULP_GPIO_ADDR + GPIO_OUT_OFFSET)); +} + +void gpio_set(uint32_t pin_mask) +{ + writew(pin_mask, (uintptr_t)(PULP_GPIO_ADDR + GPIO_SET_OFFSET)); +} + +void gpio_clear(uint32_t pin_mask) +{ + writew(pin_mask, (uintptr_t)(PULP_GPIO_ADDR + GPIO_CLR_OFFSET)); +} 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 000000000..cb79cc063 --- /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 + +#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 000000000..7ff699e7f --- /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 +#include + +#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 000000000..990595e19 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/system_core_v_mcu.c @@ -0,0 +1,125 @@ +/*************************************************************************** + * 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 +#include + +#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; +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[10U] = 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_REF_CLOCK / (uint32_t)TX_TIMER_TICKS_PER_SECOND); + irq_enable(IRQ_FC_EVT_TIMER0_LO); + + 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) +{ + 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/timer_irq.c b/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/timer_irq.c new file mode 100644 index 000000000..55d53e933 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/timer_irq.c @@ -0,0 +1,59 @@ +/***************************************************************************/ +/* 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 + +#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_CCFG_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 000000000..35476f2a5 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/uart_driver.c @@ -0,0 +1,174 @@ +/*************************************************************************** + * 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 +#include + +#include "bits.h" +#include "io.h" +#include "pulp_mem_map.h" +#include "uart_driver.h" + +#define UDMA_CTRL_CG_OFFSET 0x00U +#define UDMA_CTRL_RST_OFFSET 0x08U +#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) +#define UDMA_UART_TX_CFG_ENABLE 0x12U +#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; + uint32_t rst; + + if ((uart_id >= N_UART) || (baudrate == 0U) || (periph_freq == 0U)) + { + return -1; + } + + cg = readw((uintptr_t)(UDMA_CH_ADDR_CTRL + UDMA_CTRL_CG_OFFSET)); + rst = readw((uintptr_t)(UDMA_CH_ADDR_CTRL + UDMA_CTRL_RST_OFFSET)); + rst |= (uint32_t)(UDMA_CTRL_UART0_CLKEN << uart_id); + writew(rst, (uintptr_t)(UDMA_CH_ADDR_CTRL + UDMA_CTRL_RST_OFFSET)); + rst &= ~((uint32_t)(UDMA_CTRL_UART0_CLKEN << uart_id)); + writew(rst, (uintptr_t)(UDMA_CH_ADDR_CTRL + UDMA_CTRL_RST_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 000000000..fe78a8e64 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/build.sh @@ -0,0 +1,9 @@ +#!/bin/bash +set -e +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +THREADX_ROOT="${SCRIPT_DIR}/../../../../.." +cmake -B "${SCRIPT_DIR}/build" \ + -G Ninja \ + -DCMAKE_TOOLCHAIN_FILE="${THREADX_ROOT}/cmake/riscv64-gcc-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 000000000..3c761bbe5 --- /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 000000000..0f02ef702 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/demo_threadx.c @@ -0,0 +1,113 @@ +/*************************************************************************** + * 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 + +#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 +#define DEMO_LED_PIN 5U +#define DEMO_LED_MASK (1UL << DEMO_LED_PIN) + +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); + + gpio_set_output((uint32_t)DEMO_LED_MASK); + + for (;;) + { + gpio_toggle((uint32_t)DEMO_LED_MASK); + tx_thread_sleep(50U); + } +} + +static void uart_thread_entry(ULONG arg) +{ + TX_PARAMETER_NOT_USED(arg); + + uart_write_str(0U, "\r\nThreadX on CORE-V MCU\r\n"); + 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/gdb_init b/ports/risc-v32/gnu/example_build/core_v_mcu/gdb_init new file mode 100644 index 000000000..718a20f19 --- /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 000000000..08871387a --- /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 +#include + +/* 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 000000000..71cf57478 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/include/csr.h @@ -0,0 +1,72 @@ +/***************************************************************************/ +/* 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_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 000000000..011268df0 --- /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 + +#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 000000000..52159a4df --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/include/gpio.h @@ -0,0 +1,36 @@ +/*************************************************************************** + * 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 + +/* PULP GPIO register offsets */ +#define GPIO_DIR_OFFSET 0x000U +#define GPIO_EN_OFFSET 0x004U +#define GPIO_IN_OFFSET 0x008U +#define GPIO_OUT_OFFSET 0x00CU +#define GPIO_SET_OFFSET 0x010U +#define GPIO_CLR_OFFSET 0x014U +#define GPIO_TOGGLE_OFFSET 0x018U + +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); + +#endif /* GPIO_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 000000000..02364544f --- /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 + +#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 000000000..aafe62267 --- /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 000000000..f30483673 --- /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 000000000..33581a737 --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/include/properties.h @@ -0,0 +1,40 @@ +/***************************************************************************/ +/* 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 + +#define ARCHI_FPGA_FREQUENCY 5000000U +#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 000000000..fc71cbffa --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/include/pulp_mem_map.h @@ -0,0 +1,99 @@ +/***************************************************************************/ +/* 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_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 000000000..b56000009 --- /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 + +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 000000000..4b445e31c --- /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 + +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/timer.h b/ports/risc-v32/gnu/example_build/core_v_mcu/include/timer.h new file mode 100644 index 000000000..ecfc0480b --- /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 000000000..11e0d76a8 --- /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 + +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 000000000..cc104032d --- /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 000000000..c17710932 --- /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 + +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/link.ld b/ports/risc-v32/gnu/example_build/core_v_mcu/link.ld new file mode 100644 index 000000000..8ffb174e6 --- /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 000000000..db43eae53 --- /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 000000000..6726981f1 --- /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 000000000..7852d5f10 --- /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 000000000..c8839e1e2 --- /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 +#include + +#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 000000000..72c17f490 --- /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 +#include +#include +#include + +#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 000000000..3e1499e7d --- /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 +#include +#include +#include + +#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 000000000..3c5a38884 --- /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 000000000..f6a57d7ae --- /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 From 4412baa97446f2c233ff1178333a0bcc99ef79af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Desbiens?= Date: Wed, 20 May 2026 16:55:53 -0400 Subject: [PATCH 2/8] Improved deploy scripts and documentation for CORE-V MCU port - Added install_deps.sh to automate toolchain/OpenOCD dependency setup - Added deploy.sh for one-step GDB flashing via Ashling Opella-LD - Added gdb-multiarch fallback when riscv64-unknown-elf-gdb is absent - Suppressed spurious GDB warnings in flash mode - Documented --wsl flag required by usbipd-win v5.x Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../gnu/example_build/core_v_mcu/README.md | 113 +++++++++++++- .../gnu/example_build/core_v_mcu/deploy.sh | 141 ++++++++++++++++++ .../example_build/core_v_mcu/install_deps.sh | 53 +++++++ 3 files changed, 299 insertions(+), 8 deletions(-) create mode 100755 ports/risc-v32/gnu/example_build/core_v_mcu/deploy.sh create mode 100755 ports/risc-v32/gnu/example_build/core_v_mcu/install_deps.sh 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 index b7c0216d9..d94759d00 100644 --- a/ports/risc-v32/gnu/example_build/core_v_mcu/README.md +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/README.md @@ -21,6 +21,7 @@ FPGA board running the synthesised CORE-V MCU bitstream. 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) --- @@ -46,13 +47,21 @@ 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 | |------|----------------|-------| -| `riscv64-unknown-elf-gcc` | 13.x | Ubuntu: `apt install gcc-riscv64-unknown-elf` | +| `riscv64-unknown-elf-gcc` | 13.x | `apt install gcc-riscv64-unknown-elf binutils-riscv64-unknown-elf` | | CMake | 3.15 | `apt install cmake` | | Ninja | 1.10 | `apt install ninja-build` | -| OpenOCD | 0.11 with RISC-V support | Build from source or use the [RISC-V OpenOCD fork](https://github.com/riscv-collab/riscv-openocd) | -| GDB (RISC-V) | Any recent multiarch build | Included in most `riscv64-unknown-elf` toolchain packages | +| OpenOCD | 0.12 | `apt install openocd` | +| GDB | any multiarch | `apt install gdb-multiarch` — Ubuntu does not ship `riscv64-unknown-elf-gdb` | > **Note:** There is no `riscv32-unknown-elf-gcc` package required. The > `riscv64-unknown-elf-gcc` compiler generates RV32 code when passed @@ -232,13 +241,37 @@ CMAKE_C_FLAGS -march=rv32imc_zicsr -mabi=ilp32 -mcmodel=medlow ## 7. Flashing and debugging -### 7.1 Connect hardware +### 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. -### 7.2 Start OpenOCD +#### Start OpenOCD ```bash openocd -f openocd-nexys-Ashling-Opella-LD.cfg @@ -247,7 +280,7 @@ 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). -### 7.3 Flash and run with GDB +#### Flash and run with GDB In a second terminal: @@ -259,13 +292,13 @@ riscv64-unknown-elf-gdb --command=gdb_init 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. -### 7.4 UART monitor +#### UART monitor ```bash minicom -b 115200 -D /dev/ttyUSBx # adjust device as needed ``` -### 7.5 Useful GDB commands +#### Useful GDB commands ``` (gdb) info threads — list ThreadX threads @@ -393,6 +426,70 @@ To port to a different CORE-V MCU carrier board: --- +## 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:** +> +> +> 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 `riscv64-unknown-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. 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 000000000..c30d555eb --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/deploy.sh @@ -0,0 +1,141 @@ +#!/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 Override the ELF to flash +# (default: build/demo_threadx.elf). +# --openocd-cfg

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 'continue\ndetach\nquit\n' >> "${GDB_INIT_FILE}" + "${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}" + "${GDB}" --command="${GDB_INIT_FILE}" +fi + +info "ELF: ${ELF}" +riscv64-unknown-elf-size "${ELF}" 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 000000000..b4cd46a8a --- /dev/null +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/install_deps.sh @@ -0,0 +1,53 @@ +#!/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 +# ***************************************************************************/ + +# 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. +# +# 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)." + +PACKAGES=( + cmake + ninja-build + gcc-riscv64-unknown-elf + binutils-riscv64-unknown-elf + gdb-multiarch + openocd + usbutils + git +) + +info "Updating package lists ..." +sudo apt-get update -qq + +info "Installing: ${PACKAGES[*]}" +sudo apt-get install -y "${PACKAGES[@]}" + +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." From a28c1fc1772b0977a2dde23d99a3555a7d29a1e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Desbiens?= Date: Wed, 20 May 2026 16:56:34 -0400 Subject: [PATCH 3/8] Fixed BSP drivers: GPIO register interface, timer frequency, UART DMA width - Rewrote GPIO driver to match apb_gpiov2 pin-indexed register interface; SETGPIO/CLRGPIO/TOGGPIO/SETDIR all take a pin number, not a bitmask - Fixed pad-mux to use per-pad indexed registers at APB_SOC_CTRL + 0x400 + pad*4 (new apb_soc_ctrl v1.0.0 layout; old PADFUN0/1 layout was wrong) - Fixed FUNC_C (value 2) routes pad to GPIO/apbio; corrected io_pad offset (io_pad = gpio_pin + 7, verified in pad_control.sv) - Fixed timer CMP to use ARCHI_SOC_FREQUENCY (10 MHz, FC_CLK_PERIOD_NS=100) instead of the 32 kHz slow clock; CMP = 10000000 / 100 = 100000 - Added ARCHI_SOC_FREQUENCY = 10000000U to properties.h - Fixed UART DMA transfer width to match apb_uart 8-bit UDMA interface - Fixed tx_thread_context_restore.S: restore mscratch before mret to prevent context corruption on nested interrupts Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../gnu/example_build/core_v_mcu/bsp/gpio.c | 63 ++++++++++++++++--- .../core_v_mcu/bsp/system_core_v_mcu.c | 13 +++- .../example_build/core_v_mcu/bsp/timer_irq.c | 1 - .../core_v_mcu/bsp/uart_driver.c | 11 +--- .../example_build/core_v_mcu/include/gpio.h | 37 ++++++++--- .../core_v_mcu/include/properties.h | 3 + .../gnu/src/tx_thread_context_restore.S | 4 +- 7 files changed, 103 insertions(+), 29 deletions(-) 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 index e51bdb527..6392b1f28 100644 --- 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 @@ -19,6 +19,47 @@ #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. */ @@ -26,24 +67,32 @@ void gpio_init(void) void gpio_set_output(uint32_t pin_mask) { - uint32_t value = readw((uintptr_t)(PULP_GPIO_ADDR + GPIO_DIR_OFFSET)); + /* Route physical pads to the GPIO controller via the pad-mux (FUNC_C). */ + gpio_pinmux_set_func_c(pin_mask); - writew(value | pin_mask, (uintptr_t)(PULP_GPIO_ADDR + GPIO_DIR_OFFSET)); + /* 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) { - uint32_t value = readw((uintptr_t)(PULP_GPIO_ADDR + GPIO_OUT_OFFSET)); - - writew(value ^ pin_mask, (uintptr_t)(PULP_GPIO_ADDR + GPIO_OUT_OFFSET)); + gpio_foreach_pin(pin_mask, + (uintptr_t)(PULP_GPIO_ADDR + GPIO_TOGGPIO_OFFSET), + 0U); } void gpio_set(uint32_t pin_mask) { - writew(pin_mask, (uintptr_t)(PULP_GPIO_ADDR + GPIO_SET_OFFSET)); + gpio_foreach_pin(pin_mask, + (uintptr_t)(PULP_GPIO_ADDR + GPIO_SETGPIO_OFFSET), + 0U); } void gpio_clear(uint32_t pin_mask) { - writew(pin_mask, (uintptr_t)(PULP_GPIO_ADDR + GPIO_CLR_OFFSET)); + gpio_foreach_pin(pin_mask, + (uintptr_t)(PULP_GPIO_ADDR + GPIO_CLRGPIO_OFFSET), + 0U); } 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 index 990595e19..f142c95d0 100644 --- 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 @@ -16,6 +16,7 @@ #include #include +#include "csr.h" #include "fll.h" #include "gpio.h" #include "irq.h" @@ -28,6 +29,7 @@ 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; @@ -71,7 +73,7 @@ void system_init(void) isr_table[i] = tx_undefined_irq_handler; } - isr_table[10U] = tx_timer_irq_handler; + isr_table[7U] = tx_timer_irq_handler; for (i = 0U; i < ARCHI_NB_FLL; ++i) { @@ -79,8 +81,11 @@ void system_init(void) } pulp_irq_init(); - (void)timer_irq_init(ARCHI_REF_CLOCK / (uint32_t)TX_TIMER_TICKS_PER_SECOND); - irq_enable(IRQ_FC_EVT_TIMER0_LO); + (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) @@ -91,6 +96,8 @@ void system_init(void) 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; 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 index 55d53e933..9a085d9f4 100644 --- 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 @@ -27,7 +27,6 @@ int timer_irq_init(uint32_t ticks) writew(TIMER_CFG_LO_ENABLE_MASK | TIMER_CFG_LO_RESET_MASK | - TIMER_CFG_LO_CCFG_MASK | TIMER_CFG_LO_MODE_MASK | TIMER_CFG_LO_IRQEN_MASK, (uintptr_t)(PULP_FC_TIMER_ADDR + TIMER_CFG_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 index 35476f2a5..33b1ff150 100644 --- 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 @@ -22,7 +22,6 @@ #include "uart_driver.h" #define UDMA_CTRL_CG_OFFSET 0x00U -#define UDMA_CTRL_RST_OFFSET 0x08U #define UDMA_CTRL_UART0_CLKEN BIT(0) #define UDMA_UART_RX_SADDR_OFFSET 0x00U @@ -37,7 +36,9 @@ #define UDMA_UART_DATA_OFFSET 0x34U #define UDMA_CFG_EN_BIT BIT(4) -#define UDMA_UART_TX_CFG_ENABLE 0x12U +/* 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) @@ -60,7 +61,6 @@ int uart_init(uint8_t uart_id, uint32_t baudrate, uint32_t periph_freq) uint32_t clk_div; uint32_t setup; uint32_t cg; - uint32_t rst; if ((uart_id >= N_UART) || (baudrate == 0U) || (periph_freq == 0U)) { @@ -68,11 +68,6 @@ int uart_init(uint8_t uart_id, uint32_t baudrate, uint32_t periph_freq) } cg = readw((uintptr_t)(UDMA_CH_ADDR_CTRL + UDMA_CTRL_CG_OFFSET)); - rst = readw((uintptr_t)(UDMA_CH_ADDR_CTRL + UDMA_CTRL_RST_OFFSET)); - rst |= (uint32_t)(UDMA_CTRL_UART0_CLKEN << uart_id); - writew(rst, (uintptr_t)(UDMA_CH_ADDR_CTRL + UDMA_CTRL_RST_OFFSET)); - rst &= ~((uint32_t)(UDMA_CTRL_UART0_CLKEN << uart_id)); - writew(rst, (uintptr_t)(UDMA_CH_ADDR_CTRL + UDMA_CTRL_RST_OFFSET)); cg |= (uint32_t)(UDMA_CTRL_UART0_CLKEN << uart_id); writew(cg, (uintptr_t)(UDMA_CH_ADDR_CTRL + UDMA_CTRL_CG_OFFSET)); 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 index 52159a4df..8958035e0 100644 --- 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 @@ -18,14 +18,35 @@ #include -/* PULP GPIO register offsets */ -#define GPIO_DIR_OFFSET 0x000U -#define GPIO_EN_OFFSET 0x004U -#define GPIO_IN_OFFSET 0x008U -#define GPIO_OUT_OFFSET 0x00CU -#define GPIO_SET_OFFSET 0x010U -#define GPIO_CLR_OFFSET 0x014U -#define GPIO_TOGGLE_OFFSET 0x018U +/* 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); 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 index 33581a737..98473696f 100644 --- 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 @@ -17,7 +17,10 @@ #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 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 5fa80437d..c20cbcdf5 100644 --- a/ports/risc-v32/gnu/src/tx_thread_context_restore.S +++ b/ports/risc-v32/gnu/src/tx_thread_context_restore.S @@ -149,7 +149,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. */ @@ -284,7 +284,7 @@ _tx_thread_no_preempt_restore: csrr t1, mstatus li t2, 0x1888 // MPP(0x1800) | MPIE(0x80) | MIE(0x08) - li t3, 0x1880 // Set MPP=Machine(0x1800) + MPIE(0x80) so mret re-enables MIE + 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 From 980da1e7d2967323681126cf27fafb20a80efdb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Desbiens?= Date: Wed, 20 May 2026 16:57:40 -0400 Subject: [PATCH 4/8] Implemented demo application with startup banner and LED blink - Added startup banner assembled at compile time from tx_api.h version constants (THREADX_MAJOR/MINOR/PATCH/BUILD_VERSION) - Blinks LED[0] (IO pad 11, GPIO pin 4, MUX=2) at 1 Hz; this is the only LED wired to CORE-V MCU GPIO in the 2023-08-27 Nexys A7 pre-built bitfile - UART thread prints a dot every second to confirm scheduler is running - Banner credits: 'Eclipse ThreadX for OpenHW CORE-V MCU vX.Y.Z.BBBBB' Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../example_build/core_v_mcu/demo_threadx.c | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) 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 index 0f02ef702..c7daac3c4 100644 --- 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 @@ -22,9 +22,27 @@ #define DEMO_STACK_SIZE 1024U #define DEMO_BYTE_POOL_SIZE 4096U -#define DEMO_LED_PIN 5U +/* + * 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; @@ -42,12 +60,15 @@ 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_toggle((uint32_t)DEMO_LED_MASK); - tx_thread_sleep(50U); + 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 */ } } @@ -55,7 +76,7 @@ static void uart_thread_entry(ULONG arg) { TX_PARAMETER_NOT_USED(arg); - uart_write_str(0U, "\r\nThreadX on CORE-V MCU\r\n"); + uart_write_str(0U, banner); for (;;) { uart_write_str(0U, "."); From 5c3bf64d7b59e94c8922a733ae4ee0ebcf84d144 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Desbiens?= Date: Thu, 21 May 2026 11:16:16 -0400 Subject: [PATCH 5/8] Extended GPIO BSP with pinmux and individual-pin API - Added gpio_setpinmux(io_pad, mux) and gpio_getpinmux(io_pad) for arbitrary pad-mux control (any mux value 0-3, any io_pad) - Added gpio_pin_set_dir(pin, dir) for per-pin direction control (0=input, 1=push-pull output) without requiring a bitmask caller - Added gpio_pin_read_status(pin) that writes SETSEL and reads RDSTAT, returning the full pin status word used by the CLI gpio status command - Made APB_SOC_PADMUX_BASE and GPIO_IO_PAD_OFFSET module-level constants (no semantic change) so that gpio_setpinmux can use the same base Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../gnu/example_build/core_v_mcu/bsp/gpio.c | 30 +++++++++++++++++++ .../example_build/core_v_mcu/include/gpio.h | 11 +++++++ 2 files changed, 41 insertions(+) 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 index 6392b1f28..ddeba5b5d 100644 --- 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 @@ -96,3 +96,33 @@ void gpio_clear(uint32_t 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/include/gpio.h b/ports/risc-v32/gnu/example_build/core_v_mcu/include/gpio.h index 8958035e0..da5f60087 100644 --- 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 @@ -54,4 +54,15 @@ 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 */ From 970603d18df64ed94b16c66538bddf7f9f2a42b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Desbiens?= Date: Fri, 22 May 2026 08:29:41 -0400 Subject: [PATCH 6/8] Added polled UDMA I2C master and ADT7420 temperature sensor drivers Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../example_build/core_v_mcu/CMakeLists.txt | 2 + .../example_build/core_v_mcu/bsp/i2c_master.c | 153 ++++++++++++++++++ .../core_v_mcu/bsp/temp_sensor.c | 46 ++++++ .../example_build/core_v_mcu/include/csr.h | 9 +- .../core_v_mcu/include/i2c_master.h | 38 +++++ .../core_v_mcu/include/pulp_mem_map.h | 4 + .../core_v_mcu/include/temp_sensor.h | 29 ++++ 7 files changed, 278 insertions(+), 3 deletions(-) create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/bsp/i2c_master.c create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/bsp/temp_sensor.c create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/include/i2c_master.h create mode 100644 ports/risc-v32/gnu/example_build/core_v_mcu/include/temp_sensor.h 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 index a432bdc22..eb6b4c89f 100644 --- a/ports/risc-v32/gnu/example_build/core_v_mcu/CMakeLists.txt +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/CMakeLists.txt @@ -22,6 +22,8 @@ set(SRCS ${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 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 000000000..eb1461b9a --- /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 +#include + +#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/temp_sensor.c b/ports/risc-v32/gnu/example_build/core_v_mcu/bsp/temp_sensor.c new file mode 100644 index 000000000..6d70e9431 --- /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 + +#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/include/csr.h b/ports/risc-v32/gnu/example_build/core_v_mcu/include/csr.h index 71cf57478..69c7763e9 100644 --- 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 @@ -26,9 +26,12 @@ #define CSR_MCAUSE 0x342 #define CSR_MTVAL 0x343 #define CSR_MIP 0x344 -#define CSR_PMPCFG0 0x3A0 -#define CSR_PMPADDR0 0x3B0 -#define CSR_MHARTID 0xF14 +#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) 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 000000000..4a478b302 --- /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 + +/* 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/pulp_mem_map.h b/ports/risc-v32/gnu/example_build/core_v_mcu/include/pulp_mem_map.h index fc71cbffa..32757d624 100644 --- 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 @@ -93,6 +93,10 @@ /* 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) 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 000000000..e94c589a8 --- /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 + +/* 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 */ From 6f3a22b4819654e49bd9c830391a00ed22249216 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Desbiens?= Date: Mon, 25 May 2026 08:59:46 -0400 Subject: [PATCH 7/8] deploy: print ELF info before running; use monitor reset run Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ports/risc-v32/gnu/example_build/core_v_mcu/deploy.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) 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 index c30d555eb..c4438b702 100755 --- a/ports/risc-v32/gnu/example_build/core_v_mcu/deploy.sh +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/deploy.sh @@ -125,7 +125,9 @@ load if [ "${OPT_DEBUG}" -eq 0 ]; then info "Flashing and running (detached) ..." - printf 'continue\ndetach\nquit\n' >> "${GDB_INIT_FILE}" + printf 'monitor reset run\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" @@ -134,8 +136,7 @@ if [ "${OPT_DEBUG}" -eq 0 ]; then 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 - -info "ELF: ${ELF}" -riscv64-unknown-elf-size "${ELF}" From 2310ade00b39378ee5b9a303ec20a47d32c16268 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Desbiens?= Date: Mon, 25 May 2026 09:05:40 -0400 Subject: [PATCH 8/8] deploy: fix regression - use monitor resume not monitor reset run Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- ports/risc-v32/gnu/example_build/core_v_mcu/deploy.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index c4438b702..2e22ec1e9 100755 --- a/ports/risc-v32/gnu/example_build/core_v_mcu/deploy.sh +++ b/ports/risc-v32/gnu/example_build/core_v_mcu/deploy.sh @@ -125,7 +125,7 @@ load if [ "${OPT_DEBUG}" -eq 0 ]; then info "Flashing and running (detached) ..." - printf 'monitor reset run\ndisconnect\nquit\n' >> "${GDB_INIT_FILE}" + 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 \