diff --git a/.github/workflows/build-ferrocene.yml b/.github/workflows/build-ferrocene.yml index c9b588b..042c81c 100644 --- a/.github/workflows/build-ferrocene.yml +++ b/.github/workflows/build-ferrocene.yml @@ -5,45 +5,19 @@ name: workflow-build-everything-ferrocene run-name: Build Everything with Ferrocene on: [push] jobs: - job-build-threadx-staticlib: - runs-on: ubuntu-latest - steps: - - name: Checkout repo - uses: actions/checkout@v4 - with: - submodules: 'true' - - name: Install tools - run: | - sudo apt-get update -y && sudo apt-get -y install cmake gcc gcc-arm-none-eabi build-essential ninja-build - - name: Compile ThreadX for Cortex-M4 - run: | - cd threadx - cmake -Bbuild_m4 -GNinja -DCMAKE_TOOLCHAIN_FILE=cmake/cortex_m4.cmake - cmake --build ./build_m4 - - name: Upload staticlib - uses: actions/upload-artifact@master - with: - name: threadx-cm4 - path: threadx/build_m4/libthreadx.a job-build-demo-app: runs-on: ubuntu-latest - needs: job-build-threadx-staticlib steps: - name: Install Arm C compiler run: | sudo apt-get update -y && sudo apt-get -y install gcc-arm-none-eabi - curl --proto '=https' --tlsv1.2 -LsSf https://github.com/ferrocene/criticalup/releases/download/v1.0.0-prerelease.1/criticalup-installer.sh | sh + curl --proto '=https' --tlsv1.2 -LsSf https://github.com/ferrocene/criticalup/releases/download/v1.0.1/criticalup-installer.sh | sh - name: Checkout repo uses: actions/checkout@v4 with: submodules: 'true' - - name: Download staticlib - uses: actions/download-artifact@master - with: - name: threadx-cm4 - path: threadx/build # Where build.rs expects it - name: Install Ferrocene - env: # Or as an environment variable + env: CRITICALUP_TOKEN: ${{ secrets.CRITICALUP_TOKEN }} run: | criticalup install diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e7db193..c393662 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,29 +5,8 @@ name: workflow-build-everything run-name: Build Everything on: [push] jobs: - job-build-threadx-staticlib: - runs-on: ubuntu-latest - steps: - - name: Checkout repo - uses: actions/checkout@v4 - with: - submodules: 'true' - - name: Install tools - run: | - sudo apt-get update -y && sudo apt-get -y install cmake gcc gcc-arm-none-eabi build-essential ninja-build - - name: Compile ThreadX for Cortex-M4 - run: | - cd threadx - cmake -Bbuild_m4 -GNinja -DCMAKE_TOOLCHAIN_FILE=cmake/cortex_m4.cmake - cmake --build ./build_m4 - - name: Upload staticlib - uses: actions/upload-artifact@master - with: - name: threadx-cm4 - path: threadx/build_m4/libthreadx.a job-build-demo-app: runs-on: ubuntu-latest - needs: job-build-threadx-staticlib steps: - name: Install Arm C compiler run: | @@ -40,11 +19,6 @@ jobs: uses: actions/checkout@v4 with: submodules: 'true' - - name: Download staticlib - uses: actions/download-artifact@master - with: - name: threadx-cm4 - path: threadx/build # Where build.rs expects it - name: Add rustup target run: | rustup target add thumbv7em-none-eabi diff --git a/README.md b/README.md index 26ee5b0..e555fe8 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,13 @@ This repository shows how to compile a Rust application which runs on the ThreadX RTOS. +Note this project uses Git submodules, so you should run: + +```bash +git submodule update --init +``` + ## Licence -* Copyright (c) 2023 Ferrous Systems +* Copyright (c) 2024 Ferrous Systems * SPDX-License-Identifier: MIT OR Apache-2.0 diff --git a/build.sh b/build.sh index 99aea8c..f318018 100755 --- a/build.sh +++ b/build.sh @@ -1,18 +1,12 @@ #!/bin/bash -# Builds the Cortex-M4 version of ThreadX, then builds a Rust -# application that links to it. +# Builds the various ThreadX demo apps. -# SPDX-FileCopyrightText: Copyright (c) 2023 Ferrous Systems +# SPDX-FileCopyrightText: Copyright (c) 2024 Ferrous Systems # SPDX-License-Identifier: MIT OR Apache-2.0 set -euo pipefail -pushd threadx -cmake -Bbuild -GNinja -DCMAKE_TOOLCHAIN_FILE=cmake/cortex_m4.cmake . -cmake --build ./build -popd - pushd demo-app cargo build --release popd diff --git a/demo-app/README.md b/demo-app/README.md new file mode 100644 index 0000000..f522211 --- /dev/null +++ b/demo-app/README.md @@ -0,0 +1,50 @@ +# Rust on ThreadX on nRF52 Example Binary + +This is a Rust application which uses ThreadX and runs on an nRF52840-DK board. + +You can compile it with Rust, or with Ferrocene. You will need +`arm-none-eabi-gcc` installed to compile ThreadX, which this application does +automatically. You will also need `probe-rs` from . + +```console +$ cargo run --release + Compiling demo-app v0.0.0 (/Users/jonathan/Documents/ferrous-systems/threadx-experiments/demo-app) + Finished `dev` profile [optimized + debuginfo] target(s) in 4.81s + Running `probe-rs run --chip nRF52840_xxAA target/thumbv7em-none-eabi/debug/demo-app` + Erasing ✔ [00:00:00] [####################################] 12.00 KiB/12.00 KiB @ 33.03 KiB/s (eta 0s ) + Programming ✔ [00:00:00] [####################################] 12.00 KiB/12.00 KiB @ 42.84 KiB/s (eta 0s ) Finished in 0.661s + Hello, this is version unknown! +└─ demo_app::__cortex_m_rt_main @ src/main.rs:151 + Entering ThreadX kernel... +└─ demo_app::__cortex_m_rt_main @ src/main.rs:186 + In tx_application_define()... +└─ demo_app::tx_application_define @ src/main.rs:26 + Stack allocated @ 0x20037444 +└─ demo_app::tx_application_define @ src/main.rs:59 + Thread spawned (entry=12345678) @ 0x2003f440 +└─ demo_app::tx_application_define @ src/main.rs:85 + Stack allocated @ 0x2003944c +└─ demo_app::tx_application_define @ src/main.rs:102 + Thread spawned (entry=aabbccdd) @ 0x2003f4f8 +└─ demo_app::tx_application_define @ src/main.rs:128 + I am my_thread(12345678) +└─ demo_app::my_thread @ src/main.rs:136 + I am my_thread(aabbccdd) +└─ demo_app::my_thread @ src/main.rs:136 + I am my_thread(12345678), count = 1 +└─ demo_app::my_thread @ src/main.rs:145 + I am my_thread(aabbccdd), count = 1 +└─ demo_app::my_thread @ src/main.rs:145 + I am my_thread(12345678), count = 2 +└─ demo_app::my_thread @ src/main.rs:145 + I am my_thread(aabbccdd), count = 2 +└─ demo_app::my_thread @ src/main.rs:145 + I am my_thread(12345678), count = 3 +└─ demo_app::my_thread @ src/main.rs:145 +... +``` + +## Licence + +* Copyright (c) 2024 Ferrous Systems +* SPDX-License-Identifier: MIT OR Apache-2.0 diff --git a/demo-app/build.rs b/demo-app/build.rs index 18a0c11..cb6bf25 100644 --- a/demo-app/build.rs +++ b/demo-app/build.rs @@ -5,6 +5,203 @@ use std::{env, error::Error, fs, path::PathBuf}; +static TX_PORT_FILES: &[&str] = &[ + "tx_thread_stack_build.S", + "tx_thread_schedule.S", + "tx_thread_system_return.S", + "tx_thread_context_save.S", + "tx_thread_context_restore.S", + "tx_thread_interrupt_control.S", + "tx_timer_interrupt.S", +]; + +static TX_COMMON_FILES: &[&str] = &[ + "tx_block_allocate.c", + "tx_block_pool_cleanup.c", + "tx_block_pool_create.c", + "tx_block_pool_delete.c", + "tx_block_pool_info_get.c", + "tx_block_pool_initialize.c", + "tx_block_pool_performance_info_get.c", + "tx_block_pool_performance_system_info_get.c", + "tx_block_pool_prioritize.c", + "tx_block_release.c", + "tx_byte_allocate.c", + "tx_byte_pool_cleanup.c", + "tx_byte_pool_create.c", + "tx_byte_pool_delete.c", + "tx_byte_pool_info_get.c", + "tx_byte_pool_initialize.c", + "tx_byte_pool_performance_info_get.c", + "tx_byte_pool_performance_system_info_get.c", + "tx_byte_pool_prioritize.c", + "tx_byte_pool_search.c", + "tx_byte_release.c", + "tx_event_flags_cleanup.c", + "tx_event_flags_create.c", + "tx_event_flags_delete.c", + "tx_event_flags_get.c", + "tx_event_flags_info_get.c", + "tx_event_flags_initialize.c", + "tx_event_flags_performance_info_get.c", + "tx_event_flags_performance_system_info_get.c", + "tx_event_flags_set.c", + "tx_event_flags_set_notify.c", + "tx_initialize_high_level.c", + "tx_initialize_kernel_enter.c", + "tx_initialize_kernel_setup.c", + "tx_mutex_cleanup.c", + "tx_mutex_create.c", + "tx_mutex_delete.c", + "tx_mutex_get.c", + "tx_mutex_info_get.c", + "tx_mutex_initialize.c", + "tx_mutex_performance_info_get.c", + "tx_mutex_performance_system_info_get.c", + "tx_mutex_prioritize.c", + "tx_mutex_priority_change.c", + "tx_mutex_put.c", + "tx_queue_cleanup.c", + "tx_queue_create.c", + "tx_queue_delete.c", + "tx_queue_flush.c", + "tx_queue_front_send.c", + "tx_queue_info_get.c", + "tx_queue_initialize.c", + "tx_queue_performance_info_get.c", + "tx_queue_performance_system_info_get.c", + "tx_queue_prioritize.c", + "tx_queue_receive.c", + "tx_queue_send.c", + "tx_queue_send_notify.c", + "tx_semaphore_ceiling_put.c", + "tx_semaphore_cleanup.c", + "tx_semaphore_create.c", + "tx_semaphore_delete.c", + "tx_semaphore_get.c", + "tx_semaphore_info_get.c", + "tx_semaphore_initialize.c", + "tx_semaphore_performance_info_get.c", + "tx_semaphore_performance_system_info_get.c", + "tx_semaphore_prioritize.c", + "tx_semaphore_put.c", + "tx_semaphore_put_notify.c", + "tx_thread_create.c", + "tx_thread_delete.c", + "tx_thread_entry_exit_notify.c", + "tx_thread_identify.c", + "tx_thread_info_get.c", + "tx_thread_initialize.c", + "tx_thread_performance_info_get.c", + "tx_thread_performance_system_info_get.c", + "tx_thread_preemption_change.c", + "tx_thread_priority_change.c", + "tx_thread_relinquish.c", + "tx_thread_reset.c", + "tx_thread_resume.c", + "tx_thread_shell_entry.c", + "tx_thread_sleep.c", + "tx_thread_stack_analyze.c", + "tx_thread_stack_error_handler.c", + "tx_thread_stack_error_notify.c", + "tx_thread_suspend.c", + "tx_thread_system_preempt_check.c", + "tx_thread_system_resume.c", + "tx_thread_system_suspend.c", + "tx_thread_terminate.c", + "tx_thread_time_slice.c", + "tx_thread_time_slice_change.c", + "tx_thread_timeout.c", + "tx_thread_wait_abort.c", + "tx_time_get.c", + "tx_time_set.c", + "tx_timer_activate.c", + "tx_timer_change.c", + "tx_timer_create.c", + "tx_timer_deactivate.c", + "tx_timer_delete.c", + "tx_timer_expiration_process.c", + "tx_timer_info_get.c", + "tx_timer_initialize.c", + "tx_timer_performance_info_get.c", + "tx_timer_performance_system_info_get.c", + "tx_timer_system_activate.c", + "tx_timer_system_deactivate.c", + "tx_timer_thread_entry.c", + "tx_trace_buffer_full_notify.c", + "tx_trace_enable.c", + "tx_trace_event_filter.c", + "tx_trace_event_unfilter.c", + "tx_trace_disable.c", + "tx_trace_initialize.c", + "tx_trace_interrupt_control.c", + "tx_trace_isr_enter_insert.c", + "tx_trace_isr_exit_insert.c", + "tx_trace_object_register.c", + "tx_trace_object_unregister.c", + "tx_trace_user_event_insert.c", + "txe_block_allocate.c", + "txe_block_pool_create.c", + "txe_block_pool_delete.c", + "txe_block_pool_info_get.c", + "txe_block_pool_prioritize.c", + "txe_block_release.c", + "txe_byte_allocate.c", + "txe_byte_pool_create.c", + "txe_byte_pool_delete.c", + "txe_byte_pool_info_get.c", + "txe_byte_pool_prioritize.c", + "txe_byte_release.c", + "txe_event_flags_create.c", + "txe_event_flags_delete.c", + "txe_event_flags_get.c", + "txe_event_flags_info_get.c", + "txe_event_flags_set.c", + "txe_event_flags_set_notify.c", + "txe_mutex_create.c", + "txe_mutex_delete.c", + "txe_mutex_get.c", + "txe_mutex_info_get.c", + "txe_mutex_prioritize.c", + "txe_mutex_put.c", + "txe_queue_create.c", + "txe_queue_delete.c", + "txe_queue_flush.c", + "txe_queue_front_send.c", + "txe_queue_info_get.c", + "txe_queue_prioritize.c", + "txe_queue_receive.c", + "txe_queue_send.c", + "txe_queue_send_notify.c", + "txe_semaphore_ceiling_put.c", + "txe_semaphore_create.c", + "txe_semaphore_delete.c", + "txe_semaphore_get.c", + "txe_semaphore_info_get.c", + "txe_semaphore_prioritize.c", + "txe_semaphore_put.c", + "txe_semaphore_put_notify.c", + "txe_thread_create.c", + "txe_thread_delete.c", + "txe_thread_entry_exit_notify.c", + "txe_thread_info_get.c", + "txe_thread_preemption_change.c", + "txe_thread_priority_change.c", + "txe_thread_relinquish.c", + "txe_thread_reset.c", + "txe_thread_resume.c", + "txe_thread_suspend.c", + "txe_thread_terminate.c", + "txe_thread_time_slice_change.c", + "txe_thread_wait_abort.c", + "txe_timer_activate.c", + "txe_timer_change.c", + "txe_timer_create.c", + "txe_timer_deactivate.c", + "txe_timer_delete.c", + "txe_timer_info_get.c", +]; + fn main() -> Result<(), Box> { let out_dir = PathBuf::from(env::var("OUT_DIR")?); let crate_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR")?); @@ -14,14 +211,18 @@ fn main() -> Result<(), Box> { println!("cargo:rustc-link-search={}", out_dir.display()); println!("cargo:rerun-if-changed=memory.x"); - // Include our ThreadX static library - println!( - "cargo:rustc-link-search={}", - crate_dir.join("../threadx/build").display() - ); - println!("cargo:rustc-link-lib=static=threadx"); + // Build our ThreadX static library + let tx_common_dir = crate_dir.join("../threadx/common/src"); + let tx_common_inc = crate_dir.join("../threadx/common/inc"); + let tx_port_dir = crate_dir.join("../threadx/ports/cortex_m4/gnu/src"); + let tx_port_inc = crate_dir.join("../threadx/ports/cortex_m4/gnu/inc"); + cc::Build::new() + .include(&tx_common_inc) + .include(&tx_port_inc) + .files(TX_PORT_FILES.iter().map(|&s| tx_port_dir.join(s))) + .files(TX_COMMON_FILES.iter().map(|&s| tx_common_dir.join(s))) + .compile("threadx"); - // Compile our assembly code cc::Build::new() .file("src/tx_low_level.S") .compile("tx_low_level"); diff --git a/demo-app/src/main.rs b/demo-app/src/main.rs index 5de8ed8..93253f4 100644 --- a/demo-app/src/main.rs +++ b/demo-app/src/main.rs @@ -15,7 +15,11 @@ use static_cell::StaticCell; static BUILD_SLUG: Option<&str> = option_env!("BUILD_SLUG"); -const DEMO_STACK_SIZE: usize = 1024; +const DEMO_STACK_SIZE: usize = 8192; +const DEMO_POOL_SIZE: usize = (DEMO_STACK_SIZE * 2) + 16384; + +const SYSTEM_CLOCK: u32 = 64_000_000; +const SYSTICK_CYCLES: u32 = (SYSTEM_CLOCK / 100) - 1; #[no_mangle] extern "C" fn tx_application_define(_first_unused_memory: *mut core::ffi::c_void) { @@ -27,7 +31,7 @@ extern "C" fn tx_application_define(_first_unused_memory: *mut core::ffi::c_void let byte_pool = { static BYTE_POOL: StaticCell = StaticCell::new(); - static BYTE_POOL_STORAGE: StaticCell<[u8; 32768]> = StaticCell::new(); + static BYTE_POOL_STORAGE: StaticCell<[u8; DEMO_POOL_SIZE]> = StaticCell::new(); let byte_pool = BYTE_POOL.uninit(); let byte_pool_storage = BYTE_POOL_STORAGE.uninit(); unsafe { @@ -35,7 +39,7 @@ extern "C" fn tx_application_define(_first_unused_memory: *mut core::ffi::c_void byte_pool.as_mut_ptr(), c!("byte-pool0").as_ptr() as *mut threadx_sys::CHAR, byte_pool_storage.as_mut_ptr() as *mut _, - core::mem::size_of_val(&BYTE_POOL_STORAGE) as u32, + DEMO_POOL_SIZE as u32, ); byte_pool.assume_init_mut() } @@ -150,6 +154,7 @@ fn main() -> ! { ); let pp = nrf52840_hal::pac::Peripherals::take().unwrap(); + let mut cp = cortex_m::Peripherals::take().unwrap(); let clocks = nrf52840_hal::Clocks::new(pp.CLOCK); let clocks = clocks.enable_ext_hfosc(); @@ -166,6 +171,18 @@ fn main() -> ! { let _ = led.set_low(); + // Enable cycle counter + cp.DCB.enable_trace(); + cp.DWT.enable_cycle_counter(); + + // Enable the systick + cp.SYST.set_reload(SYSTICK_CYCLES); + cp.SYST.clear_current(); + cp.SYST.enable_interrupt(); + cp.SYST + .set_clock_source(cortex_m::peripheral::syst::SystClkSource::Core); + cp.SYST.enable_counter(); + defmt::println!("Entering ThreadX kernel..."); unsafe { threadx_sys::_tx_initialize_kernel_enter(); @@ -180,3 +197,13 @@ fn main() -> ! { fn panic() -> ! { cortex_m::asm::udf() } + +#[no_mangle] +unsafe extern "C" fn __tx_SysTickHandler() { + extern "C" { + fn _tx_timer_interrupt(); + } + // Call into OS function (not in public API) + _tx_timer_interrupt(); + // Can do any extra work here +} diff --git a/demo-app/src/tx_low_level.S b/demo-app/src/tx_low_level.S index 32b0646..d68237e 100644 --- a/demo-app/src/tx_low_level.S +++ b/demo-app/src/tx_low_level.S @@ -22,6 +22,7 @@ @/**************************************************************************/ @ @ + .global _tx_initialize_low_level2 .global _tx_thread_system_stack_ptr .global _tx_initialize_unused_memory .global __sheap @@ -99,12 +100,6 @@ _tx_initialize_low_level: ADD r1, r1, #4 @ STR r1, [r0] @ Setup first unused memory pointer @ -@ /* Setup Vector Table Offset Register. */ -@ - MOV r0, #0xE000E000 @ Build address of NVIC registers - LDR r1, =__vector_table @ Pickup address of vector table - STR r1, [r0, #0xD08] @ Set vector table address -@ @ /* Set system stack pointer from vector value. */ @ LDR r0, =_tx_thread_system_stack_ptr @ Build address of system stack pointer @@ -112,23 +107,9 @@ _tx_initialize_low_level: LDR r1, [r1] @ Pickup reset stack pointer STR r1, [r0] @ Save system stack pointer @ -@ /* Enable the cycle count register. */ -@ - LDR r0, =0xE0001000 @ Build address of DWT register - LDR r1, [r0] @ Pickup the current value - ORR r1, r1, #1 @ Set the CYCCNTENA bit - STR r1, [r0] @ Enable the cycle count register -@ -@ /* Configure SysTick for 100Hz clock, or 16384 cycles if no reference. */ -@ - MOV r0, #0xE000E000 @ Build address of NVIC registers - LDR r1, =SYSTICK_CYCLES - STR r1, [r0, #0x14] @ Setup SysTick Reload Value - MOV r1, #0x7 @ Build SysTick Control Enable Value - STR r1, [r0, #0x10] @ Setup SysTick Control -@ @ /* Configure handler priorities. */ @ + MOV r0, #0xE000E000 @ Build address of NVIC registers LDR r1, =0x00000000 @ Rsrv, UsgF, BusF, MemM STR r1, [r0, #0xD18] @ Setup System Handlers 4-7 Priority Registers @@ -146,34 +127,3 @@ _tx_initialize_low_level: BX lr @} @ - -@ /* Generic interrupt handler template */ - .global __tx_IntHandler - .thumb_func -__tx_IntHandler: -@ VOID InterruptHandler (VOID) -@ { - PUSH {r0, lr} - -@ /* Do interrupt handler work here */ -@ /* BL .... */ - - POP {r0, lr} - BX LR -@ } - -@ /* System Tick timer interrupt handler */ - .global __tx_SysTickHandler - .global SysTick_Handler - .thumb_func -__tx_SysTickHandler: - .thumb_func -SysTick_Handler: -@ VOID TimerInterruptHandler (VOID) -@ { -@ - PUSH {r0, lr} - BL _tx_timer_interrupt - POP {r0, lr} - BX LR -@ }