Vorago Repo Unification
Some checks failed
shared-hal-ci / Check build (push) Has been cancelled
shared-hal-ci / Check formatting (push) Has been cancelled
shared-hal-ci / Check Documentation Build (push) Has been cancelled
shared-hal-ci / Clippy (push) Has been cancelled
va108xx-ci / Check build (push) Has been cancelled
va108xx-ci / Run Tests (push) Has been cancelled
va108xx-ci / Check formatting (push) Has been cancelled
va108xx-ci / Check Documentation Build (push) Has been cancelled
va108xx-ci / Clippy (push) Has been cancelled
va416xx-ci / Check build (push) Has been cancelled
va416xx-ci / Run Tests (push) Has been cancelled
va416xx-ci / Check formatting (push) Has been cancelled
va416xx-ci / Check Documentation Build (push) Has been cancelled
va416xx-ci / Clippy (push) Has been cancelled
Some checks failed
shared-hal-ci / Check build (push) Has been cancelled
shared-hal-ci / Check formatting (push) Has been cancelled
shared-hal-ci / Check Documentation Build (push) Has been cancelled
shared-hal-ci / Clippy (push) Has been cancelled
va108xx-ci / Check build (push) Has been cancelled
va108xx-ci / Run Tests (push) Has been cancelled
va108xx-ci / Check formatting (push) Has been cancelled
va108xx-ci / Check Documentation Build (push) Has been cancelled
va108xx-ci / Clippy (push) Has been cancelled
va416xx-ci / Check build (push) Has been cancelled
va416xx-ci / Run Tests (push) Has been cancelled
va416xx-ci / Check formatting (push) Has been cancelled
va416xx-ci / Check Documentation Build (push) Has been cancelled
va416xx-ci / Clippy (push) Has been cancelled
This commit is contained in:
47
.github/workflows/shared-hal.yml
vendored
Normal file
47
.github/workflows/shared-hal.yml
vendored
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
name: shared-hal-ci
|
||||||
|
on: [push, pull_request]
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: ./vorago-shared-hal
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
name: Check build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
targets: "thumbv7em-none-eabihf, thumbv6m-none-eabi"
|
||||||
|
- run: cargo check --target thumbv7em-none-eabihf --features "vor4x, defmt"
|
||||||
|
- run: cargo check --target thumbv6m-none-eabi --features "vor1x, defmt"
|
||||||
|
|
||||||
|
fmt:
|
||||||
|
name: Check formatting
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
- run: cargo fmt --all -- --check
|
||||||
|
|
||||||
|
docs:
|
||||||
|
name: Check Documentation Build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@nightly
|
||||||
|
with:
|
||||||
|
targets: "thumbv7em-none-eabihf, thumbv6m-none-eabi"
|
||||||
|
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc --target thumbv7em-none-eabihf --features "vor4x, defmt"
|
||||||
|
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc --target thumbv6m-none-eabi --features "vor1x, defmt"
|
||||||
|
|
||||||
|
clippy:
|
||||||
|
name: Clippy
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
targets: "thumbv7em-none-eabihf, thumbv6m-none-eabi"
|
||||||
|
- run: cargo clippy --target thumbv7em-none-eabihf --features vor4x -- -D warnings
|
||||||
|
- run: cargo clippy --target thumbv6m-none-eabi --features vor1x -- -D warnings
|
||||||
61
.github/workflows/va108xx.yml
vendored
Normal file
61
.github/workflows/va108xx.yml
vendored
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
name: va108xx-ci
|
||||||
|
on: [push, pull_request]
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: ./va108xx
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
name: Check build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
targets: "thumbv6m-none-eabi"
|
||||||
|
- run: cargo check --target thumbv6m-none-eabi
|
||||||
|
- run: cargo check --target thumbv6m-none-eabi --examples
|
||||||
|
- run: cargo check -p va108xx --target thumbv6m-none-eabi --all-features
|
||||||
|
- run: cargo check -p va108xx-hal --target thumbv6m-none-eabi --features "defmt"
|
||||||
|
|
||||||
|
test:
|
||||||
|
name: Run Tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
- name: Install nextest
|
||||||
|
uses: taiki-e/install-action@nextest
|
||||||
|
- run: cargo nextest run --all-features -p va108xx-hal --no-tests=pass
|
||||||
|
# I think we can skip those on an embedded crate..
|
||||||
|
# - run: cargo test --doc -p va108xx-hal
|
||||||
|
|
||||||
|
fmt:
|
||||||
|
name: Check formatting
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
components: rustfmt
|
||||||
|
- run: cargo fmt --all -- --check
|
||||||
|
|
||||||
|
docs:
|
||||||
|
name: Check Documentation Build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@nightly
|
||||||
|
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va108xx --all-features
|
||||||
|
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va108xx-hal --all-features
|
||||||
|
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p vorago-reb1
|
||||||
|
|
||||||
|
clippy:
|
||||||
|
name: Clippy
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
targets: "thumbv6m-none-eabi"
|
||||||
|
- run: cargo clippy --target thumbv6m-none-eabi -- -D warnings
|
||||||
59
.github/workflows/va416xx.yml
vendored
Normal file
59
.github/workflows/va416xx.yml
vendored
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
name: va416xx-ci
|
||||||
|
on: [push, pull_request]
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: ./va416xx
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
name: Check build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
targets: "thumbv7em-none-eabihf"
|
||||||
|
- run: cargo check --target thumbv7em-none-eabihf
|
||||||
|
- run: cargo check --target thumbv7em-none-eabihf --examples
|
||||||
|
- run: cargo check -p va416xx --target thumbv7em-none-eabihf --all-features
|
||||||
|
- run: cargo check -p va416xx-hal --target thumbv7em-none-eabihf --features "defmt va41630"
|
||||||
|
|
||||||
|
test:
|
||||||
|
name: Run Tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
- name: Install nextest
|
||||||
|
uses: taiki-e/install-action@nextest
|
||||||
|
- run: cargo nextest run --features va41630 -p va416xx-hal
|
||||||
|
# I think we can skip those on an embedded crate..
|
||||||
|
# - run: cargo test --doc -p va108xx-hal
|
||||||
|
|
||||||
|
fmt:
|
||||||
|
name: Check formatting
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
- run: cargo fmt --all -- --check
|
||||||
|
|
||||||
|
docs:
|
||||||
|
name: Check Documentation Build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@nightly
|
||||||
|
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p vorago-peb1
|
||||||
|
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va416xx-hal --features va41630
|
||||||
|
- run: RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va416xx
|
||||||
|
|
||||||
|
clippy:
|
||||||
|
name: Clippy
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: dtolnay/rust-toolchain@stable
|
||||||
|
with:
|
||||||
|
targets: "thumbv7em-none-eabihf"
|
||||||
|
- run: cargo clippy --target thumbv7em-none-eabihf -- -D warnings
|
||||||
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# Generated by Cargo
|
||||||
|
# will have compiled files and executables
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
|
||||||
|
# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||||
|
Cargo.lock
|
||||||
1
va108xx/.cargo/.gitignore
vendored
Normal file
1
va108xx/.cargo/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
config.toml
|
||||||
46
va108xx/.cargo/config.toml.template
Normal file
46
va108xx/.cargo/config.toml.template
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
|
# uncomment ONE of these three option to make `cargo run` start a GDB session
|
||||||
|
# which option to pick depends on your system
|
||||||
|
# runner = "arm-none-eabi-gdb -q -x openocd.gdb"
|
||||||
|
# runner = "gdb-multiarch -q -x openocd.gdb"
|
||||||
|
# runner = "gdb -q -x openocd.gdb"
|
||||||
|
# runner = "gdb-multiarch -q -x jlink.gdb"
|
||||||
|
|
||||||
|
runner = "probe-rs run --chip VA108xx_RAM --protocol jtag"
|
||||||
|
# runner = ["probe-rs", "run", "--chip", "$CHIP", "--log-format", "{L} {s}"]
|
||||||
|
|
||||||
|
rustflags = [
|
||||||
|
# This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x
|
||||||
|
# See https://github.com/rust-embedded/cortex-m-quickstart/pull/95
|
||||||
|
"-C", "link-arg=--nmagic",
|
||||||
|
|
||||||
|
# LLD (shipped with the Rust toolchain) is used as the default linker
|
||||||
|
"-C", "link-arg=-Tlink.x",
|
||||||
|
|
||||||
|
# knurling-rs tooling. If you want to use flip-link, ensure it is installed first.
|
||||||
|
"-C", "linker=flip-link",
|
||||||
|
# Unfortunately, defmt is clunky to use without probe-rs..
|
||||||
|
"-C", "link-arg=-Tdefmt.x",
|
||||||
|
|
||||||
|
# Can be useful for debugging.
|
||||||
|
# "-Clink-args=-Map=app.map"
|
||||||
|
]
|
||||||
|
|
||||||
|
[build]
|
||||||
|
# Pick ONE of these compilation targets
|
||||||
|
target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
|
||||||
|
# target = "thumbv7m-none-eabi" # Cortex-M3
|
||||||
|
# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU)
|
||||||
|
# target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
|
||||||
|
# target = "thumbv8m.base-none-eabi" # Cortex-M23
|
||||||
|
# target = "thumbv8m.main-none-eabi" # Cortex-M33 (no FPU)
|
||||||
|
# target = "thumbv8m.main-none-eabihf" # Cortex-M33 (with FPU)
|
||||||
|
|
||||||
|
[alias]
|
||||||
|
re = "run --example"
|
||||||
|
rb = "run --bin"
|
||||||
|
rrb = "run --release --bin"
|
||||||
|
ut = "test --target x86_64-unknown-linux-gnu"
|
||||||
|
|
||||||
|
[env]
|
||||||
|
DEFMT_LOG = "info"
|
||||||
12
va108xx/.gitignore
vendored
Normal file
12
va108xx/.gitignore
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/app.map
|
||||||
|
|
||||||
|
# These are backup files generated by rustfmt
|
||||||
|
**/*.rs.bk
|
||||||
|
|
||||||
|
/.vscode
|
||||||
|
|
||||||
|
# JetBrains IDEs
|
||||||
|
/.idea
|
||||||
|
*.iml
|
||||||
|
|
||||||
|
/Embed.toml
|
||||||
0
va108xx/.gitmodules
vendored
Normal file
0
va108xx/.gitmodules
vendored
Normal file
46
va108xx/Cargo.toml
Normal file
46
va108xx/Cargo.toml
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
[workspace]
|
||||||
|
resolver = "2"
|
||||||
|
members = [
|
||||||
|
"vorago-reb1",
|
||||||
|
"va108xx",
|
||||||
|
"va108xx-hal",
|
||||||
|
"va108xx-embassy",
|
||||||
|
"examples/simple",
|
||||||
|
"examples/rtic",
|
||||||
|
"examples/embassy",
|
||||||
|
"board-tests",
|
||||||
|
"bootloader",
|
||||||
|
"flashloader",
|
||||||
|
]
|
||||||
|
exclude = [
|
||||||
|
"flashloader/slot-a-blinky",
|
||||||
|
"flashloader/slot-b-blinky",
|
||||||
|
]
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = 2
|
||||||
|
debug-assertions = true # <-
|
||||||
|
incremental = false
|
||||||
|
# 1 instead of 0, the flashloader is too larger otherwise..
|
||||||
|
# opt-level = 1 # <-
|
||||||
|
overflow-checks = true # <-
|
||||||
|
|
||||||
|
# cargo build/run --release
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = 2
|
||||||
|
debug-assertions = false # <-
|
||||||
|
incremental = false
|
||||||
|
lto = 'fat'
|
||||||
|
opt-level = 3 # <-
|
||||||
|
overflow-checks = false # <-
|
||||||
|
|
||||||
|
[profile.small]
|
||||||
|
inherits = "release"
|
||||||
|
codegen-units = 1
|
||||||
|
debug-assertions = false # <-
|
||||||
|
lto = true
|
||||||
|
opt-level = 'z' # <-
|
||||||
|
overflow-checks = false # <-
|
||||||
|
strip = true # Automatically strip symbols from the binary.
|
||||||
12
va108xx/Embed.toml.template
Normal file
12
va108xx/Embed.toml.template
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[default.probe]
|
||||||
|
protocol = "Jtag"
|
||||||
|
|
||||||
|
[default.general]
|
||||||
|
chip = "VA108xx_RAM"
|
||||||
|
|
||||||
|
[default.rtt]
|
||||||
|
enabled = true
|
||||||
|
|
||||||
|
[default.gdb]
|
||||||
|
# Whether or not a GDB server should be opened after flashing.
|
||||||
|
enabled = false
|
||||||
201
va108xx/LICENSE-APACHE
Normal file
201
va108xx/LICENSE-APACHE
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
3
va108xx/NOTICE
Normal file
3
va108xx/NOTICE
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
Rust workspace to develop code for the Vorago VA108xx family of MCUs
|
||||||
|
|
||||||
|
This software contains code developed at the University of Stuttgart.
|
||||||
189
va108xx/README.md
Normal file
189
va108xx/README.md
Normal file
@@ -0,0 +1,189 @@
|
|||||||
|
[](https://github.com/us-irs/va108xx-rs/actions/workflows/ci.yml)
|
||||||
|
|
||||||
|
Vorago VA108xx Rust Support
|
||||||
|
=========
|
||||||
|
|
||||||
|
This crate collection provides support to write Rust applications for the VA108XX family
|
||||||
|
of devices.
|
||||||
|
|
||||||
|
## List of crates
|
||||||
|
|
||||||
|
This workspace contains the following released crates:
|
||||||
|
|
||||||
|
- The [`va108xx`](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/va108xx) PAC
|
||||||
|
crate containing basic low-level register definition.
|
||||||
|
- The [`va108xx-hal`](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/va108xx-hal)
|
||||||
|
HAL crate containing higher-level abstractions on top of the PAC register crate.
|
||||||
|
- The [`va108xx-embassy`](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/va108xx-embassy)
|
||||||
|
crate containing support for running the embassy-rs RTOS.
|
||||||
|
- The [`vorago-reb1`](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1)
|
||||||
|
BSP crate containing support for the REB1 development board.
|
||||||
|
|
||||||
|
It also contains the following helper crates:
|
||||||
|
|
||||||
|
- The [`bootloader`](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/bootloader)
|
||||||
|
crate contains a sample bootloader strongly based on the one provided by Vorago.
|
||||||
|
- The [`flashloader`](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/flashloader)
|
||||||
|
crate contains a sample flashloader which is able to update the redundant images in the NVM which
|
||||||
|
is compatible to the provided bootloader as well.
|
||||||
|
- The [`board-tests`](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/board-tests)
|
||||||
|
contains an application which can be used to test the libraries on the board.
|
||||||
|
- The [`examples`](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples)
|
||||||
|
folder contains various example applications crates using the HAL and the PAC.
|
||||||
|
This folder also contains dedicated example applications using the
|
||||||
|
[`RTIC`](https://rtic.rs/2/book/en/) and [`embassy`](https://github.com/embassy-rs/embassy)
|
||||||
|
native Rust RTOSes.
|
||||||
|
|
||||||
|
The majority of the HAL implementation and the Embassy-rs support are contained in the external
|
||||||
|
[`vorago-shared-periphs`](https://egit.irs.uni-stuttgart.de/rust/vorago-shared-periphs) crate.
|
||||||
|
|
||||||
|
## Using the `.cargo/config.toml` file
|
||||||
|
|
||||||
|
Use the following command to have a starting `config.toml` file
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cp .cargo/config.toml.template .cargo/config.toml
|
||||||
|
```
|
||||||
|
|
||||||
|
You then can adapt the `config.toml` to your needs. For example, you can configure runners
|
||||||
|
to conveniently flash with `cargo run`.
|
||||||
|
|
||||||
|
## Using the sample VS Code files
|
||||||
|
|
||||||
|
Use the following command to have a starting configuration for VS Code:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cp -rT vscode .vscode
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then adapt the files in `.vscode` to your needs.
|
||||||
|
|
||||||
|
## Building projects
|
||||||
|
|
||||||
|
Building an application requires the `thumbv6m-none-eabi` cross-compiler toolchain.
|
||||||
|
If you have not installed it yet, you can do so with
|
||||||
|
|
||||||
|
```sh
|
||||||
|
rustup target add thumbv6m-none-eabi
|
||||||
|
```
|
||||||
|
|
||||||
|
After that, you can use `cargo build` to build the development version of the crate.
|
||||||
|
For example, you can use
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo build --example blinky
|
||||||
|
```
|
||||||
|
|
||||||
|
to build a simple blinky app.
|
||||||
|
|
||||||
|
## Flashing, running and debugging the software
|
||||||
|
|
||||||
|
You can use CLI or VS Code for flashing, running and debugging.
|
||||||
|
|
||||||
|
### Using CLI with probe-rs
|
||||||
|
|
||||||
|
Install [probe-rs](https://probe.rs/docs/getting-started/installation/) first.
|
||||||
|
|
||||||
|
You can use `probe-rs` to run the software and display RTT log output. However, debugging does not
|
||||||
|
work yet.
|
||||||
|
|
||||||
|
After installation, you can run the following command
|
||||||
|
|
||||||
|
```sh
|
||||||
|
probe-rs run --chip VA108xx_RAM --protocol jtag target/thumbv6m-none-eabi/debug/examples/blinky
|
||||||
|
```
|
||||||
|
|
||||||
|
to flash and run the blinky program on the RAM. There is also a `VA108xx` chip target
|
||||||
|
available for persistent flashing.
|
||||||
|
|
||||||
|
Runner configuration is available in the `.cargo/def-config.toml` file to use `probe-rs` for
|
||||||
|
convenience. `probe-rs` is also able to process and display `defmt` strings directly.
|
||||||
|
|
||||||
|
### Using VS Code
|
||||||
|
|
||||||
|
Assuming a working debug connection to your VA108xx board, you can debug using VS Code with
|
||||||
|
the [`Cortex-Debug` plugin](https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug).
|
||||||
|
Please make sure that [`objdump-multiarch` and `nm-multiarch`](https://forums.raspberrypi.com/viewtopic.php?t=333146)
|
||||||
|
are installed as well.
|
||||||
|
|
||||||
|
Some sample configuration files for VS code were provided and can be used by running
|
||||||
|
`cp -rT vscode .vscode` like specified above. After that, you can use `Run and Debug`
|
||||||
|
to automatically rebuild and flash your application.
|
||||||
|
|
||||||
|
If you would like to use a custom GDB application, you can specify the gdb binary in the following
|
||||||
|
configuration variables in your `settings.json`:
|
||||||
|
|
||||||
|
- `"cortex-debug.gdbPath"`
|
||||||
|
- `"cortex-debug.gdbPath.linux"`
|
||||||
|
- `"cortex-debug.gdbPath.windows"`
|
||||||
|
- `"cortex-debug.gdbPath.osx"`
|
||||||
|
|
||||||
|
The provided VS Code configurations also provide an integrated RTT logger, which you can access
|
||||||
|
via the terminal at `RTT Ch:0 console`. In order for the RTT block address detection to
|
||||||
|
work properly, `objdump-multiarch` and `nm-multiarch` need to be installed.
|
||||||
|
|
||||||
|
### Using CLI with GDB and Segger J-Link Tools
|
||||||
|
|
||||||
|
Install the following two tools first:
|
||||||
|
|
||||||
|
1. [SEGGER J-Link tools](https://www.segger.com/downloads/jlink/) installed
|
||||||
|
2. [gdb-multiarch](https://packages.debian.org/sid/gdb-multiarch) or similar
|
||||||
|
cross-architecture debugger installed. All commands here assume `gdb-multiarch`.
|
||||||
|
|
||||||
|
You can build the blinky example application with the following command
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cargo build --example blinky
|
||||||
|
```
|
||||||
|
|
||||||
|
Start the GDB server first. The server needs to be started with a certain configuration and with
|
||||||
|
a JLink script to disable ROM protection.
|
||||||
|
For example, on Debian based system the following command can be used to do this (this command
|
||||||
|
is also run when running the `jlink-gdb.sh` script)
|
||||||
|
|
||||||
|
```sh
|
||||||
|
JLinkGDBServer -select USB -device Cortex-M0 -endian little -if JTAG-speed auto \
|
||||||
|
-LocalhostOnly -jtagconf -1,-1
|
||||||
|
```
|
||||||
|
|
||||||
|
After this, you can flash and debug the application with the following command
|
||||||
|
|
||||||
|
```sh
|
||||||
|
gdb-mutliarch -q -x jlink/jlink.gdb target/thumbv6m-none-eabihf/debug/examples/blinky -tui
|
||||||
|
```
|
||||||
|
|
||||||
|
Please note that you can automate all steps except starting the GDB server by using a cargo
|
||||||
|
runner configuration, for example with the following lines in your `.cargo/config.toml` file:
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
|
runner = "gdb-multiarch -q -x jlink/jlink.gdb"
|
||||||
|
```
|
||||||
|
|
||||||
|
After that, you can simply use `cargo run --example blinky` to flash the blinky
|
||||||
|
example.
|
||||||
|
|
||||||
|
### Using the RTT Viewer
|
||||||
|
|
||||||
|
The Segger RTT viewer can be used to display log messages received from the target. The base
|
||||||
|
address for the RTT block placement is 0x10000000. It is recommended to use a search range of
|
||||||
|
0x1000 around that base address when using the RTT viewer.
|
||||||
|
|
||||||
|
The RTT viewer will not be able to process `defmt` printouts. However, you can view the defmt
|
||||||
|
logs by [installing defmt-print](https://crates.io/crates/defmt-print) first and then running
|
||||||
|
|
||||||
|
```sh
|
||||||
|
defmt-print -e <pathToElfFile> tcp
|
||||||
|
```
|
||||||
|
|
||||||
|
The path of the ELF file which is being debugged needs to be specified for this to work.
|
||||||
|
|
||||||
|
## Learning (Embedded) Rust
|
||||||
|
|
||||||
|
If you are unfamiliar with Rust on Embedded Systems or Rust in general, the following resources
|
||||||
|
are recommended:
|
||||||
|
|
||||||
|
- [Rust Book](https://doc.rust-lang.org/book/)
|
||||||
|
- [Embedded Rust Book](https://docs.rust-embedded.org/book/)
|
||||||
|
- [Embedded Rust Discovery](https://docs.rust-embedded.org/discovery/microbit/)
|
||||||
|
- [Awesome Embedded Rust](https://github.com/rust-embedded/awesome-embedded-rust)
|
||||||
13
va108xx/automation/Dockerfile
Normal file
13
va108xx/automation/Dockerfile
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Run the following commands from root directory to build and run locally
|
||||||
|
# docker build -f automation/Dockerfile -t <NAME> .
|
||||||
|
# docker run -it <NAME>
|
||||||
|
FROM rust:latest
|
||||||
|
RUN apt-get update
|
||||||
|
RUN apt-get --yes upgrade
|
||||||
|
# tzdata is a dependency, won't install otherwise
|
||||||
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
RUN rustup install nightly && \
|
||||||
|
rustup target add thumbv6m-none-eabi && \
|
||||||
|
rustup +nightly target add thumbv6m-none-eabi && \
|
||||||
|
rustup component add rustfmt clippy
|
||||||
45
va108xx/automation/Jenkinsfile
vendored
Normal file
45
va108xx/automation/Jenkinsfile
vendored
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
pipeline {
|
||||||
|
agent {
|
||||||
|
dockerfile {
|
||||||
|
dir 'automation'
|
||||||
|
reuseNode true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stages {
|
||||||
|
stage('Rust Toolchain Info') {
|
||||||
|
steps {
|
||||||
|
sh 'rustc --version'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Clippy') {
|
||||||
|
steps {
|
||||||
|
sh 'cargo clippy --target thumbv6m-none-eabi'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Rustfmt') {
|
||||||
|
steps {
|
||||||
|
sh 'cargo fmt'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Docs') {
|
||||||
|
steps {
|
||||||
|
sh """
|
||||||
|
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va108xx
|
||||||
|
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p va108xx-hal
|
||||||
|
RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options" cargo +nightly doc -p vorago-reb1
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Check') {
|
||||||
|
steps {
|
||||||
|
sh 'cargo check --target thumbv6m-none-eabi'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Check Examples') {
|
||||||
|
steps {
|
||||||
|
sh 'cargo check --target thumbv6m-none-eabi --examples'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
va108xx/board-tests/Cargo.toml
Normal file
18
va108xx/board-tests/Cargo.toml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
[package]
|
||||||
|
name = "board-tests"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||||
|
cortex-m-rt = "0.7"
|
||||||
|
defmt = "1"
|
||||||
|
defmt-rtt = "1"
|
||||||
|
panic-probe = { version = "1", features = ["print-defmt"] }
|
||||||
|
embedded-hal = "1"
|
||||||
|
|
||||||
|
[dependencies.va108xx-hal]
|
||||||
|
version = "0.12"
|
||||||
|
features = ["rt"]
|
||||||
|
path = "../va108xx-hal"
|
||||||
|
|
||||||
184
va108xx/board-tests/src/main.rs
Normal file
184
va108xx/board-tests/src/main.rs
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
//! Test image
|
||||||
|
//!
|
||||||
|
//! It would be nice to use a test framework like defmt-test, but I have issues
|
||||||
|
//! with probe run and it would be better to make the RTT work first
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use embedded_hal::delay::DelayNs;
|
||||||
|
// Logging provider
|
||||||
|
use defmt_rtt as _;
|
||||||
|
// Panic provider
|
||||||
|
use panic_probe as _;
|
||||||
|
use va108xx_hal::{
|
||||||
|
gpio::{regs::Gpio, Input, Output, PinState, Pull},
|
||||||
|
pac,
|
||||||
|
pins::{PinsA, PinsB, Port},
|
||||||
|
prelude::*,
|
||||||
|
time::Hertz,
|
||||||
|
timer::CountdownTimer,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug, defmt::Format)]
|
||||||
|
enum TestCase {
|
||||||
|
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||||
|
TestBasic,
|
||||||
|
TestPullup,
|
||||||
|
TestPulldown,
|
||||||
|
TestMask,
|
||||||
|
// Tie PORTB[22] to PORTB[23] for this test
|
||||||
|
PortB,
|
||||||
|
Perid,
|
||||||
|
// Tie PA0 to an oscilloscope and configure pulse detection
|
||||||
|
Pulse,
|
||||||
|
// Tie PA0, PA1 and PA3 to an oscilloscope
|
||||||
|
DelayGpio,
|
||||||
|
// PA0 can be checked with an oscillsope to verify timing correctness.
|
||||||
|
DelayMs,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
defmt::println!("-- VA108xx Test Application --");
|
||||||
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
|
let cp = cortex_m::Peripherals::take().unwrap();
|
||||||
|
let pinsa = PinsA::new(dp.porta);
|
||||||
|
let pinsb = PinsB::new(dp.portb);
|
||||||
|
let mut led1 = Output::new(pinsa.pa10, PinState::Low);
|
||||||
|
let test_case = TestCase::DelayMs;
|
||||||
|
|
||||||
|
match test_case {
|
||||||
|
TestCase::TestBasic
|
||||||
|
| TestCase::TestPulldown
|
||||||
|
| TestCase::TestPullup
|
||||||
|
| TestCase::TestMask => {
|
||||||
|
defmt::info!(
|
||||||
|
"Test case {:?}. Make sure to tie PORTA[0] to PORTA[1]",
|
||||||
|
test_case
|
||||||
|
);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
defmt::info!("Test case {:?}", test_case);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match test_case {
|
||||||
|
TestCase::TestBasic => {
|
||||||
|
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||||
|
let mut out = Output::new(pinsa.pa0, PinState::Low);
|
||||||
|
let input = Input::new_floating(pinsa.pa1);
|
||||||
|
out.set_high();
|
||||||
|
assert!(input.is_high());
|
||||||
|
out.set_low();
|
||||||
|
assert!(input.is_low());
|
||||||
|
}
|
||||||
|
TestCase::TestPullup => {
|
||||||
|
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||||
|
let input = Input::new_with_pull(pinsa.pa1, Pull::Up);
|
||||||
|
assert!(input.is_high());
|
||||||
|
let mut out = Output::new(pinsa.pa0, PinState::Low);
|
||||||
|
out.set_low();
|
||||||
|
assert!(input.is_low());
|
||||||
|
out.set_high();
|
||||||
|
assert!(input.is_high());
|
||||||
|
}
|
||||||
|
TestCase::TestPulldown => {
|
||||||
|
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||||
|
let input = Input::new_with_pull(pinsa.pa1, Pull::Down);
|
||||||
|
assert!(input.is_low());
|
||||||
|
let mut out = Output::new(pinsa.pa0, PinState::Low);
|
||||||
|
out.set_low();
|
||||||
|
assert!(input.is_low());
|
||||||
|
out.set_high();
|
||||||
|
assert!(input.is_high());
|
||||||
|
}
|
||||||
|
TestCase::TestMask => {
|
||||||
|
// Tie PORTA[0] to PORTA[1] for these tests!
|
||||||
|
// Need to test this low-level..
|
||||||
|
/*
|
||||||
|
let mut input = Input::new_with_pull(pinsa.pa1, Pull::Down);
|
||||||
|
input.clear_datamask();
|
||||||
|
assert!(!input.datamask());
|
||||||
|
let mut out = pinsa.pa0.into_push_pull_output();
|
||||||
|
out.clear_datamask();
|
||||||
|
assert!(input.is_low_masked().is_err());
|
||||||
|
assert!(out.set_high_masked().is_err());
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
TestCase::PortB => {
|
||||||
|
// Tie PORTB[22] to PORTB[23] for these tests!
|
||||||
|
let mut out = Output::new(pinsb.pb22, PinState::Low);
|
||||||
|
let input = Input::new_floating(pinsb.pb23);
|
||||||
|
out.set_high();
|
||||||
|
assert!(input.is_high());
|
||||||
|
out.set_low();
|
||||||
|
assert!(input.is_low());
|
||||||
|
}
|
||||||
|
TestCase::Perid => {
|
||||||
|
let mmio_porta = Gpio::new_mmio(Port::A);
|
||||||
|
assert_eq!(mmio_porta.read_perid(), 0x004007e1);
|
||||||
|
let mmio_porta = Gpio::new_mmio(Port::B);
|
||||||
|
assert_eq!(mmio_porta.read_perid(), 0x004007e1);
|
||||||
|
}
|
||||||
|
TestCase::Pulse => {
|
||||||
|
let mut output_pulsed = Output::new(pinsa.pa0, PinState::Low);
|
||||||
|
output_pulsed.configure_pulse_mode(true, PinState::Low);
|
||||||
|
defmt::info!("Pulsing high 10 times..");
|
||||||
|
output_pulsed.set_low();
|
||||||
|
for _ in 0..10 {
|
||||||
|
output_pulsed.set_high();
|
||||||
|
cortex_m::asm::delay(25_000_000);
|
||||||
|
}
|
||||||
|
output_pulsed.configure_pulse_mode(true, PinState::High);
|
||||||
|
defmt::info!("Pulsing low 10 times..");
|
||||||
|
for _ in 0..10 {
|
||||||
|
output_pulsed.set_low();
|
||||||
|
cortex_m::asm::delay(25_000_000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TestCase::DelayGpio => {
|
||||||
|
let mut out_0 = Output::new(pinsa.pa0, PinState::Low);
|
||||||
|
out_0.configure_delay(true, false);
|
||||||
|
let mut out_1 = Output::new(pinsa.pa1, PinState::Low);
|
||||||
|
out_1.configure_delay(false, true);
|
||||||
|
let mut out_2 = Output::new(pinsa.pa3, PinState::Low);
|
||||||
|
out_2.configure_delay(true, true);
|
||||||
|
for _ in 0..20 {
|
||||||
|
out_0.toggle();
|
||||||
|
out_1.toggle();
|
||||||
|
out_2.toggle();
|
||||||
|
cortex_m::asm::delay(25_000_000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TestCase::DelayMs => {
|
||||||
|
let mut delay_timer = CountdownTimer::new(dp.tim1, 50.MHz());
|
||||||
|
let mut pa0 = Output::new(pinsa.pa0, PinState::Low);
|
||||||
|
for _ in 0..5 {
|
||||||
|
led1.toggle();
|
||||||
|
delay_timer.delay_ms(500);
|
||||||
|
led1.toggle();
|
||||||
|
delay_timer.delay_ms(500);
|
||||||
|
}
|
||||||
|
let ahb_freq: Hertz = 50.MHz();
|
||||||
|
let mut syst_delay = cortex_m::delay::Delay::new(cp.SYST, ahb_freq.raw());
|
||||||
|
// Release image should be used to verify timings for pin PA0
|
||||||
|
for _ in 0..5 {
|
||||||
|
pa0.toggle();
|
||||||
|
syst_delay.delay_us(50);
|
||||||
|
pa0.toggle();
|
||||||
|
syst_delay.delay_us(50);
|
||||||
|
pa0.toggle();
|
||||||
|
delay_timer.delay_us(50);
|
||||||
|
pa0.toggle();
|
||||||
|
delay_timer.delay_us(50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defmt::info!("Test success");
|
||||||
|
loop {
|
||||||
|
led1.toggle();
|
||||||
|
cortex_m::asm::delay(25_000_000);
|
||||||
|
}
|
||||||
|
}
|
||||||
28
va108xx/bootloader/Cargo.toml
Normal file
28
va108xx/bootloader/Cargo.toml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
[package]
|
||||||
|
name = "bootloader"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = "0.7"
|
||||||
|
cortex-m-rt = "0.7"
|
||||||
|
embedded-hal = "1"
|
||||||
|
defmt-rtt = "1"
|
||||||
|
defmt = "1"
|
||||||
|
panic-probe = { version = "1", features = ["defmt"] }
|
||||||
|
crc = "3"
|
||||||
|
num_enum = { version = "0.7", default-features = false }
|
||||||
|
static_assertions = "1"
|
||||||
|
|
||||||
|
[dependencies.va108xx-hal]
|
||||||
|
version = "0.12"
|
||||||
|
path = "../va108xx-hal"
|
||||||
|
features = ["defmt"]
|
||||||
|
|
||||||
|
[dependencies.vorago-reb1]
|
||||||
|
version = "0.9"
|
||||||
|
path = "../vorago-reb1"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
rtt-panic = []
|
||||||
51
va108xx/bootloader/README.md
Normal file
51
va108xx/bootloader/README.md
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
VA108xx Bootloader Application
|
||||||
|
=======
|
||||||
|
|
||||||
|
This is the Rust version of the bootloader supplied by Vorago.
|
||||||
|
|
||||||
|
## Memory Map
|
||||||
|
|
||||||
|
The bootloader uses the following memory map:
|
||||||
|
|
||||||
|
| Address | Notes | Size |
|
||||||
|
| ------ | ---- | ---- |
|
||||||
|
| 0x0 | Bootloader start | code up to 0x2FFE bytes |
|
||||||
|
| 0x2FFE | Bootloader CRC | half-word |
|
||||||
|
| 0x3000 | App image A start | code up to 0xE7F4 (~59K) bytes |
|
||||||
|
| 0x117F4 | App image A CRC check length | word |
|
||||||
|
| 0x117F8 | App image A CRC check value | word |
|
||||||
|
| 0x117FC | App image B start | code up to 0xE7F4 (~59K) bytes |
|
||||||
|
| 0x1FFF0 | App image B CRC check length | word |
|
||||||
|
| 0x1FFF4 | App image B CRC check value | word |
|
||||||
|
| 0x1FFF8 | Reserved section, contains boot select parameter | 8 bytes |
|
||||||
|
| 0x20000 | End of NVM | end |
|
||||||
|
|
||||||
|
## Additional Information
|
||||||
|
|
||||||
|
This bootloader was specifically written for the REB1 board, so it assumes a M95M01 ST EEPROM
|
||||||
|
is used to load the application code. The bootloader will also delay for a configurable amount
|
||||||
|
of time before booting. This allows to catch the RTT printout, but should probably be disabled
|
||||||
|
for production firmware.
|
||||||
|
|
||||||
|
This bootloader does not provide tools to flash the NVM memory by itself. Instead, you can use
|
||||||
|
the [flashloader](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/flashloader)
|
||||||
|
application to perform this task using a CCSDS interface via a UART.
|
||||||
|
|
||||||
|
The bootloader performs the following steps:
|
||||||
|
|
||||||
|
1. The application will calculate the checksum of itself if the bootloader CRC is blank (all zeroes
|
||||||
|
or all ones). If the CRC is not blank and the checksum check fails, it will immediately boot
|
||||||
|
application image A. Otherwise, it proceeds to the next step.
|
||||||
|
2. Read the boot slot from a reserved section at the end of the EEPROM. If no valid value is read,
|
||||||
|
select boot slot A.
|
||||||
|
3. Check the checksum of the boot slot. If that checksum is valid, it will boot that slot. If not,
|
||||||
|
it will proceed to the next step.
|
||||||
|
4. Check the checksum of the other slot . If that checksum is valid, it will boot that slot. If
|
||||||
|
not, it will boot App A as the fallback image.
|
||||||
|
|
||||||
|
In your actual production application, a command to update the preferred boot slot could be exposed
|
||||||
|
to allow performing software updates in a safe way.
|
||||||
|
|
||||||
|
Please note that you *MUST* compile the application at slot A and slot B with an appropriate
|
||||||
|
`memory.x` file where the base address of the `FLASH` was adapted according to the base address
|
||||||
|
shown in the memory map above. The memory files to do this were provided in the `scripts` folder.
|
||||||
10
va108xx/bootloader/src/lib.rs
Normal file
10
va108xx/bootloader/src/lib.rs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use core::convert::Infallible;
|
||||||
|
|
||||||
|
/// Simple trait which makes swapping the NVM easier. NVMs only need to implement this interface.
|
||||||
|
pub trait NvmInterface {
|
||||||
|
fn write(&mut self, address: usize, data: &[u8]) -> Result<(), Infallible>;
|
||||||
|
fn read(&mut self, address: usize, buf: &mut [u8]) -> Result<(), Infallible>;
|
||||||
|
fn verify(&mut self, address: usize, data: &[u8]) -> Result<bool, Infallible>;
|
||||||
|
}
|
||||||
339
va108xx/bootloader/src/main.rs
Normal file
339
va108xx/bootloader/src/main.rs
Normal file
@@ -0,0 +1,339 @@
|
|||||||
|
//! Vorago bootloader which can boot from two images.
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
use bootloader::NvmInterface;
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use crc::{Crc, CRC_16_IBM_3740};
|
||||||
|
use embedded_hal::delay::DelayNs;
|
||||||
|
use num_enum::TryFromPrimitive;
|
||||||
|
// Import panic provider.
|
||||||
|
use panic_probe as _;
|
||||||
|
// Import logger.
|
||||||
|
use defmt_rtt as _;
|
||||||
|
use va108xx_hal::{pac, spi::SpiClockConfig, time::Hertz, timer::CountdownTimer};
|
||||||
|
use vorago_reb1::m95m01::M95M01;
|
||||||
|
|
||||||
|
// Useful for debugging and see what the bootloader is doing. Enabled currently, because
|
||||||
|
// the binary stays small enough.
|
||||||
|
const DEFMT_PRINTOUT: bool = true;
|
||||||
|
const DEBUG_PRINTOUTS: bool = true;
|
||||||
|
// Small delay, allows RTT printout to catch up.
|
||||||
|
const BOOT_DELAY_MS: u32 = 2000;
|
||||||
|
|
||||||
|
// Dangerous option! An image with this option set to true will flash itself from RAM directly
|
||||||
|
// into the NVM. This can be used as a recovery option from a direct RAM flash to fix the NVM
|
||||||
|
// boot process. Please note that this will flash an image which will also always perform the
|
||||||
|
// self-flash itself. It is recommended that you use a tool like probe-rs, Keil IDE, or a flash
|
||||||
|
// loader to boot a bootloader without this feature.
|
||||||
|
const FLASH_SELF: bool = false;
|
||||||
|
|
||||||
|
// Register definitions for Cortex-M0 SCB register.
|
||||||
|
pub const SCB_AIRCR_VECTKEY_POS: u32 = 16;
|
||||||
|
pub const SCB_AIRCR_VECTKEY_MSK: u32 = 0xFFFF << SCB_AIRCR_VECTKEY_POS;
|
||||||
|
|
||||||
|
pub const SCB_AIRCR_SYSRESETREQ_POS: u32 = 2;
|
||||||
|
pub const SCB_AIRCR_SYSRESETREQ_MSK: u32 = 1 << SCB_AIRCR_SYSRESETREQ_POS;
|
||||||
|
|
||||||
|
const CLOCK_FREQ: Hertz = Hertz::from_raw(50_000_000);
|
||||||
|
|
||||||
|
// Important bootloader addresses and offsets, vector table information.
|
||||||
|
|
||||||
|
const NVM_SIZE: u32 = 0x20000;
|
||||||
|
const BOOTLOADER_START_ADDR: u32 = 0x0;
|
||||||
|
const BOOTLOADER_CRC_ADDR: u32 = BOOTLOADER_END_ADDR - 2;
|
||||||
|
// This is also the maximum size of the bootloader.
|
||||||
|
const BOOTLOADER_END_ADDR: u32 = 0x3000;
|
||||||
|
const APP_A_START_ADDR: u32 = BOOTLOADER_END_ADDR;
|
||||||
|
// 0x117F8
|
||||||
|
const APP_A_SIZE_ADDR: u32 = APP_A_END_ADDR - 8;
|
||||||
|
// Four bytes reserved, even when only 2 byte CRC is used. Leaves flexibility to switch to CRC32.
|
||||||
|
// 0x117FC
|
||||||
|
const APP_A_CRC_ADDR: u32 = APP_A_END_ADDR - 4;
|
||||||
|
// 0x11800
|
||||||
|
pub const APP_A_END_ADDR: u32 = APP_A_START_ADDR + APP_IMG_SZ;
|
||||||
|
// The actual size of the image which is relevant for CRC calculation.
|
||||||
|
const APP_B_START_ADDR: u32 = APP_A_END_ADDR;
|
||||||
|
// The actual size of the image which is relevant for CRC calculation.
|
||||||
|
// 0x1FFF8
|
||||||
|
const APP_B_SIZE_ADDR: u32 = APP_B_END_ADDR - 8;
|
||||||
|
// Four bytes reserved, even when only 2 byte CRC is used. Leaves flexibility to switch to CRC32.
|
||||||
|
// 0x1FFFC
|
||||||
|
const APP_B_CRC_ADDR: u32 = APP_B_END_ADDR - 4;
|
||||||
|
// 0x20000. 8 bytes at end of EEPROM reserved for preferred image parameter. This reserved
|
||||||
|
// size should be a multiple of 8 due to alignment requirements.
|
||||||
|
pub const APP_B_END_ADDR: u32 = NVM_SIZE - 8;
|
||||||
|
pub const APP_IMG_SZ: u32 = (APP_B_END_ADDR - APP_A_START_ADDR) / 2;
|
||||||
|
|
||||||
|
static_assertions::const_assert!((APP_B_END_ADDR - BOOTLOADER_END_ADDR) % 2 == 0);
|
||||||
|
|
||||||
|
pub const VECTOR_TABLE_OFFSET: u32 = 0x0;
|
||||||
|
pub const VECTOR_TABLE_LEN: u32 = 0xC0;
|
||||||
|
pub const RESET_VECTOR_OFFSET: u32 = 0x4;
|
||||||
|
pub const PREFERRED_SLOT_OFFSET: u32 = 0x20000 - 1;
|
||||||
|
|
||||||
|
const CRC_ALGO: Crc<u16> = Crc::<u16>::new(&CRC_16_IBM_3740);
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, defmt::Format)]
|
||||||
|
#[repr(u8)]
|
||||||
|
enum AppSel {
|
||||||
|
A = 0,
|
||||||
|
B = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct NvmWrapper(pub M95M01);
|
||||||
|
|
||||||
|
// Newtype pattern. We could now more easily swap the used NVM type.
|
||||||
|
impl NvmInterface for NvmWrapper {
|
||||||
|
fn write(&mut self, address: usize, data: &[u8]) -> Result<(), core::convert::Infallible> {
|
||||||
|
self.0.write(address, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read(&mut self, address: usize, buf: &mut [u8]) -> Result<(), core::convert::Infallible> {
|
||||||
|
self.0.read(address, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify(&mut self, address: usize, data: &[u8]) -> Result<bool, core::convert::Infallible> {
|
||||||
|
self.0.verify(address, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
if DEFMT_PRINTOUT {
|
||||||
|
defmt::println!("-- VA108xx bootloader --");
|
||||||
|
}
|
||||||
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
|
let cp = cortex_m::Peripherals::take().unwrap();
|
||||||
|
let mut timer = CountdownTimer::new(dp.tim0, CLOCK_FREQ);
|
||||||
|
|
||||||
|
let clk_config = SpiClockConfig::new(2, 4);
|
||||||
|
let mut nvm = M95M01::new(dp.spic, clk_config);
|
||||||
|
|
||||||
|
if FLASH_SELF {
|
||||||
|
let mut first_four_bytes: [u8; 4] = [0; 4];
|
||||||
|
read_four_bytes_at_addr_zero(&mut first_four_bytes);
|
||||||
|
let bootloader_data = {
|
||||||
|
unsafe {
|
||||||
|
&*core::ptr::slice_from_raw_parts(
|
||||||
|
(BOOTLOADER_START_ADDR + 4) as *const u8,
|
||||||
|
(BOOTLOADER_END_ADDR - BOOTLOADER_START_ADDR - 6) as usize,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut digest = CRC_ALGO.digest();
|
||||||
|
digest.update(&first_four_bytes);
|
||||||
|
digest.update(bootloader_data);
|
||||||
|
let bootloader_crc = digest.finalize();
|
||||||
|
|
||||||
|
nvm.write(0x0, &first_four_bytes)
|
||||||
|
.expect("writing to NVM failed");
|
||||||
|
nvm.write(0x4, bootloader_data)
|
||||||
|
.expect("writing to NVM failed");
|
||||||
|
if let Err(e) = nvm.verify(0x0, &first_four_bytes) {
|
||||||
|
if DEFMT_PRINTOUT {
|
||||||
|
defmt::error!("verification of self-flash to NVM failed: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Err(e) = nvm.verify(0x4, bootloader_data) {
|
||||||
|
if DEFMT_PRINTOUT {
|
||||||
|
defmt::error!("verification of self-flash to NVM failed: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nvm.write(BOOTLOADER_CRC_ADDR as usize, &bootloader_crc.to_be_bytes())
|
||||||
|
.expect("writing CRC failed");
|
||||||
|
if let Err(e) = nvm.verify(BOOTLOADER_CRC_ADDR as usize, &bootloader_crc.to_be_bytes()) {
|
||||||
|
if DEFMT_PRINTOUT {
|
||||||
|
defmt::error!(
|
||||||
|
"error: CRC verification for bootloader self-flash failed: {:?}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut nvm = NvmWrapper(nvm);
|
||||||
|
|
||||||
|
// Check bootloader's CRC (and write it if blank)
|
||||||
|
check_own_crc(&dp.sysconfig, &cp, &mut nvm, &mut timer);
|
||||||
|
|
||||||
|
let mut preferred_app_raw = [0; 1];
|
||||||
|
nvm.read(PREFERRED_SLOT_OFFSET as usize, &mut preferred_app_raw)
|
||||||
|
.expect("reading preferred slot failed");
|
||||||
|
let preferred_app = AppSel::try_from(preferred_app_raw[0]).unwrap_or(AppSel::A);
|
||||||
|
let other_app = if preferred_app == AppSel::A {
|
||||||
|
AppSel::B
|
||||||
|
} else {
|
||||||
|
AppSel::A
|
||||||
|
};
|
||||||
|
|
||||||
|
if check_app_crc(preferred_app) {
|
||||||
|
boot_app(&dp.sysconfig, &cp, preferred_app, &mut timer)
|
||||||
|
} else if check_app_crc(other_app) {
|
||||||
|
boot_app(&dp.sysconfig, &cp, other_app, &mut timer)
|
||||||
|
} else {
|
||||||
|
if DEBUG_PRINTOUTS && DEFMT_PRINTOUT {
|
||||||
|
defmt::error!("both images corrupt! booting image A");
|
||||||
|
}
|
||||||
|
// TODO: Shift a CCSDS packet out to inform host/OBC about image corruption.
|
||||||
|
// Both images seem to be corrupt. Boot default image A.
|
||||||
|
boot_app(&dp.sysconfig, &cp, AppSel::A, &mut timer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_own_crc(
|
||||||
|
sysconfig: &pac::Sysconfig,
|
||||||
|
cp: &cortex_m::Peripherals,
|
||||||
|
nvm: &mut NvmWrapper,
|
||||||
|
timer: &mut CountdownTimer,
|
||||||
|
) {
|
||||||
|
let crc_exp = unsafe { (BOOTLOADER_CRC_ADDR as *const u16).read_unaligned().to_be() };
|
||||||
|
// I'd prefer to use [core::slice::from_raw_parts], but that is problematic
|
||||||
|
// because the address of the bootloader is 0x0, so the NULL check fails and the functions
|
||||||
|
// panics.
|
||||||
|
let mut first_four_bytes: [u8; 4] = [0; 4];
|
||||||
|
read_four_bytes_at_addr_zero(&mut first_four_bytes);
|
||||||
|
let mut digest = CRC_ALGO.digest();
|
||||||
|
digest.update(&first_four_bytes);
|
||||||
|
digest.update(unsafe {
|
||||||
|
&*core::ptr::slice_from_raw_parts(
|
||||||
|
(BOOTLOADER_START_ADDR + 4) as *const u8,
|
||||||
|
(BOOTLOADER_END_ADDR - BOOTLOADER_START_ADDR - 6) as usize,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
let crc_calc = digest.finalize();
|
||||||
|
if crc_exp == 0x0000 || crc_exp == 0xffff {
|
||||||
|
if DEBUG_PRINTOUTS && DEFMT_PRINTOUT {
|
||||||
|
defmt::info!("BL CRC blank - prog new CRC");
|
||||||
|
}
|
||||||
|
// Blank CRC, write it to NVM.
|
||||||
|
nvm.write(BOOTLOADER_CRC_ADDR as usize, &crc_calc.to_be_bytes())
|
||||||
|
.expect("writing CRC failed");
|
||||||
|
// The Vorago bootloader resets here. I am not sure why this is done but I think it is
|
||||||
|
// necessary because somehow the boot will not work if we just continue as usual.
|
||||||
|
// cortex_m::peripheral::SCB::sys_reset();
|
||||||
|
} else if crc_exp != crc_calc {
|
||||||
|
// Bootloader is corrupted. Try to run App A.
|
||||||
|
if DEBUG_PRINTOUTS && DEFMT_PRINTOUT {
|
||||||
|
defmt::warn!(
|
||||||
|
"bootloader CRC corrupt, read {} and expected {}. booting image A immediately",
|
||||||
|
crc_calc,
|
||||||
|
crc_exp
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// TODO: Shift out minimal CCSDS frame to notify about bootloader corruption.
|
||||||
|
boot_app(sysconfig, cp, AppSel::A, timer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reading from address 0x0 is problematic in Rust.
|
||||||
|
// See https://users.rust-lang.org/t/reading-from-physical-address-0x0/117408/5.
|
||||||
|
// This solution falls back to assembler to deal with this.
|
||||||
|
fn read_four_bytes_at_addr_zero(buf: &mut [u8; 4]) {
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!(
|
||||||
|
"ldr r0, [{0}]", // Load 4 bytes from src into r0 register
|
||||||
|
"str r0, [{1}]", // Store r0 register into first_four_bytes
|
||||||
|
in(reg) BOOTLOADER_START_ADDR as *const u8, // Input: src pointer (0x0)
|
||||||
|
in(reg) buf as *mut [u8; 4], // Input: destination pointer
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn check_app_crc(app_sel: AppSel) -> bool {
|
||||||
|
if DEBUG_PRINTOUTS && DEFMT_PRINTOUT {
|
||||||
|
defmt::info!("Checking image {:?}", app_sel);
|
||||||
|
}
|
||||||
|
if app_sel == AppSel::A {
|
||||||
|
check_app_given_addr(APP_A_CRC_ADDR, APP_A_START_ADDR, APP_A_SIZE_ADDR)
|
||||||
|
} else {
|
||||||
|
check_app_given_addr(APP_B_CRC_ADDR, APP_B_START_ADDR, APP_B_SIZE_ADDR)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_app_given_addr(crc_addr: u32, start_addr: u32, image_size_addr: u32) -> bool {
|
||||||
|
let crc_exp = unsafe { (crc_addr as *const u16).read_unaligned().to_be() };
|
||||||
|
let image_size = unsafe { (image_size_addr as *const u32).read_unaligned().to_be() };
|
||||||
|
// Sanity check.
|
||||||
|
if image_size > APP_A_END_ADDR - APP_A_START_ADDR - 8 {
|
||||||
|
if DEFMT_PRINTOUT {
|
||||||
|
defmt::error!("detected invalid app size {}", image_size);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let crc_calc = CRC_ALGO.checksum(unsafe {
|
||||||
|
core::slice::from_raw_parts(start_addr as *const u8, image_size as usize)
|
||||||
|
});
|
||||||
|
if crc_calc == crc_exp {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
// The boot works by copying the interrupt vector table (IVT) of the respective app to the
|
||||||
|
// base address in code RAM (0x0) and then performing a soft reset.
|
||||||
|
fn boot_app(
|
||||||
|
syscfg: &pac::Sysconfig,
|
||||||
|
cp: &cortex_m::Peripherals,
|
||||||
|
app_sel: AppSel,
|
||||||
|
timer: &mut CountdownTimer,
|
||||||
|
) -> ! {
|
||||||
|
if DEBUG_PRINTOUTS && DEFMT_PRINTOUT {
|
||||||
|
defmt::info!("booting app {:?}", app_sel);
|
||||||
|
}
|
||||||
|
timer.delay_ms(BOOT_DELAY_MS);
|
||||||
|
|
||||||
|
// Clear all interrupts set.
|
||||||
|
unsafe {
|
||||||
|
cp.NVIC.icer[0].write(0xFFFFFFFF);
|
||||||
|
cp.NVIC.icpr[0].write(0xFFFFFFFF);
|
||||||
|
}
|
||||||
|
// Disable ROM protection.
|
||||||
|
syscfg.rom_prot().write(|w| w.wren().set_bit());
|
||||||
|
let base_addr = if app_sel == AppSel::A {
|
||||||
|
APP_A_START_ADDR
|
||||||
|
} else {
|
||||||
|
APP_B_START_ADDR
|
||||||
|
};
|
||||||
|
unsafe {
|
||||||
|
// First 4 bytes done with inline assembly, writing to the physical address 0x0 can not
|
||||||
|
// be done without it. See https://users.rust-lang.org/t/reading-from-physical-address-0x0/117408/2.
|
||||||
|
let first_four_bytes = core::ptr::read(base_addr as *const u32);
|
||||||
|
core::arch::asm!(
|
||||||
|
"str {0}, [{1}]",
|
||||||
|
in(reg) first_four_bytes, // Input: App vector table.
|
||||||
|
in(reg) BOOTLOADER_START_ADDR as *mut u32, // Input: destination pointer
|
||||||
|
);
|
||||||
|
core::slice::from_raw_parts_mut(
|
||||||
|
(BOOTLOADER_START_ADDR + 4) as *mut u8,
|
||||||
|
(VECTOR_TABLE_LEN - 4) as usize,
|
||||||
|
)
|
||||||
|
.copy_from_slice(core::slice::from_raw_parts(
|
||||||
|
(base_addr + 4) as *const u8,
|
||||||
|
(VECTOR_TABLE_LEN - 4) as usize,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
// Disable re-loading from FRAM/code ROM on soft reset
|
||||||
|
syscfg
|
||||||
|
.rst_cntl_rom()
|
||||||
|
.modify(|_, w| w.sysrstreq().clear_bit());
|
||||||
|
|
||||||
|
soft_reset(cp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Soft reset based on https://github.com/ARM-software/CMSIS_6/blob/5782d6f8057906d360f4b95ec08a2354afe5c9b9/CMSIS/Core/Include/core_cm0.h#L874.
|
||||||
|
fn soft_reset(cp: &cortex_m::Peripherals) -> ! {
|
||||||
|
// Ensure all outstanding memory accesses included buffered write are completed before reset.
|
||||||
|
cortex_m::asm::dsb();
|
||||||
|
unsafe {
|
||||||
|
cp.SCB
|
||||||
|
.aircr
|
||||||
|
.write((0x5FA << SCB_AIRCR_VECTKEY_POS) | SCB_AIRCR_SYSRESETREQ_MSK);
|
||||||
|
}
|
||||||
|
// Ensure completion of memory access.
|
||||||
|
cortex_m::asm::dsb();
|
||||||
|
|
||||||
|
// Loop until the reset occurs.
|
||||||
|
loop {
|
||||||
|
cortex_m::asm::nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
va108xx/docs/ADT75.pdf
Normal file
BIN
va108xx/docs/ADT75.pdf
Normal file
Binary file not shown.
BIN
va108xx/docs/ADXL343-1503782.pdf
Normal file
BIN
va108xx/docs/ADXL343-1503782.pdf
Normal file
Binary file not shown.
BIN
va108xx/docs/MAX11618-MAX11625.pdf
Normal file
BIN
va108xx/docs/MAX11618-MAX11625.pdf
Normal file
Binary file not shown.
Binary file not shown.
BIN
va108xx/docs/REB1-Development-Board/REB1_QSG.pdf
Normal file
BIN
va108xx/docs/REB1-Development-Board/REB1_QSG.pdf
Normal file
Binary file not shown.
BIN
va108xx/docs/REB1-Development-Board/REB1_TopView.png
Normal file
BIN
va108xx/docs/REB1-Development-Board/REB1_TopView.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.5 MiB |
BIN
va108xx/docs/REB1-Development-Board/REB1_Users_Manual_v3.0.pdf
Normal file
BIN
va108xx/docs/REB1-Development-Board/REB1_Users_Manual_v3.0.pdf
Normal file
Binary file not shown.
BIN
va108xx/docs/VA108X0_PG.pdf
Normal file
BIN
va108xx/docs/VA108X0_PG.pdf
Normal file
Binary file not shown.
33
va108xx/examples/README.md
Normal file
33
va108xx/examples/README.md
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
VA108xx Example Applications
|
||||||
|
========
|
||||||
|
|
||||||
|
This folder contains various examples
|
||||||
|
Consult the main README first for setup of the repository.
|
||||||
|
|
||||||
|
## Simple examples
|
||||||
|
|
||||||
|
```rs
|
||||||
|
cargo run --example blinky
|
||||||
|
```
|
||||||
|
|
||||||
|
You can have a look at the `simple/examples` folder to see all available simple examples
|
||||||
|
|
||||||
|
## RTIC example
|
||||||
|
|
||||||
|
```rs
|
||||||
|
cargo run --bin rtic-example
|
||||||
|
```
|
||||||
|
|
||||||
|
## Embassy example
|
||||||
|
|
||||||
|
Blinky with time driver IRQs in library
|
||||||
|
|
||||||
|
```rs
|
||||||
|
cargo run --bin embassy-example
|
||||||
|
```
|
||||||
|
|
||||||
|
Blinky with custom time driver IRQs
|
||||||
|
|
||||||
|
```rs
|
||||||
|
cargo run --bin embassy-example --no-default-features --features custom-irqs
|
||||||
|
```
|
||||||
39
va108xx/examples/embassy/Cargo.toml
Normal file
39
va108xx/examples/embassy/Cargo.toml
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
[package]
|
||||||
|
name = "embassy-example"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cfg-if = "1"
|
||||||
|
cortex-m-rt = "0.7"
|
||||||
|
embedded-hal-async = "1"
|
||||||
|
embedded-io = "0.6"
|
||||||
|
embedded-io-async = "0.6"
|
||||||
|
heapless = "0.9"
|
||||||
|
static_cell = "2"
|
||||||
|
|
||||||
|
defmt = "1"
|
||||||
|
defmt-rtt = "1"
|
||||||
|
panic-probe = { version = "1", features = ["print-defmt"] }
|
||||||
|
|
||||||
|
critical-section = "1"
|
||||||
|
|
||||||
|
embassy-sync = "0.7"
|
||||||
|
embassy-time = "0.5"
|
||||||
|
embassy-executor = { version = "0.9", features = [
|
||||||
|
"arch-cortex-m",
|
||||||
|
"executor-thread",
|
||||||
|
"executor-interrupt"
|
||||||
|
]}
|
||||||
|
|
||||||
|
va108xx-hal = { version = "0.12", path = "../../va108xx-hal", features = ["defmt"] }
|
||||||
|
va108xx-embassy = { version = "0.3", path = "../../va108xx-embassy" }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["ticks-hz-1_000", "va108xx-embassy/irq-oc30-oc31"]
|
||||||
|
custom-irqs = []
|
||||||
|
ticks-hz-1_000 = ["embassy-time/tick-hz-1_000"]
|
||||||
|
ticks-hz-32_768 = ["embassy-time/tick-hz-32_768"]
|
||||||
|
|
||||||
|
[package.metadata.cargo-machete]
|
||||||
|
ignored = ["cortex-m-rt"]
|
||||||
247
va108xx/examples/embassy/src/bin/async-gpio.rs
Normal file
247
va108xx/examples/embassy/src/bin/async-gpio.rs
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
//! This example demonstrates the usage of async GPIO operations on VA108xx.
|
||||||
|
//!
|
||||||
|
//! You need to tie the PA0 to the PA1 pin for this example to work. You can optionally tie the PB22 to PB23 pins well
|
||||||
|
//! and then set the `CHECK_PB22_TO_PB23` to true to also test async operations on Port B.
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
// This imports the logger and the panic handler.
|
||||||
|
use embassy_example as _;
|
||||||
|
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_sync::channel::{Receiver, Sender};
|
||||||
|
use embassy_sync::{blocking_mutex::raw::ThreadModeRawMutex, channel::Channel};
|
||||||
|
use embassy_time::{Duration, Instant, Timer};
|
||||||
|
use embedded_hal_async::digital::Wait;
|
||||||
|
use va108xx_hal::gpio::asynch::{on_interrupt_for_async_gpio_for_port, InputPinAsync};
|
||||||
|
use va108xx_hal::gpio::{Input, Output, PinState, Port};
|
||||||
|
use va108xx_hal::pins::{PinsA, PinsB};
|
||||||
|
use va108xx_hal::{
|
||||||
|
pac::{self, interrupt},
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
|
||||||
|
|
||||||
|
const CHECK_PA0_TO_PA1: bool = true;
|
||||||
|
const CHECK_PB22_TO_PB23: bool = false;
|
||||||
|
|
||||||
|
// Can also be set to OC10 and works as well.
|
||||||
|
const PB22_TO_PB23_IRQ: pac::Interrupt = pac::Interrupt::OC11;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub struct GpioCmd {
|
||||||
|
cmd_type: GpioCmdType,
|
||||||
|
after_delay: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GpioCmd {
|
||||||
|
pub fn new(cmd_type: GpioCmdType, after_delay: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
cmd_type,
|
||||||
|
after_delay,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub enum GpioCmdType {
|
||||||
|
SetHigh,
|
||||||
|
SetLow,
|
||||||
|
RisingEdge,
|
||||||
|
FallingEdge,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Declare a bounded channel of 3 u32s.
|
||||||
|
static CHANNEL_PA0_PA1: Channel<ThreadModeRawMutex, GpioCmd, 3> = Channel::new();
|
||||||
|
static CHANNEL_PB22_TO_PB23: Channel<ThreadModeRawMutex, GpioCmd, 3> = Channel::new();
|
||||||
|
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(spawner: Spawner) {
|
||||||
|
defmt::println!("-- VA108xx Async GPIO Demo --");
|
||||||
|
|
||||||
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
|
// Safety: Only called once here.
|
||||||
|
va108xx_embassy::init(dp.tim23, dp.tim22, SYSCLK_FREQ);
|
||||||
|
|
||||||
|
let porta = PinsA::new(dp.porta);
|
||||||
|
let portb = PinsB::new(dp.portb);
|
||||||
|
let mut led0 = Output::new(porta.pa10, PinState::Low);
|
||||||
|
let out_pa0 = Output::new(porta.pa0, PinState::Low);
|
||||||
|
let in_pa1 = Input::new_floating(porta.pa1);
|
||||||
|
let out_pb22 = Output::new(portb.pb22, PinState::Low);
|
||||||
|
let in_pb23 = Input::new_floating(portb.pb23);
|
||||||
|
|
||||||
|
let in_pa1_async = InputPinAsync::new(in_pa1, pac::Interrupt::OC10);
|
||||||
|
let in_pb23_async = InputPinAsync::new(in_pb23, PB22_TO_PB23_IRQ);
|
||||||
|
|
||||||
|
spawner
|
||||||
|
.spawn(output_task(
|
||||||
|
"PA0 to PA1",
|
||||||
|
out_pa0,
|
||||||
|
CHANNEL_PA0_PA1.receiver(),
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
spawner
|
||||||
|
.spawn(output_task(
|
||||||
|
"PB22 to PB23",
|
||||||
|
out_pb22,
|
||||||
|
CHANNEL_PB22_TO_PB23.receiver(),
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if CHECK_PA0_TO_PA1 {
|
||||||
|
check_pin_to_pin_async_ops("PA0 to PA1", CHANNEL_PA0_PA1.sender(), in_pa1_async).await;
|
||||||
|
defmt::info!("Example PA0 to PA1 done");
|
||||||
|
}
|
||||||
|
if CHECK_PB22_TO_PB23 {
|
||||||
|
check_pin_to_pin_async_ops("PB22 to PB23", CHANNEL_PB22_TO_PB23.sender(), in_pb23_async)
|
||||||
|
.await;
|
||||||
|
defmt::info!("Example PB22 to PB23 done");
|
||||||
|
}
|
||||||
|
|
||||||
|
defmt::info!("Example done, toggling LED0");
|
||||||
|
loop {
|
||||||
|
led0.toggle();
|
||||||
|
Timer::after(Duration::from_millis(500)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn check_pin_to_pin_async_ops(
|
||||||
|
ctx: &'static str,
|
||||||
|
sender: Sender<'static, ThreadModeRawMutex, GpioCmd, 3>,
|
||||||
|
mut async_input: impl Wait,
|
||||||
|
) {
|
||||||
|
defmt::info!(
|
||||||
|
"{}: sending SetHigh command ({} ms)",
|
||||||
|
ctx,
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
sender.send(GpioCmd::new(GpioCmdType::SetHigh, 20)).await;
|
||||||
|
async_input.wait_for_high().await.unwrap();
|
||||||
|
defmt::info!(
|
||||||
|
"{}: Input pin is high now ({} ms)",
|
||||||
|
ctx,
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
|
||||||
|
defmt::info!(
|
||||||
|
"{}: sending SetLow command ({} ms)",
|
||||||
|
ctx,
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
sender.send(GpioCmd::new(GpioCmdType::SetLow, 20)).await;
|
||||||
|
async_input.wait_for_low().await.unwrap();
|
||||||
|
defmt::info!(
|
||||||
|
"{}: Input pin is low now ({} ms)",
|
||||||
|
ctx,
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
|
||||||
|
defmt::info!(
|
||||||
|
"{}: sending RisingEdge command ({} ms)",
|
||||||
|
ctx,
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
sender.send(GpioCmd::new(GpioCmdType::RisingEdge, 20)).await;
|
||||||
|
async_input.wait_for_rising_edge().await.unwrap();
|
||||||
|
defmt::info!(
|
||||||
|
"{}: input pin had rising edge ({} ms)",
|
||||||
|
ctx,
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
|
||||||
|
defmt::info!(
|
||||||
|
"{}: sending Falling command ({} ms)",
|
||||||
|
ctx,
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
sender
|
||||||
|
.send(GpioCmd::new(GpioCmdType::FallingEdge, 20))
|
||||||
|
.await;
|
||||||
|
async_input.wait_for_falling_edge().await.unwrap();
|
||||||
|
defmt::info!(
|
||||||
|
"{}: input pin had a falling edge ({} ms)",
|
||||||
|
ctx,
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
|
||||||
|
defmt::info!(
|
||||||
|
"{}: sending Falling command ({} ms)",
|
||||||
|
ctx,
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
sender
|
||||||
|
.send(GpioCmd::new(GpioCmdType::FallingEdge, 20))
|
||||||
|
.await;
|
||||||
|
async_input.wait_for_any_edge().await.unwrap();
|
||||||
|
defmt::info!(
|
||||||
|
"{}: input pin had a falling (any) edge ({} ms)",
|
||||||
|
ctx,
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
|
||||||
|
defmt::info!(
|
||||||
|
"{}: sending Falling command ({} ms)",
|
||||||
|
ctx,
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
sender.send(GpioCmd::new(GpioCmdType::RisingEdge, 20)).await;
|
||||||
|
async_input.wait_for_any_edge().await.unwrap();
|
||||||
|
defmt::info!(
|
||||||
|
"{}: input pin had a rising (any) edge ({} ms)",
|
||||||
|
ctx,
|
||||||
|
Instant::now().as_millis()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task(pool_size = 2)]
|
||||||
|
async fn output_task(
|
||||||
|
ctx: &'static str,
|
||||||
|
mut out: Output,
|
||||||
|
receiver: Receiver<'static, ThreadModeRawMutex, GpioCmd, 3>,
|
||||||
|
) {
|
||||||
|
loop {
|
||||||
|
let next_cmd = receiver.receive().await;
|
||||||
|
Timer::after(Duration::from_millis(next_cmd.after_delay.into())).await;
|
||||||
|
match next_cmd.cmd_type {
|
||||||
|
GpioCmdType::SetHigh => {
|
||||||
|
defmt::info!("{}: Set output high", ctx);
|
||||||
|
out.set_high();
|
||||||
|
}
|
||||||
|
GpioCmdType::SetLow => {
|
||||||
|
defmt::info!("{}: Set output low", ctx);
|
||||||
|
out.set_low();
|
||||||
|
}
|
||||||
|
GpioCmdType::RisingEdge => {
|
||||||
|
defmt::info!("{}: Rising edge", ctx);
|
||||||
|
if !out.is_set_low() {
|
||||||
|
out.set_low();
|
||||||
|
}
|
||||||
|
out.set_high();
|
||||||
|
}
|
||||||
|
GpioCmdType::FallingEdge => {
|
||||||
|
defmt::info!("{}: Falling edge", ctx);
|
||||||
|
if !out.is_set_high() {
|
||||||
|
out.set_high();
|
||||||
|
}
|
||||||
|
out.set_low();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PB22 to PB23 can be handled by both OC10 and OC11 depending on configuration.
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn OC10() {
|
||||||
|
on_interrupt_for_async_gpio_for_port(Port::A);
|
||||||
|
on_interrupt_for_async_gpio_for_port(Port::B);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This interrupt only handles PORT B interrupts.
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn OC11() {
|
||||||
|
on_interrupt_for_async_gpio_for_port(Port::B);
|
||||||
|
}
|
||||||
163
va108xx/examples/embassy/src/bin/async-uart-rx.rs
Normal file
163
va108xx/examples/embassy/src/bin/async-uart-rx.rs
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
//! Asynchronous UART reception example application.
|
||||||
|
//!
|
||||||
|
//! This application receives data on two UARTs permanently using a ring buffer.
|
||||||
|
//! The ring buffer are read them asynchronously. UART A is received on ports PA8 and PA9.
|
||||||
|
//! UART B is received on ports PA2 and PA3.
|
||||||
|
//!
|
||||||
|
//! Instructions:
|
||||||
|
//!
|
||||||
|
//! 1. Tie a USB to UART converter with RX to PA9 and TX to PA8 for UART A.
|
||||||
|
//! Tie a USB to UART converter with RX to PA3 and TX to PA2 for UART B.
|
||||||
|
//! 2. Connect to the serial interface by using an application like Putty or picocom. You can
|
||||||
|
//! type something in the terminal and check if the data is echoed back. You can also check the
|
||||||
|
//! RTT logs to see received data.
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
// This imports the logger and the panic handler.
|
||||||
|
use embassy_example as _;
|
||||||
|
|
||||||
|
use core::cell::RefCell;
|
||||||
|
use critical_section::Mutex;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_time::Instant;
|
||||||
|
use embedded_io::Write;
|
||||||
|
use embedded_io_async::Read;
|
||||||
|
use heapless::spsc::{Consumer, Producer, Queue};
|
||||||
|
use va108xx_hal::{
|
||||||
|
gpio::{Output, PinState},
|
||||||
|
pac::{self, interrupt},
|
||||||
|
pins::PinsA,
|
||||||
|
prelude::*,
|
||||||
|
uart::{
|
||||||
|
self, on_interrupt_rx_overwriting,
|
||||||
|
rx_asynch::{on_interrupt_rx, RxAsync},
|
||||||
|
Bank, RxAsyncOverwriting, Tx,
|
||||||
|
},
|
||||||
|
InterruptConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
|
||||||
|
|
||||||
|
static QUEUE_UART_A: static_cell::ConstStaticCell<Queue<u8, 256>> =
|
||||||
|
static_cell::ConstStaticCell::new(Queue::new());
|
||||||
|
static PRODUCER_UART_A: Mutex<RefCell<Option<Producer<u8>>>> = Mutex::new(RefCell::new(None));
|
||||||
|
|
||||||
|
static QUEUE_UART_B: static_cell::ConstStaticCell<Queue<u8, 256>> =
|
||||||
|
static_cell::ConstStaticCell::new(Queue::new());
|
||||||
|
static PRODUCER_UART_B: Mutex<RefCell<Option<Producer<u8>>>> = Mutex::new(RefCell::new(None));
|
||||||
|
static CONSUMER_UART_B: Mutex<RefCell<Option<Consumer<u8>>>> = Mutex::new(RefCell::new(None));
|
||||||
|
|
||||||
|
// main is itself an async function.
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(spawner: Spawner) {
|
||||||
|
defmt::println!("-- VA108xx Async UART RX Demo --");
|
||||||
|
|
||||||
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
|
// Safety: Only called once here.
|
||||||
|
va108xx_embassy::init(dp.tim23, dp.tim22, SYSCLK_FREQ);
|
||||||
|
|
||||||
|
let porta = PinsA::new(dp.porta);
|
||||||
|
let mut led0 = Output::new(porta.pa10, PinState::Low);
|
||||||
|
let mut led1 = Output::new(porta.pa7, PinState::Low);
|
||||||
|
let mut led2 = Output::new(porta.pa6, PinState::Low);
|
||||||
|
|
||||||
|
let tx_uart_a = porta.pa9;
|
||||||
|
let rx_uart_a = porta.pa8;
|
||||||
|
|
||||||
|
let uarta = uart::Uart::new_with_interrupt(
|
||||||
|
dp.uarta,
|
||||||
|
tx_uart_a,
|
||||||
|
rx_uart_a,
|
||||||
|
50.MHz(),
|
||||||
|
115200.Hz().into(),
|
||||||
|
InterruptConfig::new(pac::Interrupt::OC2, true, true),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let tx_uart_b = porta.pa3;
|
||||||
|
let rx_uart_b = porta.pa2;
|
||||||
|
|
||||||
|
let uartb = uart::Uart::new_with_interrupt(
|
||||||
|
dp.uartb,
|
||||||
|
tx_uart_b,
|
||||||
|
rx_uart_b,
|
||||||
|
50.MHz(),
|
||||||
|
115200.Hz().into(),
|
||||||
|
InterruptConfig::new(pac::Interrupt::OC3, true, true),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let (mut tx_uart_a, rx_uart_a) = uarta.split();
|
||||||
|
let (tx_uart_b, rx_uart_b) = uartb.split();
|
||||||
|
let (prod_uart_a, cons_uart_a) = QUEUE_UART_A.take().split();
|
||||||
|
// Pass the producer to the interrupt handler.
|
||||||
|
let (prod_uart_b, cons_uart_b) = QUEUE_UART_B.take().split();
|
||||||
|
critical_section::with(|cs| {
|
||||||
|
*PRODUCER_UART_A.borrow(cs).borrow_mut() = Some(prod_uart_a);
|
||||||
|
*PRODUCER_UART_B.borrow(cs).borrow_mut() = Some(prod_uart_b);
|
||||||
|
*CONSUMER_UART_B.borrow(cs).borrow_mut() = Some(cons_uart_b);
|
||||||
|
});
|
||||||
|
let mut async_rx_uart_a = RxAsync::new(rx_uart_a, cons_uart_a);
|
||||||
|
let async_rx_uart_b = RxAsyncOverwriting::new(rx_uart_b, &CONSUMER_UART_B);
|
||||||
|
spawner
|
||||||
|
.spawn(uart_b_task(async_rx_uart_b, tx_uart_b))
|
||||||
|
.unwrap();
|
||||||
|
let mut buf = [0u8; 256];
|
||||||
|
loop {
|
||||||
|
defmt::info!("Current time UART A: {}", Instant::now().as_secs());
|
||||||
|
led0.toggle();
|
||||||
|
led1.toggle();
|
||||||
|
led2.toggle();
|
||||||
|
let read_bytes = async_rx_uart_a.read(&mut buf).await.unwrap();
|
||||||
|
let read_str = core::str::from_utf8(&buf[..read_bytes]).unwrap();
|
||||||
|
defmt::info!(
|
||||||
|
"Read {} bytes asynchronously on UART A: {:?}",
|
||||||
|
read_bytes,
|
||||||
|
read_str
|
||||||
|
);
|
||||||
|
tx_uart_a.write_all(read_str.as_bytes()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[embassy_executor::task]
|
||||||
|
async fn uart_b_task(mut async_rx: RxAsyncOverwriting, mut tx: Tx) {
|
||||||
|
let mut buf = [0u8; 256];
|
||||||
|
loop {
|
||||||
|
defmt::info!("Current time UART B: {}", Instant::now().as_secs());
|
||||||
|
// Infallible asynchronous operation.
|
||||||
|
let read_bytes = async_rx.read(&mut buf).await.unwrap();
|
||||||
|
let read_str = core::str::from_utf8(&buf[..read_bytes]).unwrap();
|
||||||
|
defmt::info!(
|
||||||
|
"Read {} bytes asynchronously on UART B: {:?}",
|
||||||
|
read_bytes,
|
||||||
|
read_str
|
||||||
|
);
|
||||||
|
tx.write_all(read_str.as_bytes()).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn OC2() {
|
||||||
|
let mut prod =
|
||||||
|
critical_section::with(|cs| PRODUCER_UART_A.borrow(cs).borrow_mut().take().unwrap());
|
||||||
|
let errors = on_interrupt_rx(Bank::Uart0, &mut prod);
|
||||||
|
critical_section::with(|cs| *PRODUCER_UART_A.borrow(cs).borrow_mut() = Some(prod));
|
||||||
|
// In a production app, we could use a channel to send the errors to the main task.
|
||||||
|
if let Err(errors) = errors {
|
||||||
|
defmt::info!("UART A errors: {:?}", errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn OC3() {
|
||||||
|
let mut prod =
|
||||||
|
critical_section::with(|cs| PRODUCER_UART_B.borrow(cs).borrow_mut().take().unwrap());
|
||||||
|
let errors = on_interrupt_rx_overwriting(Bank::Uart1, &mut prod, &CONSUMER_UART_B);
|
||||||
|
critical_section::with(|cs| *PRODUCER_UART_B.borrow(cs).borrow_mut() = Some(prod));
|
||||||
|
// In a production app, we could use a channel to send the errors to the main task.
|
||||||
|
if let Err(errors) = errors {
|
||||||
|
defmt::info!("UART B errors: {:?}", errors);
|
||||||
|
}
|
||||||
|
}
|
||||||
90
va108xx/examples/embassy/src/bin/async-uart-tx.rs
Normal file
90
va108xx/examples/embassy/src/bin/async-uart-tx.rs
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
//! Asynchronous UART transmission example application.
|
||||||
|
//!
|
||||||
|
//! This application receives sends 4 strings with different sizes permanently using UART A.
|
||||||
|
//! Ports PA8 and PA9 are used for this.
|
||||||
|
//!
|
||||||
|
//! Instructions:
|
||||||
|
//!
|
||||||
|
//! 1. Tie a USB to UART converter with RX to PA9 and TX to PA8 for UART A.
|
||||||
|
//! 2. Connect to the serial interface by using an application like Putty or picocom. You can
|
||||||
|
//! can verify the correctness of the sent strings.
|
||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
// This imports the logger and the panic handler.
|
||||||
|
use embassy_example as _;
|
||||||
|
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_time::{Duration, Instant, Ticker};
|
||||||
|
use embedded_io_async::Write;
|
||||||
|
use va108xx_hal::{
|
||||||
|
gpio::{Output, PinState},
|
||||||
|
pac::{self, interrupt},
|
||||||
|
pins::PinsA,
|
||||||
|
prelude::*,
|
||||||
|
uart::{self, on_interrupt_tx, Bank, TxAsync},
|
||||||
|
InterruptConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
|
||||||
|
|
||||||
|
const STR_LIST: &[&str] = &[
|
||||||
|
"Hello World\r\n",
|
||||||
|
"Smoll\r\n",
|
||||||
|
"A string which is larger than the FIFO size\r\n",
|
||||||
|
"A really large string which is significantly larger than the FIFO size\r\n",
|
||||||
|
];
|
||||||
|
|
||||||
|
// main is itself an async function.
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(_spawner: Spawner) {
|
||||||
|
defmt::println!("-- VA108xx Async UART TX Demo --");
|
||||||
|
|
||||||
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
|
// Safety: Only called once here.
|
||||||
|
va108xx_embassy::init(dp.tim23, dp.tim22, SYSCLK_FREQ);
|
||||||
|
|
||||||
|
let porta = PinsA::new(dp.porta);
|
||||||
|
|
||||||
|
let mut led0 = Output::new(porta.pa10, PinState::Low);
|
||||||
|
let mut led1 = Output::new(porta.pa7, PinState::Low);
|
||||||
|
let mut led2 = Output::new(porta.pa6, PinState::Low);
|
||||||
|
|
||||||
|
let tx = porta.pa9;
|
||||||
|
let rx = porta.pa8;
|
||||||
|
|
||||||
|
let uarta = uart::Uart::new_with_interrupt(
|
||||||
|
dp.uarta,
|
||||||
|
tx,
|
||||||
|
rx,
|
||||||
|
50.MHz(),
|
||||||
|
115200.Hz().into(),
|
||||||
|
InterruptConfig::new(pac::Interrupt::OC2, true, true),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let (tx, _rx) = uarta.split();
|
||||||
|
let mut async_tx = TxAsync::new(tx);
|
||||||
|
let mut ticker = Ticker::every(Duration::from_secs(1));
|
||||||
|
let mut idx = 0;
|
||||||
|
loop {
|
||||||
|
defmt::info!("Current time: {}", Instant::now().as_secs());
|
||||||
|
led0.toggle();
|
||||||
|
led1.toggle();
|
||||||
|
led2.toggle();
|
||||||
|
let _written = async_tx
|
||||||
|
.write(STR_LIST[idx].as_bytes())
|
||||||
|
.await
|
||||||
|
.expect("writing failed");
|
||||||
|
idx += 1;
|
||||||
|
if idx == STR_LIST.len() {
|
||||||
|
idx = 0;
|
||||||
|
}
|
||||||
|
ticker.next().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn OC2() {
|
||||||
|
on_interrupt_tx(Bank::Uart0);
|
||||||
|
}
|
||||||
3
va108xx/examples/embassy/src/lib.rs
Normal file
3
va108xx/examples/embassy/src/lib.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
#![no_std]
|
||||||
|
use defmt_rtt as _;
|
||||||
|
use panic_probe as _;
|
||||||
62
va108xx/examples/embassy/src/main.rs
Normal file
62
va108xx/examples/embassy/src/main.rs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
use embassy_example as _;
|
||||||
|
use embassy_executor::Spawner;
|
||||||
|
use embassy_time::{Duration, Instant, Ticker};
|
||||||
|
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(feature = "custom-irqs")] {
|
||||||
|
use va108xx_embassy::embassy_time_driver_irqs;
|
||||||
|
use va108xx_hal::pac::interrupt;
|
||||||
|
embassy_time_driver_irqs!(timekeeper_irq = OC23, alarm_irq = OC24);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
use va108xx_hal::{
|
||||||
|
gpio::{Output, PinState},
|
||||||
|
pac,
|
||||||
|
pins::PinsA,
|
||||||
|
prelude::*,
|
||||||
|
};
|
||||||
|
|
||||||
|
const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
|
||||||
|
|
||||||
|
// main is itself an async function.
|
||||||
|
#[embassy_executor::main]
|
||||||
|
async fn main(_spawner: Spawner) {
|
||||||
|
defmt::println!("-- VA108xx Embassy Demo --");
|
||||||
|
|
||||||
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
|
// Safety: Only called once here.
|
||||||
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(not(feature = "custom-irqs"))] {
|
||||||
|
va108xx_embassy::init(
|
||||||
|
dp.tim23,
|
||||||
|
dp.tim22,
|
||||||
|
SYSCLK_FREQ,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
va108xx_embassy::init_with_custom_irqs(
|
||||||
|
dp.tim23,
|
||||||
|
dp.tim22,
|
||||||
|
SYSCLK_FREQ,
|
||||||
|
pac::Interrupt::OC23,
|
||||||
|
pac::Interrupt::OC24,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let porta = PinsA::new(dp.porta);
|
||||||
|
let mut led0 = Output::new(porta.pa10, PinState::Low);
|
||||||
|
let mut led1 = Output::new(porta.pa7, PinState::Low);
|
||||||
|
let mut led2 = Output::new(porta.pa6, PinState::Low);
|
||||||
|
let mut ticker = Ticker::every(Duration::from_secs(1));
|
||||||
|
loop {
|
||||||
|
ticker.next().await;
|
||||||
|
defmt::info!("Current time: {}", Instant::now().as_secs());
|
||||||
|
led0.toggle();
|
||||||
|
led1.toggle();
|
||||||
|
led2.toggle();
|
||||||
|
}
|
||||||
|
}
|
||||||
17
va108xx/examples/rtic/Cargo.toml
Normal file
17
va108xx/examples/rtic/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
[package]
|
||||||
|
name = "rtic-example"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = { version = "0.7", features = ["critical-section-single-core"] }
|
||||||
|
embedded-io = "0.6"
|
||||||
|
defmt-rtt = "1"
|
||||||
|
defmt = "1"
|
||||||
|
panic-probe = { version = "1", features = ["defmt"] }
|
||||||
|
rtic = { version = "2", features = ["thumbv6-backend"] }
|
||||||
|
rtic-monotonics = { version = "2", features = ["cortex-m-systick"] }
|
||||||
|
ringbuf = { version = "0.4.7", default-features = false, features = ["portable-atomic"] }
|
||||||
|
|
||||||
|
va108xx-hal = { version = "0.12", path = "../../va108xx-hal" }
|
||||||
|
vorago-reb1 = { version = "0.9", path = "../../vorago-reb1" }
|
||||||
98
va108xx/examples/rtic/src/bin/blinky-button-rtic.rs
Normal file
98
va108xx/examples/rtic/src/bin/blinky-button-rtic.rs
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
//! Blinky button application for the REB1 board using RTIC
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
#[rtic::app(device = pac)]
|
||||||
|
mod app {
|
||||||
|
use rtic_example::SYSCLK_FREQ;
|
||||||
|
// Import panic provider.
|
||||||
|
use panic_probe as _;
|
||||||
|
// Import global logger.
|
||||||
|
use defmt_rtt as _;
|
||||||
|
use va108xx_hal::{
|
||||||
|
clock::{set_clk_div_register, FilterClockSelect},
|
||||||
|
gpio::{FilterType, InterruptEdge},
|
||||||
|
pac,
|
||||||
|
pins::PinsA,
|
||||||
|
timer::InterruptConfig,
|
||||||
|
};
|
||||||
|
use vorago_reb1::button::Button;
|
||||||
|
use vorago_reb1::leds::Leds;
|
||||||
|
|
||||||
|
rtic_monotonics::systick_monotonic!(Mono, 1_000);
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, defmt::Format)]
|
||||||
|
pub enum PressMode {
|
||||||
|
Toggle,
|
||||||
|
Keep,
|
||||||
|
}
|
||||||
|
|
||||||
|
// You can change the press mode here
|
||||||
|
const DEFAULT_MODE: PressMode = PressMode::Toggle;
|
||||||
|
|
||||||
|
#[local]
|
||||||
|
struct Local {
|
||||||
|
leds: Leds,
|
||||||
|
button: Button,
|
||||||
|
mode: PressMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[shared]
|
||||||
|
struct Shared {}
|
||||||
|
|
||||||
|
#[init]
|
||||||
|
fn init(cx: init::Context) -> (Shared, Local) {
|
||||||
|
defmt::println!("-- Vorago Button IRQ Example --");
|
||||||
|
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
|
||||||
|
|
||||||
|
let mode = DEFAULT_MODE;
|
||||||
|
defmt::info!("Using {:?} mode", mode);
|
||||||
|
|
||||||
|
let mut dp = cx.device;
|
||||||
|
let pinsa = PinsA::new(dp.porta);
|
||||||
|
let edge_irq = match mode {
|
||||||
|
PressMode::Toggle => InterruptEdge::HighToLow,
|
||||||
|
PressMode::Keep => InterruptEdge::BothEdges,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Configure an edge interrupt on the button and route it to interrupt vector 15
|
||||||
|
let mut button = Button::new(pinsa.pa11);
|
||||||
|
|
||||||
|
if mode == PressMode::Toggle {
|
||||||
|
// This filter debounces the switch for edge based interrupts
|
||||||
|
button.configure_filter_type(FilterType::FilterFourCycles, FilterClockSelect::Clk1);
|
||||||
|
set_clk_div_register(&mut dp.sysconfig, FilterClockSelect::Clk1, 50_000);
|
||||||
|
}
|
||||||
|
button.configure_and_enable_edge_interrupt(
|
||||||
|
edge_irq,
|
||||||
|
InterruptConfig::new(pac::interrupt::OC15, true, true),
|
||||||
|
);
|
||||||
|
let mut leds = Leds::new(pinsa.pa10, pinsa.pa7, pinsa.pa6);
|
||||||
|
for led in leds.iter_mut() {
|
||||||
|
led.off();
|
||||||
|
}
|
||||||
|
(Shared {}, Local { leds, button, mode })
|
||||||
|
}
|
||||||
|
|
||||||
|
// `shared` cannot be accessed from this context
|
||||||
|
#[idle]
|
||||||
|
fn idle(_cx: idle::Context) -> ! {
|
||||||
|
loop {
|
||||||
|
cortex_m::asm::nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(binds = OC15, local=[button, leds, mode])]
|
||||||
|
fn button_task(cx: button_task::Context) {
|
||||||
|
let leds = cx.local.leds;
|
||||||
|
let button = cx.local.button;
|
||||||
|
let mode = cx.local.mode;
|
||||||
|
if *mode == PressMode::Toggle {
|
||||||
|
leds[0].toggle();
|
||||||
|
} else if button.released() {
|
||||||
|
leds[0].off();
|
||||||
|
} else {
|
||||||
|
leds[0].on();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
va108xx/examples/rtic/src/bin/rtic-empty.rs
Normal file
31
va108xx/examples/rtic/src/bin/rtic-empty.rs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
//! Empty RTIC project template
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
#[rtic::app(device = pac)]
|
||||||
|
mod app {
|
||||||
|
// Import panic provider.
|
||||||
|
use panic_probe as _;
|
||||||
|
// Import global logger.
|
||||||
|
use defmt_rtt as _;
|
||||||
|
use va108xx_hal::pac;
|
||||||
|
|
||||||
|
#[local]
|
||||||
|
struct Local {}
|
||||||
|
|
||||||
|
#[shared]
|
||||||
|
struct Shared {}
|
||||||
|
|
||||||
|
#[init]
|
||||||
|
fn init(_ctx: init::Context) -> (Shared, Local) {
|
||||||
|
defmt::println!("-- Vorago RTIC template --");
|
||||||
|
(Shared {}, Local {})
|
||||||
|
}
|
||||||
|
|
||||||
|
// `shared` cannot be accessed from this context
|
||||||
|
#[idle]
|
||||||
|
fn idle(_cx: idle::Context) -> ! {
|
||||||
|
#[allow(clippy::empty_loop)]
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
}
|
||||||
133
va108xx/examples/rtic/src/bin/uart-echo-rtic.rs
Normal file
133
va108xx/examples/rtic/src/bin/uart-echo-rtic.rs
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
//! More complex UART application on UART PA8 (TX) and PA9 (RX).
|
||||||
|
//!
|
||||||
|
//! Uses the IRQ capabilities of the VA10820 peripheral and the RTIC framework to poll the UART in
|
||||||
|
//! a non-blocking way. All received data will be sent back to the sender.
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use ringbuf::StaticRb;
|
||||||
|
|
||||||
|
// Larger buffer for TC to be able to hold the possibly large memory write packets.
|
||||||
|
const RX_RING_BUF_SIZE: usize = 1024;
|
||||||
|
|
||||||
|
#[rtic::app(device = pac, dispatchers = [OC4])]
|
||||||
|
mod app {
|
||||||
|
use super::*;
|
||||||
|
use embedded_io::Write;
|
||||||
|
use ringbuf::traits::{Consumer, Observer, Producer};
|
||||||
|
use rtic_example::SYSCLK_FREQ;
|
||||||
|
// Import panic provider.
|
||||||
|
use panic_probe as _;
|
||||||
|
// Import global logger.
|
||||||
|
use defmt_rtt as _;
|
||||||
|
use rtic_monotonics::Monotonic;
|
||||||
|
use va108xx_hal::{
|
||||||
|
pac,
|
||||||
|
pins::PinsA,
|
||||||
|
prelude::*,
|
||||||
|
uart::{self, RxWithInterrupt, Tx},
|
||||||
|
InterruptConfig,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[local]
|
||||||
|
struct Local {
|
||||||
|
rx: RxWithInterrupt,
|
||||||
|
tx: Tx,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[shared]
|
||||||
|
struct Shared {
|
||||||
|
rb: StaticRb<u8, RX_RING_BUF_SIZE>,
|
||||||
|
}
|
||||||
|
|
||||||
|
rtic_monotonics::systick_monotonic!(Mono, 1_000);
|
||||||
|
|
||||||
|
#[init]
|
||||||
|
fn init(cx: init::Context) -> (Shared, Local) {
|
||||||
|
defmt::println!("-- VA108xx UART Echo with IRQ example application--");
|
||||||
|
|
||||||
|
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
|
||||||
|
|
||||||
|
let dp = cx.device;
|
||||||
|
let gpioa = PinsA::new(dp.porta);
|
||||||
|
let tx = gpioa.pa9;
|
||||||
|
let rx = gpioa.pa8;
|
||||||
|
|
||||||
|
let irq_uart = uart::Uart::new_with_interrupt(
|
||||||
|
dp.uarta,
|
||||||
|
tx,
|
||||||
|
rx,
|
||||||
|
SYSCLK_FREQ,
|
||||||
|
115200.Hz().into(),
|
||||||
|
InterruptConfig::new(pac::Interrupt::OC3, true, true),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let (tx, rx) = irq_uart.split();
|
||||||
|
let mut rx = rx.into_rx_with_irq();
|
||||||
|
|
||||||
|
rx.start();
|
||||||
|
|
||||||
|
echo_handler::spawn().unwrap();
|
||||||
|
(
|
||||||
|
Shared {
|
||||||
|
rb: StaticRb::default(),
|
||||||
|
},
|
||||||
|
Local { rx, tx },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// `shared` cannot be accessed from this context
|
||||||
|
#[idle]
|
||||||
|
fn idle(_cx: idle::Context) -> ! {
|
||||||
|
loop {
|
||||||
|
cortex_m::asm::nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(
|
||||||
|
binds = OC3,
|
||||||
|
shared = [rb],
|
||||||
|
local = [
|
||||||
|
rx,
|
||||||
|
],
|
||||||
|
)]
|
||||||
|
fn reception_task(mut cx: reception_task::Context) {
|
||||||
|
let mut buf: [u8; 16] = [0; 16];
|
||||||
|
let mut ringbuf_full = false;
|
||||||
|
let result = cx.local.rx.on_interrupt(&mut buf);
|
||||||
|
if result.bytes_read > 0 && result.errors.is_none() {
|
||||||
|
cx.shared.rb.lock(|rb| {
|
||||||
|
if rb.vacant_len() < result.bytes_read {
|
||||||
|
ringbuf_full = true;
|
||||||
|
} else {
|
||||||
|
rb.push_slice(&buf[0..result.bytes_read]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if ringbuf_full {
|
||||||
|
// Could also drop oldest data, but that would require the consumer to be shared.
|
||||||
|
defmt::println!("buffer full, data was dropped");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(shared = [rb], local = [
|
||||||
|
buf: [u8; RX_RING_BUF_SIZE] = [0; RX_RING_BUF_SIZE],
|
||||||
|
|
||||||
|
tx
|
||||||
|
], priority=1)]
|
||||||
|
async fn echo_handler(mut cx: echo_handler::Context) {
|
||||||
|
loop {
|
||||||
|
cx.shared.rb.lock(|rb| {
|
||||||
|
let bytes_to_read = rb.occupied_len();
|
||||||
|
if bytes_to_read > 0 {
|
||||||
|
let actual_read_bytes = rb.pop_slice(&mut cx.local.buf[0..bytes_to_read]);
|
||||||
|
cx.local
|
||||||
|
.tx
|
||||||
|
.write_all(&cx.local.buf[0..actual_read_bytes])
|
||||||
|
.expect("Failed to write to TX");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Mono::delay(50.millis()).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
4
va108xx/examples/rtic/src/lib.rs
Normal file
4
va108xx/examples/rtic/src/lib.rs
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
#![no_std]
|
||||||
|
use va108xx_hal::time::Hertz;
|
||||||
|
|
||||||
|
pub const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
|
||||||
68
va108xx/examples/rtic/src/main.rs
Normal file
68
va108xx/examples/rtic/src/main.rs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
//! RTIC minimal blinky
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
#[rtic::app(device = pac, dispatchers = [OC31, OC30, OC29])]
|
||||||
|
mod app {
|
||||||
|
use cortex_m::asm;
|
||||||
|
use rtic_example::SYSCLK_FREQ;
|
||||||
|
use rtic_monotonics::systick::prelude::*;
|
||||||
|
use rtic_monotonics::Monotonic;
|
||||||
|
// Import panic provider.
|
||||||
|
use panic_probe as _;
|
||||||
|
// Import global logger.
|
||||||
|
use defmt_rtt as _;
|
||||||
|
use va108xx_hal::{
|
||||||
|
gpio::{Output, PinState},
|
||||||
|
pac,
|
||||||
|
pins::PinsA,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[local]
|
||||||
|
struct Local {
|
||||||
|
led0: Output,
|
||||||
|
led1: Output,
|
||||||
|
led2: Output,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[shared]
|
||||||
|
struct Shared {}
|
||||||
|
|
||||||
|
rtic_monotonics::systick_monotonic!(Mono, 1_000);
|
||||||
|
|
||||||
|
#[init]
|
||||||
|
fn init(cx: init::Context) -> (Shared, Local) {
|
||||||
|
defmt::println!("-- Vorago VA108xx RTIC template --");
|
||||||
|
|
||||||
|
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
|
||||||
|
|
||||||
|
let porta = PinsA::new(cx.device.porta);
|
||||||
|
let led0 = Output::new(porta.pa10, PinState::Low);
|
||||||
|
let led1 = Output::new(porta.pa7, PinState::Low);
|
||||||
|
let led2 = Output::new(porta.pa6, PinState::Low);
|
||||||
|
blinky::spawn().ok();
|
||||||
|
(Shared {}, Local { led0, led1, led2 })
|
||||||
|
}
|
||||||
|
|
||||||
|
// `shared` cannot be accessed from this context
|
||||||
|
#[idle]
|
||||||
|
fn idle(_cx: idle::Context) -> ! {
|
||||||
|
loop {
|
||||||
|
asm::nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(
|
||||||
|
priority = 3,
|
||||||
|
local=[led0, led1, led2],
|
||||||
|
)]
|
||||||
|
async fn blinky(cx: blinky::Context) {
|
||||||
|
loop {
|
||||||
|
defmt::println!("toggling LEDs");
|
||||||
|
cx.local.led0.toggle();
|
||||||
|
cx.local.led1.toggle();
|
||||||
|
cx.local.led2.toggle();
|
||||||
|
Mono::delay(1000.millis()).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
va108xx/examples/simple/Cargo.toml
Normal file
21
va108xx/examples/simple/Cargo.toml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
[package]
|
||||||
|
name = "simple-examples"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = {version = "0.7", features = ["critical-section-single-core"]}
|
||||||
|
cortex-m-rt = "0.7"
|
||||||
|
panic-halt = "1"
|
||||||
|
defmt-rtt = "1"
|
||||||
|
defmt = "1"
|
||||||
|
panic-probe = { version = "1", features = ["defmt"] }
|
||||||
|
embedded-hal = "1"
|
||||||
|
embedded-hal-nb = "1"
|
||||||
|
embedded-io = "0.6"
|
||||||
|
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"] }
|
||||||
|
|
||||||
|
[dependencies.va108xx-hal]
|
||||||
|
version = "0.12"
|
||||||
|
path = "../../va108xx-hal"
|
||||||
|
features = ["defmt"]
|
||||||
47
va108xx/examples/simple/examples/blinky-pac.rs
Normal file
47
va108xx/examples/simple/examples/blinky-pac.rs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
//! Blinky examples using only the PAC
|
||||||
|
//!
|
||||||
|
//! Additional note on LEDs:
|
||||||
|
//! Pulling the GPIOs low makes the LEDs blink. See REB1
|
||||||
|
//! schematic for more details.
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use panic_halt as _;
|
||||||
|
use va108xx_hal::pac;
|
||||||
|
|
||||||
|
// REB LED pin definitions. All on port A
|
||||||
|
const LED_D2: u32 = 1 << 10;
|
||||||
|
const LED_D3: u32 = 1 << 7;
|
||||||
|
const LED_D4: u32 = 1 << 6;
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
|
// Enable all peripheral clocks
|
||||||
|
dp.sysconfig
|
||||||
|
.peripheral_clk_enable()
|
||||||
|
.modify(|_, w| unsafe { w.bits(0xffffffff) });
|
||||||
|
dp.porta
|
||||||
|
.dir()
|
||||||
|
.modify(|_, w| unsafe { w.bits(LED_D2 | LED_D3 | LED_D4) });
|
||||||
|
dp.porta
|
||||||
|
.datamask()
|
||||||
|
.modify(|_, w| unsafe { w.bits(LED_D2 | LED_D3 | LED_D4) });
|
||||||
|
for _ in 0..10 {
|
||||||
|
dp.porta
|
||||||
|
.clrout()
|
||||||
|
.write(|w| unsafe { w.bits(LED_D2 | LED_D3 | LED_D4) });
|
||||||
|
cortex_m::asm::delay(5_000_000);
|
||||||
|
dp.porta
|
||||||
|
.setout()
|
||||||
|
.write(|w| unsafe { w.bits(LED_D2 | LED_D3 | LED_D4) });
|
||||||
|
cortex_m::asm::delay(5_000_000);
|
||||||
|
}
|
||||||
|
loop {
|
||||||
|
dp.porta
|
||||||
|
.togout()
|
||||||
|
.write(|w| unsafe { w.bits(LED_D2 | LED_D3 | LED_D4) });
|
||||||
|
cortex_m::asm::delay(25_000_000);
|
||||||
|
}
|
||||||
|
}
|
||||||
46
va108xx/examples/simple/examples/blinky.rs
Normal file
46
va108xx/examples/simple/examples/blinky.rs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
//! Simple blinky example
|
||||||
|
//!
|
||||||
|
//! Additional note on LEDs when using the REB1 development board:
|
||||||
|
//! Be not afraid: Pulling the GPIOs low makes the LEDs blink. See REB1
|
||||||
|
//! schematic for more details.
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use embedded_hal::delay::DelayNs;
|
||||||
|
use panic_halt as _;
|
||||||
|
use va108xx_hal::{
|
||||||
|
gpio::{Output, PinState},
|
||||||
|
pac::{self},
|
||||||
|
pins::PinsA,
|
||||||
|
prelude::*,
|
||||||
|
timer::CountdownTimer,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
|
let mut delay = CountdownTimer::new(dp.tim1, 50.MHz());
|
||||||
|
let porta = PinsA::new(dp.porta);
|
||||||
|
let mut led1 = Output::new(porta.pa10, PinState::Low);
|
||||||
|
let mut led2 = Output::new(porta.pa7, PinState::Low);
|
||||||
|
let mut led3 = Output::new(porta.pa6, PinState::Low);
|
||||||
|
for _ in 0..10 {
|
||||||
|
led1.set_low();
|
||||||
|
led2.set_low();
|
||||||
|
led3.set_low();
|
||||||
|
delay.delay_ms(200);
|
||||||
|
led1.set_high();
|
||||||
|
led2.set_high();
|
||||||
|
led3.set_high();
|
||||||
|
delay.delay_ms(200);
|
||||||
|
}
|
||||||
|
loop {
|
||||||
|
led1.toggle();
|
||||||
|
delay.delay_ms(200);
|
||||||
|
led2.toggle();
|
||||||
|
delay.delay_ms(200);
|
||||||
|
led3.toggle();
|
||||||
|
delay.delay_ms(200);
|
||||||
|
}
|
||||||
|
}
|
||||||
109
va108xx/examples/simple/examples/cascade.rs
Normal file
109
va108xx/examples/simple/examples/cascade.rs
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
//! Simple Cascade example
|
||||||
|
//!
|
||||||
|
//! A timer will be periodically started which starts another timer via the cascade feature.
|
||||||
|
//! This timer will then start another timer with the cascade feature as well.
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use embedded_hal::delay::DelayNs;
|
||||||
|
// Import panic provider.
|
||||||
|
use panic_probe as _;
|
||||||
|
// Import logger.
|
||||||
|
use defmt_rtt as _;
|
||||||
|
use va108xx_hal::{
|
||||||
|
pac::{self, interrupt},
|
||||||
|
prelude::*,
|
||||||
|
timer::{CascadeControl, CascadeSelect, CascadeSource, CountdownTimer, InterruptConfig},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
defmt::println!("-- VA108xx Cascade example application--");
|
||||||
|
|
||||||
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
|
let mut delay = CountdownTimer::new(dp.tim0, 50.MHz());
|
||||||
|
|
||||||
|
// Will be started periodically to trigger a cascade
|
||||||
|
let mut cascade_triggerer = CountdownTimer::new(dp.tim3, 50.MHz());
|
||||||
|
cascade_triggerer.auto_disable(true);
|
||||||
|
cascade_triggerer.enable_interrupt(InterruptConfig::new(pac::Interrupt::OC1, true, false));
|
||||||
|
cascade_triggerer.enable();
|
||||||
|
|
||||||
|
// First target for cascade
|
||||||
|
let mut cascade_target_1 = CountdownTimer::new(dp.tim4, 50.MHz());
|
||||||
|
cascade_target_1.auto_deactivate(true);
|
||||||
|
cascade_target_1
|
||||||
|
.cascade_source(CascadeSelect::Csd0, CascadeSource::Tim(3))
|
||||||
|
.unwrap();
|
||||||
|
let mut csd_cfg = CascadeControl {
|
||||||
|
enable_src_0: true,
|
||||||
|
trigger_mode_0: true,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
cascade_target_1.cascade_control(csd_cfg);
|
||||||
|
// Normally it should already be sufficient to activate IRQ in the CTRL
|
||||||
|
// register but a full interrupt is use here to display print output when
|
||||||
|
// the timer expires
|
||||||
|
cascade_target_1.enable_interrupt(InterruptConfig::new(pac::Interrupt::OC2, true, false));
|
||||||
|
// The counter will only activate when the cascade signal is coming in so
|
||||||
|
// it is okay to call start here to set the reset value
|
||||||
|
cascade_target_1.start(1.Hz());
|
||||||
|
|
||||||
|
// Activated by first cascade target
|
||||||
|
let mut cascade_target_2 = CountdownTimer::new(dp.tim5, 50.MHz());
|
||||||
|
cascade_target_2.auto_deactivate(true);
|
||||||
|
// Set TIM4 as cascade source
|
||||||
|
cascade_target_2
|
||||||
|
.cascade_source(CascadeSelect::Csd1, CascadeSource::Tim(4))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
csd_cfg = CascadeControl::default();
|
||||||
|
csd_cfg.enable_src_1 = true;
|
||||||
|
// Use trigger mode here
|
||||||
|
csd_cfg.trigger_mode_1 = true;
|
||||||
|
cascade_target_2.cascade_control(csd_cfg);
|
||||||
|
// Normally it should already be sufficient to activate IRQ in the CTRL
|
||||||
|
// register but a full interrupt is use here to display print output when
|
||||||
|
// the timer expires
|
||||||
|
cascade_target_2.enable_interrupt(InterruptConfig::new(pac::Interrupt::OC3, true, false));
|
||||||
|
// The counter will only activate when the cascade signal is coming in so
|
||||||
|
// it is okay to call start here to set the reset value
|
||||||
|
cascade_target_2.start(1.Hz());
|
||||||
|
|
||||||
|
// Unpend all IRQs
|
||||||
|
unsafe {
|
||||||
|
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC0);
|
||||||
|
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC1);
|
||||||
|
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC2);
|
||||||
|
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC3);
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
defmt::info!("-- Triggering cascade in 0.5 seconds --");
|
||||||
|
cascade_triggerer.start(2.Hz());
|
||||||
|
delay.delay_ms(5000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
fn OC1() {
|
||||||
|
static mut IDX: u32 = 0;
|
||||||
|
defmt::info!("{}: Cascade trigger timed out", &IDX);
|
||||||
|
*IDX += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
fn OC2() {
|
||||||
|
static mut IDX: u32 = 0;
|
||||||
|
defmt::info!("{}: First cascade target timed out", &IDX);
|
||||||
|
*IDX += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
fn OC3() {
|
||||||
|
static mut IDX: u32 = 0;
|
||||||
|
defmt::info!("{}: Second cascade target timed out", &IDX);
|
||||||
|
*IDX += 1;
|
||||||
|
}
|
||||||
68
va108xx/examples/simple/examples/pwm.rs
Normal file
68
va108xx/examples/simple/examples/pwm.rs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
//! Simple PWM example
|
||||||
|
//!
|
||||||
|
//! Outputs a PWM waveform on pin PA3.
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use embedded_hal::{delay::DelayNs, pwm::SetDutyCycle};
|
||||||
|
// Import panic provider.
|
||||||
|
use panic_probe as _;
|
||||||
|
// Import logger.
|
||||||
|
use defmt_rtt as _;
|
||||||
|
use va108xx_hal::{
|
||||||
|
pac,
|
||||||
|
pins::PinsA,
|
||||||
|
prelude::*,
|
||||||
|
pwm::{self, get_duty_from_percent, PwmA, PwmB, PwmPin},
|
||||||
|
timer::CountdownTimer,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
defmt::println!("-- VA108xx PWM example application--");
|
||||||
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
|
let pinsa = PinsA::new(dp.porta);
|
||||||
|
let mut pwm = pwm::PwmPin::new(pinsa.pa3, dp.tim3, 50.MHz(), 10.Hz()).unwrap();
|
||||||
|
let mut delay = CountdownTimer::new(dp.tim0, 50.MHz());
|
||||||
|
let mut current_duty_cycle = 0.0;
|
||||||
|
pwm.set_duty_cycle(get_duty_from_percent(current_duty_cycle))
|
||||||
|
.unwrap();
|
||||||
|
pwm.enable();
|
||||||
|
|
||||||
|
// Delete type information, increased code readibility for the rest of the code
|
||||||
|
loop {
|
||||||
|
let mut counter = 0;
|
||||||
|
// Increase duty cycle continuously
|
||||||
|
while current_duty_cycle < 1.0 {
|
||||||
|
delay.delay_ms(400);
|
||||||
|
current_duty_cycle += 0.02;
|
||||||
|
counter += 1;
|
||||||
|
if counter % 10 == 0 {
|
||||||
|
defmt::info!("current duty cycle: {}", current_duty_cycle);
|
||||||
|
}
|
||||||
|
|
||||||
|
pwm.set_duty_cycle(get_duty_from_percent(current_duty_cycle))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Switch to PWMB and decrease the window with a high signal from 100 % to 0 %
|
||||||
|
// continously
|
||||||
|
current_duty_cycle = 0.0;
|
||||||
|
let mut upper_limit = 1.0;
|
||||||
|
let mut lower_limit = 0.0;
|
||||||
|
let mut pwmb: PwmPin<PwmB> = PwmPin::from(pwm);
|
||||||
|
pwmb.set_pwmb_lower_limit(get_duty_from_percent(lower_limit));
|
||||||
|
pwmb.set_pwmb_upper_limit(get_duty_from_percent(upper_limit));
|
||||||
|
while lower_limit < 0.5 {
|
||||||
|
delay.delay_ms(400);
|
||||||
|
lower_limit += 0.01;
|
||||||
|
upper_limit -= 0.01;
|
||||||
|
pwmb.set_pwmb_lower_limit(get_duty_from_percent(lower_limit));
|
||||||
|
pwmb.set_pwmb_upper_limit(get_duty_from_percent(upper_limit));
|
||||||
|
defmt::info!("Lower limit: {}", pwmb.pwmb_lower_limit());
|
||||||
|
defmt::info!("Upper limit: {}", pwmb.pwmb_upper_limit());
|
||||||
|
}
|
||||||
|
pwm = PwmPin::<PwmA>::from(pwmb);
|
||||||
|
}
|
||||||
|
}
|
||||||
137
va108xx/examples/simple/examples/spi.rs
Normal file
137
va108xx/examples/simple/examples/spi.rs
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
//! SPI example application
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use embedded_hal::{
|
||||||
|
delay::DelayNs,
|
||||||
|
spi::{Mode, SpiBus, MODE_0},
|
||||||
|
};
|
||||||
|
// Import panic provider.
|
||||||
|
use panic_probe as _;
|
||||||
|
// Import logger.
|
||||||
|
use defmt_rtt as _;
|
||||||
|
use va108xx_hal::{
|
||||||
|
pac,
|
||||||
|
pins::{PinsA, PinsB},
|
||||||
|
prelude::*,
|
||||||
|
spi::{self, configure_pin_as_hw_cs_pin, Spi, SpiClockConfig, TransferConfig},
|
||||||
|
timer::CountdownTimer,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
pub enum ExampleSelect {
|
||||||
|
// Enter loopback mode. It is not necessary to tie MOSI/MISO together for this
|
||||||
|
Loopback,
|
||||||
|
MosiMisoTiedTogetherManually,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Debug)]
|
||||||
|
pub enum SpiBusSelect {
|
||||||
|
SpiAPortA,
|
||||||
|
SpiAPortB,
|
||||||
|
SpiBPortB,
|
||||||
|
}
|
||||||
|
|
||||||
|
const EXAMPLE_SEL: ExampleSelect = ExampleSelect::Loopback;
|
||||||
|
const SPI_BUS_SEL: SpiBusSelect = SpiBusSelect::SpiBPortB;
|
||||||
|
const SPI_SPEED_KHZ: u32 = 1000;
|
||||||
|
const SPI_MODE: Mode = MODE_0;
|
||||||
|
const BLOCKMODE: bool = true;
|
||||||
|
const FILL_WORD: u8 = 0x0f;
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
defmt::println!("-- VA108xx SPI example application--");
|
||||||
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
|
let mut delay = CountdownTimer::new(dp.tim0, 50.MHz());
|
||||||
|
|
||||||
|
let spi_clk_cfg = SpiClockConfig::from_clk(50.MHz(), SPI_SPEED_KHZ.kHz())
|
||||||
|
.expect("creating SPI clock config failed");
|
||||||
|
let pinsa = PinsA::new(dp.porta);
|
||||||
|
let pinsb = PinsB::new(dp.portb);
|
||||||
|
|
||||||
|
let mut spi_cfg = spi::SpiConfig::default();
|
||||||
|
if EXAMPLE_SEL == ExampleSelect::Loopback {
|
||||||
|
spi_cfg = spi_cfg.loopback(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up the SPI peripheral
|
||||||
|
let mut spi = match SPI_BUS_SEL {
|
||||||
|
SpiBusSelect::SpiAPortA => {
|
||||||
|
let (sck, mosi, miso) = (pinsa.pa31, pinsa.pa30, pinsa.pa29);
|
||||||
|
let mut spia = Spi::new(dp.spia, (sck, miso, mosi), spi_cfg).unwrap();
|
||||||
|
spia.set_fill_word(FILL_WORD);
|
||||||
|
spia
|
||||||
|
}
|
||||||
|
SpiBusSelect::SpiAPortB => {
|
||||||
|
let (sck, mosi, miso) = (pinsb.pb9, pinsb.pb8, pinsb.pb7);
|
||||||
|
let mut spia = Spi::new(dp.spia, (sck, miso, mosi), spi_cfg).unwrap();
|
||||||
|
spia.set_fill_word(FILL_WORD);
|
||||||
|
spia
|
||||||
|
}
|
||||||
|
SpiBusSelect::SpiBPortB => {
|
||||||
|
let (sck, mosi, miso) = (pinsb.pb5, pinsb.pb4, pinsb.pb3);
|
||||||
|
let mut spib = Spi::new(dp.spib, (sck, miso, mosi), spi_cfg).unwrap();
|
||||||
|
spib.set_fill_word(FILL_WORD);
|
||||||
|
spib
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Configure transfer specific properties here
|
||||||
|
match SPI_BUS_SEL {
|
||||||
|
SpiBusSelect::SpiAPortA | SpiBusSelect::SpiAPortB => {
|
||||||
|
let transfer_cfg = TransferConfig {
|
||||||
|
clk_cfg: Some(spi_clk_cfg),
|
||||||
|
mode: Some(SPI_MODE),
|
||||||
|
sod: true,
|
||||||
|
blockmode: BLOCKMODE,
|
||||||
|
bmstall: true,
|
||||||
|
hw_cs: None,
|
||||||
|
};
|
||||||
|
spi.cfg_transfer(&transfer_cfg);
|
||||||
|
}
|
||||||
|
SpiBusSelect::SpiBPortB => {
|
||||||
|
let hw_cs_pin = configure_pin_as_hw_cs_pin(pinsb.pb2);
|
||||||
|
let transfer_cfg = TransferConfig {
|
||||||
|
clk_cfg: Some(spi_clk_cfg),
|
||||||
|
mode: Some(SPI_MODE),
|
||||||
|
sod: false,
|
||||||
|
blockmode: BLOCKMODE,
|
||||||
|
bmstall: true,
|
||||||
|
hw_cs: Some(hw_cs_pin),
|
||||||
|
};
|
||||||
|
spi.cfg_transfer(&transfer_cfg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Application logic
|
||||||
|
loop {
|
||||||
|
let mut reply_buf: [u8; 8] = [0; 8];
|
||||||
|
// Can't really verify correct reply here.
|
||||||
|
spi.write(&[0x42]).expect("write failed");
|
||||||
|
// Because of the loopback mode, we should get back the fill word here.
|
||||||
|
spi.read(&mut reply_buf[0..1]).unwrap();
|
||||||
|
assert_eq!(reply_buf[0], FILL_WORD);
|
||||||
|
delay.delay_ms(500_u32);
|
||||||
|
|
||||||
|
let tx_buf: [u8; 3] = [0x01, 0x02, 0x03];
|
||||||
|
spi.transfer(&mut reply_buf[0..3], &tx_buf).unwrap();
|
||||||
|
assert_eq!(tx_buf, reply_buf[0..3]);
|
||||||
|
defmt::info!(
|
||||||
|
"Received reply: {}, {}, {}",
|
||||||
|
reply_buf[0],
|
||||||
|
reply_buf[1],
|
||||||
|
reply_buf[2]
|
||||||
|
);
|
||||||
|
delay.delay_ms(500_u32);
|
||||||
|
|
||||||
|
let mut tx_rx_buf: [u8; 3] = [0x03, 0x02, 0x01];
|
||||||
|
spi.transfer_in_place(&mut tx_rx_buf).unwrap();
|
||||||
|
defmt::info!(
|
||||||
|
"Received reply: {}, {}, {}",
|
||||||
|
tx_rx_buf[0],
|
||||||
|
tx_rx_buf[1],
|
||||||
|
tx_rx_buf[2]
|
||||||
|
);
|
||||||
|
assert_eq!(&tx_rx_buf[0..3], &[0x03, 0x02, 0x01]);
|
||||||
|
}
|
||||||
|
}
|
||||||
106
va108xx/examples/simple/examples/timer-ticks.rs
Normal file
106
va108xx/examples/simple/examples/timer-ticks.rs
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
//! MS and Second counter implemented using the TIM0 and TIM1 peripheral
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use embedded_hal::delay::DelayNs;
|
||||||
|
// Import panic provider.
|
||||||
|
use panic_probe as _;
|
||||||
|
// Import logger.
|
||||||
|
use defmt_rtt as _;
|
||||||
|
use portable_atomic::AtomicU32;
|
||||||
|
use va108xx_hal::{
|
||||||
|
pac::{self, interrupt},
|
||||||
|
prelude::*,
|
||||||
|
time::Hertz,
|
||||||
|
timer::{CountdownTimer, InterruptConfig},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
enum LibType {
|
||||||
|
Pac,
|
||||||
|
Hal,
|
||||||
|
}
|
||||||
|
|
||||||
|
static MS_COUNTER: AtomicU32 = AtomicU32::new(0);
|
||||||
|
static SEC_COUNTER: AtomicU32 = AtomicU32::new(0);
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
|
let mut delay = CountdownTimer::new(dp.tim2, 50.MHz());
|
||||||
|
let mut last_ms = 0;
|
||||||
|
defmt::info!("-- Vorago system ticks using timers --");
|
||||||
|
let lib_type = LibType::Hal;
|
||||||
|
match lib_type {
|
||||||
|
LibType::Pac => {
|
||||||
|
unsafe {
|
||||||
|
dp.sysconfig
|
||||||
|
.peripheral_clk_enable()
|
||||||
|
.modify(|_, w| w.irqsel().set_bit());
|
||||||
|
dp.sysconfig
|
||||||
|
.tim_clk_enable()
|
||||||
|
.modify(|r, w| w.bits(r.bits() | (1 << 0) | (1 << 1)));
|
||||||
|
dp.irqsel.tim(0).write(|w| w.bits(0x00));
|
||||||
|
dp.irqsel.tim(1).write(|w| w.bits(0x01));
|
||||||
|
}
|
||||||
|
|
||||||
|
let sys_clk: Hertz = 50.MHz();
|
||||||
|
let cnt_ms = sys_clk.raw() / 1000 - 1;
|
||||||
|
let cnt_sec = sys_clk.raw() - 1;
|
||||||
|
unsafe {
|
||||||
|
dp.tim0.cnt_value().write(|w| w.bits(cnt_ms));
|
||||||
|
dp.tim0.rst_value().write(|w| w.bits(cnt_ms));
|
||||||
|
dp.tim0.ctrl().write(|w| {
|
||||||
|
w.enable().set_bit();
|
||||||
|
w.irq_enb().set_bit()
|
||||||
|
});
|
||||||
|
dp.tim1.cnt_value().write(|w| w.bits(cnt_sec));
|
||||||
|
dp.tim1.rst_value().write(|w| w.bits(cnt_sec));
|
||||||
|
dp.tim1.ctrl().write(|w| {
|
||||||
|
w.enable().set_bit();
|
||||||
|
w.irq_enb().set_bit()
|
||||||
|
});
|
||||||
|
unmask_irqs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LibType::Hal => {
|
||||||
|
let mut ms_timer = CountdownTimer::new(dp.tim0, 50.MHz());
|
||||||
|
ms_timer.enable_interrupt(InterruptConfig::new(interrupt::OC0, true, true));
|
||||||
|
ms_timer.start(1.kHz());
|
||||||
|
let mut second_timer = CountdownTimer::new(dp.tim1, 50.MHz());
|
||||||
|
second_timer.enable_interrupt(InterruptConfig::new(interrupt::OC1, true, true));
|
||||||
|
second_timer.start(1.Hz());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loop {
|
||||||
|
let current_ms = MS_COUNTER.load(portable_atomic::Ordering::Relaxed);
|
||||||
|
if current_ms - last_ms >= 1000 {
|
||||||
|
// To prevent drift.
|
||||||
|
last_ms += 1000;
|
||||||
|
defmt::info!("MS counter: {}", current_ms);
|
||||||
|
let second = SEC_COUNTER.load(portable_atomic::Ordering::Relaxed);
|
||||||
|
defmt::info!("Second counter: {}", second);
|
||||||
|
}
|
||||||
|
delay.delay_ms(50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unmask_irqs() {
|
||||||
|
unsafe {
|
||||||
|
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC0);
|
||||||
|
cortex_m::peripheral::NVIC::unmask(pac::Interrupt::OC1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn OC0() {
|
||||||
|
MS_COUNTER.fetch_add(1, portable_atomic::Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn OC1() {
|
||||||
|
SEC_COUNTER.fetch_add(1, portable_atomic::Ordering::Relaxed);
|
||||||
|
}
|
||||||
46
va108xx/examples/simple/examples/uart.rs
Normal file
46
va108xx/examples/simple/examples/uart.rs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
//! UART example application. Sends a test string over a UART and then enters
|
||||||
|
//! echo mode.
|
||||||
|
//!
|
||||||
|
//! Instructions:
|
||||||
|
//!
|
||||||
|
//! 1. Tie a USB to UART converter with RX to PA9 and TX to PA8.
|
||||||
|
//! 2. Connect to the serial interface by using an application like Putty or picocom.
|
||||||
|
//! You should set a "Hello World" print when the application starts. After that, everything
|
||||||
|
//! typed on the console should be printed back by the echo application.
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use embedded_hal_nb::{nb, serial::Read};
|
||||||
|
use embedded_io::Write as _;
|
||||||
|
// Import panic provider.
|
||||||
|
use panic_probe as _;
|
||||||
|
// Import logger.
|
||||||
|
use defmt_rtt as _;
|
||||||
|
use va108xx_hal::{pac, pins::PinsA, prelude::*, uart};
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
defmt::println!("-- VA108xx UART example application--");
|
||||||
|
|
||||||
|
let dp = pac::Peripherals::take().unwrap();
|
||||||
|
|
||||||
|
let gpioa = PinsA::new(dp.porta);
|
||||||
|
let tx = gpioa.pa9;
|
||||||
|
let rx = gpioa.pa8;
|
||||||
|
let uart =
|
||||||
|
uart::Uart::new_without_interrupt(dp.uarta, tx, rx, 50.MHz(), 115200.Hz().into()).unwrap();
|
||||||
|
|
||||||
|
let (mut tx, mut rx) = uart.split();
|
||||||
|
writeln!(tx, "Hello World\r").unwrap();
|
||||||
|
loop {
|
||||||
|
// Echo what is received on the serial link.
|
||||||
|
match rx.read() {
|
||||||
|
Ok(recv) => {
|
||||||
|
nb::block!(embedded_hal_nb::serial::Write::write(&mut tx, recv))
|
||||||
|
.expect("TX send error");
|
||||||
|
}
|
||||||
|
Err(nb::Error::WouldBlock) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
va108xx/examples/simple/src/main.rs
Normal file
20
va108xx/examples/simple/src/main.rs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
//! Dummy app which does not do anything.
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use va108xx_hal as _;
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
loop {
|
||||||
|
cortex_m::asm::nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||||
|
loop {
|
||||||
|
cortex_m::asm::nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
1
va108xx/flashloader/.gitignore
vendored
Normal file
1
va108xx/flashloader/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/venv
|
||||||
35
va108xx/flashloader/Cargo.toml
Normal file
35
va108xx/flashloader/Cargo.toml
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
[package]
|
||||||
|
name = "flashloader"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = { version = "0.7", features = ["critical-section-single-core"]}
|
||||||
|
cortex-m-rt = "0.7"
|
||||||
|
embedded-io = "0.6"
|
||||||
|
defmt = "1"
|
||||||
|
defmt-rtt = { version = "1" }
|
||||||
|
panic-probe = { version = "1", features = ["print-defmt"] }
|
||||||
|
num_enum = { version = "0.7", default-features = false }
|
||||||
|
cobs = { version = "0.4", default-features = false }
|
||||||
|
satrs = { version = "0.3.0-alpha.1", default-features = false }
|
||||||
|
ringbuf = { version = "0.4.7", default-features = false, features = ["portable-atomic"] }
|
||||||
|
spacepackets = { version = "0.15", default-features = false, features = ["defmt"] }
|
||||||
|
# Even though we do not use this directly, we need to activate this feature explicitely
|
||||||
|
# so that RTIC compiles because thumv6 does not have CAS operations natively.
|
||||||
|
portable-atomic = {version = "1", features = ["unsafe-assume-single-core"]}
|
||||||
|
|
||||||
|
rtic = { version = "2", features = ["thumbv6-backend"] }
|
||||||
|
rtic-monotonics = { version = "2", features = ["cortex-m-systick"] }
|
||||||
|
|
||||||
|
[dependencies.va108xx-hal]
|
||||||
|
version = "0.12"
|
||||||
|
path = "../va108xx-hal"
|
||||||
|
features = ["defmt"]
|
||||||
|
|
||||||
|
[dependencies.vorago-reb1]
|
||||||
|
version = "0.9"
|
||||||
|
path = "../vorago-reb1"
|
||||||
|
|
||||||
|
[package.metadata.cargo-machete]
|
||||||
|
ignored = ["portable-atomic", "cortex-m-rt"]
|
||||||
75
va108xx/flashloader/README.md
Normal file
75
va108xx/flashloader/README.md
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
VA108xx Flashloader Application
|
||||||
|
========
|
||||||
|
|
||||||
|
This flashloader shows a minimal example for a self-updatable Rust software which exposes
|
||||||
|
a simple PUS (CCSDS) interface to update the software. It also provides a Python application
|
||||||
|
called the `image-loader.py` which can be used to upload compiled images to the flashloader
|
||||||
|
application to write them to the NVM.
|
||||||
|
|
||||||
|
Please note that the both the application and the image loader are tailored towards usage
|
||||||
|
with the [bootloader provided by this repository](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/bootloader).
|
||||||
|
|
||||||
|
The software can quickly be adapted to interface with a real primary on-board software instead of
|
||||||
|
the Python script provided here to upload images because it uses a low-level CCSDS based packet
|
||||||
|
interface.
|
||||||
|
|
||||||
|
## Using the Python image loader
|
||||||
|
|
||||||
|
The Python image loader communicates with the Rust flashload application using a dedicated serial
|
||||||
|
port with a baudrate of 115200.
|
||||||
|
|
||||||
|
It is recommended to run the script in a dedicated virtual environment. For example, on UNIX
|
||||||
|
systems you can use `python3 -m venv venv` and then `source venv/bin/activate` to create
|
||||||
|
and activate a virtual environment.
|
||||||
|
|
||||||
|
After that, you can use
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
to install all required dependencies.
|
||||||
|
|
||||||
|
After that, it is recommended to use `./image-load.py -h` to get an overview of some options.
|
||||||
|
The flash loader uses the UART0 with the Pins PA8 (RX) and PA9 (TX) interface of the VA108xx to perform CCSDS based
|
||||||
|
communication. The Python image loader application will search for a file named `loader.toml` and
|
||||||
|
use the `serial_port` key to determine the serial port to use for serial communication.
|
||||||
|
|
||||||
|
### Examples
|
||||||
|
|
||||||
|
You can use
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./image-loader.py -p
|
||||||
|
```
|
||||||
|
|
||||||
|
to send a ping an verify the connection.
|
||||||
|
|
||||||
|
You can use
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd flashloader/slot-a-blinky
|
||||||
|
cargo build --release
|
||||||
|
cd ../..
|
||||||
|
./image-loader.py -t a ./slot-a-blinky/target/thumbv6m-none-eabi/release/slot-a-blinky
|
||||||
|
```
|
||||||
|
|
||||||
|
to build the slot A sample application and upload it to a running flash loader application
|
||||||
|
to write it to slot A.
|
||||||
|
|
||||||
|
You can use
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./image-loader.py -s a
|
||||||
|
```
|
||||||
|
|
||||||
|
to select the Slot A as a boot slot. The boot slot is stored in a reserved section in EEPROM
|
||||||
|
and will be read and used by the bootloader to determine which slot to boot.
|
||||||
|
|
||||||
|
You can use
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./image-loader.py -c -t a
|
||||||
|
```
|
||||||
|
|
||||||
|
to corrupt the image A and test that it switches to image B after a failed CRC check instead.
|
||||||
476
va108xx/flashloader/image-loader.py
Executable file
476
va108xx/flashloader/image-loader.py
Executable file
@@ -0,0 +1,476 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
from typing import List, Tuple
|
||||||
|
from spacepackets.ecss.defs import PusService
|
||||||
|
from spacepackets.ecss.tm import PusTm
|
||||||
|
import toml
|
||||||
|
import struct
|
||||||
|
import logging
|
||||||
|
import argparse
|
||||||
|
import time
|
||||||
|
import enum
|
||||||
|
from com_interface import ComInterface
|
||||||
|
from com_interface.serial_base import SerialCfg
|
||||||
|
from com_interface.serial_cobs import SerialCobsComIF
|
||||||
|
from crcmod.predefined import PredefinedCrc
|
||||||
|
from spacepackets.ecss.tc import PusTc
|
||||||
|
from spacepackets.ecss.pus_verificator import PusVerificator, StatusField
|
||||||
|
from spacepackets.ecss.pus_1_verification import Service1Tm, UnpackParams
|
||||||
|
from spacepackets.seqcount import SeqCountProvider
|
||||||
|
from pathlib import Path
|
||||||
|
import dataclasses
|
||||||
|
from elftools.elf.elffile import ELFFile
|
||||||
|
|
||||||
|
|
||||||
|
BAUD_RATE = 115200
|
||||||
|
|
||||||
|
BOOTLOADER_START_ADDR = 0x0
|
||||||
|
BOOTLOADER_END_ADDR = 0x3000
|
||||||
|
BOOTLOADER_CRC_ADDR = BOOTLOADER_END_ADDR - 2
|
||||||
|
BOOTLOADER_MAX_SIZE = BOOTLOADER_END_ADDR - BOOTLOADER_START_ADDR - 2
|
||||||
|
|
||||||
|
APP_A_START_ADDR = 0x3000
|
||||||
|
APP_B_END_ADDR = 0x20000 - 8
|
||||||
|
IMG_SLOT_SIZE = (APP_B_END_ADDR - APP_A_START_ADDR) // 2
|
||||||
|
|
||||||
|
APP_A_END_ADDR = APP_A_START_ADDR + IMG_SLOT_SIZE
|
||||||
|
# The actual size of the image which is relevant for CRC calculation.
|
||||||
|
APP_A_SIZE_ADDR = APP_A_END_ADDR - 8
|
||||||
|
APP_A_CRC_ADDR = APP_A_END_ADDR - 4
|
||||||
|
APP_A_MAX_SIZE = APP_A_END_ADDR - APP_A_START_ADDR - 8
|
||||||
|
|
||||||
|
APP_B_START_ADDR = APP_A_END_ADDR
|
||||||
|
# The actual size of the image which is relevant for CRC calculation.
|
||||||
|
APP_B_SIZE_ADDR = APP_B_END_ADDR - 8
|
||||||
|
APP_B_CRC_ADDR = APP_B_END_ADDR - 4
|
||||||
|
APP_B_MAX_SIZE = APP_A_END_ADDR - APP_A_START_ADDR - 8
|
||||||
|
|
||||||
|
|
||||||
|
CHUNK_SIZE = 400
|
||||||
|
|
||||||
|
MEMORY_SERVICE = 6
|
||||||
|
ACTION_SERVICE = 8
|
||||||
|
|
||||||
|
RAW_MEMORY_WRITE_SUBSERVICE = 2
|
||||||
|
BOOT_NVM_MEMORY_ID = 1
|
||||||
|
PING_PAYLOAD_SIZE = 0
|
||||||
|
|
||||||
|
|
||||||
|
class ActionId(enum.IntEnum):
|
||||||
|
CORRUPT_APP_A = 128
|
||||||
|
CORRUPT_APP_B = 129
|
||||||
|
SET_BOOT_SLOT = 130
|
||||||
|
|
||||||
|
|
||||||
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
SEQ_PROVIDER = SeqCountProvider(bit_width=14)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclasses.dataclass
|
||||||
|
class LoadableSegment:
|
||||||
|
name: str
|
||||||
|
offset: int
|
||||||
|
size: int
|
||||||
|
data: bytes
|
||||||
|
|
||||||
|
|
||||||
|
class Target(enum.Enum):
|
||||||
|
BOOTLOADER = 0
|
||||||
|
APP_A = 1
|
||||||
|
APP_B = 2
|
||||||
|
|
||||||
|
|
||||||
|
class AppSel(enum.IntEnum):
|
||||||
|
APP_A = 0
|
||||||
|
APP_B = 1
|
||||||
|
|
||||||
|
|
||||||
|
class ImageLoader:
|
||||||
|
def __init__(self, com_if: ComInterface, verificator: PusVerificator) -> None:
|
||||||
|
self.com_if = com_if
|
||||||
|
self.verificator = verificator
|
||||||
|
|
||||||
|
def handle_boot_sel_cmd(self, target: AppSel):
|
||||||
|
_LOGGER.info("Sending ping command")
|
||||||
|
action_tc = PusTc(
|
||||||
|
apid=0x00,
|
||||||
|
service=PusService.S8_FUNC_CMD,
|
||||||
|
subservice=ActionId.SET_BOOT_SLOT,
|
||||||
|
seq_count=SEQ_PROVIDER.get_and_increment(),
|
||||||
|
app_data=bytes([target]),
|
||||||
|
)
|
||||||
|
self.verificator.add_tc(action_tc)
|
||||||
|
self.com_if.send(bytes(action_tc.pack()))
|
||||||
|
self.await_for_command_copletion("boot image selection command")
|
||||||
|
|
||||||
|
def handle_ping_cmd(self):
|
||||||
|
_LOGGER.info("Sending ping command")
|
||||||
|
ping_tc = PusTc(
|
||||||
|
apid=0x00,
|
||||||
|
service=PusService.S17_TEST,
|
||||||
|
subservice=1,
|
||||||
|
seq_count=SEQ_PROVIDER.get_and_increment(),
|
||||||
|
app_data=bytes(PING_PAYLOAD_SIZE),
|
||||||
|
)
|
||||||
|
self.verificator.add_tc(ping_tc)
|
||||||
|
self.com_if.send(bytes(ping_tc.pack()))
|
||||||
|
self.await_for_command_copletion("ping command")
|
||||||
|
|
||||||
|
def await_for_command_copletion(self, context: str):
|
||||||
|
done = False
|
||||||
|
now = time.time()
|
||||||
|
while time.time() - now < 2.0:
|
||||||
|
if not self.com_if.data_available():
|
||||||
|
time.sleep(0.2)
|
||||||
|
continue
|
||||||
|
for reply in self.com_if.receive():
|
||||||
|
result = self.verificator.add_tm(
|
||||||
|
Service1Tm.from_tm(PusTm.unpack(reply, 0), UnpackParams(0))
|
||||||
|
)
|
||||||
|
if result is not None and result.completed:
|
||||||
|
_LOGGER.info(f"received {context} reply")
|
||||||
|
done = True
|
||||||
|
if done:
|
||||||
|
break
|
||||||
|
if not done:
|
||||||
|
_LOGGER.warning(f"no {context} reply received")
|
||||||
|
|
||||||
|
def handle_corruption_cmd(self, target: Target):
|
||||||
|
if target == Target.BOOTLOADER:
|
||||||
|
_LOGGER.error("can not corrupt bootloader")
|
||||||
|
if target == Target.APP_A:
|
||||||
|
self.send_tc(
|
||||||
|
PusTc(
|
||||||
|
apid=0,
|
||||||
|
service=ACTION_SERVICE,
|
||||||
|
subservice=ActionId.CORRUPT_APP_A,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if target == Target.APP_B:
|
||||||
|
self.send_tc(
|
||||||
|
PusTc(
|
||||||
|
apid=0,
|
||||||
|
service=ACTION_SERVICE,
|
||||||
|
subservice=ActionId.CORRUPT_APP_B,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
def handle_flash_cmd(self, target: Target, file_path: Path) -> int:
|
||||||
|
loadable_segments = []
|
||||||
|
_LOGGER.info("Parsing ELF file for loadable sections")
|
||||||
|
total_size = 0
|
||||||
|
loadable_segments, total_size = create_loadable_segments(target, file_path)
|
||||||
|
check_segments(target, total_size)
|
||||||
|
print_segments_info(target, loadable_segments, total_size, file_path)
|
||||||
|
result = self._perform_flashing_algorithm(loadable_segments)
|
||||||
|
if result != 0:
|
||||||
|
return result
|
||||||
|
self._crc_and_app_size_postprocessing(target, total_size, loadable_segments)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def _perform_flashing_algorithm(
|
||||||
|
self,
|
||||||
|
loadable_segments: List[LoadableSegment],
|
||||||
|
) -> int:
|
||||||
|
# Perform the flashing algorithm.
|
||||||
|
for segment in loadable_segments:
|
||||||
|
segment_end = segment.offset + segment.size
|
||||||
|
current_addr = segment.offset
|
||||||
|
pos_in_segment = 0
|
||||||
|
while pos_in_segment < segment.size:
|
||||||
|
next_chunk_size = min(segment_end - current_addr, CHUNK_SIZE)
|
||||||
|
data = segment.data[pos_in_segment : pos_in_segment + next_chunk_size]
|
||||||
|
next_packet = pack_memory_write_command(current_addr, data)
|
||||||
|
_LOGGER.info(
|
||||||
|
f"Sending memory write command for address {current_addr:#08x} and data with "
|
||||||
|
f"length {len(data)}"
|
||||||
|
)
|
||||||
|
self.verificator.add_tc(next_packet)
|
||||||
|
self.com_if.send(bytes(next_packet.pack()))
|
||||||
|
current_addr += next_chunk_size
|
||||||
|
pos_in_segment += next_chunk_size
|
||||||
|
start_time = time.time()
|
||||||
|
while True:
|
||||||
|
if time.time() - start_time > 1.0:
|
||||||
|
_LOGGER.error("Timeout while waiting for reply")
|
||||||
|
return -1
|
||||||
|
data_available = self.com_if.data_available(0.1)
|
||||||
|
done = False
|
||||||
|
if not data_available:
|
||||||
|
continue
|
||||||
|
replies = self.com_if.receive()
|
||||||
|
for reply in replies:
|
||||||
|
tm = PusTm.unpack(reply, 0)
|
||||||
|
if tm.service != 1:
|
||||||
|
continue
|
||||||
|
service_1_tm = Service1Tm.from_tm(tm, UnpackParams(0))
|
||||||
|
check_result = self.verificator.add_tm(service_1_tm)
|
||||||
|
# We could send after we have received the step reply, but that can
|
||||||
|
# somehow lead to overrun errors. I think it's okay to do it like
|
||||||
|
# this as long as the flash loader only uses polling..
|
||||||
|
if (
|
||||||
|
check_result is not None
|
||||||
|
and check_result.status.completed == StatusField.SUCCESS
|
||||||
|
):
|
||||||
|
done = True
|
||||||
|
|
||||||
|
# This is an optimized variant, but I think the small delay is not an issue.
|
||||||
|
"""
|
||||||
|
if (
|
||||||
|
check_result is not None
|
||||||
|
and check_result.status.step == StatusField.SUCCESS
|
||||||
|
and len(check_result.status.step_list) == 1
|
||||||
|
):
|
||||||
|
done = True
|
||||||
|
"""
|
||||||
|
self.verificator.remove_completed_entries()
|
||||||
|
if done:
|
||||||
|
break
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def _crc_and_app_size_postprocessing(
|
||||||
|
self,
|
||||||
|
target: Target,
|
||||||
|
total_size: int,
|
||||||
|
loadable_segments: List[LoadableSegment],
|
||||||
|
):
|
||||||
|
if target == Target.BOOTLOADER:
|
||||||
|
_LOGGER.info("Blanking the bootloader checksum")
|
||||||
|
# Blank the checksum. For the bootloader, the bootloader will calculate the
|
||||||
|
# checksum itself on the initial run.
|
||||||
|
checksum_write_packet = pack_memory_write_command(
|
||||||
|
BOOTLOADER_CRC_ADDR, bytes([0x00, 0x00])
|
||||||
|
)
|
||||||
|
self.send_tc(checksum_write_packet)
|
||||||
|
else:
|
||||||
|
crc_addr = None
|
||||||
|
size_addr = None
|
||||||
|
if target == Target.APP_A:
|
||||||
|
crc_addr = APP_A_CRC_ADDR
|
||||||
|
size_addr = APP_A_SIZE_ADDR
|
||||||
|
elif target == Target.APP_B:
|
||||||
|
crc_addr = APP_B_CRC_ADDR
|
||||||
|
size_addr = APP_B_SIZE_ADDR
|
||||||
|
assert crc_addr is not None
|
||||||
|
assert size_addr is not None
|
||||||
|
_LOGGER.info(f"Writing app size {total_size} at address {size_addr:#08x}")
|
||||||
|
size_write_packet = pack_memory_write_command(
|
||||||
|
size_addr, struct.pack("!I", total_size)
|
||||||
|
)
|
||||||
|
self.com_if.send(bytes(size_write_packet.pack()))
|
||||||
|
time.sleep(0.2)
|
||||||
|
crc_calc = PredefinedCrc("crc-ccitt-false")
|
||||||
|
for segment in loadable_segments:
|
||||||
|
crc_calc.update(segment.data)
|
||||||
|
checksum = crc_calc.digest()
|
||||||
|
_LOGGER.info(
|
||||||
|
f"Writing checksum 0x[{checksum.hex(sep=',')}] at address {crc_addr:#08x}"
|
||||||
|
)
|
||||||
|
self.send_tc(pack_memory_write_command(crc_addr, checksum))
|
||||||
|
|
||||||
|
def send_tc(self, tc: PusTc):
|
||||||
|
self.com_if.send(bytes(tc.pack()))
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
print("Python VA108XX Image Loader Application")
|
||||||
|
logging.basicConfig(
|
||||||
|
format="[%(asctime)s] [%(levelname)s] %(message)s", level=logging.DEBUG
|
||||||
|
)
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
prog="image-loader", description="Python VA416XX Image Loader Application"
|
||||||
|
)
|
||||||
|
parser.add_argument("-p", "--ping", action="store_true", help="Send ping command")
|
||||||
|
parser.add_argument(
|
||||||
|
"-s", "--sel", choices=["a", "b"], help="Set boot slot (Slot A or B)"
|
||||||
|
)
|
||||||
|
parser.add_argument("-c", "--corrupt", action="store_true", help="Corrupt a target")
|
||||||
|
parser.add_argument(
|
||||||
|
"-t",
|
||||||
|
"--target",
|
||||||
|
choices=["bl", "a", "b"],
|
||||||
|
help="Target (Bootloader or slot A or B)",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"path", nargs="?", default=None, help="Path to the App to flash"
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
serial_port = None
|
||||||
|
if Path("loader.toml").exists():
|
||||||
|
with open("loader.toml", "r") as toml_file:
|
||||||
|
parsed_toml = toml.loads(toml_file.read())
|
||||||
|
if "serial_port" in parsed_toml:
|
||||||
|
serial_port = parsed_toml["serial_port"]
|
||||||
|
if serial_port is None:
|
||||||
|
serial_port = input("Please specify the serial port manually: ")
|
||||||
|
serial_cfg = SerialCfg(
|
||||||
|
com_if_id="ser_cobs",
|
||||||
|
serial_port=serial_port,
|
||||||
|
baud_rate=BAUD_RATE,
|
||||||
|
polling_frequency=0.1,
|
||||||
|
)
|
||||||
|
verificator = PusVerificator()
|
||||||
|
com_if = SerialCobsComIF(serial_cfg)
|
||||||
|
com_if.open()
|
||||||
|
target = None
|
||||||
|
if args.target == "bl":
|
||||||
|
target = Target.BOOTLOADER
|
||||||
|
elif args.target == "a":
|
||||||
|
target = Target.APP_A
|
||||||
|
elif args.target == "b":
|
||||||
|
target = Target.APP_B
|
||||||
|
|
||||||
|
boot_sel = None
|
||||||
|
if args.sel:
|
||||||
|
if args.sel == "a":
|
||||||
|
boot_sel = AppSel.APP_A
|
||||||
|
elif args.sel == "b":
|
||||||
|
boot_sel = AppSel.APP_B
|
||||||
|
|
||||||
|
image_loader = ImageLoader(com_if, verificator)
|
||||||
|
file_path = None
|
||||||
|
result = -1
|
||||||
|
if args.ping:
|
||||||
|
image_loader.handle_ping_cmd()
|
||||||
|
com_if.close()
|
||||||
|
return 0
|
||||||
|
if args.sel and boot_sel is not None:
|
||||||
|
image_loader.handle_boot_sel_cmd(boot_sel)
|
||||||
|
if target:
|
||||||
|
if not args.corrupt:
|
||||||
|
if not args.path:
|
||||||
|
_LOGGER.error("App Path needs to be specified for the flash process")
|
||||||
|
file_path = Path(args.path)
|
||||||
|
if not file_path.exists():
|
||||||
|
_LOGGER.error("File does not exist")
|
||||||
|
if args.corrupt:
|
||||||
|
if not target:
|
||||||
|
_LOGGER.error("target for corruption command required")
|
||||||
|
com_if.close()
|
||||||
|
return -1
|
||||||
|
image_loader.handle_corruption_cmd(target)
|
||||||
|
else:
|
||||||
|
if file_path is not None:
|
||||||
|
assert target is not None
|
||||||
|
result = image_loader.handle_flash_cmd(target, file_path)
|
||||||
|
|
||||||
|
com_if.close()
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def create_loadable_segments(
|
||||||
|
target: Target, file_path: Path
|
||||||
|
) -> Tuple[List[LoadableSegment], int]:
|
||||||
|
loadable_segments = []
|
||||||
|
total_size = 0
|
||||||
|
with open(file_path, "rb") as app_file:
|
||||||
|
elf_file = ELFFile(app_file)
|
||||||
|
|
||||||
|
for idx, segment in enumerate(elf_file.iter_segments("PT_LOAD")):
|
||||||
|
if segment.header.p_filesz == 0:
|
||||||
|
continue
|
||||||
|
# Basic validity checks of the base addresses.
|
||||||
|
if idx == 0:
|
||||||
|
if (
|
||||||
|
target == Target.BOOTLOADER
|
||||||
|
and segment.header.p_paddr != BOOTLOADER_START_ADDR
|
||||||
|
):
|
||||||
|
raise ValueError(
|
||||||
|
f"detected possibly invalid start address {segment.header.p_paddr:#08x} for "
|
||||||
|
f"bootloader, expected {BOOTLOADER_START_ADDR}"
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
target == Target.APP_A
|
||||||
|
and segment.header.p_paddr != APP_A_START_ADDR
|
||||||
|
):
|
||||||
|
raise ValueError(
|
||||||
|
f"detected possibly invalid start address {segment.header.p_paddr:#08x} for "
|
||||||
|
f"App A, expected {APP_A_START_ADDR}"
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
target == Target.APP_B
|
||||||
|
and segment.header.p_paddr != APP_B_START_ADDR
|
||||||
|
):
|
||||||
|
raise ValueError(
|
||||||
|
f"detected possibly invalid start address {segment.header.p_paddr:#08x} for "
|
||||||
|
f"App B, expected {APP_B_START_ADDR}"
|
||||||
|
)
|
||||||
|
name = None
|
||||||
|
for section in elf_file.iter_sections():
|
||||||
|
if (
|
||||||
|
section.header.sh_offset == segment.header.p_offset
|
||||||
|
and section.header.sh_size > 0
|
||||||
|
):
|
||||||
|
name = section.name
|
||||||
|
if name is None:
|
||||||
|
_LOGGER.warning("no fitting section found for segment")
|
||||||
|
continue
|
||||||
|
# print(f"Segment Addr: {segment.header.p_paddr}")
|
||||||
|
# print(f"Segment Offset: {segment.header.p_offset}")
|
||||||
|
# print(f"Segment Filesize: {segment.header.p_filesz}")
|
||||||
|
loadable_segments.append(
|
||||||
|
LoadableSegment(
|
||||||
|
name=name,
|
||||||
|
offset=segment.header.p_paddr,
|
||||||
|
size=segment.header.p_filesz,
|
||||||
|
data=segment.data(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
total_size += segment.header.p_filesz
|
||||||
|
return loadable_segments, total_size
|
||||||
|
|
||||||
|
|
||||||
|
def check_segments(
|
||||||
|
target: Target,
|
||||||
|
total_size: int,
|
||||||
|
):
|
||||||
|
# Set context string and perform basic sanity checks.
|
||||||
|
if target == Target.BOOTLOADER and total_size > BOOTLOADER_MAX_SIZE:
|
||||||
|
raise ValueError(
|
||||||
|
f"provided bootloader app larger than allowed {total_size} bytes"
|
||||||
|
)
|
||||||
|
elif target == Target.APP_A and total_size > APP_A_MAX_SIZE:
|
||||||
|
raise ValueError(f"provided App A larger than allowed {total_size} bytes")
|
||||||
|
elif target == Target.APP_B and total_size > APP_B_MAX_SIZE:
|
||||||
|
raise ValueError(f"provided App B larger than allowed {total_size} bytes")
|
||||||
|
|
||||||
|
|
||||||
|
def print_segments_info(
|
||||||
|
target: Target,
|
||||||
|
loadable_segments: List[LoadableSegment],
|
||||||
|
total_size: int,
|
||||||
|
file_path: Path,
|
||||||
|
):
|
||||||
|
# Set context string and perform basic sanity checks.
|
||||||
|
if target == Target.BOOTLOADER:
|
||||||
|
context_str = "Bootloader"
|
||||||
|
elif target == Target.APP_A:
|
||||||
|
context_str = "App Slot A"
|
||||||
|
elif target == Target.APP_B:
|
||||||
|
context_str = "App Slot B"
|
||||||
|
_LOGGER.info(f"Flashing {context_str} with image {file_path} (size {total_size})")
|
||||||
|
for idx, segment in enumerate(loadable_segments):
|
||||||
|
_LOGGER.info(
|
||||||
|
f"Loadable section {idx} {segment.name} with offset {segment.offset:#08x} and "
|
||||||
|
f"size {segment.size}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def pack_memory_write_command(addr: int, data: bytes) -> PusTc:
|
||||||
|
app_data = bytearray()
|
||||||
|
app_data.append(BOOT_NVM_MEMORY_ID)
|
||||||
|
# N parameter is always 1 here.
|
||||||
|
app_data.append(1)
|
||||||
|
app_data.extend(struct.pack("!I", addr))
|
||||||
|
app_data.extend(struct.pack("!I", len(data)))
|
||||||
|
app_data.extend(data)
|
||||||
|
return PusTc(
|
||||||
|
apid=0,
|
||||||
|
service=MEMORY_SERVICE,
|
||||||
|
subservice=RAW_MEMORY_WRITE_SUBSERVICE,
|
||||||
|
seq_count=SEQ_PROVIDER.get_and_increment(),
|
||||||
|
app_data=bytes(app_data),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
1
va108xx/flashloader/loader.toml
Normal file
1
va108xx/flashloader/loader.toml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
serial_port = "/dev/ttyUSB1"
|
||||||
5
va108xx/flashloader/requirements.txt
Normal file
5
va108xx/flashloader/requirements.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
spacepackets == 0.28
|
||||||
|
com-interface == 0.1
|
||||||
|
toml == 0.10
|
||||||
|
pyelftools == 0.31
|
||||||
|
crcmod == 1.7
|
||||||
2
va108xx/flashloader/slot-a-blinky/.gitignore
vendored
Normal file
2
va108xx/flashloader/slot-a-blinky/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
/app.map
|
||||||
41
va108xx/flashloader/slot-a-blinky/Cargo.toml
Normal file
41
va108xx/flashloader/slot-a-blinky/Cargo.toml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
[package]
|
||||||
|
name = "slot-a-blinky"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m-rt = "0.7"
|
||||||
|
panic-rtt-target = { version = "0.1.3" }
|
||||||
|
rtt-target = { version = "0.5" }
|
||||||
|
embedded-hal = "1"
|
||||||
|
va108xx-hal = { version = "0.11" }
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = 2
|
||||||
|
debug-assertions = true # <-
|
||||||
|
incremental = false
|
||||||
|
# This is problematic for stepping..
|
||||||
|
# opt-level = 'z' # <-
|
||||||
|
overflow-checks = true # <-
|
||||||
|
|
||||||
|
# cargo build/run --release
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = 2
|
||||||
|
debug-assertions = false # <-
|
||||||
|
incremental = false
|
||||||
|
lto = 'fat'
|
||||||
|
opt-level = 3 # <-
|
||||||
|
overflow-checks = false # <-
|
||||||
|
|
||||||
|
[profile.small]
|
||||||
|
inherits = "release"
|
||||||
|
codegen-units = 1
|
||||||
|
debug-assertions = false # <-
|
||||||
|
lto = true
|
||||||
|
opt-level = 'z' # <-
|
||||||
|
overflow-checks = false # <-
|
||||||
|
# strip = true # Automatically strip symbols from the binary.
|
||||||
11
va108xx/flashloader/slot-a-blinky/memory.x
Normal file
11
va108xx/flashloader/slot-a-blinky/memory.x
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/* Special linker script for application slot A with an offset at address 0x3000 */
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH : ORIGIN = 0x00003000, LENGTH = 0xE7FC
|
||||||
|
RAM : ORIGIN = 0x10000000, LENGTH = 0x08000 /* 32K */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is where the call stack will be allocated. */
|
||||||
|
/* The stack is of the full descending type. */
|
||||||
|
/* NOTE Do NOT modify `_stack_start` unless you know what you are doing */
|
||||||
|
_stack_start = ORIGIN(RAM) + LENGTH(RAM);
|
||||||
25
va108xx/flashloader/slot-a-blinky/src/main.rs
Normal file
25
va108xx/flashloader/slot-a-blinky/src/main.rs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
//! Simple blinky example using the HAL
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use embedded_hal::delay::DelayNs;
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
|
use va108xx_hal::{gpio::PinsA, pac, prelude::*, timer::CountdownTimer};
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
rtt_init_print!();
|
||||||
|
rprintln!("VA108xx HAL blinky example for App Slot A");
|
||||||
|
|
||||||
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
let mut timer = CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim0);
|
||||||
|
let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
|
||||||
|
let mut led1 = porta.pa10.into_readable_push_pull_output();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
led1.toggle();
|
||||||
|
timer.delay_ms(500);
|
||||||
|
}
|
||||||
|
}
|
||||||
2
va108xx/flashloader/slot-b-blinky/.gitignore
vendored
Normal file
2
va108xx/flashloader/slot-b-blinky/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
/app.map
|
||||||
41
va108xx/flashloader/slot-b-blinky/Cargo.toml
Normal file
41
va108xx/flashloader/slot-b-blinky/Cargo.toml
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
[package]
|
||||||
|
name = "slot-b-blinky"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[workspace]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m-rt = "0.7"
|
||||||
|
panic-rtt-target = { version = "0.1.3" }
|
||||||
|
rtt-target = { version = "0.5" }
|
||||||
|
embedded-hal = "1"
|
||||||
|
va108xx-hal = { version = "0.11" }
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = 2
|
||||||
|
debug-assertions = true # <-
|
||||||
|
incremental = false
|
||||||
|
# This is problematic for stepping..
|
||||||
|
# opt-level = 'z' # <-
|
||||||
|
overflow-checks = true # <-
|
||||||
|
|
||||||
|
# cargo build/run --release
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
debug = 2
|
||||||
|
debug-assertions = false # <-
|
||||||
|
incremental = false
|
||||||
|
lto = 'fat'
|
||||||
|
opt-level = 3 # <-
|
||||||
|
overflow-checks = false # <-
|
||||||
|
|
||||||
|
[profile.small]
|
||||||
|
inherits = "release"
|
||||||
|
codegen-units = 1
|
||||||
|
debug-assertions = false # <-
|
||||||
|
lto = true
|
||||||
|
opt-level = 'z' # <-
|
||||||
|
overflow-checks = false # <-
|
||||||
|
# strip = true # Automatically strip symbols from the binary.
|
||||||
11
va108xx/flashloader/slot-b-blinky/memory.x
Normal file
11
va108xx/flashloader/slot-b-blinky/memory.x
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/* Special linker script for application slot B */
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH : ORIGIN = 0x000117FC, LENGTH = 0xE7FC
|
||||||
|
RAM : ORIGIN = 0x10000000, LENGTH = 0x08000 /* 32K */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is where the call stack will be allocated. */
|
||||||
|
/* The stack is of the full descending type. */
|
||||||
|
/* NOTE Do NOT modify `_stack_start` unless you know what you are doing */
|
||||||
|
_stack_start = ORIGIN(RAM) + LENGTH(RAM);
|
||||||
25
va108xx/flashloader/slot-b-blinky/src/main.rs
Normal file
25
va108xx/flashloader/slot-b-blinky/src/main.rs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
//! Simple blinky example using the HAL
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use cortex_m_rt::entry;
|
||||||
|
use embedded_hal::delay::DelayNs;
|
||||||
|
use panic_rtt_target as _;
|
||||||
|
use rtt_target::{rprintln, rtt_init_print};
|
||||||
|
use va108xx_hal::{gpio::PinsA, pac, prelude::*, timer::CountdownTimer};
|
||||||
|
|
||||||
|
#[entry]
|
||||||
|
fn main() -> ! {
|
||||||
|
rtt_init_print!();
|
||||||
|
rprintln!("VA108xx HAL blinky example for App Slot B");
|
||||||
|
|
||||||
|
let mut dp = pac::Peripherals::take().unwrap();
|
||||||
|
let mut timer = CountdownTimer::new(&mut dp.sysconfig, 50.MHz(), dp.tim0);
|
||||||
|
let porta = PinsA::new(&mut dp.sysconfig, dp.porta);
|
||||||
|
let mut led2 = porta.pa7.into_readable_push_pull_output();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
led2.toggle();
|
||||||
|
timer.delay_ms(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
465
va108xx/flashloader/src/main.rs
Normal file
465
va108xx/flashloader/src/main.rs
Normal file
@@ -0,0 +1,465 @@
|
|||||||
|
//! Vorago flashloader which can be used to flash image A and image B via a simple
|
||||||
|
//! low-level CCSDS memory interface via a UART interface.
|
||||||
|
#![no_main]
|
||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use defmt_rtt as _; // global logger
|
||||||
|
use num_enum::TryFromPrimitive;
|
||||||
|
use panic_probe as _;
|
||||||
|
use ringbuf::{
|
||||||
|
traits::{Consumer, Observer, Producer},
|
||||||
|
StaticRb,
|
||||||
|
};
|
||||||
|
use va108xx_hal::prelude::*;
|
||||||
|
|
||||||
|
const SYSCLK_FREQ: Hertz = Hertz::from_raw(50_000_000);
|
||||||
|
|
||||||
|
const MAX_TC_SIZE: usize = 524;
|
||||||
|
const MAX_TC_FRAME_SIZE: usize = cobs::max_encoding_length(MAX_TC_SIZE);
|
||||||
|
|
||||||
|
const MAX_TM_SIZE: usize = 128;
|
||||||
|
const MAX_TM_FRAME_SIZE: usize = cobs::max_encoding_length(MAX_TM_SIZE);
|
||||||
|
|
||||||
|
const UART_BAUDRATE: u32 = 115200;
|
||||||
|
const BOOT_NVM_MEMORY_ID: u8 = 1;
|
||||||
|
const RX_DEBUGGING: bool = false;
|
||||||
|
|
||||||
|
pub enum ActionId {
|
||||||
|
CorruptImageA = 128,
|
||||||
|
CorruptImageB = 129,
|
||||||
|
SetBootSlot = 130,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, defmt::Format)]
|
||||||
|
#[repr(u8)]
|
||||||
|
enum AppSel {
|
||||||
|
A = 0,
|
||||||
|
B = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Larger buffer for TC to be able to hold the possibly large memory write packets.
|
||||||
|
const BUF_RB_SIZE_TC: usize = 1024;
|
||||||
|
const SIZES_RB_SIZE_TC: usize = 16;
|
||||||
|
|
||||||
|
const BUF_RB_SIZE_TM: usize = 256;
|
||||||
|
const SIZES_RB_SIZE_TM: usize = 16;
|
||||||
|
|
||||||
|
pub struct RingBufWrapper<const BUF_SIZE: usize, const SIZES_LEN: usize> {
|
||||||
|
pub buf: StaticRb<u8, BUF_SIZE>,
|
||||||
|
pub sizes: StaticRb<usize, SIZES_LEN>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const APP_A_START_ADDR: u32 = 0x3000;
|
||||||
|
pub const APP_A_END_ADDR: u32 = 0x117FC;
|
||||||
|
pub const APP_B_START_ADDR: u32 = APP_A_END_ADDR;
|
||||||
|
pub const APP_B_END_ADDR: u32 = 0x20000;
|
||||||
|
|
||||||
|
pub const PREFERRED_SLOT_OFFSET: u32 = 0x20000 - 1;
|
||||||
|
|
||||||
|
#[rtic::app(device = pac, dispatchers = [OC20, OC21, OC22])]
|
||||||
|
mod app {
|
||||||
|
use super::*;
|
||||||
|
use cortex_m::asm;
|
||||||
|
use embedded_io::Write;
|
||||||
|
use rtic::Mutex;
|
||||||
|
use rtic_monotonics::systick::prelude::*;
|
||||||
|
use satrs::pus::verification::{FailParams, VerificationReportCreator};
|
||||||
|
use spacepackets::ecss::PusServiceId;
|
||||||
|
use spacepackets::ecss::{
|
||||||
|
tc::PusTcReader, tm::PusTmCreator, EcssEnumU8, PusPacket, WritablePusPacket,
|
||||||
|
};
|
||||||
|
use va108xx_hal::pins::PinsA;
|
||||||
|
use va108xx_hal::spi::SpiClockConfig;
|
||||||
|
use va108xx_hal::uart::InterruptContextTimeoutOrMaxSize;
|
||||||
|
use va108xx_hal::{pac, uart, InterruptConfig};
|
||||||
|
use vorago_reb1::m95m01::M95M01;
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub enum CobsReaderStates {
|
||||||
|
#[default]
|
||||||
|
WaitingForStart,
|
||||||
|
WatingForEnd,
|
||||||
|
FrameOverflow,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[local]
|
||||||
|
struct Local {
|
||||||
|
uart_rx: uart::RxWithInterrupt,
|
||||||
|
uart_tx: uart::Tx,
|
||||||
|
rx_context: InterruptContextTimeoutOrMaxSize,
|
||||||
|
verif_reporter: VerificationReportCreator,
|
||||||
|
nvm: M95M01,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[shared]
|
||||||
|
struct Shared {
|
||||||
|
// Having this shared allows multiple tasks to generate telemetry.
|
||||||
|
tm_rb: RingBufWrapper<BUF_RB_SIZE_TM, SIZES_RB_SIZE_TM>,
|
||||||
|
tc_rb: RingBufWrapper<BUF_RB_SIZE_TC, SIZES_RB_SIZE_TC>,
|
||||||
|
}
|
||||||
|
|
||||||
|
rtic_monotonics::systick_monotonic!(Mono, 1000);
|
||||||
|
|
||||||
|
#[init]
|
||||||
|
fn init(cx: init::Context) -> (Shared, Local) {
|
||||||
|
defmt::println!("-- Vorago flashloader --");
|
||||||
|
|
||||||
|
Mono::start(cx.core.SYST, SYSCLK_FREQ.raw());
|
||||||
|
|
||||||
|
let dp = cx.device;
|
||||||
|
let spi_clock_config = SpiClockConfig::new(2, 4);
|
||||||
|
let nvm = M95M01::new(dp.spic, spi_clock_config);
|
||||||
|
|
||||||
|
let gpioa = PinsA::new(dp.porta);
|
||||||
|
let tx = gpioa.pa9;
|
||||||
|
let rx = gpioa.pa8;
|
||||||
|
|
||||||
|
let irq_uart = uart::Uart::new_with_interrupt(
|
||||||
|
dp.uarta,
|
||||||
|
tx,
|
||||||
|
rx,
|
||||||
|
SYSCLK_FREQ,
|
||||||
|
UART_BAUDRATE.Hz().into(),
|
||||||
|
InterruptConfig::new(pac::Interrupt::OC0, true, true),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
let (tx, rx) = irq_uart.split();
|
||||||
|
// Unwrap is okay, we explicitely set the interrupt ID.
|
||||||
|
let mut rx = rx.into_rx_with_irq();
|
||||||
|
|
||||||
|
let verif_reporter = VerificationReportCreator::new(0).unwrap();
|
||||||
|
|
||||||
|
let mut rx_context = InterruptContextTimeoutOrMaxSize::new(MAX_TC_FRAME_SIZE);
|
||||||
|
rx.read_fixed_len_or_timeout_based_using_irq(&mut rx_context)
|
||||||
|
.expect("initiating UART RX failed");
|
||||||
|
pus_tc_handler::spawn().unwrap();
|
||||||
|
pus_tm_tx_handler::spawn().unwrap();
|
||||||
|
(
|
||||||
|
Shared {
|
||||||
|
tc_rb: RingBufWrapper {
|
||||||
|
buf: StaticRb::default(),
|
||||||
|
sizes: StaticRb::default(),
|
||||||
|
},
|
||||||
|
tm_rb: RingBufWrapper {
|
||||||
|
buf: StaticRb::default(),
|
||||||
|
sizes: StaticRb::default(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Local {
|
||||||
|
uart_rx: rx,
|
||||||
|
uart_tx: tx,
|
||||||
|
rx_context,
|
||||||
|
verif_reporter,
|
||||||
|
nvm,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// `shared` cannot be accessed from this context
|
||||||
|
#[idle]
|
||||||
|
fn idle(_cx: idle::Context) -> ! {
|
||||||
|
loop {
|
||||||
|
asm::nop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is the interrupt handler to read all bytes received on the UART0.
|
||||||
|
#[task(
|
||||||
|
binds = OC0,
|
||||||
|
local = [
|
||||||
|
cnt: u32 = 0,
|
||||||
|
rx_buf: [u8; MAX_TC_FRAME_SIZE] = [0; MAX_TC_FRAME_SIZE],
|
||||||
|
rx_context,
|
||||||
|
uart_rx,
|
||||||
|
],
|
||||||
|
shared = [tc_rb]
|
||||||
|
)]
|
||||||
|
fn uart_rx_irq(mut cx: uart_rx_irq::Context) {
|
||||||
|
match cx
|
||||||
|
.local
|
||||||
|
.uart_rx
|
||||||
|
.on_interrupt_max_size_or_timeout_based(cx.local.rx_context, cx.local.rx_buf)
|
||||||
|
{
|
||||||
|
Ok(result) => {
|
||||||
|
if RX_DEBUGGING {
|
||||||
|
defmt::debug!("RX Info: {:?}", cx.local.rx_context);
|
||||||
|
defmt::debug!("RX Result: {:?}", result);
|
||||||
|
}
|
||||||
|
if result.complete() {
|
||||||
|
// Check frame validity (must have COBS format) and decode the frame.
|
||||||
|
// Currently, we expect a full frame or a frame received through a timeout
|
||||||
|
// to be one COBS frame. We could parse for multiple COBS packets in one
|
||||||
|
// frame, but the additional complexity is not necessary here..
|
||||||
|
if cx.local.rx_buf[0] == 0 && cx.local.rx_buf[result.bytes_read - 1] == 0 {
|
||||||
|
let decoded_size =
|
||||||
|
cobs::decode_in_place(&mut cx.local.rx_buf[1..result.bytes_read]);
|
||||||
|
if decoded_size.is_err() {
|
||||||
|
defmt::warn!("COBS decoding failed");
|
||||||
|
} else {
|
||||||
|
let decoded_size = decoded_size.unwrap();
|
||||||
|
let mut tc_rb_full = false;
|
||||||
|
cx.shared.tc_rb.lock(|rb| {
|
||||||
|
if rb.sizes.vacant_len() >= 1 && rb.buf.vacant_len() >= decoded_size
|
||||||
|
{
|
||||||
|
rb.sizes.try_push(decoded_size).unwrap();
|
||||||
|
rb.buf.push_slice(&cx.local.rx_buf[1..1 + decoded_size]);
|
||||||
|
} else {
|
||||||
|
tc_rb_full = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if tc_rb_full {
|
||||||
|
defmt::warn!("COBS TC queue full");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
defmt::warn!(
|
||||||
|
"COBS frame with invalid format, start and end bytes are not 0"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initiate next transfer.
|
||||||
|
cx.local
|
||||||
|
.uart_rx
|
||||||
|
.read_fixed_len_or_timeout_based_using_irq(cx.local.rx_context)
|
||||||
|
.expect("read operation failed");
|
||||||
|
}
|
||||||
|
if result.has_errors() {
|
||||||
|
defmt::warn!("UART error: {:?}", result.errors.unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
defmt::warn!("UART error: {:?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(
|
||||||
|
priority = 2,
|
||||||
|
local=[
|
||||||
|
tc_buf: [u8; MAX_TC_SIZE] = [0; MAX_TC_SIZE],
|
||||||
|
readback_buf: [u8; MAX_TC_SIZE] = [0; MAX_TC_SIZE],
|
||||||
|
src_data_buf: [u8; 16] = [0; 16],
|
||||||
|
verif_buf: [u8; 32] = [0; 32],
|
||||||
|
nvm,
|
||||||
|
verif_reporter
|
||||||
|
],
|
||||||
|
shared=[tm_rb, tc_rb]
|
||||||
|
)]
|
||||||
|
async fn pus_tc_handler(mut cx: pus_tc_handler::Context) {
|
||||||
|
loop {
|
||||||
|
// Try to read a TC from the ring buffer.
|
||||||
|
let packet_len = cx.shared.tc_rb.lock(|rb| rb.sizes.try_pop());
|
||||||
|
if packet_len.is_none() {
|
||||||
|
// Small delay, TCs might arrive very quickly.
|
||||||
|
Mono::delay(20.millis()).await;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let packet_len = packet_len.unwrap();
|
||||||
|
defmt::info!("received packet with length {}", packet_len);
|
||||||
|
let popped_packet_len = cx
|
||||||
|
.shared
|
||||||
|
.tc_rb
|
||||||
|
.lock(|rb| rb.buf.pop_slice(&mut cx.local.tc_buf[0..packet_len]));
|
||||||
|
assert_eq!(popped_packet_len, packet_len);
|
||||||
|
// Read a telecommand, now handle it.
|
||||||
|
handle_valid_pus_tc(&mut cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_valid_pus_tc(cx: &mut pus_tc_handler::Context) {
|
||||||
|
let pus_tc = PusTcReader::new(cx.local.tc_buf);
|
||||||
|
if pus_tc.is_err() {
|
||||||
|
defmt::warn!("PUS TC error: {}", pus_tc.unwrap_err());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let pus_tc = pus_tc.unwrap();
|
||||||
|
let mut write_and_send = |tm: &PusTmCreator| {
|
||||||
|
let written_size = tm.write_to_bytes(cx.local.verif_buf).unwrap();
|
||||||
|
cx.shared.tm_rb.lock(|prod| {
|
||||||
|
prod.sizes.try_push(tm.len_written()).unwrap();
|
||||||
|
prod.buf.push_slice(&cx.local.verif_buf[0..written_size]);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
let request_id = VerificationReportCreator::read_request_id_from_tc(&pus_tc);
|
||||||
|
let tm = cx
|
||||||
|
.local
|
||||||
|
.verif_reporter
|
||||||
|
.acceptance_success(cx.local.src_data_buf, &request_id, 0, 0, &[])
|
||||||
|
.expect("acceptance success failed");
|
||||||
|
write_and_send(&tm);
|
||||||
|
|
||||||
|
let tm = cx
|
||||||
|
.local
|
||||||
|
.verif_reporter
|
||||||
|
.start_success(cx.local.src_data_buf, &request_id, 0, 0, &[])
|
||||||
|
.expect("acceptance success failed");
|
||||||
|
write_and_send(&tm);
|
||||||
|
|
||||||
|
if pus_tc.service() == PusServiceId::Action as u8 {
|
||||||
|
let mut corrupt_image = |base_addr: u32| {
|
||||||
|
let mut buf = [0u8; 4];
|
||||||
|
cx.local
|
||||||
|
.nvm
|
||||||
|
.read(base_addr as usize + 32, &mut buf)
|
||||||
|
.expect("reading from NVM failed");
|
||||||
|
buf[0] += 1;
|
||||||
|
cx.local
|
||||||
|
.nvm
|
||||||
|
.write(base_addr as usize + 32, &buf)
|
||||||
|
.expect("writing to NVM failed");
|
||||||
|
let tm = cx
|
||||||
|
.local
|
||||||
|
.verif_reporter
|
||||||
|
.completion_success(cx.local.src_data_buf, &request_id, 0, 0, &[])
|
||||||
|
.expect("completion success failed");
|
||||||
|
write_and_send(&tm);
|
||||||
|
};
|
||||||
|
if pus_tc.subservice() == ActionId::CorruptImageA as u8 {
|
||||||
|
defmt::info!("corrupting App Image A");
|
||||||
|
corrupt_image(APP_A_START_ADDR);
|
||||||
|
}
|
||||||
|
if pus_tc.subservice() == ActionId::CorruptImageB as u8 {
|
||||||
|
defmt::info!("corrupting App Image B");
|
||||||
|
corrupt_image(APP_B_START_ADDR);
|
||||||
|
}
|
||||||
|
if pus_tc.subservice() == ActionId::SetBootSlot as u8 {
|
||||||
|
if pus_tc.app_data().is_empty() {
|
||||||
|
defmt::warn!("App data for preferred image command too short");
|
||||||
|
}
|
||||||
|
let app_sel_result = AppSel::try_from(pus_tc.app_data()[0]);
|
||||||
|
if app_sel_result.is_err() {
|
||||||
|
defmt::warn!("Invalid app selection value: {}", pus_tc.app_data()[0]);
|
||||||
|
}
|
||||||
|
defmt::info!(
|
||||||
|
"received boot selection command with app select: {:?}",
|
||||||
|
app_sel_result.unwrap()
|
||||||
|
);
|
||||||
|
cx.local
|
||||||
|
.nvm
|
||||||
|
.write(PREFERRED_SLOT_OFFSET as usize, &[pus_tc.app_data()[0]])
|
||||||
|
.expect("writing to NVM failed");
|
||||||
|
let tm = cx
|
||||||
|
.local
|
||||||
|
.verif_reporter
|
||||||
|
.completion_success(cx.local.src_data_buf, &request_id, 0, 0, &[])
|
||||||
|
.expect("completion success failed");
|
||||||
|
write_and_send(&tm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if pus_tc.service() == PusServiceId::Test as u8 && pus_tc.subservice() == 1 {
|
||||||
|
defmt::info!("received ping TC");
|
||||||
|
let tm = cx
|
||||||
|
.local
|
||||||
|
.verif_reporter
|
||||||
|
.completion_success(cx.local.src_data_buf, &request_id, 0, 0, &[])
|
||||||
|
.expect("completion success failed");
|
||||||
|
write_and_send(&tm);
|
||||||
|
} else if pus_tc.service() == PusServiceId::MemoryManagement as u8 {
|
||||||
|
let tm = cx
|
||||||
|
.local
|
||||||
|
.verif_reporter
|
||||||
|
.step_success(
|
||||||
|
cx.local.src_data_buf,
|
||||||
|
&request_id,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
&[],
|
||||||
|
EcssEnumU8::new(0),
|
||||||
|
)
|
||||||
|
.expect("step success failed");
|
||||||
|
write_and_send(&tm);
|
||||||
|
// Raw memory write TC
|
||||||
|
if pus_tc.subservice() == 2 {
|
||||||
|
let app_data = pus_tc.app_data();
|
||||||
|
if app_data.len() < 10 {
|
||||||
|
defmt::warn!(
|
||||||
|
"app data for raw memory write is too short: {}",
|
||||||
|
app_data.len()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let memory_id = app_data[0];
|
||||||
|
if memory_id != BOOT_NVM_MEMORY_ID {
|
||||||
|
defmt::warn!("memory ID {} not supported", memory_id);
|
||||||
|
// TODO: Error reporting
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let offset = u32::from_be_bytes(app_data[2..6].try_into().unwrap());
|
||||||
|
let data_len = u32::from_be_bytes(app_data[6..10].try_into().unwrap());
|
||||||
|
if 10 + data_len as usize > app_data.len() {
|
||||||
|
defmt::warn!(
|
||||||
|
"invalid data length {} for raw mem write detected",
|
||||||
|
data_len
|
||||||
|
);
|
||||||
|
// TODO: Error reporting
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let data = &app_data[10..10 + data_len as usize];
|
||||||
|
defmt::info!("writing {} bytes at offset {} to NVM", data_len, offset);
|
||||||
|
cx.local
|
||||||
|
.nvm
|
||||||
|
.write(offset as usize, data)
|
||||||
|
.expect("writing to NVM failed");
|
||||||
|
let tm = if !cx
|
||||||
|
.local
|
||||||
|
.nvm
|
||||||
|
.verify(offset as usize, data)
|
||||||
|
.expect("NVM verification failed")
|
||||||
|
{
|
||||||
|
defmt::warn!("verification of data written to NVM failed");
|
||||||
|
cx.local
|
||||||
|
.verif_reporter
|
||||||
|
.completion_failure(
|
||||||
|
cx.local.src_data_buf,
|
||||||
|
&request_id,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
FailParams::new(&[], &EcssEnumU8::new(0), &[]),
|
||||||
|
)
|
||||||
|
.expect("completion success failed")
|
||||||
|
} else {
|
||||||
|
cx.local
|
||||||
|
.verif_reporter
|
||||||
|
.completion_success(cx.local.src_data_buf, &request_id, 0, 0, &[])
|
||||||
|
.expect("completion success failed")
|
||||||
|
};
|
||||||
|
write_and_send(&tm);
|
||||||
|
defmt::info!("NVM operation done");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[task(
|
||||||
|
priority = 1,
|
||||||
|
local=[
|
||||||
|
read_buf: [u8;MAX_TM_SIZE] = [0; MAX_TM_SIZE],
|
||||||
|
encoded_buf: [u8;MAX_TM_FRAME_SIZE] = [0; MAX_TM_FRAME_SIZE],
|
||||||
|
uart_tx,
|
||||||
|
],
|
||||||
|
shared=[tm_rb]
|
||||||
|
)]
|
||||||
|
async fn pus_tm_tx_handler(mut cx: pus_tm_tx_handler::Context) {
|
||||||
|
loop {
|
||||||
|
let mut occupied_len = cx.shared.tm_rb.lock(|rb| rb.sizes.occupied_len());
|
||||||
|
while occupied_len > 0 {
|
||||||
|
let next_size = cx.shared.tm_rb.lock(|rb| {
|
||||||
|
let next_size = rb.sizes.try_pop().unwrap();
|
||||||
|
rb.buf.pop_slice(&mut cx.local.read_buf[0..next_size]);
|
||||||
|
next_size
|
||||||
|
});
|
||||||
|
cx.local.encoded_buf[0] = 0;
|
||||||
|
let send_size = cobs::encode(
|
||||||
|
&cx.local.read_buf[0..next_size],
|
||||||
|
&mut cx.local.encoded_buf[1..],
|
||||||
|
);
|
||||||
|
cx.local.encoded_buf[send_size + 1] = 0;
|
||||||
|
cx.local
|
||||||
|
.uart_tx
|
||||||
|
.write_all(&cx.local.encoded_buf[0..send_size + 2])
|
||||||
|
.unwrap();
|
||||||
|
occupied_len -= 1;
|
||||||
|
Mono::delay(2.millis()).await;
|
||||||
|
}
|
||||||
|
Mono::delay(50.millis()).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
va108xx/jlink-gdb.sh
Executable file
3
va108xx/jlink-gdb.sh
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
JLinkGDBServer -select USB -device VA10820 -endian little -if JTAG -speed auto \
|
||||||
|
-LocalhostOnly -jtagconf -1,-1
|
||||||
10
va108xx/jlink.gdb
Normal file
10
va108xx/jlink.gdb
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
target remote localhost:2331
|
||||||
|
|
||||||
|
monitor reset
|
||||||
|
|
||||||
|
# *try* to stop at the user entry point (it might be gone due to inlining)
|
||||||
|
break main
|
||||||
|
|
||||||
|
load
|
||||||
|
|
||||||
|
continue
|
||||||
10
va108xx/memory.x
Normal file
10
va108xx/memory.x
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH : ORIGIN = 0x00000000, LENGTH = 0x20000 /* 128K */
|
||||||
|
RAM : ORIGIN = 0x10000000, LENGTH = 0x08000 /* 32K */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is where the call stack will be allocated. */
|
||||||
|
/* The stack is of the full descending type. */
|
||||||
|
/* NOTE Do NOT modify `_stack_start` unless you know what you are doing */
|
||||||
|
_stack_start = ORIGIN(RAM) + LENGTH(RAM);
|
||||||
173
va108xx/scripts/VA108xx_Series.yaml
Normal file
173
va108xx/scripts/VA108xx_Series.yaml
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
name: VA108xx Series
|
||||||
|
generated_from_pack: true
|
||||||
|
pack_file_release: 1.4.0
|
||||||
|
variants:
|
||||||
|
- name: VA108xx
|
||||||
|
cores:
|
||||||
|
- name: main
|
||||||
|
type: armv6m
|
||||||
|
core_access_options: !Arm
|
||||||
|
ap: 0
|
||||||
|
psel: 0x0
|
||||||
|
jtag_tap: 1
|
||||||
|
memory_map:
|
||||||
|
- !Ram
|
||||||
|
name: DRAM
|
||||||
|
range:
|
||||||
|
start: 0x10000000
|
||||||
|
end: 0x10008000
|
||||||
|
cores:
|
||||||
|
- main
|
||||||
|
- !Nvm
|
||||||
|
name: NVM
|
||||||
|
range:
|
||||||
|
start: 0x0
|
||||||
|
end: 0x20000
|
||||||
|
cores:
|
||||||
|
- main
|
||||||
|
access:
|
||||||
|
write: false
|
||||||
|
boot: true
|
||||||
|
flash_algorithms:
|
||||||
|
- va108xx_fm25v20a_fram_128kb_prog
|
||||||
|
- va108xx_m95m01_128kb_prog
|
||||||
|
- va108xx_mr25h10_1mb_prog
|
||||||
|
- va108xx_ttflash_prog
|
||||||
|
- name: VA108xx_RAM
|
||||||
|
cores:
|
||||||
|
- name: main
|
||||||
|
type: armv6m
|
||||||
|
core_access_options: !Arm
|
||||||
|
ap: 0
|
||||||
|
psel: 0x0
|
||||||
|
jtag_tap: 1
|
||||||
|
memory_map:
|
||||||
|
- !Ram
|
||||||
|
name: DRAM
|
||||||
|
range:
|
||||||
|
start: 0x10000000
|
||||||
|
end: 0x10008000
|
||||||
|
cores:
|
||||||
|
- main
|
||||||
|
- !Ram
|
||||||
|
name: IRAM
|
||||||
|
range:
|
||||||
|
start: 0x0
|
||||||
|
end: 0x20000
|
||||||
|
cores:
|
||||||
|
- main
|
||||||
|
access:
|
||||||
|
write: false
|
||||||
|
boot: true
|
||||||
|
flash_algorithms:
|
||||||
|
- name: va108xx_fm25v20a_fram_128kb_prog
|
||||||
|
description: VA108_FM25V20A_FRAM_128KB
|
||||||
|
instructions: QLpwR8C6cEdkSMFoyQf80MFoyQb81AMhwWJwR3BHALVgSV9IyGNdSgIgEGFeSBBgXkhQYAMg0GL/9+b/XEiQYNBowAf80AEgkGDAB5Bg//fb/wAgAL0Atf/31v9PSFRJgWDBaMkH/NABIYFg/SGBYMkHgWD/98j/ACAAvXC1ACJLTUZLAiYURp1g//e9/xACnmABAgkOmWAABAAOmGCcYAAg2WiJB/zVnGBAHP8o+NPYaIAH/NUBIMAHmGD/96T/ASBAAlIcgkLe0wAgcL0AIHBHPLUFRgAgC0YAkP/3lP8uTDNIoGD/94//AiCgYCgCAA6gYCgEAA6gYOiyoGAL4OBogAf81RB4oGCgaAGQAJhSHEAcWx4AkAEr8dHgaIAH/NUQeAEhyQdAGKBg//ds/wAgPL0AIHBH8LUORgVG//dj/xZIAyGBYCkCCQ6BYCkECQ6BYOmygWAAIxlGgWDEaGQH/NWEaFscBCv32wAjDOCBYMRoZAf81YRoF3jksqdCAdDoGPC9UhxbHLNC8NMBIckHgWD/9zj/qBnwvQAgBUD///8AQAAAQAcEAACCAgAABgAAgAAAAAA=
|
||||||
|
pc_init: 0x1f
|
||||||
|
pc_uninit: 0x57
|
||||||
|
pc_program_page: 0xd3
|
||||||
|
pc_erase_sector: 0xcf
|
||||||
|
pc_erase_all: 0x7d
|
||||||
|
data_section_offset: 0x1b4
|
||||||
|
flash_properties:
|
||||||
|
address_range:
|
||||||
|
start: 0x0
|
||||||
|
end: 0x20000
|
||||||
|
page_size: 0x100
|
||||||
|
erased_byte_value: 0x0
|
||||||
|
program_page_timeout: 3000
|
||||||
|
erase_sector_timeout: 3000
|
||||||
|
sectors:
|
||||||
|
- size: 0x2000
|
||||||
|
address: 0x0
|
||||||
|
- name: va108xx_m95m01_128kb_prog
|
||||||
|
description: VA108XX_M95M01_128KB
|
||||||
|
default: true
|
||||||
|
instructions: QLpwR8C6cEd6SMFoyQf80MFoyQb81AMhwWJwR3i1//fz/wUk5QcAJnJIc0sDIoRghWDBaMkH/NDBaEkH/NWBaACRwWhJB/zVgWgAkckHwmIH0ACWAL8AmUkcAJGZQvnb5ed4vQC1ZklkSMhjYUoCIBBhZEgQYGRIUGADINBi//fD/2JIkGD/97//ASCQYMAHkGD/97n/ACAAvQC1//e+///3sv9TSllIkGD/963/ASCQYFZI9zCQYP/3pv8AIAC98LUAJFFPS00mRq9g//ec/yACAiGpYAECCQ6pYAAEAA6oYK5gACDpaIkH/NWuYEAc/yj40+hogAf81QEgwAeoYP/3gv//94r/ASBAAmQchELb0wAg8L0AIHBHfLUGRgAgFEYNRgCQ//d5///3bf8xSjZIkGD/92j/AiCQYDACAA6QYDAEAA6QYPCykGAL4NBogAf81SB4kGCQaAGQAJhkHEAcbR4AkAEt8dHQaIAH/NUgeAEhyQdAGJBg//dF///3Tf8AIHy9ACBwR/C1FEYORgVG//dD///3N/8WSQMgiGAoAgAOiGAoBAAOiGDosohgACMaRopgyGhAB/zViGhbHAQr99sAIAzgimDLaFsH/NWLaCd427KfQgHQKBjwvUAcZBywQvDTASDAB4hg//cM/6gZ8L0AIAVAUMMAAP///wBAAABABwQAAIICAAAGAACAAAAAAA==
|
||||||
|
pc_init: 0x65
|
||||||
|
pc_uninit: 0x9b
|
||||||
|
pc_program_page: 0x11b
|
||||||
|
pc_erase_sector: 0x117
|
||||||
|
pc_erase_all: 0xc1
|
||||||
|
data_section_offset: 0x210
|
||||||
|
flash_properties:
|
||||||
|
address_range:
|
||||||
|
start: 0x0
|
||||||
|
end: 0x20000
|
||||||
|
page_size: 0x100
|
||||||
|
erased_byte_value: 0x0
|
||||||
|
program_page_timeout: 3000
|
||||||
|
erase_sector_timeout: 3000
|
||||||
|
sectors:
|
||||||
|
- size: 0x2000
|
||||||
|
address: 0x0
|
||||||
|
- name: va108xx_mr25h10_1mb_prog
|
||||||
|
description: VA108_MR25H10_1Mb
|
||||||
|
instructions: QLpwR8C6cEdjSMFoyQf80MFoyQb81AMhwWJwR3BHALVfSV5IyGNcSgIgEGFdSBBgXUhQYAMg0GL/9+b/W0iQYP/34v8BIJBgwAeQYP/33P8AIAC9ALX/99f/T0pTSJBg//fS/wEgkGBQSPcwkGD/98v/ACAAvXC1ACJMTUZLAiYURp1g//fA/xACnmABAgkOmWAABAAOmGCcYAAg2WiJB/zVnGBAHP8o+NPYaIAH/NUBIMAHmGD/96f/ASBAAlIcgkLe0wAgcL0AIHBHPLUFRgAgC0YAkP/3l/8vTDNIoGD/95L/AiCgYCgCAA6gYCgEAA6gYOiyoGAL4OBogAf81RB4oGCgaAGQAJhSHEAcWx4AkAEr8dHgaIAH/NUQeAEhyQdAGKBg//dv/wAgPL0AIHBH8LUORgVG//dm/xZIAyGBYCkCCQ6BYCkECQ6BYOmygWAAIxlGgWDEaGQH/NWEaFscBCv32wAjDOCBYMRoZAf81YRoF3jksqdCAdDoGPC9UhxbHLNC8NMBIckHgWD/9zv/qBnwvQAAACAFQP///wBAAABABwQAAIICAAAGAACAAAAAAA==
|
||||||
|
pc_init: 0x1f
|
||||||
|
pc_uninit: 0x55
|
||||||
|
pc_program_page: 0xcd
|
||||||
|
pc_erase_sector: 0xc9
|
||||||
|
pc_erase_all: 0x77
|
||||||
|
data_section_offset: 0x1b0
|
||||||
|
flash_properties:
|
||||||
|
address_range:
|
||||||
|
start: 0x0
|
||||||
|
end: 0x20000
|
||||||
|
page_size: 0x100
|
||||||
|
erased_byte_value: 0x0
|
||||||
|
program_page_timeout: 10000
|
||||||
|
erase_sector_timeout: 10000
|
||||||
|
sectors:
|
||||||
|
- size: 0x2000
|
||||||
|
address: 0x0
|
||||||
|
- name: va108xx_ttflash_prog
|
||||||
|
description: VA108_TT_Flash_8MBx
|
||||||
|
instructions: QLpwR8C6cEd9SMFoyQf80MFoyQb81AMhwWJwR3i1//fz/wUk5QcAJnVIdksDIoRghWDBaMkH/NDBaEkH/NWBaACRwWhJB/zVgWgAkckHwmIH0ACWAL8AmUkcAJGZQvnb5ed4vQC1aUlnSMhjZEoCIBBhZ0gQYGdIUGADINBi//fD/2VIkGD/97//ASCQYAAgkGABIMAHkGD/97b/ACAAvQC1//e7///3r/9VSlpIkGD/96r/ASCQYP0gkGDAB5Bg//ei/wAgAL0Atf/3p///95v/S0hQSYFgT0laMYFg//eT///3m/8AIAC9ELUERv/3lf//94n/QkpHSJBg//eE/yAgkGAgAgAOkGAgBAAOkGABIeCyyQdAGJBg//d////3c/8AIBC9fLUGRgAgFEYNRgCQ//dz///3Z/8xSjZIkGD/92L/AiCQYDACAA6QYDAEAA6QYPCykGAL4NBogAf81SB4kGCQaAGQAJhkHEAcbR4AkAEt8dHQaIAH/NUgeAEhyQdAGJBg//c////3R/8AIHy9ACBwR/C1FEYORgVG//c9///3Mf8WSQMgiGAoAgAOiGAoBAAOiGDosohgACMaRopgyGhAB/zViGhbHAQr99sAIAzgimDLaFsH/NWLaCd427KfQgHQKBjwvUAcZBywQvDTASDAB4hg//cG/6gZ8L0AIAVAUMMAAP///wBAAABABwQAAIICAAAGAACAAAAAAA==
|
||||||
|
pc_init: 0x65
|
||||||
|
pc_uninit: 0xa1
|
||||||
|
pc_program_page: 0x127
|
||||||
|
pc_erase_sector: 0xeb
|
||||||
|
pc_erase_all: 0xc9
|
||||||
|
data_section_offset: 0x21c
|
||||||
|
flash_properties:
|
||||||
|
address_range:
|
||||||
|
start: 0x0
|
||||||
|
end: 0x20000
|
||||||
|
page_size: 0x100
|
||||||
|
erased_byte_value: 0xff
|
||||||
|
program_page_timeout: 10000
|
||||||
|
erase_sector_timeout: 50000
|
||||||
|
sectors:
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0x0
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0x1000
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0x2000
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0x3000
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0x4000
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0x5000
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0x6000
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0x7000
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0x8000
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0x9000
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0xa000
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0xb000
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0xc000
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0xd000
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0xe000
|
||||||
|
- size: 0x1000
|
||||||
|
address: 0xf000
|
||||||
18
va108xx/scripts/defmt-telnet.sh
Executable file
18
va108xx/scripts/defmt-telnet.sh
Executable file
@@ -0,0 +1,18 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Check if binary path was provided
|
||||||
|
if [ "$#" -ne 1 ]; then
|
||||||
|
echo "Usage: $0 <path-to-binary>"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
BINARY="$1"
|
||||||
|
|
||||||
|
# Check if file exists
|
||||||
|
if [ ! -f "$BINARY" ]; then
|
||||||
|
echo "Error: File '$BINARY' not found."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run the command
|
||||||
|
telnet localhost 19021 | defmt-print -e "$BINARY"
|
||||||
10
va108xx/scripts/memory_app_a.x
Normal file
10
va108xx/scripts/memory_app_a.x
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH : ORIGIN = 0x00003000, LENGTH = 0xE7F8 /* (128k - 12k) / 2 - 8 */
|
||||||
|
RAM : ORIGIN = 0x10000000, LENGTH = 0x08000 /* 32K */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is where the call stack will be allocated. */
|
||||||
|
/* The stack is of the full descending type. */
|
||||||
|
/* NOTE Do NOT modify `_stack_start` unless you know what you are doing */
|
||||||
|
_stack_start = ORIGIN(RAM) + LENGTH(RAM);
|
||||||
10
va108xx/scripts/memory_app_b.x
Normal file
10
va108xx/scripts/memory_app_b.x
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH : ORIGIN = 0x00011800, LENGTH = 0xE7F8 /* (128k - 12k) / 2 - 8 */
|
||||||
|
RAM : ORIGIN = 0x10000000, LENGTH = 0x08000 /* 32K */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is where the call stack will be allocated. */
|
||||||
|
/* The stack is of the full descending type. */
|
||||||
|
/* NOTE Do NOT modify `_stack_start` unless you know what you are doing */
|
||||||
|
_stack_start = ORIGIN(RAM) + LENGTH(RAM);
|
||||||
36
va108xx/va108xx-embassy/CHANGELOG.md
Normal file
36
va108xx/va108xx-embassy/CHANGELOG.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
Change Log
|
||||||
|
=======
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||||
|
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
|
## [unreleased]
|
||||||
|
|
||||||
|
## [v0.3.0] 2025-09-03
|
||||||
|
|
||||||
|
Bumped allowed va108xx-hal to v0.12
|
||||||
|
|
||||||
|
## [v0.2.1] 2025-03-07
|
||||||
|
|
||||||
|
- Bumped allowed va108xx-hal to v0.11
|
||||||
|
|
||||||
|
## [v0.2.0] 2025-02-17
|
||||||
|
|
||||||
|
- Bumped va108xx-hal to v0.10.0
|
||||||
|
- Remove `embassy` module, expose public functions in library root directly
|
||||||
|
|
||||||
|
|
||||||
|
## [v0.1.2] and [v0.1.1] 2025-02-13
|
||||||
|
|
||||||
|
Docs patch
|
||||||
|
|
||||||
|
## [v0.1.0] 2025-02-13
|
||||||
|
|
||||||
|
Initial release
|
||||||
|
|
||||||
|
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-embassy-v0.3.0...HEAD
|
||||||
|
[v0.3.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-embassy-v0.2.1...va10xx-embassy-v0.3.0
|
||||||
|
[v0.2.1]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-embassy-v0.2.0...va10xx-embassy-v0.2.1
|
||||||
|
[v0.2.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-embassy-v0.1.2...va10xx-embassy-v0.2.0
|
||||||
27
va108xx/va108xx-embassy/Cargo.toml
Normal file
27
va108xx/va108xx-embassy/Cargo.toml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
[package]
|
||||||
|
name = "va108xx-embassy"
|
||||||
|
version = "0.3.0"
|
||||||
|
edition = "2021"
|
||||||
|
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||||
|
description = "Embassy-rs support for the Vorago VA108xx family of microcontrollers"
|
||||||
|
homepage = "https://egit.irs.uni-stuttgart.de/rust/va108xx-rs"
|
||||||
|
repository = "https://egit.irs.uni-stuttgart.de/rust/va108xx-rs"
|
||||||
|
license = "Apache-2.0"
|
||||||
|
keywords = ["no-std", "hal", "cortex-m", "vorago", "va108xx"]
|
||||||
|
categories = ["aerospace", "embedded", "no-std", "hardware-support"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
vorago-shared-hal = { version = "0.2", features = ["vor1x"] }
|
||||||
|
va108xx-hal = { version = "0.12" }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["irq-oc30-oc31"]
|
||||||
|
irqs-in-lib = []
|
||||||
|
# This determines the reserved interrupt functions for the embassy time drivers. Only one
|
||||||
|
# is allowed to be selected!
|
||||||
|
irq-oc28-oc29 = ["irqs-in-lib"]
|
||||||
|
irq-oc29-oc30 = ["irqs-in-lib"]
|
||||||
|
irq-oc30-oc31 = ["irqs-in-lib"]
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
rustdoc-args = ["--generate-link-to-definition"]
|
||||||
10
va108xx/va108xx-embassy/README.md
Normal file
10
va108xx/va108xx-embassy/README.md
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
[](https://crates.io/crates/va108xx-embassy)
|
||||||
|
[](https://docs.rs/va108xx-embassy)
|
||||||
|
|
||||||
|
# Embassy-rs support for the Vorago VA108xx MCU family
|
||||||
|
|
||||||
|
This repository contains the [embassy-rs](https://github.com/embassy-rs/embassy) support for the
|
||||||
|
VA108xx family. Currently, it contains the time driver to allow using embassy-rs. It uses the TIM
|
||||||
|
peripherals provided by the VA108xx family for this purpose.
|
||||||
|
|
||||||
|
The documentation contains more information on how to use this crate.
|
||||||
3
va108xx/va108xx-embassy/docs.sh
Executable file
3
va108xx/va108xx-embassy/docs.sh
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
|
||||||
|
cargo +nightly doc --open
|
||||||
109
va108xx/va108xx-embassy/src/lib.rs
Normal file
109
va108xx/va108xx-embassy/src/lib.rs
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
//! # Embassy-rs support for the Vorago VA108xx MCU family
|
||||||
|
//!
|
||||||
|
//! This repository contains the [embassy-rs](https://github.com/embassy-rs/embassy) support for
|
||||||
|
//! the VA108xx family. Currently, it contains the time driver to allow using embassy-rs. It uses
|
||||||
|
//! the TIM peripherals provided by the VA108xx family for this purpose.
|
||||||
|
//!
|
||||||
|
//! ## Usage
|
||||||
|
//!
|
||||||
|
//! This library exposes the [init] or the [init_with_custom_irqs] functions which set up the time
|
||||||
|
//! driver. This function must be called once at the start of the application.
|
||||||
|
//!
|
||||||
|
//! This implementation requires two TIM peripherals provided by the VA108xx device.
|
||||||
|
//! The user can freely specify the two used TIM peripheral by passing the concrete TIM instances
|
||||||
|
//! into the [init_with_custom_irqs] and [init] method.
|
||||||
|
//!
|
||||||
|
//! The application also requires two interrupt handlers to handle the timekeeper and alarm
|
||||||
|
//! interrupts. By default, this library will define the interrupt handler inside the library
|
||||||
|
//! itself by using the `irq-oc30-oc31` feature flag. This library exposes three combinations:
|
||||||
|
//!
|
||||||
|
//! - `irq-oc30-oc31`: Uses [pac::Interrupt::OC30] and [pac::Interrupt::OC31]
|
||||||
|
//! - `irq-oc29-oc30`: Uses [pac::Interrupt::OC29] and [pac::Interrupt::OC30]
|
||||||
|
//! - `irq-oc28-oc29`: Uses [pac::Interrupt::OC28] and [pac::Interrupt::OC20]
|
||||||
|
//!
|
||||||
|
//! You can disable the default features and then specify one of the features above to use the
|
||||||
|
//! documented combination of IRQs. It is also possible to specify custom IRQs by importing and
|
||||||
|
//! using the [embassy_time_driver_irqs] macro to declare the IRQ handlers in the
|
||||||
|
//! application code. If this is done, [init_with_custom_irqs] must be used
|
||||||
|
//! method to pass the IRQ numbers to the library.
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! [embassy example projects](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy)
|
||||||
|
#![no_std]
|
||||||
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
|
|
||||||
|
#[cfg(feature = "irqs-in-lib")]
|
||||||
|
use va108xx_hal::pac::{self, interrupt};
|
||||||
|
use va108xx_hal::time::Hertz;
|
||||||
|
use va108xx_hal::timer::TimInstance;
|
||||||
|
use vorago_shared_hal::embassy::time_driver;
|
||||||
|
|
||||||
|
/// Macro to define the IRQ handlers for the time driver.
|
||||||
|
///
|
||||||
|
/// By default, the code generated by this macro will be defined inside the library depending on
|
||||||
|
/// the feature flags specified. However, the macro is exported to allow users to specify the
|
||||||
|
/// interrupt handlers themselves.
|
||||||
|
///
|
||||||
|
/// Please note that you have to explicitely import the [macro@va108xx_hal::pac::interrupt]
|
||||||
|
/// macro in the application code in case this macro is used there.
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! embassy_time_driver_irqs {
|
||||||
|
(
|
||||||
|
timekeeper_irq = $timekeeper_irq:ident,
|
||||||
|
alarm_irq = $alarm_irq:ident
|
||||||
|
) => {
|
||||||
|
const TIMEKEEPER_IRQ: pac::Interrupt = pac::Interrupt::$timekeeper_irq;
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn $timekeeper_irq() {
|
||||||
|
// Safety: We call it once here.
|
||||||
|
unsafe { $crate::time_driver().on_interrupt_timekeeping() }
|
||||||
|
}
|
||||||
|
|
||||||
|
const ALARM_IRQ: pac::Interrupt = pac::Interrupt::$alarm_irq;
|
||||||
|
|
||||||
|
#[interrupt]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
fn $alarm_irq() {
|
||||||
|
// Safety: We call it once here.
|
||||||
|
unsafe { $crate::time_driver().on_interrupt_alarm() }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Provide three combinations of IRQs for the time driver by default.
|
||||||
|
|
||||||
|
#[cfg(feature = "irq-oc30-oc31")]
|
||||||
|
embassy_time_driver_irqs!(timekeeper_irq = OC31, alarm_irq = OC30);
|
||||||
|
#[cfg(feature = "irq-oc29-oc30")]
|
||||||
|
embassy_time_driver_irqs!(timekeeper_irq = OC30, alarm_irq = OC29);
|
||||||
|
#[cfg(feature = "irq-oc28-oc29")]
|
||||||
|
embassy_time_driver_irqs!(timekeeper_irq = OC29, alarm_irq = OC28);
|
||||||
|
|
||||||
|
/// Initialization method for embassy.
|
||||||
|
///
|
||||||
|
/// This should be used if the interrupt handler is provided by the library, which is the
|
||||||
|
/// default case.
|
||||||
|
#[cfg(feature = "irqs-in-lib")]
|
||||||
|
pub fn init<TimekeeperTim: TimInstance, AlarmTim: TimInstance>(
|
||||||
|
timekeeper_tim: TimekeeperTim,
|
||||||
|
alarm_tim: AlarmTim,
|
||||||
|
sysclk: Hertz,
|
||||||
|
) {
|
||||||
|
time_driver().__init(sysclk, timekeeper_tim, alarm_tim, TIMEKEEPER_IRQ, ALARM_IRQ)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialization method for embassy when using custom IRQ handlers.
|
||||||
|
///
|
||||||
|
/// Requires an explicit [pac::Interrupt] argument for the timekeeper and alarm IRQs.
|
||||||
|
pub fn init_with_custom_irqs<TimekeeperTim: TimInstance, AlarmTim: TimInstance>(
|
||||||
|
timekeeper_tim: TimekeeperTim,
|
||||||
|
alarm_tim: AlarmTim,
|
||||||
|
sysclk: Hertz,
|
||||||
|
timekeeper_irq: pac::Interrupt,
|
||||||
|
alarm_irq: pac::Interrupt,
|
||||||
|
) {
|
||||||
|
time_driver().__init(sysclk, timekeeper_tim, alarm_tim, timekeeper_irq, alarm_irq)
|
||||||
|
}
|
||||||
36
va108xx/va108xx-hal/.cargo/config.toml
Normal file
36
va108xx/va108xx-hal/.cargo/config.toml
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||||
|
# uncomment ONE of these three option to make `cargo run` start a GDB session
|
||||||
|
# which option to pick depends on your system
|
||||||
|
# runner = "arm-none-eabi-gdb -q -x jlink.gdb"
|
||||||
|
# runner = "gdb-multiarch -q -x jlink.gdb"
|
||||||
|
# runner = "gdb -q -x openocd.gdb"
|
||||||
|
|
||||||
|
rustflags = [
|
||||||
|
# This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x
|
||||||
|
# See https://github.com/rust-embedded/cortex-m-quickstart/pull/95
|
||||||
|
"-C", "link-arg=--nmagic",
|
||||||
|
|
||||||
|
# LLD (shipped with the Rust toolchain) is used as the default linker
|
||||||
|
"-C", "link-arg=-Tlink.x",
|
||||||
|
|
||||||
|
# if you run into problems with LLD switch to the GNU linker by commenting out
|
||||||
|
# this line
|
||||||
|
# "-C", "linker=arm-none-eabi-ld",
|
||||||
|
|
||||||
|
# if you need to link to pre-compiled C libraries provided by a C toolchain
|
||||||
|
# use GCC as the linker by commenting out both lines above and then
|
||||||
|
# uncommenting the three lines below
|
||||||
|
# "-C", "linker=arm-none-eabi-gcc",
|
||||||
|
# "-C", "link-arg=-Wl,-Tlink.x",
|
||||||
|
# "-C", "link-arg=-nostartfiles",
|
||||||
|
]
|
||||||
|
|
||||||
|
[build]
|
||||||
|
# Pick ONE of these compilation targets
|
||||||
|
target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+
|
||||||
|
# target = "thumbv7m-none-eabi" # Cortex-M3
|
||||||
|
# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU)
|
||||||
|
# target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU)
|
||||||
|
# target = "thumbv8m.base-none-eabi" # Cortex-M23
|
||||||
|
# target = "thumbv8m.main-none-eabi" # Cortex-M33 (no FPU)
|
||||||
|
# target = "thumbv8m.main-none-eabihf" # Cortex-M33 (with FPU)
|
||||||
2
va108xx/va108xx-hal/.github/bors.toml
vendored
Normal file
2
va108xx/va108xx-hal/.github/bors.toml
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
status = ["ci"]
|
||||||
|
delete_merged_branches = true
|
||||||
20
va108xx/va108xx-hal/.github/workflows/changelog.yml
vendored
Normal file
20
va108xx/va108xx-hal/.github/workflows/changelog.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
|
||||||
|
name: Changelog check
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
changelog:
|
||||||
|
name: Changelog check
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout sources
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Changelog updated
|
||||||
|
uses: Zomzog/changelog-checker@v1.2.0
|
||||||
|
with:
|
||||||
|
fileName: CHANGELOG.md
|
||||||
|
noChangelogLabel: no changelog
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
65
va108xx/va108xx-hal/.github/workflows/ci.yml
vendored
Normal file
65
va108xx/va108xx-hal/.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
on: [push]
|
||||||
|
|
||||||
|
name: ci
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
name: Check
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: stable
|
||||||
|
target: thumbv6m-none-eabi
|
||||||
|
override: true
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: check
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: check
|
||||||
|
args: --examples
|
||||||
|
|
||||||
|
fmt:
|
||||||
|
name: Rustfmt
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: stable
|
||||||
|
override: true
|
||||||
|
- run: rustup component add rustfmt
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: fmt
|
||||||
|
args: --all -- --check
|
||||||
|
|
||||||
|
clippy:
|
||||||
|
name: Clippy
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: stable
|
||||||
|
target: thumbv6m-none-eabi
|
||||||
|
override: true
|
||||||
|
- run: rustup component add clippy
|
||||||
|
- uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: clippy
|
||||||
|
args: -- -D warnings
|
||||||
|
|
||||||
|
ci:
|
||||||
|
if: ${{ success() }}
|
||||||
|
# all new jobs must be added to this list
|
||||||
|
needs: [check, fmt, clippy]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: CI succeeded
|
||||||
|
run: exit 0
|
||||||
9
va108xx/va108xx-hal/.gitignore
vendored
Normal file
9
va108xx/va108xx-hal/.gitignore
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# Generated by Cargo
|
||||||
|
# will have compiled files and executables
|
||||||
|
/target/
|
||||||
|
|
||||||
|
# https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
|
||||||
|
Cargo.lock
|
||||||
|
|
||||||
|
# These are backup files generated by rustfmt
|
||||||
|
**/*.rs.bk
|
||||||
287
va108xx/va108xx-hal/CHANGELOG.md
Normal file
287
va108xx/va108xx-hal/CHANGELOG.md
Normal file
@@ -0,0 +1,287 @@
|
|||||||
|
Change Log
|
||||||
|
=======
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||||
|
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
|
## [unreleased]
|
||||||
|
|
||||||
|
## [v0.12.0] 2025-09-03
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- Move most library components to new [`vorago-shared-hal`](https://egit.irs.uni-stuttgart.de/rust/vorago-shared-hal)
|
||||||
|
which is mostly re-exported in this crate.
|
||||||
|
- Overhaul and simplification of several HAL APIs. The system configuration and IRQ router
|
||||||
|
peripheral instance generally does not need to be passed to HAL API anymore.
|
||||||
|
- All HAL drivers are now type erased. The constructors will still expect and consume the PAC
|
||||||
|
singleton component for resource management purposes, but are not cached anymore.
|
||||||
|
- Refactoring of GPIO library to be more inline with embassy GPIO API.
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- I2C clock timeout feature support.
|
||||||
|
|
||||||
|
## [v0.11.1] 2025-03-10
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- Fix `embedded_io` UART implementation to implement the documented contract properly.
|
||||||
|
The implementation will now block until at least one byte is available or can be written, unless
|
||||||
|
the send or receive buffer is empty.
|
||||||
|
|
||||||
|
## [v0.11.0] 2025-03-07
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- Bugfix for I2C `TimingCfg::reg`
|
||||||
|
- Simplified UART error handling. All APIs are now infallible because writing to a FIFO or
|
||||||
|
reading from a FIFO never fails. Users can either poll errors using `Rx::poll_errors` or
|
||||||
|
`Uart::poll_rx_errors` / `UartBase::poll_rx_errors`, or detect errors using the provided
|
||||||
|
interrupt handlers.
|
||||||
|
|
||||||
|
## [v0.10.0] 2025-02-17
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- A lot of missing `defmt::Format` implementations.
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- Larger refactoring of GPIO library. The edge and level interrupt configurator functions do not
|
||||||
|
enable interrupts anymore. Instead, there are explicit `enbable_interrupt` and
|
||||||
|
`disable_interrupt` methods
|
||||||
|
- Renamed GPIO `DynGroup` to `Port`
|
||||||
|
- Rename generic GPIO interrupt handler into `on_interrupt_for_asynch_gpio`
|
||||||
|
into `on_interrupt_for_async_gpio_for_port` which expects a Port argument
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- Bug in async GPIO interrupt handler where all enabled interrupts, even the ones which might
|
||||||
|
be unrelated to the pin, were disabled.
|
||||||
|
|
||||||
|
## [v0.9.0] 2025-02-13
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- Important bugfix for UART driver which causes UART B drivers not to work.
|
||||||
|
|
||||||
|
## Removed
|
||||||
|
|
||||||
|
- Deleted some HAL re-exports in the PWM module
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- GPIO API: Interrupt, pulse and filter and `set_datamask` and `clear_datamask` APIs are now
|
||||||
|
methods which mutable modify the pin instead of consuming and returning it.
|
||||||
|
- Simplified PWM module implementation.
|
||||||
|
- All error types now implement `core::error::Error` by using the `thiserror::Error` derive.
|
||||||
|
- `InvalidPinTypeError` now wraps the pin mode.
|
||||||
|
- I2C `TimingCfg` constructor now returns explicit error instead of generic Error.
|
||||||
|
Removed the timing configuration error type from the generic I2C error enumeration.
|
||||||
|
- `PinsA` and `PinsB` constructor do not expect an optional `pac::Ioconfig` argument anymore.
|
||||||
|
- `IrqCfg` renamed to `InterruptConfig`, kept alias for old name.
|
||||||
|
- All library provided interrupt handlers now start with common prefix `on_interrupt_*`
|
||||||
|
- `RxWithIrq` renamed to `RxWithInterrupt`
|
||||||
|
- `Rx::into_rx_with_irq` does not expect any arguments any more.
|
||||||
|
- `filter_type` renamed to `configure_filter_type`.
|
||||||
|
- `level_irq` renamed to `configure_level_interrupt`.
|
||||||
|
- `edge_irq` renamed to `configure_edge_interrupt`.
|
||||||
|
- `PinsA` and `PinsB` constructor do not expect an optional IOCONFIG argument anymore.
|
||||||
|
- UART interrupt management is now handled by the main constructor instead of later stages to
|
||||||
|
statically ensure one interrupt vector for the UART peripheral. `Uart::new` expects an
|
||||||
|
optional `InterruptConfig` argument.
|
||||||
|
- `enable_interrupt` and `disable_interrupt` renamed to `enable_nvic_interrupt` and
|
||||||
|
`disable_nvic_interrupt` to distinguish them from peripheral interrupts more clearly.
|
||||||
|
- `port_mux` renamed to `port_function_select`
|
||||||
|
- Renamed `IrqUartErrors` to `UartErrors`.
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- Add `downgrade` method for `Pin` and `upgrade` method for `DynPin` as explicit conversion
|
||||||
|
methods.
|
||||||
|
- Asynchronous GPIO support.
|
||||||
|
- Asynchronous UART TX support.
|
||||||
|
- Asynchronous UART RX support.
|
||||||
|
- Add new `get_tim_raw` unsafe method to retrieve TIM peripheral blocks.
|
||||||
|
- `Uart::with_with_interrupt` and `Uart::new_without_interrupt`
|
||||||
|
|
||||||
|
## [v0.8.0] 2024-09-30
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- Improves `CascardSource` handling and general API when chosing cascade sources.
|
||||||
|
- Replaced `utility::unmask_irq` by `enable_interrupt` and `disable_interrupt` API.
|
||||||
|
- Improve and fix SPI abstractions. Add new low level interface. The primary SPI constructor now
|
||||||
|
only expects a configuration structure and the transfer configuration needs to be applied in a
|
||||||
|
separate step.
|
||||||
|
- Removed complete `timer` module re-export in `pwm` module
|
||||||
|
- `CountDownTimer` renamed to `CountdownTimer`
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
- Fixes for SPI peripheral: Flush implementation was incorrect and should now flush properly.
|
||||||
|
|
||||||
|
## [v0.7.0] 2024-07-04
|
||||||
|
|
||||||
|
- Replace `uarta` and `uartb` `Uart` constructors by `new` constructor
|
||||||
|
- Replace SPI `spia`, `spib` and `spic` constructors by `new` constructor
|
||||||
|
- Replace I2C `i2ca`, `i2cb` constructors by `new` constructor. Update constructor
|
||||||
|
to fail on invalid fast I2C speed system clock values
|
||||||
|
- Renamed `gpio::pins` to `gpio::pin` and `gpio::dynpins` to `gpio::dynpin`
|
||||||
|
- Simplify UART clock divider calculations and remove `libm` dependency consequently
|
||||||
|
|
||||||
|
## [v0.6.0] 2024-06-16
|
||||||
|
|
||||||
|
- Updated `embedded-hal` to v1
|
||||||
|
- Added optional `defmt` v0.3 feature and support.
|
||||||
|
|
||||||
|
## v0.5.2 2024-06-16
|
||||||
|
|
||||||
|
## Fixed
|
||||||
|
|
||||||
|
- Replaced usage to `ptr::write_volatile` in UART module which is denied on more recent Rust
|
||||||
|
compilers.
|
||||||
|
|
||||||
|
## v0.5.1
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
|
||||||
|
- Updated dependencies:
|
||||||
|
- `cortex-m-rtic` (dev-depencency) to 1.1.2
|
||||||
|
- `once_cell` to 1.12.0
|
||||||
|
- Other dependencies: Only revision has changed
|
||||||
|
|
||||||
|
## v0.5.0
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Reactored IRQ handling, so that `unmask` operations can be moved to HAL
|
||||||
|
- Added UART IRQ handler. Right now, can only perform reception, TX still needs to be done in
|
||||||
|
a blocking manner
|
||||||
|
- Added RTIC template and RTIC UART IRQ application
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Bugfix in UART code where RX and TX could not be enabled or disabled independently
|
||||||
|
|
||||||
|
## v0.4.3
|
||||||
|
|
||||||
|
- Various smaller fixes for READMEs, update of links in documentation
|
||||||
|
- Simplified CI for github, do not use `cross`
|
||||||
|
- New `blinky-pac` example
|
||||||
|
- Use HAL delay in `blinky` example
|
||||||
|
|
||||||
|
## v0.4.2
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- `port_mux` function to set pin function select manually
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Clear TX and RX FIFO in SPI transfer function
|
||||||
|
|
||||||
|
## v0.4.1
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Initial blockmode setting was not set in SPI constructor
|
||||||
|
|
||||||
|
## v0.4.0
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Replaced `Hertz` by `impl Into<Hertz>` completely and removed
|
||||||
|
`+ Copy` where not necessary
|
||||||
|
|
||||||
|
## v0.3.1
|
||||||
|
|
||||||
|
- Updated all links to point to new repository
|
||||||
|
|
||||||
|
## v0.3.0
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- TIM Cascade example
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- `CountDownTimer` new function now expects an `impl Into<Hertz>` instead of `Hertz`
|
||||||
|
- Primary repository now hosted on IRS external git: https://egit.irs.uni-stuttgart.de/rust/va108xx-hal
|
||||||
|
- Relicensed as Apache-2.0
|
||||||
|
|
||||||
|
## v0.2.3
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Basic API for EDAC functionality
|
||||||
|
- PWM implementation and example
|
||||||
|
- API to perform peripheral resets
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Improved Timer API. It is now possible to simply use `new` on `CountDownTimer`
|
||||||
|
|
||||||
|
## v0.2.2
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- DelayUs and DelayMs trait implementations for timer
|
||||||
|
- SPI implementation for blocking API, supports blockmode as well
|
||||||
|
- Basic I2C implementation for blocking API
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- API which expects values in Hertz now uses `impl Into<Hertz>` as input parameter
|
||||||
|
|
||||||
|
## v0.2.1
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Adds the IRQ interface to configure interrupts on output and input pins
|
||||||
|
- Utility function to set up millisecond timer with `TIM0`
|
||||||
|
- Function to set clock divisor registers in `clock` module
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Minor optimizations and tweaks for GPIO module
|
||||||
|
- Moved the `FilterClkSel` struct to the `clock` module, re-exporting in `gpio`
|
||||||
|
- Clearing output state at initialization of Output pins
|
||||||
|
|
||||||
|
## v0.2.0
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- New GPIO implementation which uses type-level programming. Implementation heavily based on the
|
||||||
|
ATSAMD GPIO HAL: https://docs.rs/atsamd-hal/0.13.0/atsamd_hal/gpio/v2/index.html
|
||||||
|
- Changes to API, therefore minor version bump
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- UART implementation
|
||||||
|
- UART example
|
||||||
|
- Some bugfixes for GPIO implementation
|
||||||
|
- Rust edition updated to 2021
|
||||||
|
|
||||||
|
## v0.1.0
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- First version of the HAL which adds the GPIO implementation and timer implementation.
|
||||||
|
- Also adds some examples and helper files to set up new binary crates
|
||||||
|
- RTT example application
|
||||||
|
- Added basic test binary in form of an example
|
||||||
|
- README with basic instructions how to set up own binary crate
|
||||||
|
|
||||||
|
[unreleased]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.12.0...HEAD
|
||||||
|
[v0.12.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.11.1...va108xx-hal-v0.12.0
|
||||||
|
[v0.11.1]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.11.0...va108xx-hal-v0.11.1
|
||||||
|
[v0.11.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.10.0...va108xx-hal-v0.11.0
|
||||||
|
[v0.10.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.9.0...va108xx-hal-v0.10.0
|
||||||
|
[v0.9.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.8.0...va108xx-hal-v0.9.0
|
||||||
|
[v0.8.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.7.0...va108xx-hal-v0.8.0
|
||||||
|
[v0.7.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/compare/va108xx-hal-v0.6.0...va108xx-hal-v0.7.0
|
||||||
|
[v0.6.0]: https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/tag/va108xx-hal-v0.6.0
|
||||||
36
va108xx/va108xx-hal/Cargo.toml
Normal file
36
va108xx/va108xx-hal/Cargo.toml
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
[package]
|
||||||
|
name = "va108xx-hal"
|
||||||
|
version = "0.12.0"
|
||||||
|
authors = ["Robin Mueller <muellerr@irs.uni-stuttgart.de>"]
|
||||||
|
edition = "2021"
|
||||||
|
description = "HAL for the Vorago VA108xx family of microcontrollers"
|
||||||
|
homepage = "https://egit.irs.uni-stuttgart.de/rust/va108xx-rs"
|
||||||
|
repository = "https://egit.irs.uni-stuttgart.de/rust/va108xx-rs"
|
||||||
|
license = "Apache-2.0"
|
||||||
|
keywords = ["no-std", "hal", "cortex-m", "vorago", "va108xx"]
|
||||||
|
categories = ["aerospace", "embedded", "no-std", "hardware-support"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = { version = "0.7", features = ["critical-section-single-core"]}
|
||||||
|
vorago-shared-hal = { version = "0.2", features = ["vor1x"] }
|
||||||
|
fugit = "0.3"
|
||||||
|
thiserror = { version = "2", default-features = false }
|
||||||
|
va108xx = { version = "0.6", default-features = false, features = ["critical-section", "defmt"] }
|
||||||
|
defmt = { version = "1", optional = true }
|
||||||
|
|
||||||
|
[target.'cfg(all(target_arch = "arm", target_os = "none"))'.dependencies]
|
||||||
|
portable-atomic = { version = "1", features = ["unsafe-assume-single-core"] }
|
||||||
|
[target.'cfg(not(all(target_arch = "arm", target_os = "none")))'.dependencies]
|
||||||
|
portable-atomic = "1"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["rt"]
|
||||||
|
rt = ["va108xx/rt"]
|
||||||
|
defmt = ["dep:defmt", "vorago-shared-hal/defmt"]
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
all-features = true
|
||||||
|
rustdoc-args = ["--generate-link-to-definition"]
|
||||||
|
|
||||||
|
[package.metadata.cargo-machete]
|
||||||
|
ignored = ["cortex-m"]
|
||||||
201
va108xx/va108xx-hal/LICENSE-APACHE
Normal file
201
va108xx/va108xx-hal/LICENSE-APACHE
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
3
va108xx/va108xx-hal/NOTICE
Normal file
3
va108xx/va108xx-hal/NOTICE
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
Rust Hardware Abstraction Layer (HAL) crate for the Vorago VA108xx family of MCUs
|
||||||
|
|
||||||
|
This software contains code developed at the University of Stuttgart.
|
||||||
69
va108xx/va108xx-hal/README.md
Normal file
69
va108xx/va108xx-hal/README.md
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
[](https://crates.io/crates/va108xx-hal)
|
||||||
|
[](https://docs.rs/va108xx-hal)
|
||||||
|
|
||||||
|
# HAL for the Vorago VA108xx MCU family
|
||||||
|
|
||||||
|
This repository contains the **H**ardware **A**bstraction **L**ayer (HAL), which is an additional
|
||||||
|
hardware abstraction on top of the [peripheral access API](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/va108xx).
|
||||||
|
|
||||||
|
It is the result of reading the datasheet for the device and encoding a type-safe layer over the
|
||||||
|
raw PAC. This crate also implements traits specified by the
|
||||||
|
[embedded-hal](https://github.com/rust-embedded/embedded-hal) project, making it compatible with
|
||||||
|
various drivers in the embedded rust ecosystem.
|
||||||
|
|
||||||
|
In contrats to other HAL implementations, there is only one chip variant available here so there
|
||||||
|
is no need to pass the chip variant as a feature.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
Building an application requires the `thumbv6m-none-eabi` cross-compiler toolchain.
|
||||||
|
If you have not installed it yet, you can do so with
|
||||||
|
|
||||||
|
```sh
|
||||||
|
rustup target add thumbv6m-none-eabi
|
||||||
|
```
|
||||||
|
|
||||||
|
After that, you can use `cargo build` to build the development version of the crate.
|
||||||
|
|
||||||
|
## Setting up your own binary crate
|
||||||
|
|
||||||
|
If you have a custom board, you might be interested in setting up a new binary crate for your
|
||||||
|
project. These steps aim to provide a complete list to get a binary crate working to flash
|
||||||
|
your custom board.
|
||||||
|
|
||||||
|
The hello world of embedded development is usually to blinky a LED. This example
|
||||||
|
is contained within the
|
||||||
|
[examples folder](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/blinky.rs).
|
||||||
|
|
||||||
|
1. Set up your Rust cross-compiler if you have not done so yet. See more in the [build chapter](#Building)
|
||||||
|
2. Create a new binary crate with `cargo init`
|
||||||
|
3. To ensure that `cargo build` cross-compiles, it is recommended to create a `.cargo/config.toml`
|
||||||
|
file. A sample `.cargo/config.toml` file is provided in this repository as well
|
||||||
|
4. Copy the `memory.x` file into your project. This file contains information required by the linker.
|
||||||
|
5. Copy the `blinky.rs` file to the `src/main.rs` file in your binary crate
|
||||||
|
6. You need to add some dependencies to your `Cargo.toml` file
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[dependencies]
|
||||||
|
cortex-m = "<Compatible Version>"
|
||||||
|
cortex-m-rt = "<Compatible Version>"
|
||||||
|
panic-halt = "<Compatible Version>"
|
||||||
|
embedded-hal = "<Compatible Version>"
|
||||||
|
|
||||||
|
[dependencies.va108xx-hal]
|
||||||
|
version = "<Most Recent Version>"
|
||||||
|
features = ["rt"]
|
||||||
|
```
|
||||||
|
|
||||||
|
6. Build the application with `cargo build`
|
||||||
|
|
||||||
|
7. Flashing the board might work differently for different boards and there is usually
|
||||||
|
more than one way. You can find example instructions in primary README.
|
||||||
|
|
||||||
|
## Embedded Rust
|
||||||
|
|
||||||
|
If you have not done this yet, it is recommended to read some of the excellent resources available
|
||||||
|
to learn Rust:
|
||||||
|
|
||||||
|
- [Rust Embedded Book](https://docs.rust-embedded.org/book/)
|
||||||
|
- [Rust Discovery Book](https://docs.rust-embedded.org/discovery/)
|
||||||
13
va108xx/va108xx-hal/automation/Dockerfile
Normal file
13
va108xx/va108xx-hal/automation/Dockerfile
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Run the following commands from root directory to build and run locally
|
||||||
|
# docker build -f automation/Dockerfile -t <NAME> .
|
||||||
|
# docker run -it <NAME>
|
||||||
|
FROM rust:latest
|
||||||
|
RUN apt-get update
|
||||||
|
RUN apt-get --yes upgrade
|
||||||
|
# tzdata is a dependency, won't install otherwise
|
||||||
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
|
|
||||||
|
RUN rustup install nightly && \
|
||||||
|
rustup target add thumbv6m-none-eabi && \
|
||||||
|
rustup +nightly target add thumbv6m-none-eabi && \
|
||||||
|
rustup component add rustfmt clippy
|
||||||
37
va108xx/va108xx-hal/automation/Jenkinsfile
vendored
Normal file
37
va108xx/va108xx-hal/automation/Jenkinsfile
vendored
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
pipeline {
|
||||||
|
agent {
|
||||||
|
dockerfile {
|
||||||
|
dir 'automation'
|
||||||
|
reuseNode true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
stages {
|
||||||
|
stage('Clippy') {
|
||||||
|
steps {
|
||||||
|
sh 'cargo clippy'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Rustfmt') {
|
||||||
|
steps {
|
||||||
|
sh 'cargo fmt'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Docs') {
|
||||||
|
steps {
|
||||||
|
sh 'cargo +nightly doc'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Check') {
|
||||||
|
steps {
|
||||||
|
sh 'cargo check --target thumbv6m-none-eabi'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Check Examples') {
|
||||||
|
steps {
|
||||||
|
sh 'cargo check --target thumbv6m-none-eabi --examples'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
va108xx/va108xx-hal/docs.sh
Executable file
3
va108xx/va108xx-hal/docs.sh
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
export RUSTDOCFLAGS="--cfg docsrs --generate-link-to-definition -Z unstable-options"
|
||||||
|
cargo +nightly doc --all-features --open
|
||||||
10
va108xx/va108xx-hal/jlink.gdb
Normal file
10
va108xx/va108xx-hal/jlink.gdb
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
target remote localhost:2331
|
||||||
|
|
||||||
|
monitor reset
|
||||||
|
|
||||||
|
# *try* to stop at the user entry point (it might be gone due to inlining)
|
||||||
|
break main
|
||||||
|
|
||||||
|
load
|
||||||
|
|
||||||
|
continue
|
||||||
10
va108xx/va108xx-hal/memory.x
Normal file
10
va108xx/va108xx-hal/memory.x
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
FLASH : ORIGIN = 0x00000000, LENGTH = 0x20000 /* 128K */
|
||||||
|
RAM : ORIGIN = 0x10000000, LENGTH = 0x08000 /* 32K */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is where the call stack will be allocated. */
|
||||||
|
/* The stack is of the full descending type. */
|
||||||
|
/* NOTE Do NOT modify `_stack_start` unless you know what you are doing */
|
||||||
|
_stack_start = ORIGIN(RAM) + LENGTH(RAM);
|
||||||
32
va108xx/va108xx-hal/src/clock.rs
Normal file
32
va108xx/va108xx-hal/src/clock.rs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
//! # API for clock related functionality
|
||||||
|
//!
|
||||||
|
//! This also includes functionality to enable the peripheral clocks
|
||||||
|
pub use vorago_shared_hal::gpio::FilterClockSelect;
|
||||||
|
pub use vorago_shared_hal::sysconfig::{disable_peripheral_clock, enable_peripheral_clock};
|
||||||
|
|
||||||
|
pub fn set_clk_div_register(syscfg: &mut va108xx::Sysconfig, clk_sel: FilterClockSelect, div: u32) {
|
||||||
|
match clk_sel {
|
||||||
|
FilterClockSelect::SysClk => (),
|
||||||
|
FilterClockSelect::Clk1 => {
|
||||||
|
syscfg.ioconfig_clkdiv1().write(|w| unsafe { w.bits(div) });
|
||||||
|
}
|
||||||
|
FilterClockSelect::Clk2 => {
|
||||||
|
syscfg.ioconfig_clkdiv2().write(|w| unsafe { w.bits(div) });
|
||||||
|
}
|
||||||
|
FilterClockSelect::Clk3 => {
|
||||||
|
syscfg.ioconfig_clkdiv3().write(|w| unsafe { w.bits(div) });
|
||||||
|
}
|
||||||
|
FilterClockSelect::Clk4 => {
|
||||||
|
syscfg.ioconfig_clkdiv4().write(|w| unsafe { w.bits(div) });
|
||||||
|
}
|
||||||
|
FilterClockSelect::Clk5 => {
|
||||||
|
syscfg.ioconfig_clkdiv5().write(|w| unsafe { w.bits(div) });
|
||||||
|
}
|
||||||
|
FilterClockSelect::Clk6 => {
|
||||||
|
syscfg.ioconfig_clkdiv6().write(|w| unsafe { w.bits(div) });
|
||||||
|
}
|
||||||
|
FilterClockSelect::Clk7 => {
|
||||||
|
syscfg.ioconfig_clkdiv7().write(|w| unsafe { w.bits(div) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
va108xx/va108xx-hal/src/gpio/mod.rs
Normal file
20
va108xx/va108xx-hal/src/gpio/mod.rs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
//! GPIO support module.
|
||||||
|
//!
|
||||||
|
//! Contains abstractions to use the pins provided by the [crate::pins] module as GPIO or
|
||||||
|
//! IO peripheral pins.
|
||||||
|
//!
|
||||||
|
//! The core data structures provided for this are the
|
||||||
|
//!
|
||||||
|
//! - [Output] for push-pull output pins.
|
||||||
|
//! - [Input] for input pins.
|
||||||
|
//! - [Flex] for pins with flexible configuration requirements.
|
||||||
|
//! - [IoPeriphPin] for IO peripheral pins.
|
||||||
|
//!
|
||||||
|
//! The [crate::pins] module exposes singletons to access the [Pin]s required by this module
|
||||||
|
//! in a type-safe way.
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! - [Blinky example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/blinky.rs)
|
||||||
|
//! - [Async GPIO example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/embassy/src/bin/async-gpio.rs)
|
||||||
|
pub use vorago_shared_hal::gpio::*;
|
||||||
6
va108xx/va108xx-hal/src/i2c.rs
Normal file
6
va108xx/va108xx-hal/src/i2c.rs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
//! API for the I2C peripheral
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! - [REB1 I2C temperature sensor example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/vorago-reb1/examples/adt75-temp-sensor.rs)
|
||||||
|
pub use vorago_shared_hal::i2c::*;
|
||||||
60
va108xx/va108xx-hal/src/lib.rs
Normal file
60
va108xx/va108xx-hal/src/lib.rs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
|
||||||
|
|
||||||
|
use gpio::Port;
|
||||||
|
pub use va108xx;
|
||||||
|
pub use va108xx as pac;
|
||||||
|
|
||||||
|
pub mod clock;
|
||||||
|
pub mod gpio;
|
||||||
|
pub mod i2c;
|
||||||
|
pub mod pins;
|
||||||
|
pub mod prelude;
|
||||||
|
pub mod pwm;
|
||||||
|
pub mod spi;
|
||||||
|
pub mod sysconfig;
|
||||||
|
pub mod time;
|
||||||
|
pub mod timer;
|
||||||
|
pub mod uart;
|
||||||
|
|
||||||
|
pub use vorago_shared_hal::{
|
||||||
|
disable_nvic_interrupt, enable_nvic_interrupt, FunctionSelect, InterruptConfig,
|
||||||
|
PeripheralSelect,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// This is the NONE destination reigster value for the IRQSEL peripheral.
|
||||||
|
pub const IRQ_DST_NONE: u32 = 0xffffffff;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
|
||||||
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
|
||||||
|
#[error("invalid pin with number {0}")]
|
||||||
|
pub struct InvalidPinError(u8);
|
||||||
|
|
||||||
|
/// Can be used to manually manipulate the function select of port pins.
|
||||||
|
///
|
||||||
|
/// The function selection table can be found on p.36 of the programmers guide. Please note
|
||||||
|
/// that most of the structures and APIs in this library will automatically correctly configure
|
||||||
|
/// the pin or statically expect the correct pin type.
|
||||||
|
pub fn port_function_select(
|
||||||
|
ioconfig: &mut pac::Ioconfig,
|
||||||
|
port: Port,
|
||||||
|
pin: u8,
|
||||||
|
funsel: FunctionSelect,
|
||||||
|
) -> Result<(), InvalidPinError> {
|
||||||
|
if (port == Port::A && pin >= 32) || (port == Port::B && pin >= 24) {
|
||||||
|
return Err(InvalidPinError(pin));
|
||||||
|
}
|
||||||
|
|
||||||
|
let reg_block = match port {
|
||||||
|
Port::A => ioconfig.porta(pin as usize),
|
||||||
|
Port::B => ioconfig.portb(pin as usize),
|
||||||
|
};
|
||||||
|
|
||||||
|
reg_block.modify(|_, w| unsafe { w.funsel().bits(funsel as u8) });
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) mod sealed {
|
||||||
|
pub trait Sealed {}
|
||||||
|
}
|
||||||
6
va108xx/va108xx-hal/src/pins.rs
Normal file
6
va108xx/va108xx-hal/src/pins.rs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
//! Pin resource management singletons.
|
||||||
|
//!
|
||||||
|
//! This module contains the pin singletons. It allows creating those singletons
|
||||||
|
//! to access the [Pin] structures of individual ports in a safe way with checked ownership
|
||||||
|
//! rules.
|
||||||
|
pub use vorago_shared_hal::pins::*;
|
||||||
5
va108xx/va108xx-hal/src/prelude.rs
Normal file
5
va108xx/va108xx-hal/src/prelude.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
//! Prelude
|
||||||
|
pub use fugit::ExtU32 as _;
|
||||||
|
pub use fugit::RateExtU32 as _;
|
||||||
|
|
||||||
|
pub use crate::time::*;
|
||||||
8
va108xx/va108xx-hal/src/pwm.rs
Normal file
8
va108xx/va108xx-hal/src/pwm.rs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
//! API for Pulse-Width Modulation (PWM)
|
||||||
|
//!
|
||||||
|
//! The Vorago VA108xx devices use the TIM peripherals to perform PWM related tasks
|
||||||
|
//!
|
||||||
|
//! ## Examples
|
||||||
|
//!
|
||||||
|
//! - [PWM example](https://egit.irs.uni-stuttgart.de/rust/va108xx-rs/src/branch/main/examples/simple/examples/pwm.rs)
|
||||||
|
pub use vorago_shared_hal::pwm::*;
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user