Combine the esp-ulp-riscv-hal and esp32c6-lp-hal packages (#1115)

* Combine `esp-ulp-riscv-hal` and `esp32c6-lp-hal` into a single package

* Update LP core examples

* Update CI workflow

* Fix `LP_UART` example
This commit is contained in:
Jesse Braham 2024-01-26 13:46:51 +00:00 committed by GitHub
parent f52aa1351c
commit 9bf70ff792
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
39 changed files with 767 additions and 1024 deletions

View File

@ -65,6 +65,34 @@ jobs:
- name: rustdoc
run: cd esp-hal-smartled/ && cargo doc -Zbuild-std=core --target=riscv32imc-unknown-none-elf --features=esp32c3
esp-lp-hal:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@v1
with:
target: riscv32imc-unknown-none-elf,riscv32imac-unknown-none-elf
toolchain: nightly
components: rust-src
- uses: Swatinem/rust-cache@v2
# Perform a full build initially to verify that the examples not only
# build, but also link successfully.
- name: build esp-lp-hal (esp32c6)
run: cd esp-lp-hal/ && cargo build --release --target=riscv32imac-unknown-none-elf --features=esp32c6 --examples
- name: build esp-lp-hal (esp32s2)
run: cd esp-lp-hal/ && cargo build --release --target=riscv32imc-unknown-none-elf --features=esp32s2 --examples
- name: build esp-lp-hal (esp32s3)
run: cd esp-lp-hal/ && cargo build --release --target=riscv32imc-unknown-none-elf --features=esp32s3 --examples
# Ensure documentation can be built
- name: rustdoc
run: |
cd esp-lp-hal/
cargo doc --features=esp32c6
cargo doc --features=esp32s2
cargo doc --features=esp32s3
esp-riscv-rt:
runs-on: ubuntu-latest
@ -89,28 +117,6 @@ jobs:
- name: rustdoc
run: cd esp-riscv-rt/ && cargo doc
esp-ulp-riscv-hal:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@v1
with:
target: riscv32imc-unknown-none-elf
toolchain: nightly
components: rust-src
- uses: Swatinem/rust-cache@v2
# Perform a full build initially to verify that the examples not only
# build, but also link successfully.
- name: build esp-ulp-riscv-hal (esp32s2)
run: cd esp-ulp-riscv-hal/ && cargo build --release --features=esp32s2 --examples
- name: build esp-ulp-riscv-hal (esp32s3)
run: cd esp-ulp-riscv-hal/ && cargo build --release --features=esp32s3 --examples
# Ensure documentation can be built
- name: rustdoc
run: cd esp-ulp-riscv-hal/ && cargo doc --features=esp32s3
esp32-hal:
runs-on: ubuntu-latest
@ -290,10 +296,12 @@ jobs:
components: rust-src
- uses: Swatinem/rust-cache@v2
# build the lp-hal examples first to make sure the examples which expect
# the ELF files to be present will compile
- name: build esp32c6-lp-hal prerequisites
run: cd esp32c6-lp-hal/ && cargo +nightly build --release --examples
# Build the `esp-lp-hal` examples first to make sure the examples which
# expect these ELF files to be present will compile.
- name: build prerequisites
run: |
cd esp-lp-hal/
cargo build --release --features=esp32c6 --target=riscv32imac-unknown-none-elf --examples
# Perform a full build initially to verify that the examples not only
# build, but also link successfully.
@ -346,26 +354,6 @@ jobs:
- name: rustdoc
run: cd esp32c6-hal/ && cargo doc --features=eh1
esp32c6-lp-hal:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@v1
with:
target: riscv32imac-unknown-none-elf
toolchain: nightly
components: rust-src
- uses: Swatinem/rust-cache@v2
# Perform a full build initially to verify that the examples not only
# build, but also link successfully.
- name: build esp32c6-lp-hal (no features)
run: cd esp32c6-lp-hal/ && cargo +nightly build --release --examples
# Ensure documentation can be built
- name: rustdoc
run: cd esp32c6-lp-hal/ && cargo doc
esp32h2-hal:
runs-on: ubuntu-latest
@ -478,10 +466,12 @@ jobs:
components: rust-src
- uses: Swatinem/rust-cache@v2
# build the esp-ulp-riscv-hal examples first to make sure the examples which expect
# the ELF files to be present will compile
- name: build esp-ulp-riscv-hal prerequisites
run: cd esp-ulp-riscv-hal/ && cargo +nightly build --release --features=esp32s2 --examples
# Build the `esp-lp-hal` examples first to make sure the examples which
# expect these ELF files to be present will compile.
- name: build prerequisites
run: |
cd esp-lp-hal/
cargo +nightly build --release --features=esp32s2 --target=riscv32imc-unknown-none-elf --examples
# Perform a full build initially to verify that the examples not only
# build, but also link successfully.
@ -494,9 +484,6 @@ jobs:
cd esp32s2-hal/
cargo +esp build --examples --features=eh1,ufmt,log
cargo +esp build --examples --features=eh1,ufmt,defmt
# FIXME: `time-systick` feature disabled for now, see 'esp32s2-hal/Cargo.toml'.
# - name: check esp32s2-hal (async, systick)
# run: cd esp32s2-hal/ && cargo check --example=embassy_hello_world --features=embassy,embassy-time-systick,executor
- name: check esp32s2-hal (embassy, timg0)
run: |
cd esp32s2-hal/
@ -556,10 +543,12 @@ jobs:
components: rust-src
- uses: Swatinem/rust-cache@v2
# build the esp-ulp-riscv-hal examples first to make sure the examples which expect
# the ELF files to be present will compile
- name: build esp-ulp-riscv-hal prerequisites
run: cd esp-ulp-riscv-hal/ && cargo +nightly build --release --features=esp32s3 --examples
# Build the `esp-lp-hal` examples first to make sure the examples which
# expect these ELF files to be present will compile.
- name: build prerequisites
run: |
cd esp-lp-hal/
cargo build --release --features=esp32s3 --target=riscv32imc-unknown-none-elf --examples
# Perform a full build initially to verify that the examples not only
# build, but also link successfully.
@ -641,12 +630,13 @@ jobs:
components: rust-src
- uses: Swatinem/rust-cache@v2
# Build the esp32c6-lp-hal examples first. This is done to ensure the
# examples which expect the ELF files to be present will compile:
- name: build esp32c6-lp-hal prerequisites
run: cd esp32c6-lp-hal && cargo build --release --examples
# Verify the MSRV for all RISC-V chips.
- name: msrv (esp-lp-hal)
run: |
cd esp-lp-hal/
cargo build --features=esp32c6 --target=riscv32imac-unknown-none-elf
cargo build --features=esp32s2 --target=riscv32imc-unknown-none-elf
cargo build --features=esp32s3 --target=riscv32imc-unknown-none-elf
- name: msrv (esp32c2-hal)
run: |
cd esp32c2-hal/
@ -662,10 +652,6 @@ jobs:
cd esp32c6-hal/
cargo build --features=eh1,ufmt,log
cargo build --features=defmt
- name: msrv (esp32c6-lp-hal)
run: |
cd esp32c6-lp-hal/
cargo build
- name: msrv (esp32h2-hal)
run: |
cd esp32h2-hal/
@ -695,11 +681,6 @@ jobs:
version: ${{ env.MSRV }}
- uses: Swatinem/rust-cache@v2
# Build the `esp-ulp-riscv-hal` examples first. This is done to ensure
# the examples which expect the ELF files to be present will compile:
- name: build esp-ulp-riscv-hal prerequisites
run: cd esp-ulp-riscv-hal && cargo build --release --features=esp32s3 --examples
# Verify the MSRV for all Xtensa chips:
- name: msrv (esp32-hal)
run: |
@ -733,20 +714,20 @@ jobs:
- uses: Swatinem/rust-cache@v2
# Run 'cargo clippy' on all packages targeting RISC-V:
- name: clippy (esp-lp-hal, esp32c6)
run: cd esp-lp-hal && cargo clippy --features=esp32c6 -- -D warnings
- name: clippy (esp-lp-hal, esp32s2)
run: cd esp-lp-hal && cargo clippy --features=esp32s2 -- -D warnings
- name: clippy (esp-lp-hal, esp32s3)
run: cd esp-lp-hal && cargo clippy --features=esp32s3 -- -D warnings
- name: clippy (esp-riscv-rt)
run: cd esp-riscv-rt && cargo clippy --target=riscv32imc-unknown-none-elf -- -D warnings
- name: clippy (esp-ulp-riscv-hal, esp32s2)
run: cd esp-ulp-riscv-hal && cargo clippy --features=esp32s2 -- -D warnings
- name: clippy (esp-ulp-riscv-hal, esp32s3)
run: cd esp-ulp-riscv-hal && cargo clippy --features=esp32s3 -- -D warnings
- name: clippy (esp32c2-hal)
run: cd esp32c2-hal && cargo clippy -- -D warnings
- name: clippy (esp32c3-hal)
run: cd esp32c3-hal && cargo clippy -- -D warnings
- name: clippy (esp32c6-hal)
run: cd esp32c6-hal && cargo clippy -- -D warnings
- name: clippy (esp32c6-lp-hal)
run: cd esp32c6-lp-hal && cargo clippy -- -D warnings
- name: clippy (esp32h2-hal)
run: cd esp32h2-hal && cargo clippy -- -D warnings
- name: clippy (esp32p4-hal)
@ -790,10 +771,10 @@ jobs:
run: cargo fmt --all --manifest-path=esp-hal-procmacros/Cargo.toml -- --check
- name: rustfmt (esp-hal-smartled)
run: cargo fmt --all --manifest-path=esp-hal-smartled/Cargo.toml -- --check
- name: rustfmt (esp-lp-hal)
run: cargo fmt --all --manifest-path=esp-lp-hal/Cargo.toml -- --check
- name: rustfmt (esp-riscv-rt)
run: cargo fmt --all --manifest-path=esp-riscv-rt/Cargo.toml -- --check
- name: rustfmt (esp-ulp-riscv-hal)
run: cargo fmt --all --manifest-path=esp-ulp-riscv-hal/Cargo.toml -- --check
- name: rustfmt (esp32-hal)
run: cargo fmt --all --manifest-path=esp32-hal/Cargo.toml -- --check
- name: rustfmt (esp32c2-hal)
@ -802,8 +783,6 @@ jobs:
run: cargo fmt --all --manifest-path=esp32c3-hal/Cargo.toml -- --check
- name: rustfmt (esp32c6-hal)
run: cargo fmt --all --manifest-path=esp32c6-hal/Cargo.toml -- --check
- name: rustfmt (esp32c6-lp-hal)
run: cargo fmt --all --manifest-path=esp32c6-lp-hal/Cargo.toml -- --check
- name: rustfmt (esp32h2-hal)
run: cargo fmt --all --manifest-path=esp32h2-hal/Cargo.toml -- --check
- name: rustfmt (esp32p4-hal)

View File

@ -12,11 +12,11 @@
// Uncomment ONE line for the chip you want to work on.
// This makes rust-analyzer work on the HAL crate and all its dependencies.
"rust-analyzer.linkedProjects": [
// "esp-lp-hal/Cargo.toml"
"esp32-hal/Cargo.toml"
// "esp32c2-hal/Cargo.toml"
// "esp32c3-hal/Cargo.toml"
// "esp32c6-hal/Cargo.toml"
// "esp32c6-lp-hal/Cargo.toml"
// "esp32h2-hal/Cargo.toml"
// "esp32p4-hal/Cargo.toml"
// "esp32s2-hal/Cargo.toml"

View File

@ -108,6 +108,12 @@ fn get_hal_crate() -> (
use proc_macro_crate::crate_name;
// Package name:
#[cfg(any(
feature = "esp32c6-lp",
feature = "esp32s2-ulp",
feature = "esp32s3-ulp"
))]
let hal_crate = crate_name("esp-lp-hal");
#[cfg(feature = "esp32")]
let hal_crate = crate_name("esp32-hal");
#[cfg(feature = "esp32c2")]
@ -116,22 +122,22 @@ fn get_hal_crate() -> (
let hal_crate = crate_name("esp32c3-hal");
#[cfg(feature = "esp32c6")]
let hal_crate = crate_name("esp32c6-hal");
#[cfg(feature = "esp32c6-lp")]
let hal_crate = crate_name("esp32c6-lp-hal");
#[cfg(feature = "esp32h2")]
let hal_crate = crate_name("esp32h2-hal");
#[cfg(feature = "esp32p4")]
let hal_crate = crate_name("esp32p4-hal");
#[cfg(feature = "esp32s2")]
let hal_crate = crate_name("esp32s2-hal");
#[cfg(feature = "esp32s2-ulp")]
let hal_crate = crate_name("esp-ulp-riscv-hal");
#[cfg(feature = "esp32s3")]
let hal_crate = crate_name("esp32s3-hal");
#[cfg(feature = "esp32s3-ulp")]
let hal_crate = crate_name("esp-ulp-riscv-hal");
// Crate name:
#[cfg(any(
feature = "esp32c6-lp",
feature = "esp32s2-ulp",
feature = "esp32s3-ulp"
))]
let hal_crate_name = Ident::new("esp_lp_hal", Span::call_site().into());
#[cfg(feature = "esp32")]
let hal_crate_name = Ident::new("esp32_hal", Span::call_site().into());
#[cfg(feature = "esp32c2")]
@ -140,20 +146,14 @@ fn get_hal_crate() -> (
let hal_crate_name = Ident::new("esp32c3_hal", Span::call_site().into());
#[cfg(feature = "esp32c6")]
let hal_crate_name = Ident::new("esp32c6_hal", Span::call_site().into());
#[cfg(feature = "esp32c6-lp")]
let hal_crate_name = Ident::new("esp32c6_lp_hal", Span::call_site().into());
#[cfg(feature = "esp32h2")]
let hal_crate_name = Ident::new("esp32h2_hal", Span::call_site().into());
#[cfg(feature = "esp32p4")]
let hal_crate_name = Ident::new("esp32p4_hal", Span::call_site().into());
#[cfg(feature = "esp32s2")]
let hal_crate_name = Ident::new("esp32s2_hal", Span::call_site().into());
#[cfg(feature = "esp32s2-ulp")]
let hal_crate_name = Ident::new("esp_ulp_riscv_hal", Span::call_site().into());
#[cfg(feature = "esp32s3")]
let hal_crate_name = Ident::new("esp32s3_hal", Span::call_site().into());
#[cfg(feature = "esp32s3-ulp")]
let hal_crate_name = Ident::new("esp_ulp_riscv_hal", Span::call_site().into());
(hal_crate, hal_crate_name)
}
@ -679,18 +679,9 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
use self::lp_core::{extract_pin, get_simplename, make_magic_symbol_name};
#[cfg(feature = "esp32c6-lp")]
let found_crate =
crate_name("esp32c6-lp-hal").expect("esp32c6_lp_hal is present in `Cargo.toml`");
#[cfg(any(feature = "esp32s2-ulp", feature = "esp32s3-ulp"))]
let found_crate =
crate_name("esp-ulp-riscv-hal").expect("esp-ulp-riscv-hal is present in `Cargo.toml`");
let found_crate = crate_name("esp-lp-hal").expect("esp-lp-hal is present in `Cargo.toml`");
let hal_crate = match found_crate {
#[cfg(feature = "esp32c6-lp")]
FoundCrate::Itself => quote!(esp32c6_lp_hal),
#[cfg(any(feature = "esp32s2-ulp", feature = "esp32s3-ulp"))]
FoundCrate::Itself => quote!(esp_ulp_riscv_hal),
FoundCrate::Itself => quote!(esp_lp_hal),
FoundCrate::Name(name) => {
let ident = Ident::new(&name, Span::call_site());
quote!( #ident::Something )
@ -729,12 +720,12 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
}
used_pins.push(pin);
create_peripheral.push(quote!(
let mut #param_name = unsafe { the_hal::gpio::conjour().unwrap() };
let mut #param_name = unsafe { the_hal::gpio::conjure().unwrap() };
));
}
"LpUart" => {
create_peripheral.push(quote!(
let mut #param_name = unsafe { the_hal::uart::conjour().unwrap() };
let mut #param_name = unsafe { the_hal::uart::conjure().unwrap() };
));
}
_ => {
@ -772,6 +763,7 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
main(#(#param_names),*);
}
#f
)
.into()

View File

@ -0,0 +1,12 @@
[build]
# target = "riscv32imc-unknown-none-elf" # ESP32-S2 + ESP32-S3
target = "riscv32imac-unknown-none-elf" # ESP32-C6
[target.'cfg(target_arch = "riscv32")']
runner = "espflash flash --monitor"
rustflags = [
"-C", "link-arg=-Tlink.x",
]
[unstable]
build-std = ["core"]

View File

@ -12,12 +12,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Add the `esp32c6-lp-hal` package (#714)
- Add GPIO (output) and delay functionality to `esp32c6-lp-hal` (#715)
- Add GPIO input support and implement additional `embedded-hal` output traits for the C6's LP core [#720]
- Add the `ulp-riscv-hal` package (#840)
- Add LP_UART basic driver (#1113)
### Changed
- Renamed to `esp-ulp-riscv-hal` (#916)
### Fixed
### Removed
[Unreleased]: https://github.com/esp-rs/esp-hal/commits/main/esp32c6-lp-hal
[Unreleased]: https://github.com/esp-rs/esp-hal/commits/main/esp-lp-hal

64
esp-lp-hal/Cargo.toml Normal file
View File

@ -0,0 +1,64 @@
[package]
name = "esp-lp-hal"
version = "0.1.0"
edition = "2021"
rust-version = "1.67.0"
description = "HAL for low-power RISC-V coprocessors found in ESP32 devices"
repository = "https://github.com/esp-rs/esp-hal"
license = "MIT OR Apache-2.0"
keywords = [
"embedded",
"embedded-hal",
"esp",
"esp32",
"no-std",
]
categories = [
"embedded",
"hardware-support",
"no-std",
]
[dependencies]
cfg-if = "1.0.0"
embedded-hal-02 = { version = "0.2.7", package = "embedded-hal", optional = true, features = ["unproven"] }
embedded-hal-1 = { version = "1.0.0", package = "embedded-hal", optional = true }
esp32c6-lp = { version = "0.1.0", features = ["critical-section"], optional = true }
esp32s2-ulp = { version = "0.1.0", features = ["critical-section"], optional = true }
esp32s3-ulp = { version = "0.1.0", features = ["critical-section"], optional = true }
nb = { version = "1.1.0", optional = true }
paste = { version = "1.0.14", optional = true }
procmacros = { package = "esp-hal-procmacros", path = "../esp-hal-procmacros" }
riscv = { version = "0.11.0", features = ["critical-section-single-hart"] }
[dev-dependencies]
panic-halt = "0.2.0"
[features]
default = ["embedded-hal-02"]
embedded-hal-02 = ["dep:embedded-hal-02"]
embedded-hal-1 = ["dep:embedded-hal-1"]
esp32c6 = ["dep:esp32c6-lp", "procmacros/esp32c6-lp", "dep:nb", "dep:paste"]
esp32s2 = ["dep:esp32s2-ulp", "procmacros/esp32s2-ulp"]
esp32s3 = ["dep:esp32s3-ulp", "procmacros/esp32s3-ulp"]
debug = [
"esp32c6-lp?/impl-register-debug",
"esp32s2-ulp?/impl-register-debug",
"esp32s3-ulp?/impl-register-debug",
]
[[example]]
name = "blinky"
required-features = ["embedded-hal-02"]
[[example]]
name = "uart"
required-features = ["embedded-hal-02", "esp32c6"]
[patch.crates-io]
esp32s2-ulp = { git = "https://github.com/esp-rs/esp-pacs", rev = "bcab40a" }
esp32s3-ulp = { git = "https://github.com/esp-rs/esp-pacs", rev = "bcab40a" }

73
esp-lp-hal/README.md Normal file
View File

@ -0,0 +1,73 @@
# esp-lp-hal
[![Crates.io](https://img.shields.io/crates/v/esp-lp-hal?labelColor=1C2C2E&color=C96329&logo=Rust&style=flat-square)](https://crates.io/crates/esp-lp-hal)
[![docs.rs](https://img.shields.io/docsrs/esp-lp-hal?labelColor=1C2C2E&color=C96329&logo=rust&style=flat-square)](https://docs.rs/esp-lp-hal)
![Crates.io](https://img.shields.io/crates/l/esp-lp-hal?labelColor=1C2C2E&style=flat-square)
[![Matrix](https://img.shields.io/matrix/esp-rs:matrix.org?label=join%20matrix&labelColor=1C2C2E&color=BEC5C9&logo=matrix&style=flat-square)](https://matrix.to/#/#esp-rs:matrix.org)
`no_std` HAL for the low-power RISC-V coprocessors found on the ESP32-C6, ESP32-S2, and ESP32-S3 from Espressif.
Implements a number of the traits defined in [embedded-hal](https://github.com/rust-embedded/embedded-hal).
These devices uses the RISC-V ISA, which is officially supported by the Rust compiler via the `riscv32imc-unknown-none-elf` and `riscv32imac-unknown-none-elf` targets.
Please refer to the documentation for more information.
[c6-datasheet]: https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf
[s2-datasheet]: https://www.espressif.com/sites/default/files/documentation/esp32-s2_datasheet_en.pdf
[s3-datasheet]: https://www.espressif.com/sites/default/files/documentation/esp32-s3_datasheet_en.pdf
[c6-trm]: https://www.espressif.com/sites/default/files/documentation/esp32-c6_technical_reference_manual_en.pdf
[s2-trm]: https://www.espressif.com/sites/default/files/documentation/esp32-s2_technical_reference_manual_en.pdf
[s3-trm]: https://www.espressif.com/sites/default/files/documentation/esp32-s3_technical_reference_manual_en.pdf
## [Documentation]
[documentation]: https://docs.rs/esp-lp-hal/
## Supported Devices
| Chip | Datasheet | Technical Reference Manual | Target |
| :------: | :----------------------: | :------------------------: | :----------------------------: |
| ESP32-C6 | [ESP32-C6][c6-datasheet] | [ESP32-C6][c6-trm] | `riscv32imac-unknown-none-elf` |
| ESP32-S2 | [ESP32-S2][s2-datasheet] | [ESP32-S2][s2-trm] | `riscv32imc-unknown-none-elf` |
| ESP32-S3 | [ESP32-S3][s3-datasheet] | [ESP32-S3][s3-trm] | `riscv32imc-unknown-none-elf` |
## Resources
- [The Rust Programming Language](https://doc.rust-lang.org/book/)
- [The Embedded Rust Book](https://docs.rust-embedded.org/book/index.html)
- [The Rust on ESP Book](https://esp-rs.github.io/book/)
- Datasheets:
- [ESP32-C6](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
- [ESP32-S2](https://www.espressif.com/sites/default/files/documentation/esp32-s2_datasheet_en.pdf)
- [ESP32-S3](https://www.espressif.com/sites/default/files/documentation/esp32-s3_datasheet_en.pdf)
- Technical Reference Manuals:
- [ESP32-C6](https://www.espressif.com/sites/default/files/documentation/esp32-c6_technical_reference_manual_en.pdf)
- [ESP32-S2](https://www.espressif.com/sites/default/files/documentation/esp32-s2_technical_reference_manual_en.pdf)
- [ESP32-S3](https://www.espressif.com/sites/default/files/documentation/esp32-s3_technical_reference_manual_en.pdf)
## Getting Started
### Installing the Rust Compiler Targets
The compilation targets for these devices are officially supported by the mainline Rust compiler and can be installed using [rustup](https://rustup.rs/):
```shell
rustup target add riscv32imc-unknown-none-elf
rustup target add riscv32imac-unknown-none-elf
```
## License
Licensed under either of:
- Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in
the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without
any additional terms or conditions.

75
esp-lp-hal/build.rs Normal file
View File

@ -0,0 +1,75 @@
use std::{env, error::Error, fs, path::PathBuf};
// Macros taken from:
// https://github.com/TheDan64/inkwell/blob/36c3b10/src/lib.rs#L81-L110
// Given some features, assert that AT MOST one of the features is enabled.
macro_rules! assert_unique_features {
() => {};
( $first:tt $(,$rest:tt)* ) => {
$(
#[cfg(all(feature = $first, feature = $rest))]
compile_error!(concat!("Features \"", $first, "\" and \"", $rest, "\" cannot be used together"));
)*
assert_unique_features!($($rest),*);
};
}
// Given some features, assert that AT LEAST one of the features is enabled.
macro_rules! assert_used_features {
( $all:tt ) => {
#[cfg(not(feature = $all))]
compile_error!(concat!("The feature flag must be provided: ", $all));
};
( $($all:tt),+ ) => {
#[cfg(not(any($(feature = $all),*)))]
compile_error!(concat!("One of the feature flags must be provided: ", $($all, ", "),*));
};
}
// Given some features, assert that EXACTLY one of the features is enabled.
macro_rules! assert_unique_used_features {
( $($all:tt),* ) => {
assert_unique_features!($($all),*);
assert_used_features!($($all),*);
}
}
fn main() -> Result<(), Box<dyn Error>> {
// NOTE: update when adding new device support!
// Ensure that exactly one chip has been specified:
assert_unique_used_features!("esp32c6", "esp32s2", "esp32s3");
// NOTE: update when adding new device support!
// Determine the name of the configured device:
let device_name = if cfg!(feature = "esp32c6") {
"esp32c6"
} else if cfg!(feature = "esp32s2") {
"esp32s2"
} else if cfg!(feature = "esp32s3") {
"esp32s3"
} else {
unreachable!() // We've confirmed exactly one known device was selected
};
// Define all necessary configuration symbols for the configured device:
println!("cargo:rustc-cfg={}", device_name);
// Put the linker script somewhere the linker can find it:
let out = PathBuf::from(env::var_os("OUT_DIR").unwrap());
println!("cargo:rustc-link-search={}", out.display());
// Copy the required linker script to the `out` directory:
if cfg!(feature = "esp32c6") {
fs::copy("ld/link-lp.x", out.join("link.x"))?;
println!("cargo:rerun-if-changed=ld/link-lp.x");
} else if cfg!(feature = "esp32s2") || cfg!(feature = "esp32s3") {
fs::copy("ld/link-ulp.x", out.join("link.x"))?;
println!("cargo:rerun-if-changed=ld/link-ulp.x");
}
// Done!
Ok(())
}

View File

@ -0,0 +1,47 @@
//! Counts a 32 bit value at a known point in memory, and blink GPIO1.
//!
//! When using the ESP32-C6's LP core, this address in memory is `0x5000_2000`.
//!
//! When using the ESP32-S2 or ESP32-S3's ULP core, this address in memory is
//! `0x5000_0400` (but is `0x400`` from the ULP's point of view!).
//!
//! Make sure the LP RAM is cleared before loading the code.
#![no_std]
#![no_main]
use embedded_hal_02::{blocking::delay::DelayMs, digital::v2::OutputPin};
use esp_lp_hal::{
delay::Delay,
gpio::{GpioPin, Output, PushPull},
prelude::*,
};
use panic_halt as _;
cfg_if::cfg_if! {
if #[cfg(feature = "esp32c6")] {
const ADDRESS: u32 = 0x5000_2000;
} else if #[cfg(any(feature = "esp32s2", feature = "esp32s3"))] {
const ADDRESS: u32 = 0x400;
}
}
#[entry]
fn main(mut gpio1: GpioPin<Output<PushPull>, 1>) -> ! {
let mut i: u32 = 0;
let ptr = ADDRESS as *mut u32;
loop {
i = i.wrapping_add(1u32);
unsafe {
ptr.write_volatile(i);
}
gpio1.set_high().unwrap();
Delay.delay_ms(500);
gpio1.set_low().unwrap();
Delay.delay_ms(500);
}
}

View File

@ -8,16 +8,14 @@
use core::fmt::Write;
use esp32c6_lp_hal::{delay::Delay, prelude::*, uart::LpUart};
use embedded_hal_02::blocking::delay::DelayMs;
use esp_lp_hal::{delay::Delay, prelude::*, uart::LpUart};
use panic_halt as _;
#[entry]
fn main(mut uart: LpUart) -> ! {
let _peripherals = esp32c6_lp::Peripherals::take().unwrap();
let mut delay = Delay::new();
loop {
writeln!(uart, "Hello World from LP Core").unwrap();
delay.delay_ms(1500);
Delay.delay_ms(1000);
}
}

View File

@ -6,21 +6,23 @@
ENTRY(reset_vector)
VECTOR_TABLE_LENGTH = 0x80;
CONFIG_ULP_COPROC_RESERVE_MEM = 1024 * 16;
CONFIG_ULP_SHARED_MEM = 0;
RAM_LENGTH = CONFIG_ULP_COPROC_RESERVE_MEM - VECTOR_TABLE_LENGTH - CONFIG_ULP_SHARED_MEM;
MEMORY
{
/*first 128byte for exception/interrupt vectors*/
vector_table(RX) : ORIGIN = 0x50000000, LENGTH = 0x80
ram(RWX) : ORIGIN = 0x50000080, LENGTH = CONFIG_ULP_COPROC_RESERVE_MEM - 0x80 - CONFIG_ULP_SHARED_MEM
vector_table(RX) : ORIGIN = 0x50000000, LENGTH = VECTOR_TABLE_LENGTH
ram(RWX) : ORIGIN = 0x50000080, LENGTH = RAM_LENGTH
}
SECTIONS
{
.vector.text :
{
/*exception/interrupt vectors*/
/* Exception/interrupt vectors */
__mtvec_base = .;
KEEP (*(.init.vector))
__mtvec_end = .;

View File

@ -3,7 +3,6 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "sdkconfig.h"
ENTRY(reset_vector)
@ -17,6 +16,7 @@ MEMORY
SECTIONS
{
. = ORIGIN(ram);
.text :
{
*(.text.vectors) /* Default reset vector must link to offset 0x0 */

80
esp-lp-hal/src/delay.rs Normal file
View File

@ -0,0 +1,80 @@
//! Simple blocking delay functionality.
#[derive(Clone, Copy)]
pub struct Delay;
impl Delay {
/// Delay for at least the number of specific microseconds.
pub fn delay_micros(&mut self, mut us: u32) {
const NANOS_PER_MICRO: u32 = 1_000;
const MAX_MICROS: u32 = u32::MAX / NANOS_PER_MICRO;
// Avoid potential overflow if micro -> nano conversion is too large
while us > MAX_MICROS {
us -= MAX_MICROS;
self.delay_nanos(MAX_MICROS * NANOS_PER_MICRO);
}
self.delay_nanos(us * NANOS_PER_MICRO);
}
/// Delay for at least the number of specific nanoseconds.
pub fn delay_nanos(&mut self, ns: u32) {
let ticks_seconds = unsafe { crate::CPU_CLOCK };
let clock = (ns as u64 * (ticks_seconds as u64)) / 1_000_000_000u64;
let t0 = cycles();
while cycles().wrapping_sub(t0) <= clock {}
}
}
#[cfg(feature = "esp32c6")]
#[inline(always)]
fn cycles() -> u64 {
riscv::register::mcycle::read64()
}
#[cfg(any(feature = "esp32s2", feature = "esp32s3"))]
#[inline(always)]
fn cycles() -> u64 {
let mut cycles: u32;
unsafe {
core::arch::asm!(
"rdcycle {cycles}",
cycles = out(reg) cycles,
)
}
cycles as u64
}
#[cfg(feature = "embedded-hal-02")]
impl embedded_hal_02::blocking::delay::DelayUs<u64> for Delay {
#[inline(always)]
fn delay_us(&mut self, us: u64) {
self.delay_micros(us as u32);
}
}
#[cfg(feature = "embedded-hal-02")]
impl embedded_hal_02::blocking::delay::DelayUs<u32> for Delay {
#[inline(always)]
fn delay_us(&mut self, us: u32) {
self.delay_micros(us);
}
}
#[cfg(feature = "embedded-hal-02")]
impl embedded_hal_02::blocking::delay::DelayMs<u32> for Delay {
#[inline(always)]
fn delay_ms(&mut self, ms: u32) {
self.delay_micros(ms * 1000);
}
}
#[cfg(feature = "embedded-hal-1")]
impl embedded_hal_1::delay::DelayNs for Delay {
fn delay_ns(&mut self, ns: u32) {
self.delay_nanos(ns);
}
}

157
esp-lp-hal/src/gpio.rs Normal file
View File

@ -0,0 +1,157 @@
//! Low-power GPIO driver
//!
//! It's assumed that GPIOs are already configured correctly by the HP core.
use core::marker::PhantomData;
#[cfg(feature = "esp32c6")]
type LpIo = crate::pac::LP_IO;
#[cfg(any(feature = "esp32s2", feature = "esp32s3"))]
type LpIo = crate::pac::RTC_IO;
pub struct Unknown {}
pub struct Input<MODE> {
_mode: PhantomData<MODE>,
}
pub struct Floating;
pub struct PullDown;
pub struct PullUp;
pub struct Output<MODE> {
_mode: PhantomData<MODE>,
}
pub struct PushPull;
pub struct GpioPin<MODE, const PIN: u8> {
phantom: PhantomData<MODE>,
}
impl<MODE, const PIN: u8> GpioPin<Input<MODE>, PIN> {
pub fn input_state(&self) -> bool {
unsafe { &*LpIo::PTR }.in_().read().bits() >> PIN & 0x1 != 0
}
}
impl<MODE, const PIN: u8> GpioPin<Output<MODE>, PIN> {
pub fn output_state(&self) -> bool {
unsafe { &*LpIo::PTR }.out().read().bits() >> PIN & 0x1 != 0
}
pub fn set_output(&mut self, on: bool) {
if on {
unsafe { &*LpIo::PTR }
.out_w1ts()
.write(|w| w.out_data_w1ts().variant(1 << PIN));
} else {
unsafe { &*LpIo::PTR }
.out_w1tc()
.write(|w| w.out_data_w1tc().variant(1 << PIN));
}
}
}
// Used by the `entry` procmacro:
#[doc(hidden)]
pub unsafe fn conjure<MODE, const PIN: u8>() -> Option<GpioPin<MODE, PIN>> {
if PIN > 7 {
None
} else {
Some(GpioPin {
phantom: PhantomData,
})
}
}
#[cfg(feature = "embedded-hal-02")]
impl<MODE, const PIN: u8> embedded_hal_02::digital::v2::InputPin for GpioPin<Input<MODE>, PIN> {
type Error = core::convert::Infallible;
fn is_high(&self) -> Result<bool, Self::Error> {
Ok(self.input_state())
}
fn is_low(&self) -> Result<bool, Self::Error> {
Ok(!self.is_high()?)
}
}
#[cfg(feature = "embedded-hal-02")]
impl<MODE, const PIN: u8> embedded_hal_02::digital::v2::OutputPin for GpioPin<Output<MODE>, PIN> {
type Error = core::convert::Infallible;
fn set_low(&mut self) -> Result<(), Self::Error> {
self.set_output(false);
Ok(())
}
fn set_high(&mut self) -> Result<(), Self::Error> {
self.set_output(true);
Ok(())
}
}
#[cfg(feature = "embedded-hal-02")]
impl<MODE, const PIN: u8> embedded_hal_02::digital::v2::StatefulOutputPin
for GpioPin<Output<MODE>, PIN>
{
fn is_set_high(&self) -> Result<bool, Self::Error> {
Ok(self.output_state())
}
fn is_set_low(&self) -> Result<bool, Self::Error> {
Ok(!self.is_set_high()?)
}
}
#[cfg(feature = "embedded-hal-02")]
impl<MODE, const PIN: u8> embedded_hal_02::digital::v2::toggleable::Default
for GpioPin<Output<MODE>, PIN>
{
}
#[cfg(feature = "embedded-hal-1")]
impl<MODE, const PIN: u8> embedded_hal_1::digital::ErrorType for GpioPin<MODE, PIN> {
type Error = core::convert::Infallible;
}
#[cfg(feature = "embedded-hal-1")]
impl<MODE, const PIN: u8> embedded_hal_1::digital::InputPin for GpioPin<Input<MODE>, PIN> {
fn is_high(&mut self) -> Result<bool, Self::Error> {
Ok(self.input_state())
}
fn is_low(&mut self) -> Result<bool, Self::Error> {
Ok(!self.is_high()?)
}
}
#[cfg(feature = "embedded-hal-1")]
impl<MODE, const PIN: u8> embedded_hal_1::digital::OutputPin for GpioPin<Output<MODE>, PIN> {
fn set_low(&mut self) -> Result<(), Self::Error> {
self.set_output(false);
Ok(())
}
fn set_high(&mut self) -> Result<(), Self::Error> {
self.set_output(true);
Ok(())
}
}
#[cfg(feature = "embedded-hal-1")]
impl<MODE, const PIN: u8> embedded_hal_1::digital::StatefulOutputPin
for GpioPin<Output<MODE>, PIN>
{
fn is_set_high(&mut self) -> Result<bool, Self::Error> {
Ok(self.output_state())
}
fn is_set_low(&mut self) -> Result<bool, Self::Error> {
Ok(!self.is_set_high()?)
}
}

150
esp-lp-hal/src/lib.rs Normal file
View File

@ -0,0 +1,150 @@
#![allow(asm_sub_register)]
#![no_std]
use core::arch::global_asm;
pub mod delay;
pub mod gpio;
#[cfg(esp32c6)]
pub mod uart;
pub mod pac {
#[cfg(feature = "esp32c6")]
pub use esp32c6_lp::*;
#[cfg(feature = "esp32s2")]
pub use esp32s2_ulp::*;
#[cfg(feature = "esp32s3")]
pub use esp32s3_ulp::*;
}
pub mod prelude {
pub use procmacros::entry;
}
cfg_if::cfg_if! {
if #[cfg(feature = "esp32c6")] {
// LP_FAST_CLK is not very accurate, for now use a rough estimate
const LP_FAST_CLK_HZ: u32 = 16_000_000;
const XTAL_D2_CLK_HZ: u32 = 20_000_000;
} else if #[cfg(feature = "esp32s2")] {
const LP_FAST_CLK_HZ: u32 = 8_000_000;
} else if #[cfg(feature = "esp32s3")] {
const LP_FAST_CLK_HZ: u32 = 17_500_000;
}
}
pub static mut CPU_CLOCK: u32 = LP_FAST_CLK_HZ;
#[cfg(feature = "esp32c6")]
global_asm!(
r#"
.section .init.vector, "ax"
/* This is the vector table. It is currently empty, but will be populated
* with exception and interrupt handlers when this is supported
*/
.align 0x4, 0xff
.global _vector_table
.type _vector_table, @function
_vector_table:
.option push
.option norvc
.rept 32
nop
.endr
.option pop
.size _vector_table, .-_vector_table
.section .init, "ax"
.global reset_vector
/* The reset vector, jumps to startup code */
reset_vector:
j __start
__start:
/* setup the stack pointer */
la sp, __stack_top
call rust_main
loop:
j loop
"#
);
#[cfg(any(feature = "esp32s2", feature = "esp32s3"))]
global_asm!(
r#"
.section .text.vectors
.global irq_vector
.global reset_vector
/* The reset vector, jumps to startup code */
reset_vector:
j __start
/* Interrupt handler */
.balign 16
irq_vector:
ret
.section .text
__start:
/* setup the stack pointer */
la sp, __stack_top
call ulp_riscv_rescue_from_monitor
call rust_main
call ulp_riscv_halt
loop:
j loop
"#
);
#[link_section = ".init.rust"]
#[export_name = "rust_main"]
unsafe extern "C" fn lp_core_startup() -> ! {
extern "Rust" {
fn main() -> !;
}
#[cfg(feature = "esp32c6")]
if (*pac::LP_CLKRST::PTR)
.lp_clk_conf()
.read()
.fast_clk_sel()
.bit_is_set()
{
CPU_CLOCK = XTAL_D2_CLK_HZ;
}
main();
}
#[cfg(any(feature = "esp32s2", feature = "esp32s3"))]
#[link_section = ".init.rust"]
#[no_mangle]
unsafe extern "C" fn ulp_riscv_rescue_from_monitor() {
// Rescue RISC-V core from monitor state.
unsafe { &*pac::RTC_CNTL::PTR }
.cocpu_ctrl()
.modify(|_, w| w.cocpu_done().clear_bit().cocpu_shut_reset_en().clear_bit());
}
#[cfg(any(feature = "esp32s2", feature = "esp32s3"))]
#[link_section = ".init.rust"]
#[no_mangle]
unsafe extern "C" fn ulp_riscv_halt() {
unsafe { &*pac::RTC_CNTL::PTR }.cocpu_ctrl().modify(|_, w| {
w.cocpu_shut_2_clk_dis()
.variant(0x3f)
.cocpu_done()
.set_bit()
});
loop {
riscv::asm::wfi();
}
}

View File

@ -1,11 +1,11 @@
//! Low-power UART driver
use esp32c6_lp::LP_UART;
use crate::pac::LP_UART;
const UART_FIFO_SIZE: u16 = 128;
#[doc(hidden)]
pub unsafe fn conjour() -> Option<LpUart> {
pub unsafe fn conjure() -> Option<LpUart> {
Some(LpUart {
uart: LP_UART::steal(),
})
@ -154,7 +154,8 @@ impl core::fmt::Write for LpUart {
}
}
impl embedded_hal::serial::Read<u8> for LpUart {
#[cfg(feature = "embedded-hal-02")]
impl embedded_hal_02::serial::Read<u8> for LpUart {
type Error = Error;
fn read(&mut self) -> nb::Result<u8, Self::Error> {
@ -162,7 +163,8 @@ impl embedded_hal::serial::Read<u8> for LpUart {
}
}
impl embedded_hal::serial::Write<u8> for LpUart {
#[cfg(feature = "embedded-hal-02")]
impl embedded_hal_02::serial::Write<u8> for LpUart {
type Error = Error;
fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {

View File

@ -1,11 +0,0 @@
[target.riscv32imc-unknown-none-elf]
runner = "espflash flash --monitor"
rustflags = [
"-C", "link-arg=-Tlink.x",
]
[build]
target = "riscv32imc-unknown-none-elf"
[unstable]
build-std = ["core"]

View File

@ -1,22 +0,0 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
### Added
- Add the `ulp-riscv-hal` package (#840)
### Changed
- Renamed to `esp-ulp-riscv-hal` (#916)
### Fixed
### Removed
[Unreleased]: https://github.com/esp-rs/esp-hal/commits/main/esp-ulp-riscv-hal

View File

@ -1,38 +0,0 @@
[package]
name = "esp-ulp-riscv-hal"
version = "0.1.0"
edition = "2021"
rust-version = "1.67.0"
description = "HAL for ESP32-S2 / ESP32-S3 ULP RISC-V coprocessor"
repository = "https://github.com/esp-rs/esp-hal"
license = "MIT OR Apache-2.0"
keywords = [
"embedded",
"embedded-hal",
"esp",
"esp32s2",
"esp32s3",
"no-std",
]
categories = [
"embedded",
"hardware-support",
"no-std",
]
[dependencies]
embedded-hal = { version = "0.2.7", features = ["unproven"] }
procmacros = { package = "esp-hal-procmacros", path = "../esp-hal-procmacros" }
paste = "1.0.14"
esp32s2-ulp = { version = "0.1.0", optional = true }
esp32s3-ulp = { version = "0.1.0", optional = true }
[dev-dependencies]
panic-halt = "0.2.0"
[features]
default = []
debug = []
esp32s2 = ["dep:esp32s2-ulp", "procmacros/esp32s2-ulp"]
esp32s3 = ["dep:esp32s3-ulp", "procmacros/esp32s3-ulp"]

View File

@ -1,53 +0,0 @@
# ulp-lp-hal
[![Crates.io](https://img.shields.io/crates/v/esp-ulp-lp-hal?labelColor=1C2C2E&color=C96329&logo=Rust&style=flat-square)](https://crates.io/crates/esp-ulp-lp-hal)
[![docs.rs](https://img.shields.io/docsrs/esp-ulp-lp-hal?labelColor=1C2C2E&color=C96329&logo=rust&style=flat-square)](https://docs.rs/esp-ulp-lp-hal)
![Crates.io](https://img.shields.io/crates/l/esp-ulp-lp-hal?labelColor=1C2C2E&style=flat-square)
[![Matrix](https://img.shields.io/matrix/esp-rs:matrix.org?label=join%20matrix&labelColor=1C2C2E&color=BEC5C9&logo=matrix&style=flat-square)](https://matrix.to/#/#esp-rs:matrix.org)
`no_std` HAL for the ESP32-S2/ESP32-S3 from Espressif's ultra-low-power coprocessor.
Implements a number of the traits defined in [embedded-hal](https://github.com/rust-embedded/embedded-hal).
This device uses the RISC-V ISA, which is officially supported by the Rust compiler via the `riscv32imc-unknown-none-elf` target.
Please refer to the documentation for more information.
## [Documentation]
[documentation]: https://docs.rs/esp-ulp-lp-hal/
## Resources
- [Datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-s2_datasheet_en.pdf)
- [Datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-s3_datasheet_en.pdf)
- [Technical Reference Manual](https://www.espressif.com/sites/default/files/documentation/esp32-s2_technical_reference_manual_en.pdf)
- [Technical Reference Manual](https://www.espressif.com/sites/default/files/documentation/esp32-s3_technical_reference_manual_en.pdf)
- [The Rust Programming Language](https://doc.rust-lang.org/book/)
- [The Embedded Rust Book](https://docs.rust-embedded.org/book/index.html)
- [The Rust on ESP Book](https://esp-rs.github.io/book/)
## Getting Started
### Installing the Rust Compiler Target
The compilation target for this device is officially supported by the mainline Rust compiler and can be installed using [rustup](https://rustup.rs/):
```shell
rustup target add riscv32imc-unknown-none-elf
```
## License
Licensed under either of:
- Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in
the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without
any additional terms or conditions.

View File

@ -1,17 +0,0 @@
use std::{env, fs::File, io::Write, path::PathBuf};
fn main() {
// Put the linker script somewhere the linker can find it
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
File::create(out.join("link.x"))
.unwrap()
.write_all(include_bytes!("ld/link.x"))
.unwrap();
println!("cargo:rustc-link-search={}", out.display());
// Only re-run the build script when memory.x is changed,
// instead of when any part of the source code changes.
println!("cargo:rerun-if-changed=ld/memory.x");
}

View File

@ -1,35 +0,0 @@
//! Counts a 32 bit value at 0x5000_0400 (0x400 from the ULP's point of view)
//! and blinks GPIO 1.
//!
//! Make sure the RTC RAM is cleared before loading the code.
#![no_std]
#![no_main]
use esp_ulp_riscv_hal::{
delay::Delay,
gpio::{GpioPin, Output, PushPull},
prelude::*,
};
use panic_halt as _;
#[entry]
fn main(mut gpio1: GpioPin<Output<PushPull>, 1>) -> ! {
let mut i: u32 = 0;
let ptr = 0x400 as *mut u32;
let mut delay = Delay::new();
loop {
i = i.wrapping_add(1u32);
unsafe {
ptr.write_volatile(i);
}
gpio1.set_high().unwrap();
delay.delay_ms(500);
gpio1.set_low().unwrap();
delay.delay_ms(500);
}
}

View File

@ -1,62 +0,0 @@
//! Simple blocking delay functionality
//!
//! This uses cycle count under the hood.
use core::arch::asm;
use embedded_hal::blocking::delay::{DelayMs, DelayUs};
// see components\ulp\ulp_riscv\ulp_core\include\ulp_riscv_utils.h in esp-idf
#[cfg(feature = "esp32s2")]
const CYCLES_PER_US_M10: u32 = 85;
#[cfg(feature = "esp32s3")]
const CYCLES_PER_US_M10: u32 = 175;
pub struct Delay {}
impl Delay {
pub fn new() -> Self {
Self {}
}
}
impl Default for Delay {
fn default() -> Self {
Self::new()
}
}
impl DelayUs<u64> for Delay {
#[inline(always)]
fn delay_us(&mut self, us: u64) {
DelayUs::<u32>::delay_us(self, us as u32);
}
}
impl DelayUs<u32> for Delay {
#[inline(always)]
fn delay_us(&mut self, us: u32) {
let t0 = cycles();
let clock = us * CYCLES_PER_US_M10 / 10;
while cycles().wrapping_sub(t0) <= clock {}
}
}
impl DelayMs<u32> for Delay {
#[inline(always)]
fn delay_ms(&mut self, ms: u32) {
DelayUs::<u32>::delay_us(self, ms * 1000);
}
}
#[inline(always)]
fn cycles() -> u32 {
let mut cycles;
unsafe {
asm!(
"rdcycle {cycles}",
cycles = out(reg) cycles,
)
}
cycles
}

View File

@ -1,122 +0,0 @@
//! Low-power GPIO driver
//!
//! It's assumed that GPIOs are already configured correctly by the HP core.
use core::{convert::Infallible, marker::PhantomData};
use crate::pac::RTC_IO;
#[doc(hidden)]
pub unsafe fn conjour<MODE, const PIN: u8>() -> Option<GpioPin<MODE, PIN>> {
if PIN > 7 {
None
} else {
Some(GpioPin {
phantom: PhantomData,
})
}
}
pub struct Unknown {}
pub struct Input<MODE> {
_mode: PhantomData<MODE>,
}
pub struct Floating;
pub struct PullDown;
pub struct PullUp;
pub struct Output<MODE> {
_mode: PhantomData<MODE>,
}
pub struct PushPull;
#[non_exhaustive]
pub struct GpioPin<MODE, const PIN: u8> {
phantom: PhantomData<MODE>,
}
impl<MODE, const PIN: u8> GpioPin<Input<MODE>, PIN> {
fn input_state(&self) -> bool {
unsafe { &*RTC_IO::PTR }.rtc_gpio_in().read().bits() >> PIN & 0x1 != 0
}
}
impl<MODE, const PIN: u8> GpioPin<Output<MODE>, PIN> {
fn output_state(&self) -> bool {
unsafe { &*RTC_IO::PTR }.rtc_gpio_out().read().bits() >> PIN & 0x1 != 0
}
fn set_output_low(&mut self) {
// TODO align PAC
#[cfg(feature = "esp32s2")]
unsafe { &*RTC_IO::PTR }
.rtc_gpio_out_w1tc()
.write(|w| w.gpio_out_data_w1tc().variant(1 << PIN));
#[cfg(feature = "esp32s3")]
unsafe { &*RTC_IO::PTR }
.rtc_gpio_out_w1tc()
.write(|w| w.rtc_gpio_out_data_w1tc().variant(1 << PIN));
}
fn set_output_high(&mut self) {
#[cfg(feature = "esp32s2")]
unsafe { &*RTC_IO::PTR }
.rtc_gpio_out_w1ts()
.write(|w| w.gpio_out_data_w1ts().variant(1 << PIN));
#[cfg(feature = "esp32s3")]
unsafe { &*RTC_IO::PTR }
.rtc_gpio_out_w1ts()
.write(|w| w.rtc_gpio_out_data_w1ts().variant(1 << PIN));
}
}
impl<MODE, const PIN: u8> embedded_hal::digital::v2::InputPin for GpioPin<Input<MODE>, PIN> {
type Error = Infallible;
fn is_high(&self) -> Result<bool, Self::Error> {
Ok(self.input_state())
}
fn is_low(&self) -> Result<bool, Self::Error> {
Ok(!self.is_high()?)
}
}
impl<MODE, const PIN: u8> embedded_hal::digital::v2::OutputPin for GpioPin<Output<MODE>, PIN> {
type Error = Infallible;
fn set_low(&mut self) -> Result<(), Self::Error> {
self.set_output_low();
Ok(())
}
fn set_high(&mut self) -> Result<(), Self::Error> {
self.set_output_high();
Ok(())
}
}
impl<MODE, const PIN: u8> embedded_hal::digital::v2::StatefulOutputPin
for GpioPin<Output<MODE>, PIN>
{
fn is_set_high(&self) -> Result<bool, Self::Error> {
Ok(self.output_state())
}
fn is_set_low(&self) -> Result<bool, Self::Error> {
Ok(!self.is_set_high()?)
}
}
impl<MODE, const PIN: u8> embedded_hal::digital::v2::toggleable::Default
for GpioPin<Output<MODE>, PIN>
{
}

View File

@ -1,100 +0,0 @@
#![no_std]
#![allow(asm_sub_register)]
use core::arch::global_asm;
pub mod delay;
pub mod gpio;
pub mod prelude;
#[cfg(feature = "esp32s2")]
use esp32s2_ulp as pac;
#[cfg(feature = "esp32s3")]
use esp32s3_ulp as pac;
global_asm!(
r#"
.section .text.vectors
.global irq_vector
.global reset_vector
/* The reset vector, jumps to startup code */
reset_vector:
j __start
/* Interrupt handler */
.balign 16
irq_vector:
ret
.section .text
__start:
/* setup the stack pointer */
la sp, __stack_top
call ulp_riscv_rescue_from_monitor
call rust_main
call ulp_riscv_halt
loop:
j loop
"#
);
#[link_section = ".init.rust"]
#[export_name = "rust_main"]
unsafe extern "C" fn lp_core_startup() -> ! {
extern "Rust" {
fn main() -> !;
}
main();
}
#[link_section = ".init.rust"]
#[export_name = "ulp_riscv_rescue_from_monitor"]
unsafe extern "C" fn ulp_riscv_rescue_from_monitor() {
// Rescue RISCV from monitor state.
let rtc_cntl = unsafe { pac::RTC_CNTL::steal() };
// TODO align naming in PACs
#[cfg(feature = "esp32s2")]
rtc_cntl
.cocpu_ctrl()
.modify(|_, w| w.cocpu_done().clear_bit().cocpu_shut_reset_en().clear_bit());
#[cfg(feature = "esp32s3")]
rtc_cntl
.rtc_cocpu_ctrl()
.modify(|_, w| w.cocpu_done().clear_bit().cocpu_shut_reset_en().clear_bit());
}
#[link_section = ".init.rust"]
#[export_name = "ulp_riscv_halt"]
unsafe extern "C" fn ulp_riscv_halt() {
let rtc_cntl = unsafe { pac::RTC_CNTL::steal() };
// TODO align naming in PACs
#[cfg(feature = "esp32s2")]
{
rtc_cntl
.cocpu_ctrl()
.modify(|_, w| w.cocpu_shut_2_clk_dis().variant(0x3f));
rtc_cntl
.cocpu_ctrl()
.modify(|_, w| w.cocpu_done().set_bit());
}
#[cfg(feature = "esp32s3")]
{
rtc_cntl
.rtc_cocpu_ctrl()
.modify(|_, w| w.cocpu_shut_2_clk_dis().variant(0x3f));
rtc_cntl
.rtc_cocpu_ctrl()
.modify(|_, w| w.cocpu_done().set_bit());
}
#[allow(clippy::empty_loop)]
loop {}
}

View File

@ -1,15 +0,0 @@
//! The prelude
//!
//! Re-exports all traits required for interacting with the various peripheral
//! drivers implemented in this crate.
pub use embedded_hal::{
digital::v2::{
InputPin as _embedded_hal_digital_vs_InputPin,
OutputPin as _embedded_hal_digital_vs_OutputPin,
StatefulOutputPin as _embedded_hal_digital_vs_StatefulOutputPin,
ToggleableOutputPin as _embedded_hal_digital_vs_ToggleableOutputPin,
},
prelude::*,
};
pub use procmacros::entry;

View File

@ -3,7 +3,7 @@
//! Code on LP core increments a counter and continuously toggles GPIO1. The
//! current value is printed by the HP core.
//!
//! Make sure to first compile the `esp32c6-lp-hal/examples/blinky.rs` example
//! Make sure to first compile the `esp-lp-hal/examples/blinky.rs` example
#![no_std]
#![no_main]
@ -35,9 +35,8 @@ fn main() -> ! {
println!("lp core stopped");
// load code to LP core
let lp_core_code = load_lp_code!(
"../esp32c6-lp-hal/target/riscv32imac-unknown-none-elf/release/examples/blinky"
);
let lp_core_code =
load_lp_code!("../esp-lp-hal/target/riscv32imac-unknown-none-elf/release/examples/blinky");
// start LP core
lp_core_code.run(&mut lp_core, lp_core::LpCoreWakeupSource::HpCpu, lp_pin);

View File

@ -1,8 +1,9 @@
//! This shows a very basic example of running code on the LP core.
//!
//! Code on LP core uses LP_UART initialized on HP core. For more information
//! check `lp_core_uart` example in the `esp32c6-lp-hal.
//! Make sure to first compile the `esp32c6-lp-hal/examples/uart.rs` example
//! check `lp_core_uart` example in the `esp-lp-hal.
//!
//! Make sure to first compile the `esp-lp-hal/examples/uart.rs` example
#![no_std]
#![no_main]
@ -60,9 +61,8 @@ fn main() -> ! {
println!("lp core stopped");
// load code to LP core
let lp_core_code = load_lp_code!(
"../esp32c6-lp-hal/target/riscv32imac-unknown-none-elf/release/examples/uart"
);
let lp_core_code =
load_lp_code!("../esp-lp-hal/target/riscv32imac-unknown-none-elf/release/examples/uart");
// start LP core
lp_core_code.run(&mut lp_core, lp_core::LpCoreWakeupSource::HpCpu, lp_uart);

View File

@ -1,11 +0,0 @@
[target.riscv32imac-unknown-none-elf]
runner = "espflash flash --monitor"
rustflags = [
"-C", "link-arg=-Tlink.x",
]
[build]
target = "riscv32imac-unknown-none-elf"
[unstable]
build-std = ["core"]

View File

@ -1,37 +0,0 @@
[package]
name = "esp32c6-lp-hal"
version = "0.1.0"
edition = "2021"
rust-version = "1.67.0"
description = "HAL for ESP32-C6's low-power coprocessor"
repository = "https://github.com/esp-rs/esp-hal"
license = "MIT OR Apache-2.0"
keywords = [
"embedded",
"embedded-hal",
"esp",
"esp32c6",
"no-std",
]
categories = [
"embedded",
"hardware-support",
"no-std",
]
[dependencies]
critical-section = { version = "1.1.2", features = ["restore-state-u8"] }
embedded-hal = { version = "0.2.7", features = ["unproven"] }
esp32c6-lp = { version = "0.1.0", features = ["critical-section"] }
nb = "1.1.0"
paste = "1.0.14"
procmacros = { package = "esp-hal-procmacros", path = "../esp-hal-procmacros", features = ["esp32c6-lp"] }
riscv = "0.10.1"
[dev-dependencies]
panic-halt = "0.2.0"
[features]
default = []
debug = ["esp32c6-lp/impl-register-debug"]

View File

@ -1,51 +0,0 @@
# esp32c6-lp-hal
[![Crates.io](https://img.shields.io/crates/v/esp32c6-lp-hal?labelColor=1C2C2E&color=C96329&logo=Rust&style=flat-square)](https://crates.io/crates/esp32c6-lp-hal)
[![docs.rs](https://img.shields.io/docsrs/esp32c6-lp-hal?labelColor=1C2C2E&color=C96329&logo=rust&style=flat-square)](https://docs.rs/esp32c6-lp-hal)
![Crates.io](https://img.shields.io/crates/l/esp32c6-lp-hal?labelColor=1C2C2E&style=flat-square)
[![Matrix](https://img.shields.io/matrix/esp-rs:matrix.org?label=join%20matrix&labelColor=1C2C2E&color=BEC5C9&logo=matrix&style=flat-square)](https://matrix.to/#/#esp-rs:matrix.org)
`no_std` HAL for the ESP32-C6 from Espressif's low-power coprocessor.
Implements a number of the traits defined in [embedded-hal](https://github.com/rust-embedded/embedded-hal).
This device uses the RISC-V ISA, which is officially supported by the Rust compiler via the `riscv32imac-unknown-none-elf` target.
Please refer to the documentation for more information.
## [Documentation]
[documentation]: https://docs.rs/esp32c6-lp-hal/
## Resources
- [Datasheet](https://www.espressif.com/sites/default/files/documentation/esp32-c6_datasheet_en.pdf)
- [Technical Reference Manual](https://www.espressif.com/sites/default/files/documentation/esp32-c6_technical_reference_manual_en.pdf)
- [The Rust Programming Language](https://doc.rust-lang.org/book/)
- [The Embedded Rust Book](https://docs.rust-embedded.org/book/index.html)
- [The Rust on ESP Book](https://esp-rs.github.io/book/)
## Getting Started
### Installing the Rust Compiler Target
The compilation target for this device is officially supported by the mainline Rust compiler and can be installed using [rustup](https://rustup.rs/):
```shell
rustup target add riscv32imac-unknown-none-elf
```
## License
Licensed under either of:
- Apache License, Version 2.0 ([LICENSE-APACHE](../LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license ([LICENSE-MIT](../LICENSE-MIT) or http://opensource.org/licenses/MIT)
at your option.
### Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in
the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without
any additional terms or conditions.

View File

@ -1,15 +0,0 @@
use std::{env, error::Error, fs, path::PathBuf};
fn main() -> Result<(), Box<dyn Error>> {
// Put the linker script somewhere the linker can find it
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
println!("cargo:rustc-link-search={}", out.display());
fs::copy("ld/link.x", out.join("link.x"))?;
// Only re-run the build script when memory.x is changed,
// instead of when any part of the source code changes.
println!("cargo:rerun-if-changed=ld/memory.x");
Ok(())
}

View File

@ -1,33 +0,0 @@
//! Counts a 32 bit value at 0x5000_2000 and blinks GPIO 1.
//! Make sure the LP RAM is cleared before loading the code.
#![no_std]
#![no_main]
use esp32c6_lp_hal::{
delay::Delay,
gpio::{GpioPin, Output, PushPull},
prelude::*,
};
use panic_halt as _;
#[entry]
fn main(mut gpio1: GpioPin<Output<PushPull>, 1>) -> ! {
let mut i: u32 = 0;
let ptr = 0x5000_2000 as *mut u32;
let mut delay = Delay::new();
loop {
i = i.wrapping_add(1u32);
unsafe {
ptr.write_volatile(i);
}
gpio1.set_high().unwrap();
delay.delay_ms(500);
gpio1.set_low().unwrap();
delay.delay_ms(500);
}
}

View File

@ -1,47 +0,0 @@
//! Simple blocking delay functionality
//!
//! This uses the delay functionality provided by the `riscv` crate under the
//! hood.
use embedded_hal::blocking::delay::{DelayMs, DelayUs};
use crate::CPU_CLOCK;
pub struct Delay {
rv_delay: riscv::delay::McycleDelay,
}
impl Delay {
pub fn new() -> Self {
Self {
rv_delay: riscv::delay::McycleDelay::new(unsafe { CPU_CLOCK }),
}
}
}
impl Default for Delay {
fn default() -> Self {
Self::new()
}
}
impl DelayUs<u64> for Delay {
#[inline(always)]
fn delay_us(&mut self, us: u64) {
self.rv_delay.delay_us(us);
}
}
impl DelayUs<u32> for Delay {
#[inline(always)]
fn delay_us(&mut self, us: u32) {
self.rv_delay.delay_us(us);
}
}
impl DelayMs<u32> for Delay {
#[inline(always)]
fn delay_ms(&mut self, ms: u32) {
self.rv_delay.delay_ms(ms);
}
}

View File

@ -1,108 +0,0 @@
//! Low-power GPIO driver
//!
//! It's assumed that GPIOs are already configured correctly by the HP core.
use core::{convert::Infallible, marker::PhantomData};
use esp32c6_lp::LP_IO;
#[doc(hidden)]
pub unsafe fn conjour<MODE, const PIN: u8>() -> Option<GpioPin<MODE, PIN>> {
if PIN > 7 {
None
} else {
Some(GpioPin {
phantom: PhantomData,
})
}
}
pub struct Unknown {}
pub struct Input<MODE> {
_mode: PhantomData<MODE>,
}
pub struct Floating;
pub struct PullDown;
pub struct PullUp;
pub struct Output<MODE> {
_mode: PhantomData<MODE>,
}
pub struct PushPull;
#[non_exhaustive]
pub struct GpioPin<MODE, const PIN: u8> {
phantom: PhantomData<MODE>,
}
impl<MODE, const PIN: u8> GpioPin<Input<MODE>, PIN> {
fn input_state(&self) -> bool {
unsafe { &*LP_IO::PTR }.in_().read().bits() >> PIN & 0x1 != 0
}
}
impl<MODE, const PIN: u8> GpioPin<Output<MODE>, PIN> {
fn output_state(&self) -> bool {
unsafe { &*LP_IO::PTR }.out().read().bits() >> PIN & 0x1 != 0
}
fn set_output_low(&mut self) {
unsafe { &*LP_IO::PTR }
.out_w1tc()
.write(|w| w.out_data_w1tc().variant(1 << PIN));
}
fn set_output_high(&mut self) {
unsafe { &*LP_IO::PTR }
.out_w1ts()
.write(|w| w.out_data_w1ts().variant(1 << PIN));
}
}
impl<MODE, const PIN: u8> embedded_hal::digital::v2::InputPin for GpioPin<Input<MODE>, PIN> {
type Error = Infallible;
fn is_high(&self) -> Result<bool, Self::Error> {
Ok(self.input_state())
}
fn is_low(&self) -> Result<bool, Self::Error> {
Ok(!self.is_high()?)
}
}
impl<MODE, const PIN: u8> embedded_hal::digital::v2::OutputPin for GpioPin<Output<MODE>, PIN> {
type Error = Infallible;
fn set_low(&mut self) -> Result<(), Self::Error> {
self.set_output_low();
Ok(())
}
fn set_high(&mut self) -> Result<(), Self::Error> {
self.set_output_high();
Ok(())
}
}
impl<MODE, const PIN: u8> embedded_hal::digital::v2::StatefulOutputPin
for GpioPin<Output<MODE>, PIN>
{
fn is_set_high(&self) -> Result<bool, Self::Error> {
Ok(self.output_state())
}
fn is_set_low(&self) -> Result<bool, Self::Error> {
Ok(!self.is_set_high()?)
}
}
impl<MODE, const PIN: u8> embedded_hal::digital::v2::toggleable::Default
for GpioPin<Output<MODE>, PIN>
{
}

View File

@ -1,96 +0,0 @@
#![no_std]
#![allow(asm_sub_register)]
use core::arch::global_asm;
pub mod delay;
pub mod gpio;
pub mod prelude;
pub mod uart;
pub mod riscv {
//! Low level access to RISC-V processors.
//!
//! Re-exports <https://crates.io/crates/riscv>
pub use riscv::*;
}
// LP_FAST_CLK is not very accurate, for now use a rough estimate
const LP_FAST_CLK_HZ: u32 = 16_000_000;
const XTAL_D2_CLK_HZ: u32 = 20_000_000;
pub static mut CPU_CLOCK: u32 = LP_FAST_CLK_HZ;
global_asm!(
r#"
.section .init.vector, "ax"
/* This is the vector table. It is currently empty, but will be populated
* with exception and interrupt handlers when this is supported
*/
.align 0x4, 0xff
.global _vector_table
.type _vector_table, @function
_vector_table:
.option push
.option norvc
.rept 32
nop
.endr
.option pop
.size _vector_table, .-_vector_table
.section .init, "ax"
.global reset_vector
/* The reset vector, jumps to startup code */
reset_vector:
j __start
__start:
/* setup the stack pointer */
la sp, __stack_top
call lp_core_startup
loop:
j loop
"#
);
#[link_section = ".init.rust"]
#[export_name = "lp_core_startup"]
unsafe extern "C" fn lp_core_startup() -> ! {
extern "Rust" {
fn main() -> !;
}
let clkrst = &*esp32c6_lp::LP_CLKRST::PTR;
if clkrst.lp_clk_conf().read().fast_clk_sel().bit_is_set() {
CPU_CLOCK = XTAL_D2_CLK_HZ;
}
main();
}
mod critical_section_impl {
struct CriticalSection;
critical_section::set_impl!(CriticalSection);
unsafe impl critical_section::Impl for CriticalSection {
unsafe fn acquire() -> critical_section::RawRestoreState {
let mut mstatus = 0u32;
core::arch::asm!("csrrci {0}, mstatus, 8", inout(reg) mstatus);
let interrupts_active = (mstatus & 0b1000) != 0;
interrupts_active as _
}
unsafe fn release(token: critical_section::RawRestoreState) {
if token != 0 {
riscv::interrupt::enable();
}
}
}
}

View File

@ -1,15 +0,0 @@
//! The prelude
//!
//! Re-exports all traits required for interacting with the various peripheral
//! drivers implemented in this crate.
pub use embedded_hal::{
digital::v2::{
InputPin as _embedded_hal_digital_vs_InputPin,
OutputPin as _embedded_hal_digital_vs_OutputPin,
StatefulOutputPin as _embedded_hal_digital_vs_StatefulOutputPin,
ToggleableOutputPin as _embedded_hal_digital_vs_ToggleableOutputPin,
},
prelude::*,
};
pub use procmacros::entry;

View File

@ -29,9 +29,8 @@ fn main() -> ! {
let mut ulp_core = ulp_core::UlpCore::new(peripherals.ULP_RISCV_CORE);
// load code to LP core
let lp_core_code = load_lp_code!(
"../esp-ulp-riscv-hal/target/riscv32imc-unknown-none-elf/release/examples/blinky"
);
let lp_core_code =
load_lp_code!("../esp-lp-hal/target/riscv32imc-unknown-none-elf/release/examples/blinky");
// start LP core
lp_core_code.run(&mut ulp_core, ulp_core::UlpCoreWakeupSource::HpCpu, pin);

View File

@ -31,9 +31,8 @@ fn main() -> ! {
println!("ulp core stopped");
// load code to LP core
let lp_core_code = load_lp_code!(
"../esp-ulp-riscv-hal/target/riscv32imc-unknown-none-elf/release/examples/blinky"
);
let lp_core_code =
load_lp_code!("../esp-lp-hal/target/riscv32imc-unknown-none-elf/release/examples/blinky");
// start LP core
lp_core_code.run(&mut ulp_core, ulp_core::UlpCoreWakeupSource::HpCpu, pin);