Added port for the OpenHW CORE-V MCU platform (#535)

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 10 MHz SOC clock
     fll.c                Frequency Locked Loop -- 5 MHz to 50 MHz (FPGA)
     uart_driver.c        UDMA UART channel 0 -- polled TX, non-blocking RX (8-bit transfer width)
     gpio.c               PULP apb_gpiov2 driver: set/clear/toggle/direction by pin index;
                          pad-mux via per-pad indexed registers at APB_SOC_CTRL + 0x400 + pad*4;
                          full pinmux API (gpio_setpinmux, gpio_getpinmux, gpio_pin_set_dir,
                          gpio_pin_read_status)
     i2c_master.c         Polled UDMA I2C master
     adt7420.c            ADT7420 temperature sensor driver
     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: LED[0] blink at 1 Hz (IO pad 11, GPIO pin 4, MUX=2)
                      + UART heartbeat with startup banner
                      ("Eclipse ThreadX for OpenHW CORE-V MCU vX.Y.Z.BBBBB")
     link.ld          Linker script: .vectors@0x1c000800, .text@0x1c000880
     CMakeLists.txt   Build definition (references THREADX_ROOT)
     build.sh         One-shot CMake+Ninja build script

Tooling
     install_deps.sh               Automates toolchain/OpenOCD dependency setup
     deploy.sh                      One-step GDB flashing via Ashling Opella-LD;
                                           gdb-multiarch fallback when riscv64-unknown-elf-gdb is absent;
                                           supports --wsl flag required by usbipd-win v5.x
     openocd-nexys-Ashling-Opella-LD.cfg   OpenOCD config for Opella-LD over JTAG

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 the PULP/PULPissimo interrupt controller (not CLINT); IRQ lines
   are masked via APB registers, not the 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):
   (c) 2019-2020 ETH Zurich and University of Bologna
   (c) 2020 GreenWaves Technologies
   (c) 2011-2014 Wind River Systems, Inc.
   (c) 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>
This commit is contained in:
Frédéric Desbiens
2026-05-25 11:28:55 -04:00
committed by GitHub
parent 9ccdd5d822
commit 5513b1724a
45 changed files with 3358 additions and 2 deletions
+64
View File
@@ -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")
@@ -0,0 +1,65 @@
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/i2c_master.c
${CORE_V_MCU_DIR}/bsp/temp_sensor.c
${CORE_V_MCU_DIR}/bsp/system_core_v_mcu.c
${CORE_V_MCU_DIR}/bsp/string.c
${CORE_V_MCU_DIR}/demo_threadx.c
)
add_executable(demo_threadx ${SRCS})
set_target_properties(demo_threadx PROPERTIES OUTPUT_NAME "demo_threadx.elf")
target_include_directories(demo_threadx PRIVATE
${CORE_V_MCU_DIR}/include
${THREADX_ROOT}/common/inc
${THREADX_ROOT}/ports/risc-v32/gnu/inc
)
target_link_libraries(demo_threadx PRIVATE threadx gcc)
target_link_options(demo_threadx PRIVATE
-T${CORE_V_MCU_DIR}/link.ld
-nostartfiles
-nodefaultlibs
-Wl,--gc-sections
-Wl,-Map=${CMAKE_CURRENT_BINARY_DIR}/demo_threadx.map
)
target_compile_options(demo_threadx PRIVATE
-Os
-g
-Wall
-Wextra
-ffreestanding
-ffunction-sections
-fdata-sections
-DTX_INCLUDE_USER_DEFINE_FILE
)
add_custom_command(TARGET demo_threadx POST_BUILD
COMMAND ${OBJCOPY} -O binary $<TARGET_FILE:demo_threadx> ${CMAKE_CURRENT_BINARY_DIR}/demo_threadx.bin
COMMENT "Generating demo_threadx.bin"
)
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,209 @@
/***************************************************************************/
/* Copyright 2020 GreenWaves Technologies
* Copyright (C) 2026 Eclipse ThreadX contributors
*
* Derived from core-v-freertos (https://github.com/openhwgroup/core-v-freertos)
* Original work licensed under the Apache License, Version 2.0.
* See https://www.apache.org/licenses/LICENSE-2.0
* Modifications licensed under MIT (https://opensource.org/licenses/MIT).
*
* AI Disclosure: Some portions generated or modified by Copilot (Sonnet 4.6).
*
* SPDX-License-Identifier: Apache-2.0 AND MIT
***************************************************************************/
#include <stddef.h>
#include <stdint.h>
#include "bits.h"
#include "csr.h"
#include "fll.h"
#include "irq.h"
#include "properties.h"
#include "system_core_v_mcu.h"
#define FLL_LOG2_MAXDCO 29U
#define FLL_LOG2_REFCLK ARCHI_REF_CLOCK_LOG2
#define FLL_LOG2_MAXM (FLL_LOG2_MAXDCO - FLL_LOG2_REFCLK)
static volatile uint32_t flls_frequency[FLL_NUM];
static uint32_t fll_max_u32(uint32_t a, uint32_t b)
{
return (a > b) ? a : b;
}
static uint32_t fll_fl1(uint32_t value)
{
return (uint32_t)(31U - (uint32_t)__builtin_clz(value));
}
static uint32_t fll_get_mult_div_from_frequency(uint32_t freq, uint32_t *mult,
uint32_t *div)
{
uint32_t fref = FLL_REF_CLK;
uint32_t log2_m;
uint32_t d;
uint32_t m;
uint32_t fres;
if ((freq == 0U) || (mult == NULL) || (div == NULL))
{
if (mult != NULL)
{
*mult = 0U;
}
if (div != NULL)
{
*div = 0U;
}
return 0U;
}
if (freq > fref)
{
log2_m = fll_fl1(freq) - fll_fl1(fref);
}
else
{
log2_m = 0U;
}
if (FLL_LOG2_MAXM > log2_m)
{
d = fll_max_u32(1U, (FLL_LOG2_MAXM - log2_m) >> 1U);
}
else
{
d = 1U;
}
m = (freq << d) / fref;
fres = (fref * m + (1UL << (d - 1U))) >> d;
*mult = m;
*div = d + 1U;
return fres;
}
static uint32_t fll_get_frequency_from_mult_div(uint32_t mult, uint32_t div)
{
uint32_t fref = FLL_REF_CLK;
if (div == 0U)
{
return fref * mult;
}
return (fref * mult) >> (div - 1U);
}
int pi_fll_set_frequency(fll_type_t which_fll, uint32_t frequency, int check)
{
uint32_t mult;
uint32_t div;
uint32_t reg1;
uint32_t saved_irq;
(void)check;
if ((uint32_t)which_fll >= FLL_NUM)
{
return -1;
}
if (frequency == 0U)
{
return -1;
}
saved_irq = irq_clint_disable();
(void)fll_get_mult_div_from_frequency(frequency, &mult, &div);
reg1 = FLL_CTRL[which_fll].FLL_CONF1;
reg1 &= ~FLL_CTRL_CONF1_MULTI_FACTOR_MASK;
reg1 |= REG_SET(FLL_CTRL_CONF1_MULTI_FACTOR, mult);
reg1 &= ~FLL_CTRL_CONF1_CLK_OUT_DIV_MASK;
reg1 |= REG_SET(FLL_CTRL_CONF1_CLK_OUT_DIV, div);
FLL_CTRL[which_fll].FLL_CONF1 = reg1;
flls_frequency[which_fll] = frequency;
if (which_fll == FLL_SOC)
{
system_core_clock = frequency;
}
if ((saved_irq & MSTATUS_IE) != 0U)
{
(void)irq_clint_enable();
}
return (int)frequency;
}
void pi_fll_init(fll_type_t which_fll, uint32_t ret_state)
{
uint32_t reg1;
if ((uint32_t)which_fll >= FLL_NUM)
{
return;
}
if (ret_state != 0U)
{
(void)pi_fll_get_frequency(which_fll, 1U);
return;
}
reg1 = FLL_CTRL[which_fll].FLL_CONF1;
if (REG_GET(FLL_CTRL_CONF1_MODE, reg1) == 0U)
{
uint32_t reg2 = FLL_CTRL[which_fll].FLL_CONF2;
uint32_t regint = FLL_CTRL[which_fll].FLL_INTEGRATOR;
reg2 &= ~FLL_CTRL_CONF2_ASSERT_CYCLES_MASK;
reg2 |= REG_SET(FLL_CTRL_CONF2_ASSERT_CYCLES, 0x6U);
reg2 &= ~FLL_CTRL_CONF2_LOCK_TOLERANCE_MASK;
reg2 |= REG_SET(FLL_CTRL_CONF2_LOCK_TOLERANCE, 0x50U);
FLL_CTRL[which_fll].FLL_CONF2 = reg2;
regint &= ~FLL_CTRL_INTEGRATOR_INT_PART_MASK;
regint |= REG_SET(FLL_CTRL_INTEGRATOR_INT_PART, 332U);
FLL_CTRL[which_fll].FLL_INTEGRATOR = regint;
reg1 &= ~FLL_CTRL_CONF1_OUTPUT_LOCK_EN_MASK;
reg1 |= REG_SET(FLL_CTRL_CONF1_OUTPUT_LOCK_EN, 1U);
reg1 &= ~FLL_CTRL_CONF1_MODE_MASK;
reg1 |= REG_SET(FLL_CTRL_CONF1_MODE, 1U);
FLL_CTRL[which_fll].FLL_CONF1 = reg1;
}
if (flls_frequency[which_fll] != 0U)
{
(void)pi_fll_set_frequency(which_fll, flls_frequency[which_fll], 0);
}
else
{
flls_frequency[which_fll] = fll_get_frequency_from_mult_div(
REG_GET(FLL_CTRL_CONF1_MULTI_FACTOR, reg1),
REG_GET(FLL_CTRL_CONF1_CLK_OUT_DIV, reg1));
}
}
int pi_fll_get_frequency(fll_type_t which_fll, uint8_t real)
{
if ((uint32_t)which_fll >= FLL_NUM)
{
return -1;
}
if (real != 0U)
{
flls_frequency[which_fll] = fll_get_frequency_from_mult_div(
FLL_CTRL[which_fll].FLL_STATUS,
REG_GET(FLL_CTRL_CONF1_CLK_OUT_DIV, FLL_CTRL[which_fll].FLL_CONF1));
}
return (int)flls_frequency[which_fll];
}
@@ -0,0 +1,128 @@
/***************************************************************************
* Copyright (C) 2026 Eclipse ThreadX contributors
*
* This program and the accompanying materials are made available under the
* terms of the MIT License which is available at
* https://opensource.org/licenses/MIT.
*
* AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6).
* The AI-generated portions may be considered public domain (CC0-1.0)
* and not subject to the project's licence. The human contributor has
* reviewed and verified that the code is correct.
*
* SPDX-License-Identifier: MIT and CC0-1.0
**************************************************************************/
#include <stdint.h>
#include "pulp_mem_map.h"
#include "io.h"
#include "gpio.h"
/* APB SOC CTRL pad-mux interface (apb_soc_ctrl v1.0.0):
* Each IO pad has a dedicated 32-bit register at base + 0x400 + pad_index*4.
* PWDATA[1:0] = mux function (0=sys, 1=perio, 2=apbio/GPIO, 3=fpgaio)
* PWDATA[13:8] = pad config (drive/pull, optional)
*
* FUNC_C (2) routes the pad to the GPIO (apbio) controller.
* For the standard GPIO pins: io_pad = gpio_pin + GPIO_IO_PAD_OFFSET (=7).
* Verified in pad_control.sv: apbio[N] ↔ io_pad[N+7] when pad_mux[N+7]==2. */
#define APB_SOC_PADMUX_BASE 0x400U
#define PINMUX_FUNC_C 2U
#define GPIO_IO_PAD_OFFSET 7U
static void gpio_pinmux_set_func_c(uint32_t pin_mask)
{
for (uint32_t gpio_pin = 0U; gpio_pin < 32U; gpio_pin++)
{
if ((pin_mask & (1UL << gpio_pin)) == 0U)
{
continue;
}
uint32_t io_pad = gpio_pin + GPIO_IO_PAD_OFFSET;
uintptr_t reg = PULP_APB_SOC_CTRL_ADDR + APB_SOC_PADMUX_BASE
+ io_pad * 4U;
writew(PINMUX_FUNC_C, reg);
}
}
/* Iterate over every set bit in pin_mask and call writew(pin_number, addr).
* The apb_gpiov2 SETGPIO/CLRGPIO/TOGGPIO/SETDIR registers are all
* pin-indexed: the written word encodes a pin number, not a bitmask. */
static void gpio_foreach_pin(uint32_t pin_mask, uintptr_t reg_addr, uint32_t extra_bits)
{
for (uint32_t pin = 0U; pin < 32U; pin++)
{
if ((pin_mask & (1UL << pin)) != 0U)
{
writew(pin | extra_bits, reg_addr);
}
}
}
void gpio_init(void)
{
/* GPIO clocking is handled by platform boot code on CORE-V MCU. */
}
void gpio_set_output(uint32_t pin_mask)
{
/* Route physical pads to the GPIO controller via the pad-mux (FUNC_C). */
gpio_pinmux_set_func_c(pin_mask);
/* Configure each pin as push-pull output via SETDIR. */
gpio_foreach_pin(pin_mask,
(uintptr_t)(PULP_GPIO_ADDR + GPIO_SETDIR_OFFSET),
GPIO_DIR_OUTPUT);
}
void gpio_toggle(uint32_t pin_mask)
{
gpio_foreach_pin(pin_mask,
(uintptr_t)(PULP_GPIO_ADDR + GPIO_TOGGPIO_OFFSET),
0U);
}
void gpio_set(uint32_t pin_mask)
{
gpio_foreach_pin(pin_mask,
(uintptr_t)(PULP_GPIO_ADDR + GPIO_SETGPIO_OFFSET),
0U);
}
void gpio_clear(uint32_t pin_mask)
{
gpio_foreach_pin(pin_mask,
(uintptr_t)(PULP_GPIO_ADDR + GPIO_CLRGPIO_OFFSET),
0U);
}
void gpio_setpinmux(uint8_t io_pad, uint8_t mux)
{
uintptr_t reg = PULP_APB_SOC_CTRL_ADDR + APB_SOC_PADMUX_BASE
+ (uintptr_t)io_pad * 4U;
writew((uint32_t)mux & 0x3U, reg);
}
uint8_t gpio_getpinmux(uint8_t io_pad)
{
uintptr_t reg = PULP_APB_SOC_CTRL_ADDR + APB_SOC_PADMUX_BASE
+ (uintptr_t)io_pad * 4U;
return (uint8_t)(readw(reg) & 0x3U);
}
void gpio_pin_set_dir(uint8_t pin, uint8_t dir)
{
uint32_t val = (uint32_t)pin;
if (dir != 0U)
{
val |= GPIO_DIR_OUTPUT;
}
writew(val, (uintptr_t)(PULP_GPIO_ADDR + GPIO_SETDIR_OFFSET));
}
uint32_t gpio_pin_read_status(uint8_t pin)
{
writew((uint32_t)pin, (uintptr_t)(PULP_GPIO_ADDR + GPIO_SETSEL_OFFSET));
return readw((uintptr_t)(PULP_GPIO_ADDR + GPIO_RDSTAT_OFFSET));
}
@@ -0,0 +1,153 @@
/***************************************************************************
* Copyright (C) 2026 Eclipse ThreadX contributors
*
* This program and the accompanying materials are made available under the
* terms of the MIT License which is available at
* https://opensource.org/licenses/MIT.
*
* AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6).
* The AI-generated portions may be considered public domain (CC0-1.0)
* and not subject to the project's licence. The human contributor has
* reviewed and verified that the code is correct.
*
* SPDX-License-Identifier: MIT and CC0-1.0
**************************************************************************/
#include <stddef.h>
#include <stdint.h>
#include "bits.h"
#include "i2c_master.h"
#include "io.h"
#include "pulp_mem_map.h"
#define UDMA_I2C_RX_SADDR_OFFSET 0x00U
#define UDMA_I2C_RX_SIZE_OFFSET 0x04U
#define UDMA_I2C_RX_CFG_OFFSET 0x08U
#define UDMA_I2C_TX_SADDR_OFFSET 0x10U
#define UDMA_I2C_TX_SIZE_OFFSET 0x14U
#define UDMA_I2C_TX_CFG_OFFSET 0x18U
#define UDMA_I2C_STATUS_OFFSET 0x20U
#define UDMA_I2C_SETUP_OFFSET 0x24U
#define UDMA_CTRL_CG_OFFSET 0x00U
#define UDMA_CFG_EN_BIT BIT(4)
#define UDMA_CFG_PENDING_BIT BIT(5)
#define UDMA_I2C_STATUS_AL_BIT BIT(1)
#define UDMA_I2C_SETUP_RESET_BIT BIT(0)
#define I2C_MASTER_COUNT 2U
#define I2C_TIMEOUT_ITERATIONS 1000000U
#define I2C_CMD_CFG 0xE0U
#define I2C_CMD_START 0x00U
#define I2C_CMD_STOP 0x20U
#define I2C_CMD_RD_ACK 0x40U
#define I2C_CMD_RD_NACK 0x60U
#define I2C_CMD_WR 0x80U
#define I2C_CMD_WAIT 0xA0U
#define I2C_CMD_RPT 0xC0U
static uint8_t s_cmd_buf[32];
static uint8_t s_clkdiv[I2C_MASTER_COUNT];
static uintptr_t i2c_master_base(uint8_t id)
{
return (id == 0U) ? (uintptr_t)UDMA_CH_ADDR_I2CM0 : (uintptr_t)UDMA_CH_ADDR_I2CM1;
}
int i2c_master_init(uint8_t id, uint32_t i2c_freq, uint32_t periph_freq)
{
uint32_t cg;
uint32_t clk_div;
uintptr_t base;
if ((id >= I2C_MASTER_COUNT) || (i2c_freq == 0U) || (periph_freq == 0U))
{
return -1;
}
cg = readw((uintptr_t)(UDMA_CH_ADDR_CTRL + UDMA_CTRL_CG_OFFSET));
cg |= (id == 0U) ? UDMA_CTRL_I2CM0_CLKEN : UDMA_CTRL_I2CM1_CLKEN;
writew(cg, (uintptr_t)(UDMA_CH_ADDR_CTRL + UDMA_CTRL_CG_OFFSET));
clk_div = periph_freq / i2c_freq;
if (clk_div == 0U)
{
clk_div = 1U;
}
else if (clk_div > 255U)
{
clk_div = 255U;
}
s_clkdiv[id] = (uint8_t)clk_div;
base = i2c_master_base(id);
writew(UDMA_I2C_SETUP_RESET_BIT, base + UDMA_I2C_SETUP_OFFSET);
writew(0U, base + UDMA_I2C_SETUP_OFFSET);
writew(0U, base + UDMA_I2C_RX_CFG_OFFSET);
writew(0U, base + UDMA_I2C_TX_CFG_OFFSET);
return 0;
}
int i2c_master_read_reg(uint8_t id, uint8_t dev_addr7, uint8_t reg_addr,
uint8_t *buf, uint8_t len)
{
uintptr_t base;
uint32_t timeout;
uint32_t cmd_len;
uint32_t status;
if ((id >= I2C_MASTER_COUNT) || (dev_addr7 > 0x7FU) || (buf == NULL) || (len == 0U))
{
return -1;
}
cmd_len = 0U;
s_cmd_buf[cmd_len++] = I2C_CMD_CFG;
s_cmd_buf[cmd_len++] = 0U;
s_cmd_buf[cmd_len++] = s_clkdiv[id];
s_cmd_buf[cmd_len++] = I2C_CMD_START;
s_cmd_buf[cmd_len++] = I2C_CMD_WR;
s_cmd_buf[cmd_len++] = (uint8_t)(dev_addr7 << 1U);
s_cmd_buf[cmd_len++] = I2C_CMD_WR;
s_cmd_buf[cmd_len++] = reg_addr;
s_cmd_buf[cmd_len++] = I2C_CMD_START;
s_cmd_buf[cmd_len++] = I2C_CMD_WR;
s_cmd_buf[cmd_len++] = (uint8_t)((dev_addr7 << 1U) | 1U);
if (len > 1U)
{
s_cmd_buf[cmd_len++] = I2C_CMD_RPT;
s_cmd_buf[cmd_len++] = (uint8_t)(len - 1U);
s_cmd_buf[cmd_len++] = I2C_CMD_RD_ACK;
}
s_cmd_buf[cmd_len++] = I2C_CMD_RD_NACK;
s_cmd_buf[cmd_len++] = I2C_CMD_STOP;
s_cmd_buf[cmd_len++] = I2C_CMD_WAIT;
s_cmd_buf[cmd_len++] = 0U;
base = i2c_master_base(id);
writew((uint32_t)(uintptr_t)buf, base + UDMA_I2C_RX_SADDR_OFFSET);
writew((uint32_t)len, base + UDMA_I2C_RX_SIZE_OFFSET);
writew(UDMA_CFG_EN_BIT, base + UDMA_I2C_RX_CFG_OFFSET);
writew((uint32_t)(uintptr_t)s_cmd_buf, base + UDMA_I2C_TX_SADDR_OFFSET);
writew(cmd_len, base + UDMA_I2C_TX_SIZE_OFFSET);
writew(UDMA_CFG_EN_BIT, base + UDMA_I2C_TX_CFG_OFFSET);
for (timeout = 0U; timeout < I2C_TIMEOUT_ITERATIONS; ++timeout)
{
status = readw(base + UDMA_I2C_STATUS_OFFSET);
if ((status & UDMA_I2C_STATUS_AL_BIT) != 0U)
{
return -1;
}
if (((readw(base + UDMA_I2C_TX_CFG_OFFSET) & UDMA_CFG_PENDING_BIT) == 0U) &&
((readw(base + UDMA_I2C_RX_CFG_OFFSET) & UDMA_CFG_PENDING_BIT) == 0U))
{
return 0;
}
}
return -1;
}
@@ -0,0 +1,50 @@
/***************************************************************************/
/* Copyright (C) 2019 ETH Zurich and University of Bologna
* Copyright (C) 2026 Eclipse ThreadX contributors
*
* Derived from core-v-freertos (https://github.com/openhwgroup/core-v-freertos)
* Original work licensed under the Apache License, Version 2.0.
* See https://www.apache.org/licenses/LICENSE-2.0
* Modifications licensed under MIT (https://opensource.org/licenses/MIT).
*
* AI Disclosure: Some portions generated or modified by Copilot (Sonnet 4.6).
*
* SPDX-License-Identifier: Apache-2.0 AND MIT
***************************************************************************/
#include <stdint.h>
#include "pulp_mem_map.h"
#include "io.h"
#include "irq.h"
#include "csr.h"
void irq_mask(uint32_t mask)
{
writew(mask, (uintptr_t)(PULP_FC_IRQ_ADDR + IRQ_REG_MASK_OFFSET));
}
void irq_enable(uint32_t mask)
{
writew(mask, (uintptr_t)(PULP_FC_IRQ_ADDR + IRQ_REG_MASK_SET_OFFSET));
}
void irq_disable(uint32_t mask)
{
writew(mask, (uintptr_t)(PULP_FC_IRQ_ADDR + IRQ_REG_MASK_CLEAR_OFFSET));
}
uint32_t irq_clint_disable(void)
{
return (uint32_t)csr_read_clear(CSR_MSTATUS, MSTATUS_IE);
}
uint32_t irq_clint_enable(void)
{
return (uint32_t)csr_read_set(CSR_MSTATUS, MSTATUS_IE);
}
void pulp_irq_init(void)
{
irq_disable(0xFFFFFFFFUL);
}
@@ -0,0 +1,64 @@
/***************************************************************************
* Copyright (C) 2026 Eclipse ThreadX contributors
*
* This program and the accompanying materials are made available under the
* terms of the MIT License which is available at
* https://opensource.org/licenses/MIT.
*
* AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6).
* The AI-generated portions may be considered public domain (CC0-1.0)
* and not subject to the project's licence. The human contributor has
* reviewed and verified that the code is correct.
*
* SPDX-License-Identifier: MIT and CC0-1.0
**************************************************************************/
#include <stddef.h>
#include <stdint.h>
#include "string.h"
void *memcpy(void *dest, const void *src, size_t n)
{
size_t index;
uint8_t *dst = (uint8_t *)dest;
const uint8_t *source = (const uint8_t *)src;
for (index = 0U; index < n; ++index)
{
dst[index] = source[index];
}
return dest;
}
void *memset(void *dest, int c, size_t n)
{
size_t index;
uint8_t *dst = (uint8_t *)dest;
uint8_t value = (uint8_t)c;
for (index = 0U; index < n; ++index)
{
dst[index] = value;
}
return dest;
}
int memcmp(const void *lhs, const void *rhs, size_t n)
{
size_t index;
const uint8_t *left = (const uint8_t *)lhs;
const uint8_t *right = (const uint8_t *)rhs;
for (index = 0U; index < n; ++index)
{
if (left[index] != right[index])
{
return (left[index] < right[index]) ? -1 : 1;
}
}
return 0;
}
@@ -0,0 +1,132 @@
/***************************************************************************
* Copyright (C) 2026 Eclipse ThreadX contributors
*
* This program and the accompanying materials are made available under the
* terms of the MIT License which is available at
* https://opensource.org/licenses/MIT.
*
* AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6).
* The AI-generated portions may be considered public domain (CC0-1.0)
* and not subject to the project's licence. The human contributor has
* reviewed and verified that the code is correct.
*
* SPDX-License-Identifier: MIT and CC0-1.0
**************************************************************************/
#include <stddef.h>
#include <stdint.h>
#include "csr.h"
#include "fll.h"
#include "gpio.h"
#include "irq.h"
#include "properties.h"
#include "system_core_v_mcu.h"
#include "timer_irq.h"
#include "tx_api.h"
#include "uart_driver.h"
extern void _tx_timer_interrupt(void);
volatile uint32_t system_core_clock = DEFAULT_SYSTEM_CLOCK;
volatile uint32_t last_trap_mcause;
void (*isr_table[32])(void);
static uint32_t uart_console_ready;
static void uart_write_hex32(uint32_t value)
{
static const char hex[] = "0123456789ABCDEF";
int shift;
uart_write_str(0U, "0x");
for (shift = 28; shift >= 0; shift -= 4)
{
uart_write_byte(0U, (uint8_t)hex[(value >> (uint32_t)shift) & 0xFU]);
}
}
void tx_undefined_irq_handler(void)
{
if (uart_console_ready != 0U)
{
uart_write_str(0U, "\r\nUndefined IRQ\r\n");
}
/* MISRA deviation: intentional infinite loop used as a fault sink. */
for (;;)
{
}
}
void tx_timer_irq_handler(void)
{
_tx_timer_interrupt();
}
void system_init(void)
{
uint32_t i;
for (i = 0U; i < 32U; ++i)
{
isr_table[i] = tx_undefined_irq_handler;
}
isr_table[7U] = tx_timer_irq_handler;
for (i = 0U; i < ARCHI_NB_FLL; ++i)
{
pi_fll_init((fll_type_t)i, 0U);
}
pulp_irq_init();
(void)timer_irq_init(ARCHI_SOC_FREQUENCY / (uint32_t)TX_TIMER_TICKS_PER_SECOND);
/* CV32E40P routes the FC Timer LO to irq_i[7] (MTI, bit 7), which maps
* to mip[7]. IRQ_MASK in cv32e40p_cs_registers forces mie[10] to zero,
* so bit 7 is the correct enable bit. mcause will be 0x80000007. */
(void)csr_read_set(CSR_MIE, BIT(7));
gpio_init();
if (uart_init(0U, 115200U, ARCHI_FPGA_FREQUENCY) == 0)
{
uart_console_ready = 1U;
}
}
void tx_trap_handler(uint32_t mcause, uint32_t mepc, uint32_t mtval)
{
last_trap_mcause = mcause;
if ((mcause & 0x80000000UL) != 0UL)
{
uint32_t irq_id = mcause & 0x1FUL;
if (isr_table[irq_id] != NULL)
{
isr_table[irq_id]();
}
else
{
tx_undefined_irq_handler();
}
}
else
{
if (uart_console_ready != 0U)
{
uart_write_str(0U, "\r\nTrap mcause=");
uart_write_hex32(mcause);
uart_write_str(0U, " mepc=");
uart_write_hex32(mepc);
uart_write_str(0U, " mtval=");
uart_write_hex32(mtval);
uart_write_str(0U, "\r\n");
}
/* MISRA deviation: intentional infinite loop used as a fault sink. */
for (;;)
{
}
}
}
@@ -0,0 +1,46 @@
/***************************************************************************
* Copyright (C) 2026 Eclipse ThreadX contributors
*
* This program and the accompanying materials are made available under the
* terms of the MIT License which is available at
* https://opensource.org/licenses/MIT.
*
* AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6).
* The AI-generated portions may be considered public domain (CC0-1.0)
* and not subject to the project's licence. The human contributor has
* reviewed and verified that the code is correct.
*
* SPDX-License-Identifier: MIT and CC0-1.0
**************************************************************************/
#include <stdint.h>
#include "i2c_master.h"
#include "properties.h"
#include "temp_sensor.h"
#define ADT7420_I2C_ID 1U
#define ADT7420_ADDR7 0x4BU
#define ADT7420_TEMP_REG 0x00U
#define ADT7420_I2C_FREQ_HZ 200000U
void temp_sensor_init(void)
{
(void)i2c_master_init(ADT7420_I2C_ID, ADT7420_I2C_FREQ_HZ, ARCHI_FPGA_FREQUENCY);
}
int16_t temp_read_celsius_x10(void)
{
uint8_t buf[2];
int16_t raw;
int16_t value;
if (i2c_master_read_reg(ADT7420_I2C_ID, ADT7420_ADDR7, ADT7420_TEMP_REG, &buf[0], 2U) != 0)
{
return INT16_MIN;
}
raw = (int16_t)(((uint16_t)buf[0] << 8U) | (uint16_t)buf[1]);
value = (int16_t)(raw >> 3);
return (int16_t)(((int32_t)value * 5) / 8);
}
@@ -0,0 +1,58 @@
/***************************************************************************/
/* Copyright (C) 2019 ETH Zurich and University of Bologna
* Copyright (C) 2026 Eclipse ThreadX contributors
*
* Derived from core-v-freertos (https://github.com/openhwgroup/core-v-freertos)
* Original work licensed under the Apache License, Version 2.0.
* See https://www.apache.org/licenses/LICENSE-2.0
* Modifications licensed under MIT (https://opensource.org/licenses/MIT).
*
* AI Disclosure: Some portions generated or modified by Copilot (Sonnet 4.6).
*
* SPDX-License-Identifier: Apache-2.0 AND MIT
***************************************************************************/
#include <stdint.h>
#include "pulp_mem_map.h"
#include "io.h"
#include "timer.h"
#include "timer_irq.h"
static uint32_t last_count;
int timer_irq_init(uint32_t ticks)
{
(void)timer_irq_set_timeout(ticks);
writew(TIMER_CFG_LO_ENABLE_MASK |
TIMER_CFG_LO_RESET_MASK |
TIMER_CFG_LO_MODE_MASK |
TIMER_CFG_LO_IRQEN_MASK,
(uintptr_t)(PULP_FC_TIMER_ADDR + TIMER_CFG_LO_OFFSET));
last_count = 0U;
return 0;
}
int timer_irq_set_timeout(uint32_t ticks)
{
writew(1U, (uintptr_t)(PULP_FC_TIMER_ADDR + TIMER_RESET_LO_OFFSET));
writew(ticks, (uintptr_t)(PULP_FC_TIMER_ADDR + TIMER_CMP_LO_OFFSET));
last_count = 0U;
return 0;
}
uint32_t timer_irq_clock_elapsed(void)
{
uint32_t current = timer_irq_cycle_get_32();
uint32_t elapsed = current - last_count;
last_count = current;
return elapsed;
}
uint32_t timer_irq_cycle_get_32(void)
{
return readw((uintptr_t)(PULP_FC_TIMER_ADDR + TIMER_CNT_LO_OFFSET));
}
@@ -0,0 +1,169 @@
/***************************************************************************
* Copyright (C) 2026 Eclipse ThreadX contributors
*
* This program and the accompanying materials are made available under the
* terms of the MIT License which is available at
* https://opensource.org/licenses/MIT.
*
* AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6).
* The AI-generated portions may be considered public domain (CC0-1.0)
* and not subject to the project's licence. The human contributor has
* reviewed and verified that the code is correct.
*
* SPDX-License-Identifier: MIT and CC0-1.0
**************************************************************************/
#include <stddef.h>
#include <stdint.h>
#include "bits.h"
#include "io.h"
#include "pulp_mem_map.h"
#include "uart_driver.h"
#define UDMA_CTRL_CG_OFFSET 0x00U
#define UDMA_CTRL_UART0_CLKEN BIT(0)
#define UDMA_UART_RX_SADDR_OFFSET 0x00U
#define UDMA_UART_RX_SIZE_OFFSET 0x04U
#define UDMA_UART_RX_CFG_OFFSET 0x08U
#define UDMA_UART_TX_SADDR_OFFSET 0x10U
#define UDMA_UART_TX_SIZE_OFFSET 0x14U
#define UDMA_UART_TX_CFG_OFFSET 0x18U
#define UDMA_UART_STATUS_OFFSET 0x20U
#define UDMA_UART_SETUP_OFFSET 0x24U
#define UDMA_UART_VALID_OFFSET 0x30U
#define UDMA_UART_DATA_OFFSET 0x34U
#define UDMA_CFG_EN_BIT BIT(4)
/* EN (bit 4) only; DATASIZE=0 for 8-bit transfers; 0x12 would set DATASIZE=1
* (16-bit) and cause the EN bit to never clear for single-byte buffers. */
#define UDMA_UART_TX_CFG_ENABLE 0x10U
#define UDMA_UART_WORDLEN_8_BITS 3U
#define UDMA_UART_TX_ENABLE_BIT BIT(8)
#define UDMA_UART_RX_ENABLE_BIT BIT(9)
#define UDMA_UART_RX_POLLING_BIT BIT(4)
#define UDMA_UART_RX_CLEAN_BIT BIT(5)
#define UDMA_UART_DIV_SHIFT 16U
#define UDMA_UART_VALID_DATA_BIT BIT(0)
#define UDMA_UART_STATUS_TX_BUSY BIT(0)
#define UDMA_UART_MAX_TRANSFER 0xFFFFU
static uint8_t uart_tx_byte[N_UART];
static uintptr_t uart_base(uint8_t uart_id)
{
return (uintptr_t)(UDMA_CH_ADDR_UART + ((uintptr_t)uart_id * (uintptr_t)UDMA_CH_SIZE));
}
int uart_init(uint8_t uart_id, uint32_t baudrate, uint32_t periph_freq)
{
uint32_t clk_div;
uint32_t setup;
uint32_t cg;
if ((uart_id >= N_UART) || (baudrate == 0U) || (periph_freq == 0U))
{
return -1;
}
cg = readw((uintptr_t)(UDMA_CH_ADDR_CTRL + UDMA_CTRL_CG_OFFSET));
cg |= (uint32_t)(UDMA_CTRL_UART0_CLKEN << uart_id);
writew(cg, (uintptr_t)(UDMA_CH_ADDR_CTRL + UDMA_CTRL_CG_OFFSET));
clk_div = periph_freq / baudrate;
if (clk_div == 0U)
{
clk_div = 1U;
}
setup = (clk_div << UDMA_UART_DIV_SHIFT) |
UDMA_UART_TX_ENABLE_BIT |
UDMA_UART_RX_ENABLE_BIT |
UDMA_UART_RX_POLLING_BIT |
UDMA_UART_RX_CLEAN_BIT |
(UDMA_UART_WORDLEN_8_BITS << 1U);
writew(setup, uart_base(uart_id) + UDMA_UART_SETUP_OFFSET);
writew(0U, uart_base(uart_id) + UDMA_UART_RX_SADDR_OFFSET);
writew(0U, uart_base(uart_id) + UDMA_UART_RX_SIZE_OFFSET);
writew(0U, uart_base(uart_id) + UDMA_UART_RX_CFG_OFFSET);
return 0;
}
void uart_write_byte(uint8_t uart_id, uint8_t c)
{
if (uart_id >= N_UART)
{
return;
}
uart_tx_byte[uart_id] = c;
uart_write_buf(uart_id, &uart_tx_byte[uart_id], 1U);
}
int uart_read_byte(uint8_t uart_id)
{
if (uart_id >= N_UART)
{
return -1;
}
if ((readw(uart_base(uart_id) + UDMA_UART_VALID_OFFSET) & UDMA_UART_VALID_DATA_BIT) == 0U)
{
return -1;
}
return (int)(readw(uart_base(uart_id) + UDMA_UART_DATA_OFFSET) & 0xFFU);
}
void uart_write_str(uint8_t uart_id, const char *str)
{
if (str == NULL)
{
return;
}
while (*str != '\0')
{
uart_write_byte(uart_id, (uint8_t)*str);
++str;
}
}
void uart_write_buf(uint8_t uart_id, const uint8_t *buf, uint32_t len)
{
uintptr_t base_addr;
const uint8_t *current;
uint32_t remaining;
if ((uart_id >= N_UART) || (buf == NULL) || (len == 0U))
{
return;
}
base_addr = uart_base(uart_id);
current = buf;
remaining = len;
while (remaining > 0U)
{
uint32_t chunk = (remaining > UDMA_UART_MAX_TRANSFER) ? UDMA_UART_MAX_TRANSFER : remaining;
while ((readw(base_addr + UDMA_UART_STATUS_OFFSET) & UDMA_UART_STATUS_TX_BUSY) != 0U)
{
}
writew((uint32_t)(uintptr_t)current, base_addr + UDMA_UART_TX_SADDR_OFFSET);
writew(chunk, base_addr + UDMA_UART_TX_SIZE_OFFSET);
writew(UDMA_UART_TX_CFG_ENABLE, base_addr + UDMA_UART_TX_CFG_OFFSET);
while ((readw(base_addr + UDMA_UART_TX_CFG_OFFSET) & UDMA_CFG_EN_BIT) != 0U)
{
}
current += chunk;
remaining -= chunk;
}
}
+9
View File
@@ -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"
@@ -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
@@ -0,0 +1,134 @@
/***************************************************************************
* Copyright (C) 2026 Eclipse ThreadX contributors
*
* This program and the accompanying materials are made available under the
* terms of the MIT License which is available at
* https://opensource.org/licenses/MIT.
*
* AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6).
* The AI-generated portions may be considered public domain (CC0-1.0)
* and not subject to the project's licence. The human contributor has
* reviewed and verified that the code is correct.
*
* SPDX-License-Identifier: MIT and CC0-1.0
**************************************************************************/
#include <stdint.h>
#include "gpio.h"
#include "system_core_v_mcu.h"
#include "tx_api.h"
#include "uart_driver.h"
#define DEMO_STACK_SIZE 1024U
#define DEMO_BYTE_POOL_SIZE 4096U
/*
* LED[0] on Nexys A7 = IO pad 11 = GPIO/apbio pin 4 (MUX=2).
* Verified in the quick-start guide and the 2022 nexys-pin-table.csv used to
* synthesise the pre-built bitfile (io_pad = gpio_pin + 7).
*/
#define DEMO_LED_PIN 4U
#define DEMO_LED_MASK (1UL << DEMO_LED_PIN)
/* Assemble the version banner string at compile time from tx_api.h constants. */
#define _TX_STR(x) #x
#define TX_STR(x) _TX_STR(x)
#define TX_VERSION_STRING \
"v" TX_STR(THREADX_MAJOR_VERSION) \
"." TX_STR(THREADX_MINOR_VERSION) \
"." TX_STR(THREADX_PATCH_VERSION) \
"." TX_STR(THREADX_BUILD_VERSION)
static const char banner[] =
"\r\nEclipse ThreadX for OpenHW CORE-V MCU " TX_VERSION_STRING "\r\n"
"Copyright (c) 2026 Eclipse ThreadX Contributors\r\n\r\n";
static TX_THREAD blinky_thread;
static TX_THREAD uart_thread;
static TX_BYTE_POOL byte_pool;
static uint8_t memory_area[DEMO_BYTE_POOL_SIZE];
static void demo_error_loop(void)
{
/* MISRA deviation: intentional infinite loop used as a fault sink. */
for (;;)
{
}
}
static void blinky_thread_entry(ULONG arg)
{
TX_PARAMETER_NOT_USED(arg);
/* Configure LED[0] as push-pull output (sets pad-mux to FUNC_C = 2). */
gpio_set_output((uint32_t)DEMO_LED_MASK);
for (;;)
{
gpio_set((uint32_t)DEMO_LED_MASK);
tx_thread_sleep(100U); /* 1 s on (100 ticks × 10 ms) */
gpio_clear((uint32_t)DEMO_LED_MASK);
tx_thread_sleep(100U); /* 1 s off */
}
}
static void uart_thread_entry(ULONG arg)
{
TX_PARAMETER_NOT_USED(arg);
uart_write_str(0U, banner);
for (;;)
{
uart_write_str(0U, ".");
tx_thread_sleep(100U);
}
}
void tx_application_define(void *first_unused_memory)
{
CHAR *stack_ptr = TX_NULL;
UINT status;
TX_PARAMETER_NOT_USED(first_unused_memory);
status = tx_byte_pool_create(&byte_pool, "demo pool", memory_area, DEMO_BYTE_POOL_SIZE);
if (status != TX_SUCCESS)
{
demo_error_loop();
}
status = tx_byte_allocate(&byte_pool, (VOID **)&stack_ptr, DEMO_STACK_SIZE, TX_NO_WAIT);
if (status != TX_SUCCESS)
{
demo_error_loop();
}
status = tx_thread_create(&blinky_thread, "blinky", blinky_thread_entry, 0U,
stack_ptr, DEMO_STACK_SIZE, 1U, 1U,
TX_NO_TIME_SLICE, TX_AUTO_START);
if (status != TX_SUCCESS)
{
demo_error_loop();
}
status = tx_byte_allocate(&byte_pool, (VOID **)&stack_ptr, DEMO_STACK_SIZE, TX_NO_WAIT);
if (status != TX_SUCCESS)
{
demo_error_loop();
}
status = tx_thread_create(&uart_thread, "uart", uart_thread_entry, 0U,
stack_ptr, DEMO_STACK_SIZE, 2U, 2U,
TX_NO_TIME_SLICE, TX_AUTO_START);
if (status != TX_SUCCESS)
{
demo_error_loop();
}
}
int main(void)
{
system_init();
tx_kernel_enter();
return 0;
}
+142
View File
@@ -0,0 +1,142 @@
#!/usr/bin/env bash
# /***************************************************************************/
# /* Copyright (C) 2026 Eclipse ThreadX contributors
# *
# * This program and the accompanying materials are made available under the
# * terms of the MIT License which is available at
# * https://opensource.org/licenses/MIT.
# *
# * AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6).
# * The AI-generated portions may be considered public domain (CC0-1.0)
# * and not subject to the project's licence.
# *
# * SPDX-License-Identifier: MIT AND CC0-1.0
# ***************************************************************************/
# deploy.sh -- build (optionally), flash, and run/debug the CORE-V MCU target
# via the Ashling Opella LD debug probe.
#
# Usage:
# bash deploy.sh [options]
#
# Options:
# --build Run build.sh before flashing (default: skip).
# --debug After loading, stop at main and leave GDB attached
# (default: run free and exit GDB).
# --elf <path> Override the ELF to flash
# (default: build/demo_threadx.elf).
# --openocd-cfg <p> Override the OpenOCD config file.
#
# Prerequisites:
# * riscv64-unknown-elf-gdb and openocd must be on PATH.
# * Run setup_opella.sh once to configure udev rules.
# * The Nexys A7 board must be powered on.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Prefer a bare-metal RISC-V GDB; fall back to gdb-multiarch (Ubuntu apt default)
if command -v riscv64-unknown-elf-gdb >/dev/null 2>&1; then
GDB="riscv64-unknown-elf-gdb"
elif command -v gdb-multiarch >/dev/null 2>&1; then
GDB="gdb-multiarch"
else
GDB="riscv64-unknown-elf-gdb" # will produce a clear error below
fi
OPENOCD_LOG="/tmp/openocd_demo_threadx.log"
OPENOCD_PID=""
OPENOCD_TIMEOUT=10
DEFAULT_ELF="${SCRIPT_DIR}/build/demo_threadx.elf"
DEFAULT_CFG="${SCRIPT_DIR}/openocd-nexys-Ashling-Opella-LD.cfg"
OPT_BUILD=0
OPT_DEBUG=0
OPT_ELF=""
OPT_CFG=""
usage() { grep '^# ' "$0" | sed 's/^# //' | head -30; exit 0; }
while [[ $# -gt 0 ]]; do
case "$1" in
--build) OPT_BUILD=1; shift ;;
--debug) OPT_DEBUG=1; shift ;;
--elf) OPT_ELF="$2"; shift 2 ;;
--openocd-cfg) OPT_CFG="$2"; shift 2 ;;
-h|--help) usage ;;
*) echo "[ERROR] Unknown option: $1" >&2; usage ;;
esac
done
ELF="${OPT_ELF:-${DEFAULT_ELF}}"
CFG="${OPT_CFG:-${DEFAULT_CFG}}"
info() { echo "[INFO] $*"; }
die() { echo "[ERROR] $*" >&2; exit 1; }
stop_openocd() {
if [ -n "${OPENOCD_PID}" ] && kill -0 "${OPENOCD_PID}" 2>/dev/null; then
info "Stopping OpenOCD (PID ${OPENOCD_PID}) ..."
kill "${OPENOCD_PID}" 2>/dev/null || true
wait "${OPENOCD_PID}" 2>/dev/null || true
fi
}
trap stop_openocd EXIT INT TERM
if [ "${OPT_BUILD}" -eq 1 ]; then
info "Running build.sh ..."
bash "${SCRIPT_DIR}/build.sh"
fi
[ -f "${ELF}" ] || die "ELF not found: ${ELF} (run with --build to build first)"
[ -f "${CFG}" ] || die "OpenOCD config not found: ${CFG}"
command -v openocd >/dev/null 2>&1 || die "openocd not found on PATH"
command -v "${GDB}" >/dev/null 2>&1 || die "${GDB} not found on PATH"
info "Starting OpenOCD with config: ${CFG}"
info "Log: ${OPENOCD_LOG}"
openocd -f "${CFG}" >"${OPENOCD_LOG}" 2>&1 &
OPENOCD_PID=$!
info "Waiting for OpenOCD to be ready (up to ${OPENOCD_TIMEOUT}s) ..."
ELAPSED=0
while ! grep -q "Ready for Remote Connections" "${OPENOCD_LOG}" 2>/dev/null; do
sleep 1
ELAPSED=$((ELAPSED + 1))
if [ "${ELAPSED}" -ge "${OPENOCD_TIMEOUT}" ]; then
echo "--- OpenOCD log ---" && cat "${OPENOCD_LOG}"
die "OpenOCD did not become ready within ${OPENOCD_TIMEOUT}s"
fi
if ! kill -0 "${OPENOCD_PID}" 2>/dev/null; then
echo "--- OpenOCD log ---" && cat "${OPENOCD_LOG}"
die "OpenOCD exited unexpectedly"
fi
done
info "OpenOCD is ready."
GDB_INIT_FILE="/tmp/gdb_deploy_demo_threadx.gdb"
printf 'set confirm off
set remotetimeout 60
file %s
target extended-remote localhost:3333
load
' "${ELF}" > "${GDB_INIT_FILE}"
if [ "${OPT_DEBUG}" -eq 0 ]; then
info "Flashing and running (detached) ..."
printf 'monitor resume\ndisconnect\nquit\n' >> "${GDB_INIT_FILE}"
info "ELF: ${ELF}"
riscv64-unknown-elf-size "${ELF}"
"${GDB}" --batch --command="${GDB_INIT_FILE}" 2>&1 \
| grep -v "keep_alive() was not invoked" \
| grep -v "GDB alive packet not sent"
info "Flash complete. Application is running on the target."
info "Serial console: minicom -b 115200 -D /dev/ttyUSBx"
else
info "Flashing and stopping at main for interactive debug ..."
printf 'break main\ncontinue\n' >> "${GDB_INIT_FILE}"
info "ELF: ${ELF}"
riscv64-unknown-elf-size "${ELF}"
"${GDB}" --command="${GDB_INIT_FILE}"
fi
@@ -0,0 +1,6 @@
set arch riscv:rv32
target extended-remote localhost:3333
file build/demo_threadx.elf
load
break main
continue
@@ -0,0 +1,59 @@
/***************************************************************************/
/* Copyright (c) 2011-2014, Wind River Systems, Inc.
* Copyright 2020 ETH Zurich
* Copyright (C) 2026 Eclipse ThreadX contributors
*
* Derived from core-v-freertos (https://github.com/openhwgroup/core-v-freertos)
* Original work licensed under the Apache License, Version 2.0.
* See https://www.apache.org/licenses/LICENSE-2.0
* Modifications licensed under MIT (https://opensource.org/licenses/MIT).
*
* AI Disclosure: Some portions generated or modified by Copilot (Sonnet 4.6).
*
* SPDX-License-Identifier: Apache-2.0 AND MIT
***************************************************************************/
#ifndef BITS_H
#define BITS_H
#include <stdbool.h>
#include <stdint.h>
/* Helper to pass an integer as a pointer or vice versa. */
#define POINTER_TO_UINT(x) ((uintptr_t)(x))
#define UINT_TO_POINTER(x) ((void *)(uintptr_t)(x))
#define POINTER_TO_INT(x) ((intptr_t)(x))
#define INT_TO_POINTER(x) ((void *)(intptr_t)(x))
#if !(defined(__CHAR_BIT__) && defined(__SIZEOF_LONG__))
#error Missing required predefined macros for BITS_PER_LONG calculation
#endif
#define BITS_PER_LONG (__CHAR_BIT__ * __SIZEOF_LONG__)
#define GENMASK(h, l) \
(((~0UL) - (1UL << (l)) + 1UL) & (~0UL >> (BITS_PER_LONG - 1UL - (h))))
#define KB(x) ((x) << 10)
#define MB(x) (KB(x) << 10)
#define GB(x) (MB(x) << 10)
#define KHZ(x) ((x) * 1000)
#define MHZ(x) (KHZ(x) * 1000)
#ifndef BIT
#ifdef _ASMLANGUAGE
#define BIT(n) (1 << (n))
#else
#define BIT(n) (1UL << (n))
#endif
#endif
#define WRITE_BIT(var, bit, set) \
((var) = ((set) != 0) ? ((var) | BIT(bit)) : ((var) & ~BIT(bit)))
#define BIT_MASK(n) (BIT(n) - 1UL)
#define REG_SET(FIELD, v) (((uint32_t)(v) << FIELD##_SHIFT) & FIELD##_MASK)
#define REG_GET(FIELD, v) (((uint32_t)(v) & FIELD##_MASK) >> FIELD##_SHIFT)
#endif /* BITS_H */
@@ -0,0 +1,75 @@
/***************************************************************************/
/* Copyright (C) 2020 ETH Zurich and University of Bologna
* Copyright (C) 2026 Eclipse ThreadX contributors
*
* Derived from core-v-freertos (https://github.com/openhwgroup/core-v-freertos)
* Original work licensed under the Apache License, Version 2.0.
* See https://www.apache.org/licenses/LICENSE-2.0
* Modifications licensed under MIT (https://opensource.org/licenses/MIT).
*
* AI Disclosure: Some portions generated or modified by Copilot (Sonnet 4.6).
*
* SPDX-License-Identifier: Apache-2.0 AND MIT
***************************************************************************/
#ifndef CSR_H
#define CSR_H
#include "bits.h"
#define CSR_MSTATUS 0x300
#define CSR_MISA 0x301
#define CSR_MIE 0x304
#define CSR_MTVEC 0x305
#define CSR_MSCRATCH 0x340
#define CSR_MEPC 0x341
#define CSR_MCAUSE 0x342
#define CSR_MTVAL 0x343
#define CSR_MIP 0x344
#define CSR_PMPCFG0 0x3A0
#define CSR_PMPADDR0 0x3B0
#define CSR_MVENDORID 0xF11
#define CSR_MARCHID 0xF12
#define CSR_MIMPID 0xF13
#define CSR_MHARTID 0xF14
#define MSTATUS_IE BIT(3)
#define __CSR_EXPAND(x) #x
#ifndef csr_read
#define csr_read(csr) \
({ \
register unsigned long __val; \
__asm__ volatile("csrr %0, " __CSR_EXPAND(csr) : "=r"(__val) : : "memory"); \
__val; \
})
#endif
#ifndef csr_write
#define csr_write(csr, val) \
({ \
unsigned long __val = (unsigned long)(val); \
__asm__ volatile("csrw " __CSR_EXPAND(csr) ", %0" : : "rK"(__val) : "memory"); \
})
#endif
#ifndef csr_read_clear
#define csr_read_clear(csr, val) \
({ \
unsigned long __val = (unsigned long)(val); \
__asm__ volatile("csrrc %0, " __CSR_EXPAND(csr) ", %1" : "=r"(__val) : "rK"(__val) : "memory"); \
__val; \
})
#endif
#ifndef csr_read_set
#define csr_read_set(csr, val) \
({ \
unsigned long __val = (unsigned long)(val); \
__asm__ volatile("csrrs %0, " __CSR_EXPAND(csr) ", %1" : "=r"(__val) : "rK"(__val) : "memory"); \
__val; \
})
#endif
#endif /* CSR_H */
@@ -0,0 +1,93 @@
/***************************************************************************/
/* Copyright 2020 GreenWaves Technologies
* Copyright (C) 2026 Eclipse ThreadX contributors
*
* Derived from core-v-freertos (https://github.com/openhwgroup/core-v-freertos)
* Original work licensed under the Apache License, Version 2.0.
* See https://www.apache.org/licenses/LICENSE-2.0
* Modifications licensed under MIT (https://opensource.org/licenses/MIT).
*
* AI Disclosure: Some portions generated or modified by Copilot (Sonnet 4.6).
*
* SPDX-License-Identifier: Apache-2.0 AND MIT
***************************************************************************/
#ifndef FLL_H
#define FLL_H
#include <stdint.h>
#include "properties.h"
#include "pulp_mem_map.h"
#define FLL_STATUS_OFFSET 0x000U
#define FLL_CONF1_OFFSET 0x004U
#define FLL_CONF2_OFFSET 0x008U
#define FLL_INTEGRATOR_OFFSET 0x00CU
typedef struct
{
volatile uint32_t FLL_STATUS;
volatile uint32_t FLL_CONF1;
volatile uint32_t FLL_CONF2;
volatile uint32_t FLL_INTEGRATOR;
} fll_ctrl_t;
typedef enum _fll_type
{
FLL_SOC = 0,
FLL_PERI = 1,
FLL_CLUSTER = 2
} fll_type_t;
typedef enum
{
PI_FREQ_DOMAIN_FC = 0,
PI_FREQ_DOMAIN_CL = 1,
PI_FREQ_DOMAIN_PERIPH = 2
} pi_freq_domain_e;
#define FLL_CTRL_STATUS_MULTI_FACTOR_MASK 0xFFFFU
#define FLL_CTRL_STATUS_MULTI_FACTOR_SHIFT 0U
#define FLL_CTRL_CONF1_MULTI_FACTOR_MASK 0xFFFFU
#define FLL_CTRL_CONF1_MULTI_FACTOR_SHIFT 0U
#define FLL_CTRL_CONF1_DCO_INPUT_MASK 0x03FF0000U
#define FLL_CTRL_CONF1_DCO_INPUT_SHIFT 16U
#define FLL_CTRL_CONF1_CLK_OUT_DIV_MASK 0x3C000000U
#define FLL_CTRL_CONF1_CLK_OUT_DIV_SHIFT 26U
#define FLL_CTRL_CONF1_OUTPUT_LOCK_EN_MASK 0x40000000U
#define FLL_CTRL_CONF1_OUTPUT_LOCK_EN_SHIFT 30U
#define FLL_CTRL_CONF1_MODE_MASK 0x80000000U
#define FLL_CTRL_CONF1_MODE_SHIFT 31U
#define FLL_CTRL_CONF2_LOOPGAIN_MASK 0xFU
#define FLL_CTRL_CONF2_LOOPGAIN_SHIFT 0U
#define FLL_CTRL_CONF2_DEASSERT_CYCLES_MASK 0x3F0U
#define FLL_CTRL_CONF2_DEASSERT_CYCLES_SHIFT 4U
#define FLL_CTRL_CONF2_ASSERT_CYCLES_MASK 0xFC00U
#define FLL_CTRL_CONF2_ASSERT_CYCLES_SHIFT 10U
#define FLL_CTRL_CONF2_LOCK_TOLERANCE_MASK 0x0FFF0000U
#define FLL_CTRL_CONF2_LOCK_TOLERANCE_SHIFT 16U
#define FLL_CTRL_CONF2_CONF_CLK_SEL_MASK 0x20000000U
#define FLL_CTRL_CONF2_CONF_CLK_SEL_SHIFT 29U
#define FLL_CTRL_CONF2_OPEN_LOOP_MASK 0x40000000U
#define FLL_CTRL_CONF2_OPEN_LOOP_SHIFT 30U
#define FLL_CTRL_CONF2_DITHERING_MASK 0x80000000U
#define FLL_CTRL_CONF2_DITHERING_SHIFT 31U
#define FLL_CTRL_INTEGRATOR_FRACT_PART_MASK 0x0000FFC0U
#define FLL_CTRL_INTEGRATOR_FRACT_PART_SHIFT 6U
#define FLL_CTRL_INTEGRATOR_INT_PART_MASK 0x03FF0000U
#define FLL_CTRL_INTEGRATOR_INT_PART_SHIFT 16U
#define FLL_CTRL_SOC_FLL_CONV_MASK 0x1U
#define FLL_CTRL_SOC_FLL_CONV_SHIFT 0U
#define FLL_CTRL_CLUSTER_FLL_CONV_MASK 0x2U
#define FLL_CTRL_CLUSTER_FLL_CONV_SHIFT 1U
#define FLL_NUM ARCHI_NB_FLL
#define FLL_REF_CLK ARCHI_REF_CLOCK
#define FLL_CTRL ((volatile fll_ctrl_t *)PULP_FLL_ADDR)
void pi_fll_init(fll_type_t which_fll, uint32_t ret_state);
int pi_fll_set_frequency(fll_type_t which_fll, uint32_t frequency, int check);
int pi_fll_get_frequency(fll_type_t which_fll, uint8_t real);
#endif /* FLL_H */
@@ -0,0 +1,68 @@
/***************************************************************************
* Copyright (C) 2026 Eclipse ThreadX contributors
*
* This program and the accompanying materials are made available under the
* terms of the MIT License which is available at
* https://opensource.org/licenses/MIT.
*
* AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6).
* The AI-generated portions may be considered public domain (CC0-1.0)
* and not subject to the project's licence. The human contributor has
* reviewed and verified that the code is correct.
*
* SPDX-License-Identifier: MIT and CC0-1.0
**************************************************************************/
#ifndef GPIO_H
#define GPIO_H
#include <stdint.h>
/* apb_gpiov2 register map (pin-indexed interface)
*
* Offset Name Access Description
* 0x000 SETGPIO W PWDATA[6:0] = pin number → drive pin high
* 0x004 CLRGPIO W PWDATA[6:0] = pin number → drive pin low
* 0x008 TOGGPIO W PWDATA[6:0] = pin number → toggle pin
* 0x010 PIN0 R bits [31:0] = sampled input state of pins 0-31
* 0x020 OUT0 R/W bits [31:0] = output state bitmask, pins 0-31
* 0x030 SETSEL W select pin for RDSTAT
* 0x034 RDSTAT R status of selected pin
* 0x038 SETDIR W PWDATA[6:0]=pin, PWDATA[25:24]=dir
* dir[0]=1 → push-pull output; dir[1]=1 → open-drain
* 0x03C SETINT W interrupt configuration
* 0x040 INTACK W interrupt acknowledge
*/
#define GPIO_SETGPIO_OFFSET 0x000U
#define GPIO_CLRGPIO_OFFSET 0x004U
#define GPIO_TOGGPIO_OFFSET 0x008U
#define GPIO_PIN0_OFFSET 0x010U
#define GPIO_OUT0_OFFSET 0x020U
#define GPIO_SETSEL_OFFSET 0x030U
#define GPIO_RDSTAT_OFFSET 0x034U
#define GPIO_SETDIR_OFFSET 0x038U
#define GPIO_SETINT_OFFSET 0x03CU
#define GPIO_INTACK_OFFSET 0x040U
/* Direction field in SETDIR: bits [25:24] of the written word.
* 0b01 = push-pull output enable; 0b10 = open-drain; 0b00 = input */
#define GPIO_DIR_OUTPUT (1U << 24U)
void gpio_init(void);
void gpio_set_output(uint32_t pin_mask);
void gpio_toggle(uint32_t pin_mask);
void gpio_set(uint32_t pin_mask);
void gpio_clear(uint32_t pin_mask);
/* Pad-mux control (APB SOC CTRL v1.0.0).
* Each IO pad has a 32-bit register at SOC_CTRL_BASE + 0x400 + pad*4.
* bits[1:0] = mux function: 0=sys, 1=perio, 2=apbio/GPIO, 3=fpgaio. */
void gpio_setpinmux(uint8_t io_pad, uint8_t mux);
uint8_t gpio_getpinmux(uint8_t io_pad);
/* Single-pin direction and status (for CLI use).
* dir: 0 = input, 1 = push-pull output. */
void gpio_pin_set_dir(uint8_t pin, uint8_t dir);
uint32_t gpio_pin_read_status(uint8_t pin);
#endif /* GPIO_H */
@@ -0,0 +1,38 @@
/***************************************************************************
* Copyright (C) 2026 Eclipse ThreadX contributors
*
* This program and the accompanying materials are made available under the
* terms of the MIT License which is available at
* https://opensource.org/licenses/MIT.
*
* AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6).
* The AI-generated portions may be considered public domain (CC0-1.0)
* and not subject to the project's licence. The human contributor has
* reviewed and verified that the code is correct.
*
* SPDX-License-Identifier: MIT and CC0-1.0
**************************************************************************/
#ifndef I2C_MASTER_H
#define I2C_MASTER_H
#include <stdint.h>
/* i2c_master_init: enable I2C channel clock gate, configure clock divider.
* id: 0 or 1 (I2CM0 or I2CM1)
* i2c_freq: desired I2C clock in Hz (e.g. 200000)
* periph_freq: UDMA peripheral clock in Hz (5000000 on CORE-V FPGA)
* Returns 0 on success, -1 on bad params. */
int i2c_master_init(uint8_t id, uint32_t i2c_freq, uint32_t periph_freq);
/* i2c_master_read_reg: read len bytes from register reg_addr of 7-bit addressed device.
* id: I2CM channel (0 or 1)
* dev_addr7: 7-bit I2C device address
* reg_addr: register address to read from
* buf: output buffer (must be in RAM, not stack if possible)
* len: number of bytes to read (1..255)
* Returns 0 on success, -1 on timeout or arbitration loss. */
int i2c_master_read_reg(uint8_t id, uint8_t dev_addr7, uint8_t reg_addr,
uint8_t *buf, uint8_t len);
#endif /* I2C_MASTER_H */
@@ -0,0 +1,61 @@
/***************************************************************************/
/* Copyright 2020 ETH Zurich
* Copyright (C) 2026 Eclipse ThreadX contributors
*
* Derived from core-v-freertos (https://github.com/openhwgroup/core-v-freertos)
* Original work licensed under the Apache License, Version 2.0.
* See https://www.apache.org/licenses/LICENSE-2.0
* Modifications licensed under MIT (https://opensource.org/licenses/MIT).
*
* AI Disclosure: Some portions generated or modified by Copilot (Sonnet 4.6).
*
* SPDX-License-Identifier: Apache-2.0 AND MIT
***************************************************************************/
#ifndef IO_H
#define IO_H
#include <stdint.h>
#ifndef CORE_V_MCU_MMIO_MOCK
static inline void writeb(uint8_t val, uintptr_t addr)
{
__asm__ volatile("sb %0, 0(%1)" : : "r"(val), "r"((volatile uint8_t *)addr));
}
static inline void writeh(uint16_t val, uintptr_t addr)
{
__asm__ volatile("sh %0, 0(%1)" : : "r"(val), "r"((volatile uint16_t *)addr));
}
static inline void writew(uint32_t val, uintptr_t addr)
{
__asm__ volatile("sw %0, 0(%1)" : : "r"(val), "r"((volatile uint32_t *)addr));
}
static inline uint8_t readb(const uintptr_t addr)
{
uint8_t val;
__asm__ volatile("lb %0, 0(%1)" : "=r"(val) : "r"((const volatile uint8_t *)addr));
return val;
}
static inline uint16_t readh(const uintptr_t addr)
{
uint16_t val;
__asm__ volatile("lh %0, 0(%1)" : "=r"(val) : "r"((const volatile uint16_t *)addr));
return val;
}
static inline uint32_t readw(const uintptr_t addr)
{
uint32_t val;
__asm__ volatile("lw %0, 0(%1)" : "=r"(val) : "r"((const volatile uint32_t *)addr));
return val;
}
#endif /* CORE_V_MCU_MMIO_MOCK */
#endif /* IO_H */
@@ -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 */
@@ -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 */
@@ -0,0 +1,43 @@
/***************************************************************************/
/* Copyright (C) 2019 ETH Zurich, University of Bologna and GreenWaves Technologies
* Copyright (C) 2026 Eclipse ThreadX contributors
*
* Derived from core-v-freertos (https://github.com/openhwgroup/core-v-freertos)
* Original work licensed under the Apache License, Version 2.0.
* See https://www.apache.org/licenses/LICENSE-2.0
* Modifications licensed under MIT (https://opensource.org/licenses/MIT).
*
* AI Disclosure: Some portions generated or modified by Copilot (Sonnet 4.6).
*
* SPDX-License-Identifier: Apache-2.0 AND MIT
***************************************************************************/
#ifndef PROPERTIES_H
#define PROPERTIES_H
#define PULP
/* Peripheral bus clock (clk_per): PER_CLK_PERIOD_NS=200 → 5 MHz */
#define ARCHI_FPGA_FREQUENCY 5000000U
/* FC/SOC clock (clk_soc): FC_CLK_PERIOD_NS=100 → 10 MHz */
#define ARCHI_SOC_FREQUENCY 10000000U
#define ARCHI_NUM_TIMER 1U
#define ARCHI_NUM_FLL 2U
#define ARCHI_REF_CLOCK_LOG2 15U
#define ARCHI_REF_CLOCK (1U << ARCHI_REF_CLOCK_LOG2)
#define ARCHI_NB_FLL 3U
#define __RT_FLL_CL 2U
#define __RT_FLL_PERIPH 1U
#define __RT_FLL_FC 0U
#define __RT_FREQ_DOMAIN_FC 0U
#define __RT_FREQ_DOMAIN_CL 2U
#define __RT_FREQ_DOMAIN_PERIPH 1U
#define RT_FREQ_NB_DOMAIN 3U
#define DEFAULT_SYSTEM_CLOCK 50000000U
#endif /* PROPERTIES_H */
@@ -0,0 +1,103 @@
/***************************************************************************/
/* Copyright (C) 2019 ETH Zurich and University of Bologna
* Copyright (C) 2026 Eclipse ThreadX contributors
*
* Derived from core-v-freertos (https://github.com/openhwgroup/core-v-freertos)
* Original work licensed under the Apache License, Version 2.0.
* See https://www.apache.org/licenses/LICENSE-2.0
* Modifications licensed under MIT (https://opensource.org/licenses/MIT).
*
* AI Disclosure: Some portions generated or modified by Copilot (Sonnet 4.6).
*
* SPDX-License-Identifier: Apache-2.0 AND MIT
***************************************************************************/
#ifndef PULP_MEM_MAP_H
#define PULP_MEM_MAP_H
#ifndef PULP_SOC_PERIPHERALS_ADDR
#define PULP_SOC_PERIPHERALS_ADDR 0x1A100000UL
#endif
#ifndef PULP_FC_TIMER_SIZE
#define PULP_FC_TIMER_SIZE 0x00000800UL
#endif
#ifndef PULP_FLL_OFFSET
#define PULP_FLL_OFFSET 0x00000000UL
#endif
#ifndef PULP_GPIO_OFFSET
#define PULP_GPIO_OFFSET 0x00001000UL
#endif
#ifndef PULP_UDMA_OFFSET
#define PULP_UDMA_OFFSET 0x00002000UL
#endif
#ifndef PULP_APB_SOC_CTRL_OFFSET
#define PULP_APB_SOC_CTRL_OFFSET 0x00004000UL
#endif
#ifndef PULP_ADV_TIMER_OFFSET
#define PULP_ADV_TIMER_OFFSET 0x00005000UL
#endif
#ifndef PULP_SOC_EU_OFFSET
#define PULP_SOC_EU_OFFSET 0x00006000UL
#endif
#ifndef PULP_FC_IRQ_OFFSET
#define PULP_FC_IRQ_OFFSET 0x00009800UL
#endif
#ifndef PULP_FC_TIMER_OFFSET
#define PULP_FC_TIMER_OFFSET 0x0000B000UL
#endif
#ifndef PULP_FC_HWPE_OFFSET
#define PULP_FC_HWPE_OFFSET 0x0000C000UL
#endif
#ifndef PULP_STDOUT_OFFSET
#define PULP_STDOUT_OFFSET 0x0000F000UL
#endif
#ifndef PULP_DEBUG_OFFSET
#define PULP_DEBUG_OFFSET 0x00010000UL
#endif
#ifndef PULP_FLL_ADDR
#define PULP_FLL_ADDR (PULP_SOC_PERIPHERALS_ADDR + PULP_FLL_OFFSET)
#endif
#ifndef PULP_GPIO_ADDR
#define PULP_GPIO_ADDR (PULP_SOC_PERIPHERALS_ADDR + PULP_GPIO_OFFSET)
#endif
#ifndef PULP_UDMA_ADDR
#define PULP_UDMA_ADDR (PULP_SOC_PERIPHERALS_ADDR + PULP_UDMA_OFFSET)
#endif
#ifndef PULP_APB_SOC_CTRL_ADDR
#define PULP_APB_SOC_CTRL_ADDR (PULP_SOC_PERIPHERALS_ADDR + PULP_APB_SOC_CTRL_OFFSET)
#endif
#ifndef PULP_ADV_TIMER_ADDR
#define PULP_ADV_TIMER_ADDR (PULP_SOC_PERIPHERALS_ADDR + PULP_ADV_TIMER_OFFSET)
#endif
#ifndef PULP_SOC_EU_ADDR
#define PULP_SOC_EU_ADDR (PULP_SOC_PERIPHERALS_ADDR + PULP_SOC_EU_OFFSET)
#endif
#ifndef PULP_FC_IRQ_ADDR
#define PULP_FC_IRQ_ADDR (PULP_SOC_PERIPHERALS_ADDR + PULP_FC_IRQ_OFFSET)
#endif
#ifndef PULP_FC_TIMER_ADDR
#define PULP_FC_TIMER_ADDR (PULP_SOC_PERIPHERALS_ADDR + PULP_FC_TIMER_OFFSET)
#endif
#ifndef PULP_FC_HWPE_ADDR
#define PULP_FC_HWPE_ADDR (PULP_SOC_PERIPHERALS_ADDR + PULP_FC_HWPE_OFFSET)
#endif
#ifndef PULP_STDOUT_ADDR
#define PULP_STDOUT_ADDR (PULP_SOC_PERIPHERALS_ADDR + PULP_STDOUT_OFFSET)
#endif
#define PULP_FLL_AREA_SIZE 0x00000010UL
/* UDMA */
#define UDMA_CH_ADDR_CTRL (PULP_UDMA_ADDR)
#define UDMA_CH_ADDR_UART (PULP_UDMA_ADDR + 0x80U)
#define UDMA_CH_ADDR_I2CM0 (PULP_UDMA_ADDR + 5U * UDMA_CH_SIZE)
#define UDMA_CH_ADDR_I2CM1 (PULP_UDMA_ADDR + 6U * UDMA_CH_SIZE)
#define UDMA_CTRL_I2CM0_CLKEN BIT(4)
#define UDMA_CTRL_I2CM1_CLKEN BIT(5)
#define UDMA_CH_SIZE (0x80U)
#define N_UART (2U)
#endif /* PULP_MEM_MAP_H */
@@ -0,0 +1,25 @@
/***************************************************************************
* Copyright (C) 2026 Eclipse ThreadX contributors
*
* This program and the accompanying materials are made available under the
* terms of the MIT License which is available at
* https://opensource.org/licenses/MIT.
*
* AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6).
* The AI-generated portions may be considered public domain (CC0-1.0)
* and not subject to the project's licence. The human contributor has
* reviewed and verified that the code is correct.
*
* SPDX-License-Identifier: MIT and CC0-1.0
**************************************************************************/
#ifndef STRING_H
#define STRING_H
#include <stddef.h>
void *memcpy(void *dest, const void *src, size_t n);
void *memset(void *dest, int c, size_t n);
int memcmp(const void *lhs, const void *rhs, size_t n);
#endif /* STRING_H */
@@ -0,0 +1,29 @@
/***************************************************************************
* Copyright (C) 2026 Eclipse ThreadX contributors
*
* This program and the accompanying materials are made available under the
* terms of the MIT License which is available at
* https://opensource.org/licenses/MIT.
*
* AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6).
* The AI-generated portions may be considered public domain (CC0-1.0)
* and not subject to the project's licence. The human contributor has
* reviewed and verified that the code is correct.
*
* SPDX-License-Identifier: MIT and CC0-1.0
**************************************************************************/
#ifndef SYSTEM_CORE_V_MCU_H
#define SYSTEM_CORE_V_MCU_H
#include <stdint.h>
extern void (*isr_table[32])(void);
extern volatile uint32_t system_core_clock;
void system_init(void);
void tx_trap_handler(uint32_t mcause, uint32_t mepc, uint32_t mtval);
void tx_timer_irq_handler(void);
void tx_undefined_irq_handler(void);
#endif /* SYSTEM_CORE_V_MCU_H */
@@ -0,0 +1,29 @@
/***************************************************************************
* Copyright (C) 2026 Eclipse ThreadX contributors
*
* This program and the accompanying materials are made available under the
* terms of the MIT License which is available at
* https://opensource.org/licenses/MIT.
*
* AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6).
* The AI-generated portions may be considered public domain (CC0-1.0)
* and not subject to the project's licence. The human contributor has
* reviewed and verified that the code is correct.
*
* SPDX-License-Identifier: MIT and CC0-1.0
**************************************************************************/
#ifndef TEMP_SENSOR_H
#define TEMP_SENSOR_H
#include <stdint.h>
/* Read ADT7420 temperature sensor on I2CM1 (Nexys A7 on-board sensor).
* Returns temperature in tenths of degrees Celsius (e.g. 213 = 21.3°C).
* Returns INT16_MIN on I2C error. */
int16_t temp_read_celsius_x10(void);
/* Initialize the temperature sensor I2C channel. Call once at startup. */
void temp_sensor_init(void);
#endif /* TEMP_SENSOR_H */
@@ -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 */
@@ -0,0 +1,26 @@
/***************************************************************************
* Copyright (C) 2026 Eclipse ThreadX contributors
*
* This program and the accompanying materials are made available under the
* terms of the MIT License which is available at
* https://opensource.org/licenses/MIT.
*
* AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6).
* The AI-generated portions may be considered public domain (CC0-1.0)
* and not subject to the project's licence. The human contributor has
* reviewed and verified that the code is correct.
*
* SPDX-License-Identifier: MIT and CC0-1.0
**************************************************************************/
#ifndef TIMER_IRQ_H
#define TIMER_IRQ_H
#include <stdint.h>
int timer_irq_init(uint32_t ticks);
int timer_irq_set_timeout(uint32_t ticks);
uint32_t timer_irq_clock_elapsed(void);
uint32_t timer_irq_cycle_get_32(void);
#endif /* TIMER_IRQ_H */
@@ -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 */
@@ -0,0 +1,27 @@
/***************************************************************************
* Copyright (C) 2026 Eclipse ThreadX contributors
*
* This program and the accompanying materials are made available under the
* terms of the MIT License which is available at
* https://opensource.org/licenses/MIT.
*
* AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6).
* The AI-generated portions may be considered public domain (CC0-1.0)
* and not subject to the project's licence. The human contributor has
* reviewed and verified that the code is correct.
*
* SPDX-License-Identifier: MIT and CC0-1.0
**************************************************************************/
#ifndef UART_DRIVER_H
#define UART_DRIVER_H
#include <stdint.h>
int uart_init(uint8_t uart_id, uint32_t baudrate, uint32_t periph_freq);
void uart_write_byte(uint8_t uart_id, uint8_t c);
int uart_read_byte(uint8_t uart_id);
void uart_write_str(uint8_t uart_id, const char *str);
void uart_write_buf(uint8_t uart_id, const uint8_t *buf, uint32_t len);
#endif /* UART_DRIVER_H */
@@ -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."
@@ -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.*)
}
}
@@ -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"
@@ -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)
@@ -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];
@@ -0,0 +1,45 @@
/***************************************************************************
* Copyright (C) 2026 Eclipse ThreadX contributors
*
* This program and the accompanying materials are made available under the
* terms of the MIT License which is available at
* https://opensource.org/licenses/MIT.
*
* AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6).
* The AI-generated portions may be considered public domain (CC0-1.0)
* and not subject to the project's licence. The human contributor has
* reviewed and verified that the code is correct.
*
* SPDX-License-Identifier: MIT and CC0-1.0
**************************************************************************/
#ifndef MMIO_MOCK_H
#define MMIO_MOCK_H
#include <stdint.h>
#include <string.h>
#define CORE_V_MCU_MMIO_MOCK 1
#define MOCK_MMIO_SIZE 1024U
extern uint32_t mock_mmio[MOCK_MMIO_SIZE];
static inline void writew(uint32_t val, uintptr_t addr)
{
if (addr < (uintptr_t)(MOCK_MMIO_SIZE * 4U))
{
mock_mmio[addr / 4U] = val;
}
}
static inline uint32_t readw(const uintptr_t addr)
{
if (addr < (uintptr_t)(MOCK_MMIO_SIZE * 4U))
{
return mock_mmio[addr / 4U];
}
return 0U;
}
#endif /* MMIO_MOCK_H */
@@ -0,0 +1,66 @@
/***************************************************************************
* Copyright (C) 2026 Eclipse ThreadX contributors
*
* This program and the accompanying materials are made available under the
* terms of the MIT License which is available at
* https://opensource.org/licenses/MIT.
*
* AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6).
* The AI-generated portions may be considered public domain (CC0-1.0)
* and not subject to the project's licence. The human contributor has
* reviewed and verified that the code is correct.
*
* SPDX-License-Identifier: MIT and CC0-1.0
**************************************************************************/
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "mmio_mock.h"
static uint32_t mock_mstatus = 0U;
#define csr_read(csr) (mock_mstatus)
#define csr_write(csr, val) do { mock_mstatus = (uint32_t)(val); } while (0)
#define csr_read_clear(csr, val) ({ uint32_t _v = mock_mstatus; mock_mstatus &= ~(uint32_t)(val); _v; })
#define csr_read_set(csr, val) ({ uint32_t _v = mock_mstatus; mock_mstatus |= (uint32_t)(val); _v; })
#include "bits.h"
#define PULP_SOC_PERIPHERALS_ADDR 0U
#define PULP_FC_IRQ_OFFSET 0U
#include "irq.h"
#include "../../bsp/irq.c"
static void test_irq_enable(void)
{
memset(mock_mmio, 0, sizeof(mock_mmio));
irq_enable(BIT(10));
assert(mock_mmio[IRQ_REG_MASK_SET_OFFSET / 4U] == BIT(10));
printf("test_irq_enable: PASS\n");
}
static void test_irq_disable(void)
{
memset(mock_mmio, 0, sizeof(mock_mmio));
irq_disable(BIT(10));
assert(mock_mmio[IRQ_REG_MASK_CLEAR_OFFSET / 4U] == BIT(10));
printf("test_irq_disable: PASS\n");
}
static void test_irq_clint_enable(void)
{
mock_mstatus = 0U;
(void)irq_clint_enable();
assert((mock_mstatus & 0x8U) != 0U);
printf("test_irq_clint_enable: PASS\n");
}
int main(void)
{
test_irq_enable();
test_irq_disable();
test_irq_clint_enable();
printf("All IRQ tests passed.\n");
return 0;
}
@@ -0,0 +1,53 @@
/***************************************************************************
* Copyright (C) 2026 Eclipse ThreadX contributors
*
* This program and the accompanying materials are made available under the
* terms of the MIT License which is available at
* https://opensource.org/licenses/MIT.
*
* AI Disclosure: This file was largely AI-generated by Copilot (Sonnet 4.6).
* The AI-generated portions may be considered public domain (CC0-1.0)
* and not subject to the project's licence. The human contributor has
* reviewed and verified that the code is correct.
*
* SPDX-License-Identifier: MIT and CC0-1.0
**************************************************************************/
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "mmio_mock.h"
#define PULP_SOC_PERIPHERALS_ADDR 0U
#define PULP_FC_TIMER_OFFSET 0U
#include "bits.h"
#include "timer.h"
#include "timer_irq.h"
#include "../../bsp/timer_irq.c"
static void test_timer_irq_init(void)
{
uint32_t cfg;
int ret;
memset(mock_mmio, 0, sizeof(mock_mmio));
ret = timer_irq_init(327U);
assert(ret == 0);
assert(mock_mmio[TIMER_CMP_LO_OFFSET / 4U] == 327U);
cfg = mock_mmio[TIMER_CFG_LO_OFFSET / 4U];
assert((cfg & TIMER_CFG_LO_ENABLE_MASK) != 0U);
assert((cfg & TIMER_CFG_LO_IRQEN_MASK) != 0U);
assert((cfg & TIMER_CFG_LO_CCFG_MASK) != 0U);
assert((cfg & TIMER_CFG_LO_MODE_MASK) != 0U);
printf("test_timer_irq_init: PASS\n");
}
int main(void)
{
test_timer_irq_init();
printf("All timer tests passed.\n");
return 0;
}
@@ -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
@@ -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
@@ -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