mirror of
https://github.com/eclipse-threadx/threadx.git
synced 2026-10-06 06:59:08 +08:00
Added an Erbium example to the RISC-V64 GNU port
ThreadX had no board support for Erbium, the OpenHW Foundation CORE-ET RISC-V platform that Zephyr and NuttX support. Its hart implements only part of the F extension, needs a 4 KiB aligned mtvec and uses the original Shakti UART. The example runs the standard demo on hart 0 in machine mode. It builds the library and the demo for rv64imc_zicsr_zifencei with the soft-float lp64 ABI and links without libc or libgcc, so the pinned riscv64-unknown-elf toolchain can build it. It programs the machine timer, a polling UART console and the PLIC, writing the source priorities and threshold that silicon hardwires because the simulator resets them to 0. No shared file changes. The demo built without warnings and ran on erbium_emu from et-platform 836a4ab, with all eight threads reporting and five thread 0 wakeups in 100M cycles, which matches the 2 MHz tick. Separate test images took five PLIC UART interrupts through the ThreadX ISR path and halted with mcause 2 on an illegal instruction. check_ai_disclosure.sh and check_ports.sh passed. Assisted-by: Claude Code (Opus 5.5) <noreply@anthropic.com>
This commit is contained in:
committed by
Frédéric Desbiens
parent
0a52df8f87
commit
7212b366ae
@@ -0,0 +1,43 @@
|
||||
cmake_minimum_required(VERSION 3.13 FATAL_ERROR)
|
||||
|
||||
project(demo_threadx_erbium LANGUAGES C ASM)
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
set(CMAKE_C_STANDARD_REQUIRED ON)
|
||||
|
||||
set(ERBIUM_DIR ${CMAKE_CURRENT_LIST_DIR})
|
||||
set(THREADX_ROOT ${CMAKE_CURRENT_LIST_DIR}/../../../../..)
|
||||
|
||||
add_subdirectory(${THREADX_ROOT} threadx)
|
||||
|
||||
add_executable(demo_threadx
|
||||
${ERBIUM_DIR}/entry.S
|
||||
${ERBIUM_DIR}/tx_initialize_low_level.S
|
||||
${ERBIUM_DIR}/board.c
|
||||
${ERBIUM_DIR}/hwtimer.c
|
||||
${ERBIUM_DIR}/plic.c
|
||||
${ERBIUM_DIR}/trap.c
|
||||
${ERBIUM_DIR}/uart.c
|
||||
${ERBIUM_DIR}/demo_threadx.c
|
||||
)
|
||||
set_target_properties(demo_threadx PROPERTIES
|
||||
OUTPUT_NAME "demo_threadx.elf"
|
||||
LINK_DEPENDS ${ERBIUM_DIR}/link.lds
|
||||
)
|
||||
|
||||
target_include_directories(demo_threadx PRIVATE ${ERBIUM_DIR})
|
||||
|
||||
target_link_libraries(demo_threadx PRIVATE threadx)
|
||||
|
||||
target_compile_options(demo_threadx PRIVATE
|
||||
-Wall
|
||||
-ffreestanding
|
||||
-ffunction-sections
|
||||
-fdata-sections
|
||||
)
|
||||
|
||||
target_link_options(demo_threadx PRIVATE
|
||||
-T${ERBIUM_DIR}/link.lds
|
||||
-nostdlib
|
||||
-Wl,--gc-sections
|
||||
-Wl,-Map=${CMAKE_CURRENT_BINARY_DIR}/demo_threadx.map
|
||||
)
|
||||
@@ -0,0 +1,125 @@
|
||||
# ThreadX example for Erbium
|
||||
|
||||
This directory runs the standard ThreadX demo on one hart of Erbium, the
|
||||
16-hart RISC-V platform of the OpenHW Foundation CORE-ET project, using the
|
||||
RISC-V64 GNU port in machine mode. The public `et-platform` functional
|
||||
simulator, `erbium_emu`, is the reference target.
|
||||
|
||||
## Hardware used
|
||||
|
||||
| Item | Address | Notes |
|
||||
|------|---------|-------|
|
||||
| Hart 0 | - | RV64IMC, machine mode; other harts are parked |
|
||||
| MRAM | `0x4000_0200` | 16 MiB window; the first 512 bytes belong to the boot flow |
|
||||
| System registers | `0x0200_0000` | `SystemConfig` bit 6 enables the UART |
|
||||
| UART0 | `0x0200_4000` | Original Shakti integration, polling, 115200 8N1 |
|
||||
| Machine timer | `0x80F4_0200` | `mtime`, with `mtimecmp` at `+0x08` |
|
||||
| PLIC | `0xA000_0000` | Machine-mode context of hart 0; sources 1 to 6 |
|
||||
|
||||
## Port notes
|
||||
|
||||
* **Soft float.** Erbium implements only part of the F extension: divide,
|
||||
square root and conversions between single precision and 64-bit integers
|
||||
raise an emulation exception. The example and the ThreadX library are
|
||||
built for `rv64imc_zicsr_zifencei` with the `lp64` ABI, so the port saves
|
||||
no FP state. `csr.h` rejects a build with F enabled.
|
||||
* **No C library.** The `riscv64-unknown-elf` toolchain installed by
|
||||
`scripts/install_riscv.sh` ships `lp64d` libraries only, so the example
|
||||
links with `-nostdlib` and `board.c` provides `memset`. A missing runtime
|
||||
helper shows up as a link error, not as a mix of ABIs.
|
||||
* **No WFI.** The example never executes `wfi`, like the Zephyr Erbium
|
||||
port. `TX_USE_WFI_IDLE` stays undefined, so the scheduler spins while
|
||||
idle, and parked harts and fatal-trap halts spin as well.
|
||||
* **Trap vector.** Erbium ignores `mtvec` base bits 11:1, so the trap
|
||||
vector must be 4 KiB aligned. `trap_entry` is placed in its own 4 KiB
|
||||
aligned section and used in direct mode.
|
||||
* **PLIC.** The silicon hardwires source priorities to 1 and thresholds to
|
||||
0. The simulator models them as writable registers that reset to 0, so
|
||||
`plic_init()` writes the hardwired values. Drivers register a callback and
|
||||
enable their source with `plic_irq_enable()`.
|
||||
* **Clocks.** The UART divisor assumes a 400 MHz input clock and the tick
|
||||
assumes a 2 MHz `mtime` rate, the values used by the Zephyr and NuttX
|
||||
Erbium ports. If the boot firmware programs other rates, define
|
||||
`ERBIUM_UART_CLOCK_HZ` and `ERBIUM_MTIME_FREQ_HZ` when you build.
|
||||
* **UART.** The console only polls. `uart_init()` sets the UART enable bit
|
||||
in `SystemConfig` with a read-modify-write, which leaves the watchdog
|
||||
setting as the boot flow left it.
|
||||
|
||||
## Building
|
||||
|
||||
Install the toolchain with `scripts/install_riscv.sh`, or put another
|
||||
`riscv64-unknown-elf` GCC on `PATH`, then run:
|
||||
|
||||
```bash
|
||||
./build.sh
|
||||
```
|
||||
|
||||
This configures `CMakeLists.txt` with `erbium_gnu.cmake`, builds
|
||||
`libthreadx.a` for the soft-float ABI and links `build/demo_threadx.elf`.
|
||||
Set `BUILD_DIR` to build elsewhere.
|
||||
|
||||
## Running on the simulator
|
||||
|
||||
Build `erbium_emu` from
|
||||
[et-platform](https://github.com/aifoundry-org/et-platform). The example was
|
||||
tested at revision `836a4ab600e93c3059bb58c898edbc37744cd8d0`:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/aifoundry-org/et-platform.git
|
||||
cd et-platform
|
||||
git checkout 836a4ab600e93c3059bb58c898edbc37744cd8d0
|
||||
cmake -S erbium-hal -B build-hal -DCMAKE_INSTALL_PREFIX="$PWD/install"
|
||||
cmake --build build-hal --target install
|
||||
cmake -S sw-sysemu -B build-emu -GNinja -DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_CXX_FLAGS=-Wno-error=unused-result \
|
||||
-DCMAKE_PREFIX_PATH="$PWD/install"
|
||||
cmake --build build-emu --target erbium_emu
|
||||
```
|
||||
|
||||
At that revision a release build with a distribution GCC that enables
|
||||
`_FORTIFY_SOURCE` stops on an unused `write()` result in the UART model;
|
||||
the `CMAKE_CXX_FLAGS` setting above keeps that warning from being an error.
|
||||
|
||||
Load the ELF and start hart 0 at the MRAM load address, bypassing the boot
|
||||
ROM:
|
||||
|
||||
```bash
|
||||
erbium_emu -elf_load build/demo_threadx.elf -reset_pc 0x40000200 \
|
||||
-minions 1 -single_thread -max_cycles 100000000 \
|
||||
-uart_rx_file /dev/null -uart_tx_file uart.log
|
||||
cat uart.log
|
||||
```
|
||||
|
||||
The log starts with `[UART0] : Erbium UART initialized`, followed by lines
|
||||
from all eight demo threads. The simulator ends with a `max cycles reached`
|
||||
error once the cycle budget is spent; judge the run by the UART log, not by
|
||||
the exit status. With the default clocks one ThreadX tick is 2,000,000
|
||||
simulated cycles, so the 100,000,000-cycle run shows `thread_0` five times.
|
||||
|
||||
The simulator verifies register accesses, the trap flow and the console.
|
||||
It does not verify baud timing or the timer rate of real silicon.
|
||||
|
||||
## Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `entry.S` | Reset entry: parks other harts, sets `gp`, stack and `.bss` |
|
||||
| `tx_initialize_low_level.S` | Trap vector and `_tx_initialize_low_level` |
|
||||
| `trap.c` | Dispatches the timer, the PLIC and fatal exceptions |
|
||||
| `board.c` | `board_init()` and `memset` |
|
||||
| `plic.c`, `plic.h` | PLIC driver for the hart's machine-mode context |
|
||||
| `hwtimer.c`, `hwtimer.h` | Periodic tick from `mtime` and `mtimecmp` |
|
||||
| `uart.c`, `uart.h` | Polling console |
|
||||
| `demo_threadx.c` | The standard ThreadX demo |
|
||||
| `link.lds` | MRAM layout |
|
||||
| `erbium_gnu.cmake` | Toolchain file: `rv64imc_zicsr_zifencei`, `lp64` |
|
||||
| `CMakeLists.txt`, `build.sh` | Build of the library and the demo |
|
||||
|
||||
## References
|
||||
|
||||
* [Erbium processor](https://github.com/openhwfoundation/core-et-erbium)
|
||||
* [UART registers](https://github.com/openhwfoundation/core-et-erbium/blob/325b32b7efaa2c2ab9c91001c89d3f49a4740826/doc/uart.md)
|
||||
* [CPU memory map](https://github.com/openhwfoundation/core-et-erbium/blob/325b32b7efaa2c2ab9c91001c89d3f49a4740826/doc/cpu_mm.md)
|
||||
* [Platform interrupts](https://github.com/openhwfoundation/core-et-erbium/blob/325b32b7efaa2c2ab9c91001c89d3f49a4740826/doc/interrupts.md)
|
||||
* [CPU subsystem](https://github.com/openhwfoundation/core-et-erbium/blob/325b32b7efaa2c2ab9c91001c89d3f49a4740826/doc/cpu_subsystem.md)
|
||||
* [et-platform simulator](https://github.com/aifoundry-org/et-platform)
|
||||
@@ -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 Claude Code (Opus 5.5).
|
||||
* 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 "plic.h"
|
||||
#include "hwtimer.h"
|
||||
#include "uart.h"
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/* The example links without a C library; ThreadX needs memset. */
|
||||
void *memset(void *des, int c, size_t n)
|
||||
{
|
||||
unsigned char *target = des;
|
||||
|
||||
for (size_t i = 0; i < n; i++)
|
||||
target[i] = (unsigned char)c;
|
||||
return des;
|
||||
}
|
||||
|
||||
int board_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = plic_init();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = uart_init();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = hwtimer_init();
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
#!/bin/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 Claude Code (Opus 5.5).
|
||||
# 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
|
||||
##############################################################################
|
||||
|
||||
# Build the ThreadX library and the Erbium demo, build/demo_threadx.elf.
|
||||
|
||||
set -eu
|
||||
|
||||
SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
|
||||
BUILD_DIR=${BUILD_DIR:-"${SCRIPT_DIR}/build"}
|
||||
|
||||
cmake -S "${SCRIPT_DIR}" -B "${BUILD_DIR}" -GNinja \
|
||||
-DCMAKE_TOOLCHAIN_FILE="${SCRIPT_DIR}/erbium_gnu.cmake"
|
||||
cmake --build "${BUILD_DIR}" --target demo_threadx
|
||||
|
||||
printf 'Built %s\n' "${BUILD_DIR}/demo_threadx.elf"
|
||||
@@ -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 Claude Code (Opus 5.5).
|
||||
* 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
|
||||
**************************************************************************/
|
||||
|
||||
/* Machine-mode CSR definitions and helpers for the Erbium example. */
|
||||
|
||||
#ifndef RISCV_CSR_H
|
||||
#define RISCV_CSR_H
|
||||
|
||||
/* The Erbium FPU implements only part of the F extension: single-precision
|
||||
divide, square root and conversions to and from 64-bit integers raise an
|
||||
emulation exception that this example does not handle. Build for the
|
||||
integer ISA with the soft-float lp64 ABI. */
|
||||
#ifdef __riscv_flen
|
||||
#error "The Erbium example requires a soft-float build: use -march=rv64imc_zicsr_zifencei -mabi=lp64."
|
||||
#endif
|
||||
|
||||
/* Machine Status Register, mstatus */
|
||||
#define MSTATUS_MIE (1L << 3) /* machine-mode interrupt enable */
|
||||
#define MSTATUS_MPIE (1L << 7) /* previous MIE */
|
||||
#define MSTATUS_MPP_M (3L << 11) /* previous mode: machine */
|
||||
|
||||
/* Machine Interrupt Enable, mie */
|
||||
#define MIE_MTIE (1L << 7) /* machine timer */
|
||||
#define MIE_MEIE (1L << 11) /* machine external */
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
static inline uintptr_t riscv_get_core(void)
|
||||
{
|
||||
uintptr_t x;
|
||||
__asm__ volatile("csrr %0, mhartid" : "=r" (x));
|
||||
return x;
|
||||
}
|
||||
|
||||
/* Clear mstatus.MIE and return whether it was set. */
|
||||
static inline int riscv_mintr_save(void)
|
||||
{
|
||||
uintptr_t x;
|
||||
__asm__ volatile("csrrci %0, mstatus, %1" : "=r" (x) : "i" (MSTATUS_MIE) : "memory");
|
||||
return (x & MSTATUS_MIE) != 0;
|
||||
}
|
||||
|
||||
/* Set mstatus.MIE again if riscv_mintr_save found it set. */
|
||||
static inline void riscv_mintr_restore(int enabled)
|
||||
{
|
||||
if (enabled)
|
||||
__asm__ volatile("csrsi mstatus, %0" : : "i" (MSTATUS_MIE) : "memory");
|
||||
}
|
||||
|
||||
#endif /* __ASSEMBLER__ */
|
||||
|
||||
#endif /* RISCV_CSR_H */
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,50 @@
|
||||
/***************************************************************************
|
||||
* 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 Claude Code (Opus 5.5).
|
||||
* 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
|
||||
**************************************************************************/
|
||||
|
||||
/* Reset entry for the Erbium example. Hart 0 sets up the global pointer,
|
||||
the system stack and .bss, then calls main(). Any other hart is parked. */
|
||||
|
||||
.section .text.entry, "ax", @progbits
|
||||
.align 2
|
||||
.global _start
|
||||
.extern main
|
||||
.extern _sysstack_end
|
||||
.extern _bss_start
|
||||
.extern _bss_end
|
||||
_start:
|
||||
csrr t0, mhartid
|
||||
bnez t0, _park
|
||||
|
||||
.option push
|
||||
.option norelax
|
||||
la gp, __global_pointer$ // norelax keeps this load absolute
|
||||
.option pop
|
||||
|
||||
la sp, _sysstack_end
|
||||
|
||||
la t0, _bss_start // .bss is 16-byte aligned in link.lds
|
||||
la t1, _bss_end
|
||||
_bss_clear:
|
||||
bgeu t0, t1, _bss_done
|
||||
sd zero, 0(t0)
|
||||
addi t0, t0, 8
|
||||
j _bss_clear
|
||||
_bss_done:
|
||||
|
||||
call main
|
||||
|
||||
/* The example never executes WFI; a parked hart spins. */
|
||||
_park:
|
||||
j _park
|
||||
@@ -0,0 +1,39 @@
|
||||
# /***************************************************************************
|
||||
# * 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 Claude Code (Opus 5.5).
|
||||
# * 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 the Erbium example.
|
||||
#
|
||||
# Erbium implements only part of the F extension, so the example and the
|
||||
# ThreadX library are built for the integer ISA with the soft-float lp64 ABI.
|
||||
#
|
||||
# Target ISA : rv64imc_zicsr_zifencei
|
||||
# ABI : lp64 (64-bit long/ptr, soft-float)
|
||||
# Code model : medany (MRAM at 0x40000000, peripherals above 2 GiB)
|
||||
#
|
||||
# The riscv64-unknown-elf toolchain installed by scripts/install_riscv.sh is
|
||||
# built for lp64d only, so the example links without libc and libgcc
|
||||
# (-nostdlib); a missing helper is a link error rather than an ABI mix.
|
||||
|
||||
set(CMAKE_SYSTEM_NAME Generic)
|
||||
set(CMAKE_SYSTEM_PROCESSOR risc-v64)
|
||||
|
||||
set(THREADX_ARCH "risc-v64")
|
||||
set(THREADX_TOOLCHAIN "gnu")
|
||||
set(ARCH_FLAGS "-g -march=rv64imc_zicsr_zifencei -mabi=lp64 -mcmodel=medany")
|
||||
set(CFLAGS "${ARCH_FLAGS}")
|
||||
set(ASFLAGS "${ARCH_FLAGS}")
|
||||
set(LDFLAGS "${ARCH_FLAGS}")
|
||||
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/../../../../../cmake/riscv64-unknown-elf.cmake)
|
||||
@@ -0,0 +1,42 @@
|
||||
/***************************************************************************
|
||||
* 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 Claude Code (Opus 5.5).
|
||||
* 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 "tx_api.h"
|
||||
#include "hwtimer.h"
|
||||
|
||||
/* Naturally aligned 64-bit MMIO accesses are single loads and stores on
|
||||
RV64; volatile keeps the compiler from caching or reordering them. */
|
||||
#define MTIME (*(volatile uint64_t *)ERBIUM_MTIME)
|
||||
#define MTIMECMP (*(volatile uint64_t *)ERBIUM_MTIMECMP)
|
||||
|
||||
int hwtimer_init(void)
|
||||
{
|
||||
MTIMECMP = MTIME + TICKNUM_PER_TIMER;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int hwtimer_handler(void)
|
||||
{
|
||||
/* Advance from the previous compare value, so trap latency does not
|
||||
accumulate as tick drift. */
|
||||
uint64_t next = MTIMECMP + TICKNUM_PER_TIMER;
|
||||
uint64_t now = MTIME;
|
||||
|
||||
if (next <= now)
|
||||
next = now + TICKNUM_PER_TIMER;
|
||||
|
||||
MTIMECMP = next;
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/***************************************************************************
|
||||
* 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 Claude Code (Opus 5.5).
|
||||
* 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 RISCV_HWTIMER_H
|
||||
#define RISCV_HWTIMER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* Erbium machine timer. mtime and a single mtimecmp live in the CPU
|
||||
register block; mtime_local_target selects which harts see the timer
|
||||
interrupt and resets to all harts.
|
||||
|
||||
The mtime rate depends on the reference clock and time_config, which the
|
||||
boot firmware programs. The default is the 2 MHz timebase used by the
|
||||
Zephyr and NuttX Erbium ports; define ERBIUM_MTIME_FREQ_HZ when the
|
||||
firmware selects another rate. */
|
||||
#define ERBIUM_MTIME 0x80F40200UL
|
||||
#define ERBIUM_MTIMECMP 0x80F40208UL
|
||||
|
||||
#ifndef ERBIUM_MTIME_FREQ_HZ
|
||||
#define ERBIUM_MTIME_FREQ_HZ 2000000UL
|
||||
#endif
|
||||
|
||||
#define TICKNUM_PER_TIMER (ERBIUM_MTIME_FREQ_HZ / TX_TIMER_TICKS_PER_SECOND)
|
||||
|
||||
int hwtimer_init(void);
|
||||
int hwtimer_handler(void);
|
||||
|
||||
#endif /* RISCV_HWTIMER_H */
|
||||
@@ -0,0 +1,92 @@
|
||||
/***************************************************************************
|
||||
* 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 Claude Code (Opus 5.5).
|
||||
* 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
|
||||
**************************************************************************/
|
||||
|
||||
/* Erbium memory layout.
|
||||
*
|
||||
* Address Size Usage
|
||||
* ───────────── ──────── ──────────────────────────────────────
|
||||
* 0x0200_0000 4 KiB System registers (SystemConfig at +0x08)
|
||||
* 0x0200_4000 4 KiB UART0
|
||||
* 0x4000_0000 512 B MRAM, reserved for the boot flow
|
||||
* 0x4000_0200 ~16 MiB MRAM: code, data, stacks and free memory
|
||||
* 0x80F4_0200 16 B mtime, mtimecmp
|
||||
* 0xA000_0000 64 MiB PLIC
|
||||
*
|
||||
* MRAM is directly readable, writable and executable, so .data runs in
|
||||
* place and needs no copy from a load address.
|
||||
*/
|
||||
|
||||
OUTPUT_ARCH( "riscv" )
|
||||
ENTRY( _start )
|
||||
|
||||
MEMORY
|
||||
{
|
||||
MRAM (rwx) : ORIGIN = 0x40000200, LENGTH = 0x00FFFE00
|
||||
}
|
||||
|
||||
PHDRS
|
||||
{
|
||||
text PT_LOAD FLAGS(5); /* PF_R | PF_X */
|
||||
data PT_LOAD FLAGS(6); /* PF_R | PF_W */
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
/* _start must stay at the load address, so the 4 KiB aligned trap
|
||||
vector goes in the next output section rather than this one. */
|
||||
.text.entry : {
|
||||
KEEP(*(.text.entry))
|
||||
} > MRAM :text
|
||||
|
||||
.text : {
|
||||
KEEP(*(.text.trap))
|
||||
*(.text .text.*)
|
||||
} > MRAM :text
|
||||
|
||||
.rodata : {
|
||||
. = ALIGN(16);
|
||||
*(.srodata .srodata.*)
|
||||
. = ALIGN(16);
|
||||
*(.rodata .rodata.*)
|
||||
} > MRAM :text
|
||||
|
||||
.data : {
|
||||
. = ALIGN(16);
|
||||
PROVIDE( __global_pointer$ = . + 0x800 );
|
||||
*(.sdata .sdata.*)
|
||||
. = ALIGN(16);
|
||||
*(.data .data.*)
|
||||
} > MRAM :data
|
||||
|
||||
.bss (NOLOAD) : {
|
||||
. = ALIGN(16);
|
||||
_bss_start = .;
|
||||
*(.sbss .sbss.*)
|
||||
. = ALIGN(16);
|
||||
*(.bss .bss.*)
|
||||
*(COMMON)
|
||||
. = ALIGN(16);
|
||||
_bss_end = .;
|
||||
} > MRAM :data
|
||||
|
||||
.stack (NOLOAD) : {
|
||||
. = ALIGN(16);
|
||||
_sysstack_start = .;
|
||||
. += 0x1000;
|
||||
_sysstack_end = .;
|
||||
} > MRAM :data
|
||||
|
||||
PROVIDE(_end = .);
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/***************************************************************************
|
||||
* 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 Claude Code (Opus 5.5).
|
||||
* 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
|
||||
**************************************************************************/
|
||||
|
||||
/* PLIC driver for Erbium, machine-mode context of the current hart. */
|
||||
|
||||
#include "plic.h"
|
||||
#include <stddef.h>
|
||||
|
||||
static irq_callback callbacks[MAX_CALLBACK_NUM];
|
||||
|
||||
/* Source 0 is reserved and the six sources fit in the first enable word. */
|
||||
static int plic_source_valid(int irqno)
|
||||
{
|
||||
return (irqno > 0) && (irqno < MAX_CALLBACK_NUM);
|
||||
}
|
||||
|
||||
void plic_irq_enable(int irqno)
|
||||
{
|
||||
volatile uint32_t *reg = (volatile uint32_t *)PLIC_MENABLE(riscv_get_core());
|
||||
|
||||
if (!plic_source_valid(irqno))
|
||||
return;
|
||||
|
||||
*reg = *reg | (1u << (unsigned int)irqno);
|
||||
}
|
||||
|
||||
void plic_irq_disable(int irqno)
|
||||
{
|
||||
volatile uint32_t *reg = (volatile uint32_t *)PLIC_MENABLE(riscv_get_core());
|
||||
|
||||
if (!plic_source_valid(irqno))
|
||||
return;
|
||||
|
||||
*reg = *reg & ~(1u << (unsigned int)irqno);
|
||||
}
|
||||
|
||||
int plic_register_callback(int irqno, irq_callback callback)
|
||||
{
|
||||
if (!plic_source_valid(irqno))
|
||||
return -1;
|
||||
callbacks[irqno] = callback;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int plic_unregister_callback(int irqno)
|
||||
{
|
||||
return plic_register_callback(irqno, NULL);
|
||||
}
|
||||
|
||||
int plic_init(void)
|
||||
{
|
||||
uintptr_t hart = riscv_get_core();
|
||||
|
||||
for (int i = 0; i < MAX_CALLBACK_NUM; i++)
|
||||
callbacks[i] = NULL;
|
||||
|
||||
/* Do not depend on the reset state or on a prior boot stage: start
|
||||
with every source disabled for this context. Drivers enable their
|
||||
own sources. */
|
||||
*(volatile uint32_t *)PLIC_MENABLE(hart) = 0;
|
||||
|
||||
/* The values the silicon hardwires; see plic.h. */
|
||||
*(volatile uint32_t *)PLIC_MTHRESHOLD(hart) = 0;
|
||||
for (int i = 1; i <= PLIC_NUM_SOURCES; i++)
|
||||
*(volatile uint32_t *)PLIC_PRIORITY(i) = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int plic_claim(void)
|
||||
{
|
||||
return (int)(*(volatile uint32_t *)PLIC_MCLAIM(riscv_get_core()));
|
||||
}
|
||||
|
||||
void plic_complete(int irqno)
|
||||
{
|
||||
*(volatile uint32_t *)PLIC_MCOMPLETE(riscv_get_core()) = (uint32_t)irqno;
|
||||
}
|
||||
|
||||
int plic_irq_intr(void)
|
||||
{
|
||||
int ret = -1;
|
||||
int irqno = plic_claim();
|
||||
|
||||
/* Nothing pending: another context claimed it first. */
|
||||
if (irqno == 0)
|
||||
return 0;
|
||||
|
||||
if (!plic_source_valid(irqno))
|
||||
{
|
||||
plic_complete(irqno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (callbacks[irqno] != NULL)
|
||||
ret = (callbacks[irqno])(irqno);
|
||||
|
||||
plic_complete(irqno);
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/***************************************************************************
|
||||
* 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 Claude Code (Opus 5.5).
|
||||
* 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 RISCV_PLIC_H
|
||||
#define RISCV_PLIC_H
|
||||
|
||||
#include "csr.h"
|
||||
#include <stdint.h>
|
||||
|
||||
/* Erbium PLIC, RISC-V PLIC 1.0. Each hart has two contexts, machine mode
|
||||
first, so hart h uses context 2h.
|
||||
|
||||
The silicon hardwires source priorities to 1 and context thresholds to
|
||||
0, so an enabled, pending source always interrupts. The et-platform
|
||||
simulator implements them as writable registers that reset to 0, which
|
||||
masks every source, so plic_init() programs the silicon values. There is
|
||||
no priority control beyond that.
|
||||
|
||||
Sources: 1 MRAM, 2 QSPI, 3 UART, 4 system register, 5 xSPI, 6 GPIO. */
|
||||
#define PLIC 0xA0000000UL
|
||||
#define PLIC_PRIORITY(irqno) (PLIC + (unsigned long)(irqno) * 4UL)
|
||||
#define PLIC_MENABLE(hart) (PLIC + 0x2000UL + (hart) * 0x100UL)
|
||||
#define PLIC_MTHRESHOLD(hart) (PLIC + 0x200000UL + (hart) * 0x2000UL)
|
||||
#define PLIC_MCLAIM(hart) (PLIC + 0x200004UL + (hart) * 0x2000UL)
|
||||
#define PLIC_MCOMPLETE(hart) PLIC_MCLAIM(hart)
|
||||
|
||||
#define PLIC_NUM_SOURCES 6
|
||||
#define MAX_CALLBACK_NUM (PLIC_NUM_SOURCES + 1)
|
||||
|
||||
typedef int (*irq_callback)(int irqno);
|
||||
|
||||
void plic_irq_enable(int irqno);
|
||||
void plic_irq_disable(int irqno);
|
||||
int plic_register_callback(int irqno, irq_callback callback);
|
||||
int plic_unregister_callback(int irqno);
|
||||
int plic_init(void);
|
||||
int plic_claim(void);
|
||||
void plic_complete(int irqno);
|
||||
|
||||
int plic_irq_intr(void);
|
||||
|
||||
#endif /* RISCV_PLIC_H */
|
||||
@@ -0,0 +1,78 @@
|
||||
/***************************************************************************
|
||||
* 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 Claude Code (Opus 5.5).
|
||||
* 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
|
||||
**************************************************************************/
|
||||
|
||||
/* Machine-mode trap dispatcher for the Erbium example. */
|
||||
|
||||
#include "csr.h"
|
||||
#include "uart.h"
|
||||
#include "hwtimer.h"
|
||||
#include "plic.h"
|
||||
#include <stdint.h>
|
||||
#include <tx_api.h>
|
||||
|
||||
#define MCAUSE_INT_BIT ((uintptr_t)1 << (__riscv_xlen - 1))
|
||||
#define MCAUSE_M_TIMER (MCAUSE_INT_BIT | 7u)
|
||||
#define MCAUSE_M_EXTERNAL (MCAUSE_INT_BIT | 11u)
|
||||
|
||||
extern void _tx_timer_interrupt(void);
|
||||
|
||||
static void print_hex(uintptr_t val)
|
||||
{
|
||||
const char digits[] = "0123456789ABCDEF";
|
||||
|
||||
uart_putc('0');
|
||||
uart_putc('x');
|
||||
for (int i = (int)(sizeof(uintptr_t) * 2) - 1; i >= 0; i--)
|
||||
uart_putc(digits[(val >> (i * 4)) & 0xFu]);
|
||||
uart_putc('\r');
|
||||
uart_putc('\n');
|
||||
}
|
||||
|
||||
static void trap_halt(const char *reason, uintptr_t mcause, uintptr_t mepc, uintptr_t mtval)
|
||||
{
|
||||
uart_puts(reason);
|
||||
uart_puts("mcause:");
|
||||
print_hex(mcause);
|
||||
uart_puts("mepc:");
|
||||
print_hex(mepc);
|
||||
uart_puts("mtval:");
|
||||
print_hex(mtval);
|
||||
|
||||
/* Spin rather than WFI, as everywhere else in this example. */
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
|
||||
void trap_handler(uintptr_t mcause, uintptr_t mepc, uintptr_t mtval)
|
||||
{
|
||||
if (mcause == MCAUSE_M_TIMER)
|
||||
{
|
||||
hwtimer_handler();
|
||||
_tx_timer_interrupt();
|
||||
}
|
||||
else if (mcause == MCAUSE_M_EXTERNAL)
|
||||
{
|
||||
if (plic_irq_intr() != 0)
|
||||
trap_halt("[INTERRUPT]: external interrupt not handled, halting", mcause, mepc, mtval);
|
||||
}
|
||||
else if ((mcause & MCAUSE_INT_BIT) != 0u)
|
||||
{
|
||||
trap_halt("[INTERRUPT]: unexpected interrupt, halting", mcause, mepc, mtval);
|
||||
}
|
||||
else
|
||||
{
|
||||
trap_halt("[EXCEPTION]: unhandled exception, halting", mcause, mepc, mtval);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
/***************************************************************************
|
||||
* 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 Claude Code (Opus 5.5).
|
||||
* 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 "csr.h"
|
||||
#include "tx_port.h"
|
||||
|
||||
/* Erbium ignores mtvec base bits 11:1, so the trap vector must be 4 KiB
|
||||
aligned. */
|
||||
.section .text.trap, "ax", @progbits
|
||||
.balign 4096
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* FUNCTION RELEASE */
|
||||
/* */
|
||||
/* trap_entry RISC-V64/GNU */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function is the machine-mode trap vector (direct mode). It */
|
||||
/* saves the interrupted context, calls trap_handler with mcause, */
|
||||
/* mepc and mtval, and leaves through the ThreadX context restore. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* None */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* None */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* _tx_thread_context_save Save interrupted context */
|
||||
/* trap_handler Dispatch the trap */
|
||||
/* _tx_thread_context_restore Restore or schedule a thread */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* Hardware trap */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
.global trap_entry
|
||||
.extern trap_handler
|
||||
.extern _tx_thread_context_save
|
||||
.extern _tx_thread_context_restore
|
||||
trap_entry:
|
||||
addi sp, sp, -TX_RISCV_TRAP_FRAME_SIZE // Interrupt frame from the port contract
|
||||
sd x1, 28*8(sp) // Save RA before the call below overwrites it
|
||||
|
||||
call _tx_thread_context_save
|
||||
|
||||
csrr a0, mcause
|
||||
csrr a1, mepc
|
||||
csrr a2, mtval
|
||||
addi sp, sp, -16 // 16 bytes keep sp 16-byte aligned at the call
|
||||
sd ra, 0(sp)
|
||||
call trap_handler
|
||||
ld ra, 0(sp)
|
||||
addi sp, sp, 16
|
||||
|
||||
call _tx_thread_context_restore
|
||||
/* _tx_thread_context_restore does not return. */
|
||||
_trap_entry_hang:
|
||||
j _trap_entry_hang
|
||||
|
||||
|
||||
.section .text
|
||||
/**************************************************************************/
|
||||
/* */
|
||||
/* FUNCTION RELEASE */
|
||||
/* */
|
||||
/* _tx_initialize_low_level RISC-V64/GNU */
|
||||
/* */
|
||||
/* DESCRIPTION */
|
||||
/* */
|
||||
/* This function is responsible for any low-level processor */
|
||||
/* initialization, including setting up interrupt vectors, setting */
|
||||
/* up a periodic timer interrupt source, saving the system stack */
|
||||
/* pointer for use in ISR processing later, and finding the first */
|
||||
/* available RAM memory address for tx_application_define. */
|
||||
/* */
|
||||
/* INPUT */
|
||||
/* */
|
||||
/* None */
|
||||
/* */
|
||||
/* OUTPUT */
|
||||
/* */
|
||||
/* None */
|
||||
/* */
|
||||
/* CALLS */
|
||||
/* */
|
||||
/* board_init Initialize the peripherals */
|
||||
/* */
|
||||
/* CALLED BY */
|
||||
/* */
|
||||
/* _tx_initialize_kernel_enter ThreadX entry function */
|
||||
/* */
|
||||
/**************************************************************************/
|
||||
/* VOID _tx_initialize_low_level(VOID)
|
||||
{ */
|
||||
.global _tx_initialize_low_level // Strong definition: must beat the port's weak default
|
||||
.extern _end
|
||||
.extern board_init
|
||||
.extern _tx_thread_system_stack_ptr
|
||||
.extern _tx_initialize_unused_memory
|
||||
_tx_initialize_low_level:
|
||||
la t0, _tx_thread_system_stack_ptr
|
||||
sd sp, 0(t0) // Save system stack pointer
|
||||
|
||||
la t0, _end // Pickup first free address
|
||||
la t1, _tx_initialize_unused_memory
|
||||
sd t0, 0(t1) // Save unused memory address
|
||||
|
||||
li t0, MSTATUS_MIE
|
||||
csrrc zero, mstatus, t0 // Keep interrupts off until the scheduler runs
|
||||
li t0, (MSTATUS_MPP_M | MSTATUS_MPIE)
|
||||
csrrs zero, mstatus, t0 // mret stays in M-mode with interrupts enabled
|
||||
li t0, (MIE_MTIE | MIE_MEIE)
|
||||
csrrs zero, mie, t0 // Enable timer and external interrupts
|
||||
|
||||
addi sp, sp, -16 // 16 bytes keep sp 16-byte aligned at the call
|
||||
sd ra, 0(sp)
|
||||
call board_init
|
||||
ld ra, 0(sp)
|
||||
addi sp, sp, 16
|
||||
|
||||
la t0, trap_entry
|
||||
csrw mtvec, t0 // Direct mode: trap_entry is 4 KiB aligned
|
||||
ret
|
||||
/* } */
|
||||
@@ -0,0 +1,106 @@
|
||||
/***************************************************************************
|
||||
* 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 Claude Code (Opus 5.5).
|
||||
* 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
|
||||
**************************************************************************/
|
||||
|
||||
/* Polling console driver for the Erbium UART (original Shakti integration).
|
||||
*
|
||||
* Offset Name Access Description
|
||||
* ────── ──────────── ────── ─────────────────────────────────────
|
||||
* 0x00 BaudReg R/W Divisor: clock / (16 * baud)
|
||||
* 0x08 TxReg W Transmit data
|
||||
* 0x10 RxReg R Receive data
|
||||
* 0x18 StatusReg R FIFO and error status
|
||||
* 0x20 DelayReg R/W Transmit delay
|
||||
* 0x28 ControlReg R/W Character size, parity, stop bits
|
||||
* 0x30 InterruptEn R/W Interrupt enables
|
||||
* 0x40 Rx_Threshold R/W Receive FIFO threshold
|
||||
*
|
||||
* All registers are accessed as 32-bit words. The transmit and receive
|
||||
* FIFOs hold 16 entries each.
|
||||
*/
|
||||
|
||||
#include "uart.h"
|
||||
#include "csr.h"
|
||||
#include <stdint.h>
|
||||
|
||||
#define UART_DIVISOR (ERBIUM_UART_CLOCK_HZ / (16UL * ERBIUM_UART_BAUD))
|
||||
|
||||
#if (UART_DIVISOR == 0) || (UART_DIVISOR > 0xFFFF)
|
||||
#error "The Erbium UART baud divisor must be in the range 1..65535."
|
||||
#endif
|
||||
|
||||
/* SystemConfig in the system register block gates the UART. */
|
||||
#define SYSREG_SYSTEM_CONFIG 0x02000008UL
|
||||
#define SYSTEM_CONFIG_UART_EN (1u << 6)
|
||||
|
||||
#define UART_BAUD 0x00
|
||||
#define UART_TX 0x08
|
||||
#define UART_STATUS 0x18
|
||||
#define UART_DELAY 0x20
|
||||
#define UART_CONTROL 0x28
|
||||
#define UART_IEN 0x30
|
||||
#define UART_RX_THRESHOLD 0x40
|
||||
|
||||
#define STATUS_TX_FULL (1u << 1)
|
||||
|
||||
#define CONTROL_CHARSIZE_SHIFT 5 /* bits 10:5; stop bits and parity 0 */
|
||||
|
||||
#define REG32(addr) (*(volatile uint32_t *)(addr))
|
||||
#define UART_REG(off) REG32(UART0 + (off))
|
||||
|
||||
int uart_init(void)
|
||||
{
|
||||
/* Ungate the UART. Read-modify-write keeps the watchdog and the
|
||||
other peripheral enables as the boot flow left them. */
|
||||
REG32(SYSREG_SYSTEM_CONFIG) = REG32(SYSREG_SYSTEM_CONFIG) | SYSTEM_CONFIG_UART_EN;
|
||||
|
||||
/* Polling only: no UART interrupts. */
|
||||
UART_REG(UART_IEN) = 0;
|
||||
|
||||
/* 8 data bits, no parity, one stop bit. */
|
||||
UART_REG(UART_BAUD) = (uint32_t)UART_DIVISOR;
|
||||
UART_REG(UART_CONTROL) = 8u << CONTROL_CHARSIZE_SHIFT;
|
||||
UART_REG(UART_DELAY) = 0;
|
||||
UART_REG(UART_RX_THRESHOLD) = 0;
|
||||
|
||||
puts("[UART0] : Erbium UART initialized");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void uart_putc_nolock(int ch)
|
||||
{
|
||||
while ((UART_REG(UART_STATUS) & STATUS_TX_FULL) != 0u)
|
||||
;
|
||||
UART_REG(UART_TX) = (uint32_t)(ch & 0xFF);
|
||||
}
|
||||
|
||||
int uart_putc(int ch)
|
||||
{
|
||||
int intr_enable = riscv_mintr_save();
|
||||
uart_putc_nolock(ch);
|
||||
riscv_mintr_restore(intr_enable);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int uart_puts(const char *str)
|
||||
{
|
||||
int i;
|
||||
int intr_enable = riscv_mintr_save();
|
||||
for (i = 0; str[i] != 0; i++)
|
||||
uart_putc_nolock(str[i]);
|
||||
uart_putc_nolock('\r');
|
||||
uart_putc_nolock('\n');
|
||||
riscv_mintr_restore(intr_enable);
|
||||
return i;
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/***************************************************************************
|
||||
* 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 Claude Code (Opus 5.5).
|
||||
* 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 RISCV_UART_H
|
||||
#define RISCV_UART_H
|
||||
|
||||
/* Erbium UART0: the original Shakti UART integration, with 32-bit registers
|
||||
at eight-byte intervals. It is PLIC source 3. */
|
||||
#define UART0 0x02004000UL
|
||||
#define UART0_IRQ 3
|
||||
|
||||
/* The UART input clock and the console rate. The defaults match the
|
||||
public et-platform simulator; define these on the command line when the
|
||||
boot firmware programs a different peripheral clock. */
|
||||
#ifndef ERBIUM_UART_CLOCK_HZ
|
||||
#define ERBIUM_UART_CLOCK_HZ 400000000UL
|
||||
#endif
|
||||
#ifndef ERBIUM_UART_BAUD
|
||||
#define ERBIUM_UART_BAUD 115200UL
|
||||
#endif
|
||||
|
||||
#define puts uart_puts
|
||||
|
||||
int uart_init(void);
|
||||
int uart_putc(int ch);
|
||||
int uart_puts(const char *str);
|
||||
|
||||
#endif /* RISCV_UART_H */
|
||||
Reference in New Issue
Block a user