diff --git a/.github/workflows/build-ferrocene.yml b/.github/workflows/build-ferrocene.yml index 4c4bcee..25b23da 100644 --- a/.github/workflows/build-ferrocene.yml +++ b/.github/workflows/build-ferrocene.yml @@ -7,7 +7,6 @@ on: [push] jobs: job-build-nrf52-app: runs-on: ubuntu-latest - needs: job-build-threadx-staticlib steps: - name: Install Arm C compiler run: | @@ -28,13 +27,13 @@ jobs: slug=$(./describe.sh "${GITHUB_REF}") echo "Building with slug '${slug}'" echo "BUILD_SLUG=${slug}" >> "${GITHUB_ENV}" - - name: Check Demo App + - name: Check nRF52 App run: | cd nrf52-app rustc --version cargo --version cargo check --target=thumbv7em-none-eabi - - name: Build Demo App + - name: Build nRF52 App run: | cd nrf52-app rustc --version @@ -45,6 +44,45 @@ jobs: with: name: nrf52-app path: nrf52-app/target/thumbv7em-none-eabi/release/nrf52-app + job-build-qemu-cortex-r5-app: + runs-on: ubuntu-latest + 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 + - name: Checkout repo + uses: actions/checkout@v4 + with: + submodules: 'true' + - name: Install Ferrocene + env: + CRITICALUP_TOKEN: ${{ secrets.CRITICALUP_TOKEN }} + run: | + criticalup install + echo "$HOME/.local/share/criticalup/bin" >> $GITHUB_PATH + - name: Find slug name + run: | + slug=$(./describe.sh "${GITHUB_REF}") + echo "Building with slug '${slug}'" + echo "BUILD_SLUG=${slug}" >> "${GITHUB_ENV}" + - name: Check QEMU Cortex-R5 App + run: | + cd qemu-cortex-r5-app + rustc --version + cargo --version + cargo check --target=armv7r-none-eabihf + - name: Build QEMU Cortex-R5 App + run: | + cd qemu-cortex-r5-app + rustc --version + cargo --version + cargo build --target=armv7r-none-eabihf --release + - name: Upload QEMU Cortex-R5 App + uses: actions/upload-artifact@master + with: + name: qemu-cortex-r5-app + path: qemu-cortex-r5-app/target/armv7r-none-eabihf/release/qemu-cortex-r5-app job-build-threadx-sys: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 69553cd..233c5d4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,7 +7,6 @@ on: [push] jobs: job-build-nrf52-app: runs-on: ubuntu-latest - needs: job-build-threadx-staticlib steps: - name: Install Arm C compiler run: | @@ -28,11 +27,11 @@ jobs: slug=$(./describe.sh "${GITHUB_REF}") echo "Building with slug '${slug}'" echo "BUILD_SLUG=${slug}" >> "${GITHUB_ENV}" - - name: Check Demo App + - name: Check nRF52 App run: | cd nrf52-app cargo check --target=thumbv7em-none-eabi - - name: Build Demo App + - name: Build nRF52 App run: | cd nrf52-app cargo build --target=thumbv7em-none-eabi --release @@ -41,6 +40,37 @@ jobs: with: name: nrf52-app path: nrf52-app/target/thumbv7em-none-eabi/release/nrf52-app + job-build-qemu-cortex-r5-app: + runs-on: ubuntu-latest + steps: + - name: Install Arm C compiler + run: | + sudo apt-get update -y && sudo apt-get -y install gcc-arm-none-eabi + - name: Checkout repo + uses: actions/checkout@v4 + with: + submodules: 'true' + - name: Add rustup target + run: | + rustup target add armv7r-none-eabihf + - name: Find slug name + run: | + slug=$(./describe.sh "${GITHUB_REF}") + echo "Building with slug '${slug}'" + echo "BUILD_SLUG=${slug}" >> "${GITHUB_ENV}" + - name: Check QEMU Cortex-R5 App + run: | + cd qemu-cortex-r5-app + cargo check --target=armv7r-none-eabihf + - name: Build QEMU Cortex-R5 App + run: | + cd qemu-cortex-r5-app + cargo build --target=armv7r-none-eabihf --release + - name: Upload qemu-cortex-r5-app + uses: actions/upload-artifact@master + with: + name: qemu-cortex-r5-app + path: qemu-cortex-r5-app/target/armv7r-none-eabihf/release/qemu-cortex-r5-app job-build-threadx-sys: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 79f2290..c772bdd 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -18,12 +18,31 @@ jobs: - name: Add rustup target run: | rustup target add thumbv7em-none-eabi - - name: Check Clippy on Demo App + - name: Check Clippy on nRF52 App env: RUSTFLAGS: "-Dwarnings" run: | cd nrf52-app cargo clippy --all-features + job-clippy-qemu-cortex-r5-app: + 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 gcc-arm-none-eabi + - name: Add rustup target + run: | + rustup target add armv7r-none-eabihf + - name: Check Clippy on QEMU Cortex-R5 App + env: + RUSTFLAGS: "-Dwarnings" + run: | + cd qemu-cortex-r5-app + cargo clippy --all-features job-clippy-threadx-sys: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 8a3eca1..fe24ea9 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -10,10 +10,19 @@ jobs: steps: - name: Checkout repo uses: actions/checkout@v4 - - name: Check Formatting + - name: Check Formatting on nRF52 App run: | cd nrf52-app cargo fmt -- --check + job-format-qemu-cortex-r5-app: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v4 + - name: Check Formatting on QEMU Cortex-R5 App + run: | + cd qemu-cortex-r5-app + cargo fmt -- --check job-format-threadx-sys: runs-on: ubuntu-latest steps: diff --git a/build.sh b/build.sh index da8e6ed..d7ee109 100755 --- a/build.sh +++ b/build.sh @@ -10,3 +10,7 @@ set -euo pipefail pushd nrf52-app cargo build --release popd + +pushd qemu-cortex-r5-app +cargo build --release +popd diff --git a/criticalup.toml b/criticalup.toml index e468a73..79c8c62 100644 --- a/criticalup.toml +++ b/criticalup.toml @@ -16,4 +16,5 @@ packages = [ "flip-link-${rustc-host}", "llvm-tools-${rustc-host}", "rust-std-thumbv7em-none-eabi", + "rust-std-armv7r-none-eabihf", ] diff --git a/nrf52-app/Cargo.lock b/nrf52-app/Cargo.lock index ad2821a..ecf514d 100644 --- a/nrf52-app/Cargo.lock +++ b/nrf52-app/Cargo.lock @@ -242,23 +242,6 @@ dependencies = [ "defmt", ] -[[package]] -name = "nrf52-app" -version = "0.0.0" -dependencies = [ - "byte-strings", - "cc", - "cortex-m", - "cortex-m-rt", - "defmt", - "defmt-rtt", - "heapless", - "nrf52840-hal", - "panic-probe", - "static_cell", - "threadx-sys", -] - [[package]] name = "either" version = "1.9.0" @@ -478,6 +461,23 @@ dependencies = [ "vcell", ] +[[package]] +name = "nrf52-app" +version = "0.0.0" +dependencies = [ + "byte-strings", + "cc", + "cortex-m", + "cortex-m-rt", + "defmt", + "defmt-rtt", + "heapless", + "nrf52840-hal", + "panic-probe", + "static_cell", + "threadx-sys", +] + [[package]] name = "nrf52840-hal" version = "0.16.0" diff --git a/qemu-cortex-r5-app/.cargo/config.toml b/qemu-cortex-r5-app/.cargo/config.toml new file mode 100644 index 0000000..fa7148c --- /dev/null +++ b/qemu-cortex-r5-app/.cargo/config.toml @@ -0,0 +1,9 @@ +[target.armv7r-none-eabihf] +rustflags = [ + "-Clink-arg=-Tlinker.ld", + "-Ctarget-cpu=cortex-r5", +] +runner = "qemu-system-arm -machine versatileab -cpu cortex-r5f -semihosting -nographic -kernel" + +[build] +target = ["armv7r-none-eabihf"] diff --git a/qemu-cortex-r5-app/.gitignore b/qemu-cortex-r5-app/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/qemu-cortex-r5-app/.gitignore @@ -0,0 +1 @@ +/target diff --git a/qemu-cortex-r5-app/.vscode/settings.json b/qemu-cortex-r5-app/.vscode/settings.json new file mode 100644 index 0000000..7d7a538 --- /dev/null +++ b/qemu-cortex-r5-app/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "rust-analyzer.procMacro.enable": false +} diff --git a/qemu-cortex-r5-app/Cargo.lock b/qemu-cortex-r5-app/Cargo.lock new file mode 100644 index 0000000..165d69d --- /dev/null +++ b/qemu-cortex-r5-app/Cargo.lock @@ -0,0 +1,441 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "bindgen" +version = "0.69.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bitflags" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "byte-strings" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "002ee5531feb8450e59862fefa550eeac39b726d60b186071672751045ebc29a" +dependencies = [ + "byte-strings-proc_macros", +] + +[[package]] +name = "byte-strings-proc_macros" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62f7e0e71f98d6c71bfe42b0a7a47d0f870ad808401fad2d44fa156ed5b0ae03" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cc" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "critical-section" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "embedded-alloc" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddae17915accbac2cfbc64ea0ae6e3b330e6ea124ba108dada63646fd3c6f815" +dependencies = [ + "critical-section", + "linked_list_allocator", +] + +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libloading" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" +dependencies = [ + "cfg-if", + "windows-targets", +] + +[[package]] +name = "linked_list_allocator" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "portable-atomic" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" + +[[package]] +name = "prettyplease" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "qemu-cortex-r5-app" +version = "0.1.0" +dependencies = [ + "byte-strings", + "cc", + "critical-section", + "embedded-alloc", + "static_cell", + "threadx-sys", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "static_cell" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d89b0684884a883431282db1e4343f34afc2ff6996fe1f4a1664519b66e14c1e" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "syn" +version = "2.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "threadx-sys" +version = "0.1.0" +dependencies = [ + "bindgen", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/qemu-cortex-r5-app/Cargo.toml b/qemu-cortex-r5-app/Cargo.toml new file mode 100644 index 0000000..ff5183b --- /dev/null +++ b/qemu-cortex-r5-app/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "qemu-cortex-r5-app" +version = "0.1.0" +edition = "2021" +authors = ["Ferrous Systems"] +license = "MIT OR Apache-2.0" +description = "A simple ARMv7-R demo application that runs ThreadX in QEMU and compiles with Ferrocene" + +[dependencies] +critical-section = { version = "1.1.2", features = ["restore-state-bool"] } +embedded-alloc = "0.5.1" +static_cell = "2.1.0" +threadx-sys = { path = "../threadx-sys" } +byte-strings = "0.3.1" + +[profile.release] +opt-level = "s" + +[build-dependencies] +cc = "1.1.6" diff --git a/qemu-cortex-r5-app/build.rs b/qemu-cortex-r5-app/build.rs new file mode 100644 index 0000000..eeba1ed --- /dev/null +++ b/qemu-cortex-r5-app/build.rs @@ -0,0 +1,243 @@ +//! Build script for the Rust/ThreadX demo + +// SPDX-FileCopyrightText: Copyright (c) 2023 Ferrous Systems +// SPDX-License-Identifier: MIT OR Apache-2.0 + +use std::{env, error::Error, fs, path::PathBuf}; + +static TX_PORT_FILES: &[&str] = &[ + "tx_thread_context_restore.S", + "tx_thread_fiq_nesting_end.S", + "tx_thread_interrupt_restore.S", + "tx_thread_stack_build.S", + "tx_thread_context_save.S", + "tx_thread_fiq_nesting_start.S", + "tx_thread_irq_nesting_end.S", + "tx_thread_system_return.S", + "tx_thread_fiq_context_restore.S", + "tx_thread_interrupt_control.S", + "tx_thread_irq_nesting_start.S", + "tx_thread_vectored_context_save.S", + "tx_thread_fiq_context_save.S", + "tx_thread_interrupt_disable.S", + "tx_thread_schedule.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")?); + + // put memory layout (linker script) in the linker search path + fs::copy("linker.ld", out_dir.join("linker.ld"))?; + println!("cargo:rustc-link-search={}", out_dir.display()); + println!("cargo:rerun-if-changed=linker.ld"); + + // 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_r5/gnu/src"); + let tx_port_inc = crate_dir.join("../threadx/ports/cortex_r5/gnu/inc"); + cc::Build::new() + .include(&tx_common_inc) + .include(&tx_port_inc) + .define("TX_ENABLE_VFP_SUPPORT", "1") + .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"); + + cc::Build::new() + .include(&tx_common_inc) + .include(&tx_port_inc) + .file("src/tx_initialize_low_level.S") + .compile("startup"); + + Ok(()) +} diff --git a/qemu-cortex-r5-app/commands.gdb b/qemu-cortex-r5-app/commands.gdb new file mode 100644 index 0000000..bbfd2a5 --- /dev/null +++ b/qemu-cortex-r5-app/commands.gdb @@ -0,0 +1,3 @@ +target extended-remote :1234 +layout split + diff --git a/qemu-cortex-r5-app/linker.ld b/qemu-cortex-r5-app/linker.ld new file mode 100644 index 0000000..cf182ab --- /dev/null +++ b/qemu-cortex-r5-app/linker.ld @@ -0,0 +1,30 @@ +MEMORY { + RAM : ORIGIN = 0, LENGTH = 0x1000000 +} + +ENTRY(_start) +SECTIONS { + .startup ORIGIN(RAM) : { + *(.text.startup) + } > RAM + .text : { *(.text .text*) } > RAM + .rodata : { *(.rodata .rodata*) } > RAM + .data : { *(.data .data*) } > RAM + .bss : { *(.bss .bss* COMMON) } > RAM + /DISCARD/ : { + *(.note .note*) + } + + . = ALIGN(16); + .stack : { + _stack_bottom = ABSOLUTE(.) ; + /* Allocate room for stack. This must be big enough for the IRQ, FIQ, and + SYS stack if nested interrupts are enabled. */ + . = ALIGN(8) ; + . += 0x10000; + _sp = . - 16 ; + _stack_top = ABSOLUTE(.) ; + } > RAM + + _end = .; __end__ = . ; +} diff --git a/qemu-cortex-r5-app/src/critical_section.rs b/qemu-cortex-r5-app/src/critical_section.rs new file mode 100644 index 0000000..2961d92 --- /dev/null +++ b/qemu-cortex-r5-app/src/critical_section.rs @@ -0,0 +1,34 @@ +//! Code that implements the `critical-section` traits on Cortex-R. + +struct SingleCoreCriticalSection; +critical_section::set_impl!(SingleCoreCriticalSection); + +/// Reads the CPU interrupt status bit from CPSR +/// +/// Returns true if interrupts enabled. +#[inline] +pub fn interrupts_enabled() -> bool { + const CPSR_I_BIT: u32 = 1 << 7; + let r: u32; + unsafe { + core::arch::asm!("mrs {}, CPSR", out(reg) r, options(nomem, nostack, preserves_flags)) + }; + r & CPSR_I_BIT != 0 +} + +unsafe impl critical_section::Impl for SingleCoreCriticalSection { + unsafe fn acquire() -> critical_section::RawRestoreState { + let was_active = interrupts_enabled(); + core::arch::asm!("cpsid i", options(nomem, nostack, preserves_flags)); + core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); + was_active + } + + unsafe fn release(was_active: critical_section::RawRestoreState) { + // Only re-enable interrupts if they were enabled before the critical section. + if was_active { + core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst); + core::arch::asm!("cpsie i", options(nomem, nostack, preserves_flags)); + } + } +} diff --git a/qemu-cortex-r5-app/src/lib.rs b/qemu-cortex-r5-app/src/lib.rs new file mode 100644 index 0000000..8f48406 --- /dev/null +++ b/qemu-cortex-r5-app/src/lib.rs @@ -0,0 +1,64 @@ +#![no_std] + +pub mod critical_section; +pub mod virt_uart; + +core::arch::global_asm!( + r#" + +.section .text.startup +.global _start +.global _vectors +.code 32 +.align 0 +// Work around https://github.com/rust-lang/rust/issues/127269 +.fpu vfp3-d16 + +__vectors: + LDR pc, STARTUP @ Reset goes to startup function + LDR pc, UNDEFINED @ Undefined handler + LDR pc, SWI @ Software interrupt handler + LDR pc, PREFETCH @ Prefetch exception handler + LDR pc, ABORT @ Abort exception handler + LDR pc, RESERVED @ Reserved exception handler + LDR pc, IRQ @ IRQ interrupt handler + LDR pc, FIQ @ FIQ interrupt handler + +STARTUP: + .word _start @ Reset goes to C startup function +UNDEFINED: + .word __tx_undefined @ Undefined handler +SWI: + .word __tx_swi_interrupt @ Software interrupt handler +PREFETCH: + .word __tx_prefetch_handler @ Prefetch exception handler +ABORT: + .word __tx_abort_handler @ Abort exception handler +RESERVED: + .word __tx_reserved_handler @ Reserved exception handler +IRQ: + .word __tx_irq_handler @ IRQ interrupt handler +FIQ: + .word __tx_fiq_handler @ FIQ interrupt handler + +_start: + // Set stack pointer + ldr sp, =_stack_top + + // Allow VFP coprocessor access + mrc p15, 0, r0, c1, c0, 2 + orr r0, r0, #0xF00000 + mcr p15, 0, r0, c1, c0, 2 + + // Enable VFP + mov r0, #0x40000000 + vmsr fpexc, r0 + + // Jump to application + bl kmain + + // In case the application returns, loop forever + b . + +"# +); diff --git a/qemu-cortex-r5-app/src/main.rs b/qemu-cortex-r5-app/src/main.rs new file mode 100644 index 0000000..7887677 --- /dev/null +++ b/qemu-cortex-r5-app/src/main.rs @@ -0,0 +1,228 @@ +//! Rust Demo for a QEMU Cortex-R machine, running ThreadX + +// SPDX-FileCopyrightText: Copyright (c) 2023 Ferrous Systems +// SPDX-License-Identifier: MIT OR Apache-2.0 + +#![no_std] +#![no_main] + +use byte_strings::c; +use core::{cell::RefCell, fmt::Write}; +use critical_section::Mutex; +use qemu_cortex_r5_app::virt_uart::Uart; +use static_cell::StaticCell; + +static BUILD_SLUG: Option<&str> = option_env!("BUILD_SLUG"); + +const DEMO_STACK_SIZE: usize = 1024; + +static UART: GlobalUart = GlobalUart::new(); + +struct GlobalUart { + inner: Mutex>>>, +} + +impl GlobalUart { + /// Create a new, empty, global UART wrapper + const fn new() -> GlobalUart { + GlobalUart { + inner: Mutex::new(RefCell::new(None)), + } + } + + /// Store a new UART at run-time + /// + /// Gives you back the old one, if any. + fn store(&self, uart: Uart<0x101f_1000>) -> Option> { + critical_section::with(|cs| { + let mut uart_ref = self.inner.borrow_ref_mut(cs); + uart_ref.replace(uart) + }) + } +} + +// Note that we're implementing for `&GlobalUart`, so we can write to a shared +// reference instead of requiring an exclusive-mutable reference. +impl core::fmt::Write for &GlobalUart { + /// Write the string to the inner UART, with a lock held + fn write_str(&mut self, s: &str) -> core::fmt::Result { + critical_section::with(|cs| { + let mut maybe_uart = self.inner.borrow_ref_mut(cs); + let Some(uart) = maybe_uart.as_mut() else { + return Err(core::fmt::Error); + }; + uart.write_str(s) + }) + } +} + +#[no_mangle] +extern "C" fn tx_application_define(_first_unused_memory: *mut core::ffi::c_void) { + _ = writeln!(&UART, "In tx_application_define()..."); + + // ThreadX requires a non-const pointer to char for the names, which it + // wil hold on to in the object, so it must have static lifetime. So we + // cast-away-const on a static string slice to appease the API. + + let byte_pool = { + static BYTE_POOL: StaticCell = StaticCell::new(); + static BYTE_POOL_STORAGE: StaticCell<[u8; 32768]> = StaticCell::new(); + let byte_pool = BYTE_POOL.uninit(); + let byte_pool_storage = BYTE_POOL_STORAGE.uninit(); + unsafe { + threadx_sys::_tx_byte_pool_create( + 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, + ); + byte_pool.assume_init_mut() + } + }; + + let entry = 0x12345678; + let thread0 = { + let mut stack_pointer = core::ptr::null_mut(); + unsafe { + threadx_sys::_tx_byte_allocate( + byte_pool, + &mut stack_pointer, + DEMO_STACK_SIZE as _, + threadx_sys::TX_NO_WAIT, + ); + } + _ = writeln!(&UART, "Stack allocated @ {:p}", stack_pointer); + if stack_pointer.is_null() { + panic!("No space for stack"); + } + + static THREAD_STORAGE: StaticCell = StaticCell::new(); + let thread = THREAD_STORAGE.uninit(); + unsafe { + let res = threadx_sys::_tx_thread_create( + thread.as_mut_ptr(), + c!("thread0").as_ptr() as *mut threadx_sys::CHAR, + Some(my_thread), + entry, + stack_pointer, + DEMO_STACK_SIZE as _, + 1, + 1, + threadx_sys::TX_NO_TIME_SLICE, + threadx_sys::TX_AUTO_START, + ); + if res != threadx_sys::TX_SUCCESS { + panic!("Failed to create thread: {}", res); + } + thread.assume_init_mut() + } + }; + _ = writeln!( + &UART, + "Thread spawned (entry={:08x}) @ {:p}", + entry, thread0 as *const _ + ); + + let entry = 0xAABBCCDD; + let thread1 = { + let mut stack_pointer = core::ptr::null_mut(); + unsafe { + threadx_sys::_tx_byte_allocate( + byte_pool, + &mut stack_pointer, + DEMO_STACK_SIZE as _, + threadx_sys::TX_NO_WAIT, + ); + } + _ = writeln!(&UART, "Stack allocated @ {:p}", stack_pointer); + if stack_pointer.is_null() { + panic!("No space for stack"); + } + + static THREAD_STORAGE: StaticCell = StaticCell::new(); + let thread = THREAD_STORAGE.uninit(); + unsafe { + let res = threadx_sys::_tx_thread_create( + thread.as_mut_ptr(), + c!("thread1").as_ptr() as *mut threadx_sys::CHAR, + Some(my_thread), + entry, + stack_pointer, + DEMO_STACK_SIZE as _, + 1, + 1, + threadx_sys::TX_NO_TIME_SLICE, + threadx_sys::TX_AUTO_START, + ); + if res != threadx_sys::TX_SUCCESS { + panic!("Failed to create thread: {}", res); + } + thread.assume_init_mut() + } + }; + _ = writeln!( + &UART, + "Thread spawned (entry={:08x}) @ {:p}", + entry, thread1 as *const _ + ); +} + +extern "C" fn my_thread(value: u32) { + _ = writeln!(&UART, "I am my_thread({:08x})", value); + let mut thread_counter = 0; + loop { + thread_counter += 1; + + unsafe { + threadx_sys::_tx_thread_sleep(100); + } + + _ = writeln!( + &UART, + "I am my_thread({:08x}), count = {}", + value, thread_counter + ); + } +} + +/// The entry-point to the Rust application. +/// +/// It is called by the start-up code in `lib.rs`. +#[no_mangle] +pub extern "C" fn kmain() { + let uart0 = unsafe { Uart::new_uart0() }; + UART.store(uart0); + _ = writeln!( + &UART, + "Hello, this is version {}!", + BUILD_SLUG.unwrap_or("unknown") + ); + _ = writeln!(&UART, "Entering ThreadX kernel..."); + unsafe { + threadx_sys::_tx_initialize_kernel_enter(); + } + + panic!("Kernel exited"); +} + +/// Called when the application raises an unrecoverable `panic!`. +/// +/// Prints the panic to the console and then exits QEMU using a semihosting +/// breakpoint. +#[panic_handler] +fn panic(info: &core::panic::PanicInfo) -> ! { + const SYS_REPORTEXC: u32 = 0x18; + let _ = writeln!(&UART, "PANIC: {:?}", info); + loop { + // Exit, using semihosting + unsafe { + core::arch::asm!( + "svc 0x123456", + in("r0") SYS_REPORTEXC, + in("r1") 0x20026 + ) + } + } +} + +// End of file diff --git a/qemu-cortex-r5-app/src/tx_initialize_low_level.S b/qemu-cortex-r5-app/src/tx_initialize_low_level.S new file mode 100644 index 0000000..a347127 --- /dev/null +++ b/qemu-cortex-r5-app/src/tx_initialize_low_level.S @@ -0,0 +1,346 @@ +@/*************************************************************************** +@ * Copyright (c) 2024 Microsoft Corporation +@ * +@ * This program and the accompanying materials are made available under the +@ * terms of the MIT License which is available at +@ * https://opensource.org/licenses/MIT. +@ * +@ * SPDX-License-Identifier: MIT +@ **************************************************************************/ +@ +@ +@/**************************************************************************/ +@/**************************************************************************/ +@/** */ +@/** ThreadX Component */ +@/** */ +@/** Initialize */ +@/** */ +@/**************************************************************************/ +@/**************************************************************************/ +@ +@ +@#define TX_SOURCE_CODE +@ +@ +@/* Include necessary system files. */ +@ +@#include "tx_api.h" +@#include "tx_initialize.h" +@#include "tx_thread.h" +@#include "tx_timer.h" + + .arm + +SVC_MODE = 0xD3 @ Disable IRQ/FIQ SVC mode +IRQ_MODE = 0xD2 @ Disable IRQ/FIQ IRQ mode +FIQ_MODE = 0xD1 @ Disable IRQ/FIQ FIQ mode +SYS_MODE = 0xDF @ Disable IRQ/FIQ SYS mode +FIQ_STACK_SIZE = 512 @ FIQ stack size +IRQ_STACK_SIZE = 1024 @ IRQ stack size +SYS_STACK_SIZE = 1024 @ System stack size +@ +@ + .global _tx_thread_system_stack_ptr + .global _tx_initialize_unused_memory + .global _tx_thread_context_save + .global _tx_thread_context_restore + .global _tx_timer_interrupt + .global _end + .global _sp + .global _stack_bottom + +@ +@ +@/* Define the 16-bit Thumb mode veneer for _tx_initialize_low_level for +@ applications calling this function from to 16-bit Thumb mode. */ +@ + .text + .align 2 + .thumb + .global $_tx_initialize_low_level + .type $_tx_initialize_low_level,function +$_tx_initialize_low_level: + BX pc @ Switch to 32-bit mode + NOP @ + .arm + STMFD sp!, {lr} @ Save return address + BL _tx_initialize_low_level @ Call _tx_initialize_low_level function + LDMFD sp!, {lr} @ Recover saved return address + BX lr @ Return to 16-bit caller +@ +@ + .text + .align 2 +@/**************************************************************************/ +@/* */ +@/* FUNCTION RELEASE */ +@/* */ +@/* _tx_initialize_low_level Cortex-R5/GNU */ +@/* 6.1 */ +@/* AUTHOR */ +@/* */ +@/* William E. Lamie, Microsoft Corporation */ +@/* */ +@/* DESCRIPTION */ +@/* */ +@/* This function is responsible for any low-level processor */ +@/* initialization, including setting up interrupt vectors, setting */ +@/* up a periodic timer interrupt source, saving the system stack */ +@/* pointer for use in ISR processing later, and finding the first */ +@/* available RAM memory address for tx_application_define. */ +@/* */ +@/* INPUT */ +@/* */ +@/* None */ +@/* */ +@/* OUTPUT */ +@/* */ +@/* None */ +@/* */ +@/* CALLS */ +@/* */ +@/* None */ +@/* */ +@/* CALLED BY */ +@/* */ +@/* _tx_initialize_kernel_enter ThreadX entry function */ +@/* */ +@/* RELEASE HISTORY */ +@/* */ +@/* DATE NAME DESCRIPTION */ +@/* */ +@/* 09-30-2020 William E. Lamie Initial Version 6.1 */ +@/* */ +@/**************************************************************************/ +@VOID _tx_initialize_low_level(VOID) +@{ + .global _tx_initialize_low_level + .type _tx_initialize_low_level,function +_tx_initialize_low_level: +@ +@ /* We must be in SVC mode at this point! */ +@ +@ /* Setup various stack pointers. */ +@ + LDR r1, =_sp @ Get pointer to stack area + +#ifdef TX_ENABLE_IRQ_NESTING +@ +@ /* Setup the system mode stack for nested interrupt support */ +@ + LDR r2, =SYS_STACK_SIZE @ Pickup stack size + MOV r3, #SYS_MODE @ Build SYS mode CPSR + MSR CPSR_c, r3 @ Enter SYS mode + SUB r1, r1, #1 @ Backup 1 byte + BIC r1, r1, #7 @ Ensure 8-byte alignment + MOV sp, r1 @ Setup SYS stack pointer + SUB r1, r1, r2 @ Calculate start of next stack +#endif + + LDR r2, =FIQ_STACK_SIZE @ Pickup stack size + MOV r0, #FIQ_MODE @ Build FIQ mode CPSR + MSR CPSR, r0 @ Enter FIQ mode + SUB r1, r1, #1 @ Backup 1 byte + BIC r1, r1, #7 @ Ensure 8-byte alignment + MOV sp, r1 @ Setup FIQ stack pointer + SUB r1, r1, r2 @ Calculate start of next stack + LDR r2, =IRQ_STACK_SIZE @ Pickup IRQ stack size + MOV r0, #IRQ_MODE @ Build IRQ mode CPSR + MSR CPSR, r0 @ Enter IRQ mode + SUB r1, r1, #1 @ Backup 1 byte + BIC r1, r1, #7 @ Ensure 8-byte alignment + MOV sp, r1 @ Setup IRQ stack pointer + SUB r3, r1, r2 @ Calculate end of IRQ stack + MOV r0, #SVC_MODE @ Build SVC mode CPSR + MSR CPSR, r0 @ Enter SVC mode + LDR r2, =_stack_bottom @ Pickup stack bottom + CMP r3, r2 @ Compare the current stack end with the bottom +_stack_error_loop: + BLT _stack_error_loop @ If the IRQ stack exceeds the stack bottom, just sit here! +@ +@ /* Save the system stack pointer. */ +@ _tx_thread_system_stack_ptr = (VOID_PTR) (sp); +@ + LDR r2, =_tx_thread_system_stack_ptr @ Pickup stack pointer + STR r1, [r2] @ Save the system stack +@ +@ /* Save the first available memory address. */ +@ _tx_initialize_unused_memory = (VOID_PTR) _end; +@ + LDR r1, =_end @ Get end of non-initialized RAM area + LDR r2, =_tx_initialize_unused_memory @ Pickup unused memory ptr address + ADD r1, r1, #8 @ Increment to next free word + STR r1, [r2] @ Save first free memory address +@ +@ /* Setup Timer for periodic interrupts. */ +@ +@ /* Done, return to caller. */ +@ +#ifdef __THUMB_INTERWORK + BX lr @ Return to caller +#else + MOV pc, lr @ Return to caller +#endif +@} +@ +@ +@/* Define shells for each of the interrupt vectors. */ +@ + .global __tx_undefined +__tx_undefined: + B __tx_undefined @ Undefined handler +@ + .global __tx_swi_interrupt +__tx_swi_interrupt: + B __tx_swi_interrupt @ Software interrupt handler +@ + .global __tx_prefetch_handler +__tx_prefetch_handler: + B __tx_prefetch_handler @ Prefetch exception handler +@ + .global __tx_abort_handler +__tx_abort_handler: + B __tx_abort_handler @ Abort exception handler +@ + .global __tx_reserved_handler +__tx_reserved_handler: + B __tx_reserved_handler @ Reserved exception handler +@ + .global __tx_irq_handler + .global __tx_irq_processing_return +__tx_irq_handler: +@ +@ /* Jump to context save to save system context. */ + B _tx_thread_context_save +__tx_irq_processing_return: +@ +@ /* At this point execution is still in the IRQ mode. The CPSR, point of +@ interrupt, and all C scratch registers are available for use. In +@ addition, IRQ interrupts may be re-enabled - with certain restrictions - +@ if nested IRQ interrupts are desired. Interrupts may be re-enabled over +@ small code sequences where lr is saved before enabling interrupts and +@ restored after interrupts are again disabled. */ +@ +@ /* Interrupt nesting is allowed after calling _tx_thread_irq_nesting_start +@ from IRQ mode with interrupts disabled. This routine switches to the +@ system mode and returns with IRQ interrupts enabled. +@ +@ NOTE: It is very important to ensure all IRQ interrupts are cleared +@ prior to enabling nested IRQ interrupts. */ +#ifdef TX_ENABLE_IRQ_NESTING + BL _tx_thread_irq_nesting_start +#endif +@ +@ /* For debug purpose, execute the timer interrupt processing here. In +@ a real system, some kind of status indication would have to be checked +@ before the timer interrupt handler could be called. */ +@ + BL _tx_timer_interrupt @ Timer interrupt handler +@ +@ +@ /* If interrupt nesting was started earlier, the end of interrupt nesting +@ service must be called before returning to _tx_thread_context_restore. +@ This routine returns in processing in IRQ mode with interrupts disabled. */ +#ifdef TX_ENABLE_IRQ_NESTING + BL _tx_thread_irq_nesting_end +#endif +@ +@ /* Jump to context restore to restore system context. */ + B _tx_thread_context_restore +@ +@ +@ /* This is an example of a vectored IRQ handler. */ +@ +@ .global __tx_example_vectored_irq_handler +@__tx_example_vectored_irq_handler: +@ +@ +@ /* Save initial context and call context save to prepare for +@ vectored ISR execution. */ +@ +@ STMDB sp!, {r0-r3} @ Save some scratch registers +@ MRS r0, SPSR @ Pickup saved SPSR +@ SUB lr, lr, #4 @ Adjust point of interrupt +@ STMDB sp!, {r0, r10, r12, lr} @ Store other scratch registers +@ BL _tx_thread_vectored_context_save @ Vectored context save +@ +@ /* At this point execution is still in the IRQ mode. The CPSR, point of +@ interrupt, and all C scratch registers are available for use. In +@ addition, IRQ interrupts may be re-enabled - with certain restrictions - +@ if nested IRQ interrupts are desired. Interrupts may be re-enabled over +@ small code sequences where lr is saved before enabling interrupts and +@ restored after interrupts are again disabled. */ +@ +@ +@ /* Interrupt nesting is allowed after calling _tx_thread_irq_nesting_start +@ from IRQ mode with interrupts disabled. This routine switches to the +@ system mode and returns with IRQ interrupts enabled. +@ +@ NOTE: It is very important to ensure all IRQ interrupts are cleared +@ prior to enabling nested IRQ interrupts. */ +@#ifdef TX_ENABLE_IRQ_NESTING +@ BL _tx_thread_irq_nesting_start +@#endif +@ +@ /* Application IRQ handlers can be called here! */ +@ +@ /* If interrupt nesting was started earlier, the end of interrupt nesting +@ service must be called before returning to _tx_thread_context_restore. +@ This routine returns in processing in IRQ mode with interrupts disabled. */ +@#ifdef TX_ENABLE_IRQ_NESTING +@ BL _tx_thread_irq_nesting_end +@#endif +@ +@ /* Jump to context restore to restore system context. */ +@ B _tx_thread_context_restore +@ +@ +#ifdef TX_ENABLE_FIQ_SUPPORT + .global __tx_fiq_handler + .global __tx_fiq_processing_return +__tx_fiq_handler: +@ +@ /* Jump to fiq context save to save system context. */ + B _tx_thread_fiq_context_save +__tx_fiq_processing_return: +@ +@ /* At this point execution is still in the FIQ mode. The CPSR, point of +@ interrupt, and all C scratch registers are available for use. */ +@ +@ /* Interrupt nesting is allowed after calling _tx_thread_fiq_nesting_start +@ from FIQ mode with interrupts disabled. This routine switches to the +@ system mode and returns with FIQ interrupts enabled. +@ +@ NOTE: It is very important to ensure all FIQ interrupts are cleared +@ prior to enabling nested FIQ interrupts. */ +#ifdef TX_ENABLE_FIQ_NESTING + BL _tx_thread_fiq_nesting_start +#endif +@ +@ /* Application FIQ handlers can be called here! */ +@ +@ /* If interrupt nesting was started earlier, the end of interrupt nesting +@ service must be called before returning to _tx_thread_fiq_context_restore. */ +#ifdef TX_ENABLE_FIQ_NESTING + BL _tx_thread_fiq_nesting_end +#endif +@ +@ /* Jump to fiq context restore to restore system context. */ + B _tx_thread_fiq_context_restore +@ +@ +#else + .global __tx_fiq_handler +__tx_fiq_handler: + B __tx_fiq_handler @ FIQ interrupt handler +#endif +@ +@ +BUILD_OPTIONS: + .word _tx_build_options @ Reference to bring in +VERSION_ID: + .word _tx_version_id @ Reference to bring in + + + diff --git a/qemu-cortex-r5-app/src/virt_uart.rs b/qemu-cortex-r5-app/src/virt_uart.rs new file mode 100644 index 0000000..fab913b --- /dev/null +++ b/qemu-cortex-r5-app/src/virt_uart.rs @@ -0,0 +1,75 @@ +//! A driver the Arm PL011 Uart +//! +//! Written by Jonathan Pallant at Ferrous Systems +//! +//! Copyright (c) Ferrous Systems, 2024 + +/// A driver for a virtual PL011 Uart +/// +/// It skips almost all the important initialisation, but it works on QEMU. +pub struct Uart(); + +impl Uart<0x101f_1000> { + /// Create a new UART object for UART0 + /// + /// # Safety + /// + /// Only construct one object per UART at any given time. + pub unsafe fn new_uart0() -> Self { + let mut u = Uart(); + u.set_control(Self::CONTROL_UARTEN | Self::CONTROL_TXE); + u + } +} + +impl Uart { + const FLAG_TXFF: u32 = 1 << 5; + const CONTROL_UARTEN: u32 = 1 << 0; + const CONTROL_TXE: u32 = 1 << 8; + + const DATA_OFFSET: usize = 0x000 >> 2; + const FLAG_OFFSET: usize = 0x018 >> 2; + const CONTROL_OFFSET: usize = 0x030 >> 2; + + /// Write a byte (blocking if there's no space) + pub fn write(&mut self, byte: u8) { + // Check the TX FIFO Full bit + while (self.get_flags() & Self::FLAG_TXFF) != 0 {} + self.write_data(byte); + } + + /// Write to the data register + fn write_data(&mut self, value: u8) { + unsafe { + let ptr = (ADDR as *mut u32).add(Self::DATA_OFFSET); + ptr.write_volatile(value as u32); + } + } + + /// Read from the Flag Register + fn get_flags(&mut self) -> u32 { + unsafe { + let ptr = (ADDR as *const u32).add(Self::FLAG_OFFSET); + ptr.read_volatile() + } + } + + /// Write to the control register + fn set_control(&mut self, value: u32) { + unsafe { + let ptr = (ADDR as *mut u32).add(Self::CONTROL_OFFSET); + ptr.write_volatile(value); + } + } +} + +impl core::fmt::Write for Uart { + fn write_str(&mut self, s: &str) -> core::fmt::Result { + for b in s.bytes() { + self.write(b); + } + Ok(()) + } +} + +// End of file