Skip to content

Commit

Permalink
Load config for each device from a TOML files instead of hard-coding …
Browse files Browse the repository at this point in the history
…in build script (#415)

* Move chip metadata into TOML files and update build script to consume them

* Make necessary changes to get all examples building again
  • Loading branch information
jessebraham authored Mar 16, 2023
1 parent 2e97c7b commit 2eeb3db
Show file tree
Hide file tree
Showing 12 changed files with 535 additions and 198 deletions.
4 changes: 4 additions & 0 deletions esp-hal-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ esp32c6 = { version = "0.2.0", features = ["critical-section"], optional = true
esp32s2 = { version = "0.13.0", features = ["critical-section"], optional = true }
esp32s3 = { version = "0.16.0", features = ["critical-section"], optional = true }

[build-dependencies]
basic-toml = "0.1.1"
serde = { version = "1.0.152", features = ["derive"] }

[features]
esp32 = ["esp32/rt" , "xtensa", "xtensa-lx/esp32", "xtensa-lx-rt/esp32", "lock_api", "procmacros/esp32"]
esp32c2 = ["esp32c2/rt", "riscv", "procmacros/esp32c2"]
Expand Down
292 changes: 130 additions & 162 deletions esp-hal-common/build.rs
Original file line number Diff line number Diff line change
@@ -1,183 +1,151 @@
use std::{env, fs, path::PathBuf};

fn main() {
let esp32 = cfg!(feature = "esp32");
let esp32c2 = cfg!(feature = "esp32c2");
let esp32c3 = cfg!(feature = "esp32c3");
let esp32c6 = cfg!(feature = "esp32c6");
let esp32s2 = cfg!(feature = "esp32s2");
let esp32s3 = cfg!(feature = "esp32s3");

// Ensure that exactly one chip has been specified
let chip_features = [esp32, esp32c2, esp32c3, esp32c6, esp32s2, esp32s3];
match chip_features.iter().filter(|&&f| f).count() {
1 => {}
n => panic!("Exactly 1 chip must be enabled via its Cargo feature, {n} provided"),
use serde::Deserialize;

// Macros taken from:
// https://github.com/TheDan64/inkwell/blob/36c3b10/src/lib.rs#L81-L110

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),*);
};
}

macro_rules! assert_used_features {
( $($all:tt),* ) => {
#[cfg(not(any($(feature = $all),*)))]
compile_error!(concat!("One of the feature flags must be provided: ", $($all, ", "),*));
}
}

macro_rules! assert_unique_used_features {
( $($all:tt),* ) => {
assert_unique_features!($($all),*);
assert_used_features!($($all),*);
}
}

#[derive(Debug, Deserialize)]
enum Arch {
#[serde(rename = "riscv")]
RiscV,
#[serde(rename = "xtensa")]
Xtensa,
}

impl core::fmt::Display for Arch {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Arch::RiscV => "riscv",
Arch::Xtensa => "xtensa",
}
)
}
}

#[derive(Debug, Deserialize)]
enum CoreCount {
#[serde(rename = "single_core")]
Single,
#[serde(rename = "multi_core")]
Multi,
}

impl core::fmt::Display for CoreCount {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"{}",
match self {
CoreCount::Single => "single_core",
CoreCount::Multi => "multi_core",
}
)
}
}

#[derive(Debug, Deserialize)]
struct Device {
pub arch: Arch,
pub cores: CoreCount,
pub peripherals: Vec<String>,
}

#[derive(Debug, Deserialize)]
struct Config {
pub device: Device,
}

fn main() {
// NOTE: update when adding new device support!
// Ensure that exactly one chip has been specified:
assert_unique_used_features!("esp32", "esp32c2", "esp32c3", "esp32c6", "esp32s2", "esp32s3");

if cfg!(feature = "esp32") && cfg!(feature = "esp32_40mhz") && cfg!(feature = "esp32_26mhz") {
panic!("Only one xtal speed feature can be selected");
// Handle the features for the ESP32's different crystal frequencies:
#[cfg(feature = "esp32")]
{
assert_unique_used_features!("esp32_26mhz", "esp32_40mhz");
}
if cfg!(feature = "esp32c2")
&& cfg!(feature = "esp32c2_40mhz")
&& cfg!(feature = "esp32c2_26mhz")

// Handle the features for the ESP32-C2's different crystal frequencies:
#[cfg(feature = "esp32c2")]
{
panic!("Only one xtal speed feature can be selected");
assert_unique_used_features!("esp32c2_26mhz", "esp32c2_40mhz");
}

// Define all required configuration symbols for the enabled chip.
//
// When adding a new device, at the bare minimum the following symbols MUST be
// defined:
// - the name of the device
// - the architecture ('riscv' or 'xtensa')
// - the core count ('single_core' or 'multi_core')
//
// Additionally, the following symbols MAY be defined if present:
// - 'aes'
// - 'dac'
// - 'gdma'
// - 'i2c1'
// - 'i2s'
// - 'large_intr_status'
// - 'mcpwm'
// - 'pcnt'
// - 'pdma'
// - 'plic'
// - 'radio'
// - 'rmt'
// - 'spi3'
// - 'systimer'
// - 'timg0'
// - 'timg1'
// - 'twai'
// - 'uart2'
// - 'usb_otg'
// - 'usb_serial_jtag'
//
// New symbols can be added as needed, but please be sure to update both this
// comment and the required vectors below.
let symbols = if esp32 {
vec![
"esp32",
"xtensa",
"multi_core",
"aes",
"dac",
"i2c1",
"i2s",
"mcpwm",
"pcnt",
"pdma",
"radio",
"rmt",
"spi3",
"timg0",
"timg1",
"uart2",
]
} else if esp32c2 {
vec![
"esp32c2",
"riscv",
"single_core",
"gdma",
"radio",
"systimer",
"timg0",
]
} else if esp32c3 {
vec![
"esp32c3",
"riscv",
"single_core",
"aes",
"gdma",
"i2s",
"radio",
"rmt",
"spi3",
"systimer",
"timg0",
"timg1",
"twai",
"usb_serial_jtag",
]
} else if esp32c6 {
vec![
"esp32c6",
"riscv",
"single_core",
"aes",
"gdma",
"i2s",
"large_intr_status",
"mcpwm",
"pcnt",
"plic",
"radio",
"rmt",
"systimer",
"timg0",
"timg1",
"twai",
"usb_serial_jtag",
]
} else if esp32s2 {
vec![
"esp32s2",
"xtensa",
"single_core",
"aes",
"dac",
"i2c1",
"i2s",
"pcnt",
"pdma",
"radio",
"rmt",
"spi3",
"systimer",
"timg0",
"timg1",
"usb_otg",
]
} else if esp32s3 {
vec![
"esp32s3",
"xtensa",
"multi_core",
"aes",
"gdma",
"i2c1",
"i2s",
"mcpwm",
"pcnt",
"radio",
"rmt",
"spi3",
"systimer",
"timg0",
"timg1",
"twai",
"uart2",
"usb_otg",
"usb_serial_jtag",
]
// NOTE: update when adding new device support!
// Determine the name of the configured device:
let device_name = if cfg!(feature = "esp32") {
"esp32"
} else if cfg!(feature = "esp32c2") {
"esp32c2"
} else if cfg!(feature = "esp32c3") {
"esp32c3"
} else if cfg!(feature = "esp32c6") {
"esp32c6"
} else if cfg!(feature = "esp32s2") {
"esp32s2"
} else if cfg!(feature = "esp32s3") {
"esp32s3"
} else {
unreachable!(); // We've already confirmed exactly one chip was selected
unreachable!() // We've confirmed exactly one known device was selected
};

for symbol in symbols {
println!("cargo:rustc-cfg={symbol}");
// Load the configuration file for the configured device:
let chip_toml_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("devices")
.join(format!("{}.toml", device_name))
.canonicalize()
.unwrap();

let config = fs::read_to_string(chip_toml_path).unwrap();
let config: Config = basic_toml::from_str(&config).unwrap();
let device = config.device;

// Define all necessary configuration symbols for the configured device:
println!("cargo:rustc-cfg={}", device_name);
println!("cargo:rustc-cfg={}", device.arch);
println!("cargo:rustc-cfg={}", device.cores);

for peripheral in device.peripherals {
println!("cargo:rustc-cfg={peripheral}");
}

// Place all linker scripts in `OUT_DIR`, and instruct Cargo how to find these
// files:
let out = PathBuf::from(env::var_os("OUT_DIR").unwrap());
println!("cargo:rustc-link-search={}", out.display());

if esp32 || esp32s2 || esp32s3 {
if cfg!(feature = "esp32") || cfg!(feature = "esp32s2") || cfg!(feature = "esp32s3") {
fs::copy("ld/xtensa/hal-defaults.x", out.join("hal-defaults.x")).unwrap();
fs::copy("ld/xtensa/rom.x", out.join("alias.x")).unwrap();
} else {
Expand Down
56 changes: 56 additions & 0 deletions esp-hal-common/devices/esp32.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
[device]
arch = "xtensa"
cores = "multi_core"

peripherals = [
# Peripherals available in the PAC:
"aes",
"apb_ctrl",
"bb",
"dport",
"efuse",
"flash_encryption",
"frc_timer",
"gpio",
"gpio_sd",
"hinf",
"i2c0",
"i2c1",
"i2s0",
"i2s1",
"io_mux",
"ledc",
"mcpwm0",
"mcpwm1",
"nrx",
"pcnt",
"rmt",
"rng",
"rsa",
"rtc_cntl",
"rtc_i2c",
"rtc_io",
"sdmmc",
"sens",
"sha",
"slc",
"slchost",
"spi0",
"spi1",
"spi2",
"spi3",
"timg0",
"timg1",
"twai0",
"uart0",
"uart1",
"uart2",
"uhci0",
"uhci1",

# Additional peripherals defined by us (the developers):
"adc",
"dac",
"pdma",
"radio",
]
Loading

0 comments on commit 2eeb3db

Please sign in to comment.