* Fixed triggering a debug-assertion during scan * CHANGELOGs * Change `debug_assert` into warning level log * Enable debug-asserts in hil-test, qa-test and examples * Change the way we detect and warn about debug-builds * Warn if opt-level is `0` or `1`
271 lines
8.1 KiB
Rust
271 lines
8.1 KiB
Rust
use std::{
|
|
env,
|
|
error::Error,
|
|
fs::{self, File},
|
|
io::{BufRead, Write},
|
|
path::{Path, PathBuf},
|
|
str::FromStr,
|
|
};
|
|
|
|
use esp_build::assert_unique_used_features;
|
|
use esp_config::{generate_config, Value};
|
|
use esp_metadata::{Chip, Config};
|
|
|
|
fn main() -> Result<(), Box<dyn Error>> {
|
|
println!("cargo:rustc-check-cfg=cfg(is_debug_build)");
|
|
if let Ok(level) = std::env::var("OPT_LEVEL") {
|
|
if level == "0" || level == "1" {
|
|
println!("cargo:rustc-cfg=is_debug_build");
|
|
}
|
|
}
|
|
|
|
// NOTE: update when adding new device support!
|
|
// Ensure that exactly one chip has been specified:
|
|
assert_unique_used_features!(
|
|
"esp32", "esp32c2", "esp32c3", "esp32c6", "esp32h2", "esp32s2", "esp32s3"
|
|
);
|
|
|
|
#[cfg(all(
|
|
feature = "flip-link",
|
|
not(any(feature = "esp32c6", feature = "esp32h2"))
|
|
))]
|
|
esp_build::error!("flip-link is only available on ESP32-C6/ESP32-H2");
|
|
|
|
// 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 = "esp32h2") {
|
|
"esp32h2"
|
|
} else if cfg!(feature = "esp32s2") {
|
|
"esp32s2"
|
|
} else if cfg!(feature = "esp32s3") {
|
|
"esp32s3"
|
|
} else {
|
|
unreachable!() // We've confirmed exactly one known device was selected
|
|
};
|
|
|
|
// Load the configuration file for the configured device:
|
|
let chip = Chip::from_str(device_name)?;
|
|
let config = Config::for_chip(&chip);
|
|
|
|
// Check PSRAM features are only given if the target supports PSRAM:
|
|
if !config.contains(&String::from("psram")) && cfg!(feature = "quad-psram") {
|
|
panic!("The target does not support PSRAM");
|
|
}
|
|
|
|
if !config.contains(&String::from("octal_psram")) && cfg!(feature = "octal-psram") {
|
|
panic!("The target does not support Octal PSRAM");
|
|
}
|
|
|
|
// Define all necessary configuration symbols for the configured device:
|
|
config.define_symbols();
|
|
|
|
// 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());
|
|
|
|
// emit config
|
|
let cfg = generate_config(
|
|
"esp_hal",
|
|
&[
|
|
(
|
|
"place-spi-driver-in-ram",
|
|
"Places the SPI driver in RAM for better performance",
|
|
Value::Bool(false),
|
|
None
|
|
),
|
|
(
|
|
"spi-address-workaround",
|
|
"(ESP32 only) Enables a workaround for the issue where SPI in half-duplex mode incorrectly transmits the address on a single line if the data buffer is empty.",
|
|
Value::Bool(true),
|
|
None
|
|
),
|
|
(
|
|
"place-switch-tables-in-ram",
|
|
"Places switch-tables, some lookup tables and constants related to interrupt handling into RAM - resulting in better performance but slightly more RAM consumption.",
|
|
Value::Bool(true),
|
|
None
|
|
),
|
|
(
|
|
"place-anon-in-ram",
|
|
"Places anonymous symbols into RAM - resulting in better performance at the cost of significant more RAM consumption. Best to be combined with `place-switch-tables-in-ram`.",
|
|
Value::Bool(false),
|
|
None
|
|
),
|
|
],
|
|
true,
|
|
);
|
|
|
|
// RISC-V and Xtensa devices each require some special handling and processing
|
|
// of linker scripts:
|
|
|
|
#[allow(unused_mut)]
|
|
let mut config_symbols = config.all().collect::<Vec<_>>();
|
|
#[cfg(feature = "flip-link")]
|
|
config_symbols.push("flip-link");
|
|
|
|
for (key, value) in &cfg {
|
|
if let Value::Bool(true) = value {
|
|
config_symbols.push(key);
|
|
}
|
|
}
|
|
|
|
if cfg!(feature = "esp32") || cfg!(feature = "esp32s2") || cfg!(feature = "esp32s3") {
|
|
// Xtensa devices:
|
|
|
|
#[cfg(any(feature = "esp32", feature = "esp32s2"))]
|
|
File::create(out.join("memory_extras.x"))?.write_all(&generate_memory_extras())?;
|
|
|
|
let (irtc, drtc) = if cfg!(feature = "esp32s3") {
|
|
("rtc_fast_seg", "rtc_fast_seg")
|
|
} else {
|
|
("rtc_fast_iram_seg", "rtc_fast_dram_seg")
|
|
};
|
|
|
|
let alias = format!(
|
|
r#"
|
|
REGION_ALIAS("ROTEXT", irom_seg);
|
|
REGION_ALIAS("RWTEXT", iram_seg);
|
|
REGION_ALIAS("RODATA", drom_seg);
|
|
REGION_ALIAS("RWDATA", dram_seg);
|
|
REGION_ALIAS("RTC_FAST_RWTEXT", {});
|
|
REGION_ALIAS("RTC_FAST_RWDATA", {});
|
|
"#,
|
|
irtc, drtc
|
|
);
|
|
|
|
fs::write(out.join("alias.x"), alias)?;
|
|
fs::copy("ld/xtensa/hal-defaults.x", out.join("hal-defaults.x"))?;
|
|
} else {
|
|
// RISC-V devices:
|
|
|
|
preprocess_file(&config_symbols, "ld/riscv/asserts.x", out.join("asserts.x"))?;
|
|
preprocess_file(&config_symbols, "ld/riscv/debug.x", out.join("debug.x"))?;
|
|
preprocess_file(
|
|
&config_symbols,
|
|
"ld/riscv/hal-defaults.x",
|
|
out.join("hal-defaults.x"),
|
|
)?;
|
|
}
|
|
|
|
// With the architecture-specific linker scripts taken care of, we can copy all
|
|
// remaining linker scripts which are common to all devices:
|
|
copy_dir_all(&config_symbols, "ld/sections", &out)?;
|
|
copy_dir_all(&config_symbols, format!("ld/{device_name}"), &out)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------
|
|
// Helper Functions
|
|
|
|
fn copy_dir_all(
|
|
config_symbols: &[&str],
|
|
src: impl AsRef<Path>,
|
|
dst: impl AsRef<Path>,
|
|
) -> std::io::Result<()> {
|
|
fs::create_dir_all(&dst)?;
|
|
for entry in fs::read_dir(src)? {
|
|
let entry = entry?;
|
|
let ty = entry.file_type()?;
|
|
if ty.is_dir() {
|
|
copy_dir_all(
|
|
config_symbols,
|
|
entry.path(),
|
|
dst.as_ref().join(entry.file_name()),
|
|
)?;
|
|
} else {
|
|
preprocess_file(
|
|
config_symbols,
|
|
entry.path(),
|
|
dst.as_ref().join(entry.file_name()),
|
|
)?;
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
/// A naive pre-processor for linker scripts
|
|
fn preprocess_file(
|
|
config: &[&str],
|
|
src: impl AsRef<Path>,
|
|
dst: impl AsRef<Path>,
|
|
) -> std::io::Result<()> {
|
|
let file = File::open(src)?;
|
|
let mut out_file = File::create(dst)?;
|
|
|
|
let mut take = Vec::new();
|
|
take.push(true);
|
|
|
|
for line in std::io::BufReader::new(file).lines() {
|
|
let line = line?;
|
|
let trimmed = line.trim();
|
|
|
|
if let Some(condition) = trimmed.strip_prefix("#IF ") {
|
|
let should_take = take.iter().all(|v| *v);
|
|
let should_take = should_take && config.contains(&condition);
|
|
take.push(should_take);
|
|
continue;
|
|
} else if trimmed == "#ELSE" {
|
|
let taken = take.pop().unwrap();
|
|
let should_take = take.iter().all(|v| *v);
|
|
let should_take = should_take && !taken;
|
|
take.push(should_take);
|
|
continue;
|
|
} else if trimmed == "#ENDIF" {
|
|
take.pop();
|
|
continue;
|
|
}
|
|
|
|
if *take.last().unwrap() {
|
|
out_file.write_all(line.as_bytes())?;
|
|
let _ = out_file.write(b"\n")?;
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(feature = "esp32")]
|
|
fn generate_memory_extras() -> Vec<u8> {
|
|
let reserve_dram = if cfg!(feature = "bluetooth") {
|
|
"0x10000"
|
|
} else {
|
|
"0x0"
|
|
};
|
|
|
|
format!(
|
|
"
|
|
/* reserved at the start of DRAM for e.g. the BT stack */
|
|
RESERVE_DRAM = {reserve_dram};
|
|
"
|
|
)
|
|
.as_bytes()
|
|
.to_vec()
|
|
}
|
|
|
|
#[cfg(feature = "esp32s2")]
|
|
fn generate_memory_extras() -> Vec<u8> {
|
|
let reserved_cache = if cfg!(feature = "quad-psram") {
|
|
"0x4000"
|
|
} else {
|
|
"0x2000"
|
|
};
|
|
|
|
format!(
|
|
"
|
|
/* reserved at the start of DRAM/IRAM */
|
|
RESERVE_CACHES = {reserved_cache};
|
|
"
|
|
)
|
|
.as_bytes()
|
|
.to_vec()
|
|
}
|