1use std::sync::OnceLock;
4
5use anyhow::{bail, Result};
6use strum::IntoEnumIterator;
7
8macro_rules! include_toml {
9 ($type:ty, $file:expr) => {{
10 static LOADED_TOML: OnceLock<$type> = OnceLock::new();
11 LOADED_TOML.get_or_init(|| basic_toml::from_str(include_str!($file)).unwrap())
12 }};
13}
14
15#[derive(
17 Debug,
18 Clone,
19 Copy,
20 PartialEq,
21 Eq,
22 PartialOrd,
23 Ord,
24 serde::Deserialize,
25 serde::Serialize,
26 strum::Display,
27 strum::EnumIter,
28 strum::EnumString,
29 strum::AsRefStr,
30)]
31#[serde(rename_all = "lowercase")]
32#[strum(serialize_all = "lowercase")]
33pub enum Arch {
34 RiscV,
36 Xtensa,
38}
39
40#[derive(
42 Debug,
43 Clone,
44 Copy,
45 PartialEq,
46 Eq,
47 PartialOrd,
48 Ord,
49 serde::Deserialize,
50 serde::Serialize,
51 strum::Display,
52 strum::EnumIter,
53 strum::EnumString,
54 strum::AsRefStr,
55)]
56pub enum Cores {
57 #[serde(rename = "single_core")]
59 #[strum(serialize = "single_core")]
60 Single,
61 #[serde(rename = "multi_core")]
63 #[strum(serialize = "multi_core")]
64 Multi,
65}
66
67#[derive(
69 Debug,
70 Clone,
71 Copy,
72 PartialEq,
73 Eq,
74 PartialOrd,
75 Ord,
76 Hash,
77 serde::Deserialize,
78 serde::Serialize,
79 strum::Display,
80 strum::EnumIter,
81 strum::EnumString,
82 strum::AsRefStr,
83)]
84#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
85#[serde(rename_all = "kebab-case")]
86#[strum(serialize_all = "kebab-case")]
87pub enum Chip {
88 Esp32,
90 Esp32c2,
92 Esp32c3,
94 Esp32c6,
96 Esp32h2,
98 Esp32s2,
100 Esp32s3,
102}
103
104impl Chip {
105 pub fn target(&self) -> &str {
106 use Chip::*;
107
108 match self {
109 Esp32 => "xtensa-esp32-none-elf",
110 Esp32c2 | Esp32c3 => "riscv32imc-unknown-none-elf",
111 Esp32c6 | Esp32h2 => "riscv32imac-unknown-none-elf",
112 Esp32s2 => "xtensa-esp32s2-none-elf",
113 Esp32s3 => "xtensa-esp32s3-none-elf",
114 }
115 }
116
117 pub fn has_lp_core(&self) -> bool {
118 use Chip::*;
119
120 matches!(self, Esp32c6 | Esp32s2 | Esp32s3)
121 }
122
123 pub fn lp_target(&self) -> Result<&str> {
124 use Chip::*;
125
126 match self {
127 Esp32c6 => Ok("riscv32imac-unknown-none-elf"),
128 Esp32s2 | Esp32s3 => Ok("riscv32imc-unknown-none-elf"),
129 _ => bail!("Chip does not contain an LP core: '{}'", self),
130 }
131 }
132
133 pub fn pretty_name(&self) -> &str {
134 match self {
135 Chip::Esp32 => "ESP32",
136 Chip::Esp32c2 => "ESP32-C2",
137 Chip::Esp32c3 => "ESP32-C3",
138 Chip::Esp32c6 => "ESP32-C6",
139 Chip::Esp32h2 => "ESP32-H2",
140 Chip::Esp32s2 => "ESP32-S2",
141 Chip::Esp32s3 => "ESP32-S3",
142 }
143 }
144
145 pub fn is_xtensa(&self) -> bool {
146 matches!(self, Chip::Esp32 | Chip::Esp32s2 | Chip::Esp32s3)
147 }
148
149 pub fn is_riscv(&self) -> bool {
150 !self.is_xtensa()
151 }
152}
153
154#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
155struct Device {
156 pub name: String,
157 pub arch: Arch,
158 pub cores: Cores,
159 pub peripherals: Vec<String>,
160 pub symbols: Vec<String>,
161}
162
163#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
165pub struct Config {
166 device: Device,
167}
168
169impl Config {
170 pub fn for_chip(chip: &Chip) -> &Self {
172 match chip {
173 Chip::Esp32 => include_toml!(Config, "../devices/esp32.toml"),
174 Chip::Esp32c2 => include_toml!(Config, "../devices/esp32c2.toml"),
175 Chip::Esp32c3 => include_toml!(Config, "../devices/esp32c3.toml"),
176 Chip::Esp32c6 => include_toml!(Config, "../devices/esp32c6.toml"),
177 Chip::Esp32h2 => include_toml!(Config, "../devices/esp32h2.toml"),
178 Chip::Esp32s2 => include_toml!(Config, "../devices/esp32s2.toml"),
179 Chip::Esp32s3 => include_toml!(Config, "../devices/esp32s3.toml"),
180 }
181 }
182
183 pub fn name(&self) -> String {
185 self.device.name.clone()
186 }
187
188 pub fn arch(&self) -> Arch {
190 self.device.arch
191 }
192
193 pub fn cores(&self) -> Cores {
195 self.device.cores
196 }
197
198 pub fn peripherals(&self) -> &[String] {
200 &self.device.peripherals
201 }
202
203 pub fn symbols(&self) -> &[String] {
205 &self.device.symbols
206 }
207
208 pub fn all(&self) -> impl Iterator<Item = &str> + '_ {
210 [
211 self.device.name.as_str(),
212 self.device.arch.as_ref(),
213 self.device.cores.as_ref(),
214 ]
215 .into_iter()
216 .chain(self.device.peripherals.iter().map(|s| s.as_str()))
217 .chain(self.device.symbols.iter().map(|s| s.as_str()))
218 }
219
220 pub fn contains(&self, item: &str) -> bool {
222 self.all().any(|i| i == item)
223 }
224
225 pub fn define_symbols(&self) {
227 define_all_possible_symbols();
228 for symbol in self.all() {
230 println!("cargo:rustc-cfg={symbol}");
231 }
232 }
233}
234
235fn define_all_possible_symbols() {
240 println!("cargo:rustc-check-cfg=cfg(not_really_docsrs)");
242
243 for chip in Chip::iter() {
244 let config = Config::for_chip(&chip);
245 for symbol in config.all() {
246 println!("cargo:rustc-check-cfg=cfg({})", symbol);
248 }
249 }
250}