From b98631570f0c522b4a30558f6f4c6392e26d2395 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1niel=20Buga?= Date: Fri, 20 Jun 2025 09:45:49 +0200 Subject: [PATCH] Move GPIO pin properties to metadata (#3656) * Reorganize esp-metadata * Move GPIO properties to metadata * Fix AF enum variants, remove need for paste for them --- esp-hal/src/gpio/mod.rs | 8 +- esp-hal/src/peripheral.rs | 6 +- esp-hal/src/soc/esp32/peripherals.rs | 74 +- esp-hal/src/soc/esp32c2/peripherals.rs | 30 +- esp-hal/src/soc/esp32c3/peripherals.rs | 46 +- esp-hal/src/soc/esp32c6/peripherals.rs | 64 +- esp-hal/src/soc/esp32h2/peripherals.rs | 58 +- esp-hal/src/soc/esp32s2/peripherals.rs | 89 +- esp-hal/src/soc/esp32s3/peripherals.rs | 92 +- esp-metadata/devices/esp32.toml | 40 + esp-metadata/devices/esp32c2.toml | 18 + esp-metadata/devices/esp32c3.toml | 26 + esp-metadata/devices/esp32c6.toml | 35 + esp-metadata/devices/esp32h2.toml | 32 + esp-metadata/devices/esp32s2.toml | 47 + esp-metadata/devices/esp32s3.toml | 50 ++ esp-metadata/src/cfg.rs | 577 +++++++++++++ esp-metadata/src/generate_cfg.rs | 1090 ------------------------ esp-metadata/src/lib.rs | 667 ++++++++++++++- 19 files changed, 1728 insertions(+), 1321 deletions(-) create mode 100644 esp-metadata/src/cfg.rs delete mode 100644 esp-metadata/src/generate_cfg.rs diff --git a/esp-hal/src/gpio/mod.rs b/esp-hal/src/gpio/mod.rs index a47d9e1a2..c08042f0f 100644 --- a/esp-hal/src/gpio/mod.rs +++ b/esp-hal/src/gpio/mod.rs @@ -779,8 +779,8 @@ macro_rules! gpio { $( ($gpionum:literal, [$($type:tt),*] $( - ( $( $af_input_num:literal => $af_input_signal:ident )* ) - ( $( $af_output_num:literal => $af_output_signal:ident )* ) + ( $( $af_input_num:ident => $af_input_signal:ident )* ) + ( $( $af_output_num:ident => $af_output_signal:ident )* ) )? ) )+ @@ -834,7 +834,7 @@ macro_rules! gpio { $( $( ( - $crate::gpio::AlternateFunction::[< _ $af_output_num >], + $crate::gpio::AlternateFunction::$af_output_num, $crate::gpio::OutputSignal::$af_output_signal ), )* @@ -847,7 +847,7 @@ macro_rules! gpio { $( $( ( - $crate::gpio::AlternateFunction::[< _ $af_input_num >], + $crate::gpio::AlternateFunction::$af_input_num, $crate::gpio::InputSignal::$af_input_signal ), )* diff --git a/esp-hal/src/peripheral.rs b/esp-hal/src/peripheral.rs index 8500e2258..bb8d6c7b9 100644 --- a/esp-hal/src/peripheral.rs +++ b/esp-hal/src/peripheral.rs @@ -25,7 +25,7 @@ macro_rules! peripherals { ),* $(,)? ], pins: [ - $( ( $pin:literal, $($pin_tokens:tt)* ) )* + $( $pin:literal, )* ] ) => { paste::paste! { @@ -109,10 +109,6 @@ macro_rules! peripherals { } } } - - $crate::gpio! { - $( ($pin, $($pin_tokens)* ) )* - } } $( diff --git a/esp-hal/src/soc/esp32/peripherals.rs b/esp-hal/src/soc/esp32/peripherals.rs index 4dd320e48..5ac75142d 100644 --- a/esp-hal/src/soc/esp32/peripherals.rs +++ b/esp-hal/src/soc/esp32/peripherals.rs @@ -83,41 +83,43 @@ crate::peripherals! { DMA_I2S1 <= I2S1, ], pins: [ - (0, [Input, Output, Analog, RtcIo, Touch] (5 => EMAC_TX_CLK) (1 => CLK_OUT1)) - (1, [Input, Output] (5 => EMAC_RXD2) (0 => U0TXD 1 => CLK_OUT3)) - (2, [Input, Output, Analog, RtcIo, Touch] (1 => HSPIWP 3 => HS2_DATA0 4 => SD_DATA0) (3 => HS2_DATA0 4 => SD_DATA0)) - (3, [Input, Output] (0 => U0RXD) (1 => CLK_OUT2)) - (4, [Input, Output, Analog, RtcIo, Touch] (1 => HSPIHD 3 => HS2_DATA1 4 => SD_DATA1 5 => EMAC_TX_ER) (3 => HS2_DATA1 4 => SD_DATA1)) - (5, [Input, Output] (1 => VSPICS0 3 => HS1_DATA6 5 => EMAC_RX_CLK) (3 => HS1_DATA6)) - (6, [Input, Output] (4 => U1CTS) (0 => SD_CLK 1 => SPICLK 3 => HS1_CLK)) - (7, [Input, Output] (0 => SD_DATA0 1 => SPIQ 3 => HS1_DATA0) (0 => SD_DATA0 1 => SPIQ 3 => HS1_DATA0 4 => U2RTS)) - (8, [Input, Output] (0 => SD_DATA1 1 => SPID 3 => HS1_DATA1 4 => U2CTS) (0 => SD_DATA1 1 => SPID 3 => HS1_DATA1)) - (9, [Input, Output] (0 => SD_DATA2 1 => SPIHD 3 => HS1_DATA2 4 => U1RXD) (0 => SD_DATA2 1 => SPIHD 3 => HS1_DATA2)) - (10, [Input, Output] ( 0 => SD_DATA3 1 => SPIWP 3 => HS1_DATA3) (0 => SD_DATA3 1 => SPIWP 3 => HS1_DATA3 4 => U1TXD)) - (11, [Input, Output] ( 1 => SPICS0) (0 => SD_CMD 1 => SPICS0 3 => HS1_CMD 4 => U1RTS)) - (12, [Input, Output, Analog, RtcIo, Touch] (0 => MTDI 1 => HSPIQ 3 => HS2_DATA2 4 => SD_DATA2) (1 => HSPIQ 3 => HS2_DATA2 4 => SD_DATA2 5 => EMAC_TXD3)) - (13, [Input, Output, Analog, RtcIo, Touch] (0 => MTCK 1 => HSPID 3 => HS2_DATA3 4 => SD_DATA3) (1 => HSPID 3 => HS2_DATA3 4 => SD_DATA3 5 => EMAC_RX_ER)) - (14, [Input, Output, Analog, RtcIo, Touch] (0 => MTMS 1 => HSPICLK) (1 => HSPICLK 3 => HS2_CLK 4 => SD_CLK 5 => EMAC_TXD2)) - (15, [Input, Output, Analog, RtcIo, Touch] (1 => HSPICS0 5 => EMAC_RXD3) (0 => MTDO 1 => HSPICS0 3 => HS2_CMD 4 => SD_CMD)) - (16, [Input, Output] (3 => HS1_DATA4 4 => U2RXD) (3 => HS1_DATA4 5 => EMAC_CLK_OUT)) - (17, [Input, Output] (3 => HS1_DATA5) (3 => HS1_DATA5 4 => U2TXD 5 => EMAC_CLK_180)) - (18, [Input, Output] (1 => VSPICLK 3 => HS1_DATA7) (1 => VSPICLK 3 => HS1_DATA7)) - (19, [Input, Output] (1 => VSPIQ 3 => U0CTS) (1 => VSPIQ 5 => EMAC_TXD0)) - (20, [Input, Output]) - (21, [Input, Output] (1 => VSPIHD) (1 => VSPIHD 5 => EMAC_TX_EN)) - (22, [Input, Output] (1 => VSPIWP) (1 => VSPIWP 3 => U0RTS 5 => EMAC_TXD1)) - (23, [Input, Output] (1 => VSPID) (1 => VSPID 3 => HS1_STROBE)) - (24, [Input, Output]) - (25, [Input, Output, Analog, RtcIo] (5 => EMAC_RXD0) ()) - (26, [Input, Output, Analog, RtcIo] (5 => EMAC_RXD1) ()) - (27, [Input, Output, Analog, RtcIo, Touch] (5 => EMAC_RX_DV) ()) - (32, [Input, Output, Analog, RtcIo, Touch]) - (33, [Input, Output, Analog, RtcIo, Touch]) - (34, [Input, Analog, RtcIoInput]) - (35, [Input, Analog, RtcIoInput]) - (36, [Input, Analog, RtcIoInput]) - (37, [Input, Analog, RtcIoInput]) - (38, [Input, Analog, RtcIoInput]) - (39, [Input, Analog, RtcIoInput]) + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, ] } + +include!(concat!(env!("OUT_DIR"), "/_generated_gpio.rs")); diff --git a/esp-hal/src/soc/esp32c2/peripherals.rs b/esp-hal/src/soc/esp32c2/peripherals.rs index 0a5224281..3297c0266 100644 --- a/esp-hal/src/soc/esp32c2/peripherals.rs +++ b/esp-hal/src/soc/esp32c2/peripherals.rs @@ -65,19 +65,21 @@ crate::peripherals! { DMA_CH0 <= virtual, ], pins: [ - (0, [Input, Output, Analog, RtcIo]) - (1, [Input, Output, Analog, RtcIo]) - (2, [Input, Output, Analog, RtcIo] (2 => FSPIQ) (2 => FSPIQ)) - (3, [Input, Output, Analog, RtcIo]) - (4, [Input, Output, Analog, RtcIo] (2 => FSPIHD) (2 => FSPIHD)) - (5, [Input, Output, Analog, RtcIo] (2 => FSPIWP) (2 => FSPIWP)) - (6, [Input, Output] (2 => FSPICLK) (2 => FSPICLK_MUX)) - (7, [Input, Output] (2 => FSPID) (2 => FSPID)) - (8, [Input, Output]) - (9, [Input, Output]) - (10, [Input, Output] (2 => FSPICS0) (2 => FSPICS0)) - (18, [Input, Output]) - (19, [Input, Output]) - (20, [Input, Output] (0 => U0RXD) ()) + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 18, + 19, + 20, ] } + +include!(concat!(env!("OUT_DIR"), "/_generated_gpio.rs")); diff --git a/esp-hal/src/soc/esp32c3/peripherals.rs b/esp-hal/src/soc/esp32c3/peripherals.rs index f47558061..0646ff2d5 100644 --- a/esp-hal/src/soc/esp32c3/peripherals.rs +++ b/esp-hal/src/soc/esp32c3/peripherals.rs @@ -75,27 +75,29 @@ crate::peripherals! { DMA_CH2 <= virtual, ], pins: [ - (0, [Input, Output, Analog, RtcIo]) - (1, [Input, Output, Analog, RtcIo]) - (2, [Input, Output, Analog, RtcIo] (2 => FSPIQ) (2 => FSPIQ)) - (3, [Input, Output, Analog, RtcIo]) - (4, [Input, Output, Analog, RtcIo] (2 => FSPIHD) (0 => USB_JTAG_TMS 2 => FSPIHD)) - (5, [Input, Output, Analog, RtcIo] (2 => FSPIWP) (0 => USB_JTAG_TDI 2 => FSPIWP)) - (6, [Input, Output] (2 => FSPICLK) (0 => USB_JTAG_TCK 2 => FSPICLK_MUX)) - (7, [Input, Output] (2 => FSPID) (0 => USB_JTAG_TDO 2 => FSPID)) - (8, [Input, Output]) - (9, [Input, Output]) - (10, [Input, Output] (2 => FSPICS0) (2 => FSPICS0)) - (11, [Input, Output]) - (12, [Input, Output] (0 => SPIHD) (0 => SPIHD)) - (13, [Input, Output] (0 => SPIWP) (0 => SPIWP)) - (14, [Input, Output] () (0 => SPICS0)) - (15, [Input, Output] () (0 => SPICLK_MUX)) - (16, [Input, Output] (0 => SPID) (0 => SPID)) - (17, [Input, Output] (0 => SPIQ) (0 => SPIQ)) - (18, [Input, Output]) - (19, [Input, Output]) - (20, [Input, Output] (0 => U0RXD) ()) - (21, [Input, Output] () (0 => U0TXD)) + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, ] } + +include!(concat!(env!("OUT_DIR"), "/_generated_gpio.rs")); diff --git a/esp-hal/src/soc/esp32c6/peripherals.rs b/esp-hal/src/soc/esp32c6/peripherals.rs index 4999f3cb4..f3ceaa8f9 100644 --- a/esp-hal/src/soc/esp32c6/peripherals.rs +++ b/esp-hal/src/soc/esp32c6/peripherals.rs @@ -111,36 +111,38 @@ crate::peripherals! { DMA_CH2 <= virtual, ], pins: [ - (0, [Input, Output, Analog, RtcIo]) - (1, [Input, Output, Analog, RtcIo]) - (2, [Input, Output, Analog, RtcIo] (2 => FSPIQ) (2 => FSPIQ)) - (3, [Input, Output, Analog, RtcIo]) - (4, [Input, Output, Analog, RtcIo] (2 => FSPIHD) (0 => USB_JTAG_TMS 2 => FSPIHD)) - (5, [Input, Output, Analog, RtcIo] (2 => FSPIWP) (0 => USB_JTAG_TDI 2 => FSPIWP)) - (6, [Input, Output, Analog, RtcIo] (2 => FSPICLK) (0 => USB_JTAG_TCK 2 => FSPICLK_MUX)) - (7, [Input, Output, Analog, RtcIo] (2 => FSPID) (0 => USB_JTAG_TDO 2 => FSPID)) - (8, [Input, Output]) - (9, [Input, Output]) - (10, [Input, Output]) - (11, [Input, Output]) - (12, [Input, Output]) - (13, [Input, Output]) - (14, [Input, Output]) - (15, [Input, Output]) - (16, [Input, Output] (0 => U0RXD) (2 => FSPICS0)) - (17, [Input, Output] () (0 => U0TXD 2 => FSPICS1)) - (18, [Input, Output] () (2 => FSPICS2)) // 0 => SDIO_CMD but there are no signals since it's a fixed pin - (19, [Input, Output] () (2 => FSPICS3)) // 0 => SDIO_CLK but there are no signals since it's a fixed pin - (20, [Input, Output] () (2 => FSPICS4)) // 0 => SDIO_DATA0 but there are no signals since it's a fixed pin - (21, [Input, Output] () (2 => FSPICS5)) // 0 => SDIO_DATA1 but there are no signals since it's a fixed pin - (22, [Input, Output] () ()) // 0 => SDIO_DATA2 but there are no signals since it's a fixed pin - (23, [Input, Output] () ()) // 0 => SDIO_DATA3 but there are no signals since it's a fixed pin - (24, [Input, Output] () (0 => SPICS0)) - (25, [Input, Output] (0 => SPIQ) (0 => SPIQ)) - (26, [Input, Output] (0 => SPIWP) (0 => SPIWP)) - (27, [Input, Output]) - (28, [Input, Output] (0 => SPIHD) (0 => SPIHD)) - (29, [Input, Output] () (0 => SPICLK_MUX)) - (30, [Input, Output] (0 => SPID) (0 => SPID)) + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, ] } + +include!(concat!(env!("OUT_DIR"), "/_generated_gpio.rs")); diff --git a/esp-hal/src/soc/esp32h2/peripherals.rs b/esp-hal/src/soc/esp32h2/peripherals.rs index f56edc758..ba285e48f 100644 --- a/esp-hal/src/soc/esp32h2/peripherals.rs +++ b/esp-hal/src/soc/esp32h2/peripherals.rs @@ -99,33 +99,35 @@ crate::peripherals! { DMA_CH2 <= virtual, ], pins: [ - (0, [Input, Output, Analog] (2 => FSPIQ) (2 => FSPIQ)) - (1, [Input, Output, Analog] (2 => FSPICS0) (2 => FSPICS0)) - (2, [Input, Output, Analog] (2 => FSPIWP) (2 => FSPIWP)) - (3, [Input, Output, Analog] (2 => FSPIHD) (2 => FSPIHD)) - (4, [Input, Output, Analog] (2 => FSPICLK) (2 => FSPICLK_MUX)) - (5, [Input, Output, Analog] (2 => FSPID) (2 => FSPID)) - (6, [Input, Output]) - (7, [Input, Output]) - (8, [Input, Output]) - (9, [Input, Output]) - (10, [Input, Output]) - (11, [Input, Output]) - (12, [Input, Output]) - (13, [Input, Output]) - (14, [Input, Output]) - (15, [Input, Output] () (0 => SPICS0)) - (16, [Input, Output] (0 => SPIQ) (0 => SPIQ)) - (17, [Input, Output] (0 => SPIWP) (0 => SPIWP)) - (18, [Input, Output] (0 => SPIHD) (0 => SPIHD)) - (19, [Input, Output] () (0 => SPICLK)) - (20, [Input, Output] (0 => SPID) (0 => SPID)) - (21, [Input, Output]) - (22, [Input, Output]) - (23, [Input, Output] () (2 => FSPICS1)) - (24, [Input, Output] () (2 => FSPICS2)) - (25, [Input, Output] () (2 => FSPICS3)) - (26, [Input, Output] () (2 => FSPICS4)) - (27, [Input, Output] () (2 => FSPICS5)) + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, ] } + +include!(concat!(env!("OUT_DIR"), "/_generated_gpio.rs")); diff --git a/esp-hal/src/soc/esp32s2/peripherals.rs b/esp-hal/src/soc/esp32s2/peripherals.rs index 9d29f4a30..4fece2434 100644 --- a/esp-hal/src/soc/esp32s2/peripherals.rs +++ b/esp-hal/src/soc/esp32s2/peripherals.rs @@ -83,49 +83,50 @@ crate::peripherals! { DMA_COPY <= COPY_DMA, ], pins: [ - (0, [Input, Output, Analog, RtcIo]) - (1, [Input, Output, Analog, RtcIo]) - (2, [Input, Output, Analog, RtcIo]) - (3, [Input, Output, Analog, RtcIo]) - (4, [Input, Output, Analog, RtcIo]) - (5, [Input, Output, Analog, RtcIo]) - (6, [Input, Output, Analog, RtcIo]) - (7, [Input, Output, Analog, RtcIo]) - (8, [Input, Output, Analog, RtcIo] ( ) ( 3 => SUBSPICS1 )) - (9, [Input, Output, Analog, RtcIo] ( 3 => SUBSPIHD 4 => FSPIHD ) ( 3 => SUBSPIHD 4 => FSPIHD )) - (10, [Input, Output, Analog, RtcIo] (2 => FSPIIO4 4 => FSPICS0) (2 => FSPIIO4 3 => SUBSPICS0 4 => FSPICS0)) - (11, [Input, Output, Analog, RtcIo] (2 => FSPIIO5 3 => SUBSPID 4 => FSPID ) (2 => FSPIIO5 3 => SUBSPID 4 => FSPID )) - (12, [Input, Output, Analog, RtcIo] (2 => FSPIIO6 4 => FSPICLK) (2 => FSPIIO6 3 => SUBSPICLK 4 => FSPICLK)) - (13, [Input, Output, Analog, RtcIo] (2 => FSPIIO7 3 => SUBSPIQ 4 => FSPIQ ) (2 => FSPIIO7 3 => SUBSPIQ 4 => FSPIQ )) - (14, [Input, Output, Analog, RtcIo] ( 3 => SUBSPIWP 4 => FSPIWP ) (2 => FSPIDQS 3 => SUBSPIWP 4 => FSPIWP )) - (15, [Input, Output, Analog, RtcIo] ( ) (2 => U0RTS)) - (16, [Input, Output, Analog, RtcIo] (2 => U0CTS) ( )) - (17, [Input, Output, Analog, RtcIo] ( ) (2 => U1TXD)) - (18, [Input, Output, Analog, RtcIo] (2 => U1RXD) ( )) - (19, [Input, Output, Analog, RtcIo] ( ) (2 => U1RTS)) - (20, [Input, Output, Analog, RtcIo] (2 => U1CTS) ( )) - (21, [Input, Output, Analog, RtcIo]) - - (26, [Input, Output]) - (27, [Input, Output]) - (28, [Input, Output]) - (29, [Input, Output]) - (30, [Input, Output]) - (31, [Input, Output]) - (32, [Input, Output]) - (33, [Input, Output] (2 => FSPIHD 3 => SUBSPIHD ) (2 => FSPIHD 3 => SUBSPIHD )) - (34, [Input, Output] (2 => FSPICS0 ) (2 => FSPICS0 3 => SUBSPICS0 )) - (35, [Input, Output] (2 => FSPID 3 => SUBSPID ) (2 => FSPID 3 => SUBSPID )) - (36, [Input, Output] (2 => FSPICLK ) (2 => FSPICLK 3 => SUBSPICLK )) - (37, [Input, Output] (2 => FSPIQ 3 => SUBSPIQ 4 => SPIDQS) (2 => FSPIQ 3 => SUBSPIQ 4 => SPIDQS)) - (38, [Input, Output] (2 => FSPIWP 3 => SUBSPIWP ) (2 => FSPIWP 3 => SUBSPIWP )) - (39, [Input, Output] ( ) ( 3 => SUBSPICS1 )) - (40, [Input, Output]) - (41, [Input, Output]) - (42, [Input, Output]) - (43, [Input, Output]) - (44, [Input, Output]) - (45, [Input, Output]) - (46, [Input, Output]) + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, ] } + +include!(concat!(env!("OUT_DIR"), "/_generated_gpio.rs")); diff --git a/esp-hal/src/soc/esp32s3/peripherals.rs b/esp-hal/src/soc/esp32s3/peripherals.rs index aa21d36f9..cf7a94bd2 100644 --- a/esp-hal/src/soc/esp32s3/peripherals.rs +++ b/esp-hal/src/soc/esp32s3/peripherals.rs @@ -90,50 +90,52 @@ crate::peripherals! { DMA_CH4 <= virtual, ], pins: [ - (0, [Input, Output, Analog, RtcIo]) - (1, [Input, Output, Analog, RtcIo]) - (2, [Input, Output, Analog, RtcIo]) - (3, [Input, Output, Analog, RtcIo]) - (4, [Input, Output, Analog, RtcIo]) - (5, [Input, Output, Analog, RtcIo]) - (6, [Input, Output, Analog, RtcIo]) - (7, [Input, Output, Analog, RtcIo]) - (8, [Input, Output, Analog, RtcIo] () (3 => SUBSPICS1)) - (9, [Input, Output, Analog, RtcIo] (3 => SUBSPIHD 4 => FSPIHD) (3 => SUBSPIHD 4 => FSPIHD)) - (10, [Input, Output, Analog, RtcIo] (2 => FSPIIO4 4 => FSPICS0) (2 => FSPIIO4 3 => SUBSPICS0 4 => FSPICS0)) - (11, [Input, Output, Analog, RtcIo] (2 => FSPIIO5 3 => SUBSPID 4 => FSPID) (2 => FSPIIO5 3 => SUBSPID 4 => FSPID)) - (12, [Input, Output, Analog, RtcIo] (2 => FSPIIO6 4 => FSPICLK) (2 => FSPIIO6 3=> SUBSPICLK 4 => FSPICLK)) - (13, [Input, Output, Analog, RtcIo] (2 => FSPIIO7 3 => SUBSPIQ 4 => FSPIQ) (2 => FSPIIO7 3 => SUBSPIQ 4 => FSPIQ)) - (14, [Input, Output, Analog, RtcIo] (3 => SUBSPIWP 4 => FSPIWP) (2 => FSPIDQS 3 => SUBSPIWP 4 => FSPIWP)) - (15, [Input, Output, Analog, RtcIo] () (2 => U0RTS)) - (16, [Input, Output, Analog, RtcIo] (2 => U0CTS) ()) - (17, [Input, Output, Analog, RtcIo] () (2 => U1TXD)) - (18, [Input, Output, Analog, RtcIo] (2 => U1RXD) ()) - (19, [Input, Output, Analog, RtcIo] () (2 => U1RTS)) - (20, [Input, Output, Analog, RtcIo] (2 => U1CTS) ()) - (21, [Input, Output, Analog, RtcIo]) - (26, [Input, Output]) - (27, [Input, Output]) - (28, [Input, Output]) - (29, [Input, Output]) - (30, [Input, Output]) - (31, [Input, Output]) - (32, [Input, Output]) - (33, [Input, Output] (2 => FSPIHD 3 => SUBSPIHD) (2 => FSPIHD 3 => SUBSPIHD)) - (34, [Input, Output] (2 => FSPICS0) (2 => FSPICS0 3 => SUBSPICS0)) - (35, [Input, Output] (2 => FSPID 3 => SUBSPID) (2 => FSPID 3 => SUBSPID)) - (36, [Input, Output] (2 => FSPICLK) (2 => FSPICLK 3 => SUBSPICLK)) - (37, [Input, Output] (2 => FSPIQ 3 => SUBSPIQ 4 => SPIDQS) (2 => FSPIQ 3=> SUBSPIQ 4 => SPIDQS)) - (38, [Input, Output] (2 => FSPIWP 3 => SUBSPIWP) (3 => FSPIWP 3 => SUBSPIWP)) - (39, [Input, Output] () (4 => SUBSPICS1)) - (40, [Input, Output]) - (41, [Input, Output]) - (42, [Input, Output]) - (43, [Input, Output]) - (44, [Input, Output]) - (45, [Input, Output]) - (46, [Input, Output]) - (47, [Input, Output]) - (48, [Input, Output]) + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, ] } + +include!(concat!(env!("OUT_DIR"), "/_generated_gpio.rs")); diff --git a/esp-metadata/devices/esp32.toml b/esp-metadata/devices/esp32.toml index c3a21761a..44f32096f 100644 --- a/esp-metadata/devices/esp32.toml +++ b/esp-metadata/devices/esp32.toml @@ -104,6 +104,46 @@ instances = [ [device.gpio] support_status = "supported" has_bank_1 = true +instances = [ + { name = "gpio", pins = [ + { pin = 0, kind = ["input", "output", "analog", "rtc", "touch"], af_input = { 5 = "EMAC_TX_CLK" }, af_output = { 1 = "CLK_OUT1" } }, + { pin = 1, kind = ["input", "output"], af_input = { 5 = "EMAC_RXD2" }, af_output = { 0 = "U0TXD", 1 = "CLK_OUT3" } }, + { pin = 2, kind = ["input", "output", "analog", "rtc", "touch"], af_input = { 1 = "HSPIWP", 3 = "HS2_DATA0", 4 = "SD_DATA0" }, af_output = { 3 = "HS2_DATA0", 4 = "SD_DATA0" } }, + { pin = 3, kind = ["input", "output"], af_input = { 0 = "U0RXD" }, af_output = { 1 = "CLK_OUT2" } }, + { pin = 4, kind = ["input", "output", "analog", "rtc", "touch"], af_input = { 1 = "HSPIHD", 3 = "HS2_DATA1", 4 = "SD_DATA1", 5 = "EMAC_TX_ER" }, af_output = { 3 = "HS2_DATA1", 4 = "SD_DATA1" } }, + { pin = 5, kind = ["input", "output"], af_input = { 1 = "VSPICS0", 3 = "HS1_DATA6", 5 = "EMAC_RX_CLK" }, af_output = { 3 = "HS1_DATA6" } }, + { pin = 6, kind = ["input", "output"], af_input = { 4 = "U1CTS" }, af_output = { 0 = "SD_CLK", 1 = "SPICLK", 3 = "HS1_CLK" } }, + { pin = 7, kind = ["input", "output"], af_input = { 0 = "SD_DATA0", 1 = "SPIQ", 3 = "HS1_DATA0" }, af_output = { 0 = "SD_DATA0", 1 = "SPIQ", 3 = "HS1_DATA0", 4 = "U2RTS"} }, + { pin = 8, kind = ["input", "output"], af_input = { 0 = "SD_DATA1", 1 = "SPID", 3 = "HS1_DATA1" }, af_output = { 0 = "SD_DATA1", 1 = "SPID", 3 = "HS1_DATA1"} }, + { pin = 9, kind = ["input", "output"], af_input = { 0 = "SD_DATA2", 1 = "SPIHD", 3 = "HS1_DATA2", 4 = "U1RXD" }, af_output = { 0 = "SD_DATA2", 1 = "SPIHD", 3 = "HS1_DATA2" } }, + { pin = 10, kind = ["input", "output"], af_input = { 0 = "SD_DATA3", 1 = "SPIWP", 3 = "HS1_DATA3" }, af_output = { 0 = "SD_DATA3", 1 = "SPIWP", 3 = "HS1_DATA3", 4 = "U1TXD" } }, + { pin = 11, kind = ["input", "output"], af_input = { 1 = "SPICS0" }, af_output = { 0 = "SD_CMD", 1 = "SPICS0", 3 = "HS1_CMD", 4 = "U1RTS" } }, + { pin = 12, kind = ["input", "output", "analog", "rtc", "touch"], af_input = { 0 = "MTDI", 1 = "HSPIQ", 3 = "HS2_DATA2", 4 = "SD_DATA2" }, af_output = { 1 = "HSPIQ", 3 = "HS2_DATA2", 4 = "SD_DATA2", 5 = "EMAC_TXD3" } }, + { pin = 13, kind = ["input", "output", "analog", "rtc", "touch"], af_input = { 0 = "MTCK", 1 = "HSPID", 3 = "HS2_DATA3", 4 = "SD_DATA3" }, af_output = { 1 = "HSPID", 3 = "HS2_DATA3", 4 = "SD_DATA3", 5 = "EMAC_RX_ER" } }, + { pin = 14, kind = ["input", "output", "analog", "rtc", "touch"], af_input = { 0 = "MTMS", 1 = "HSPICLK" }, af_output = { 1 = "HSPICLK", 3 = "HS2_CLK", 4 = "SD_CLK", 5 = "EMAC_TXD2" } }, + { pin = 15, kind = ["input", "output", "analog", "rtc", "touch"], af_input = { 1 = "HSPICS0", 5 = "EMAC_RXD3" }, af_output = { 0 = "MTDO", 1 = "HSPICS0", 3 = "HS2_CMD", 4 = "SD_CMD" } }, + { pin = 16, kind = ["input", "output"], af_input = { 3 = "HS1_DATA4", 4 = "U2RXD" }, af_output = { 3 = "HS1_DATA4", 5 = "EMAC_CLK_OUT" } }, + { pin = 17, kind = ["input", "output"], af_input = { 3 = "HS1_DATA5" }, af_output = { 3 = "HS1_DATA5", 4 = "U2TXD", 5 = "EMAC_CLK_180" } }, + { pin = 18, kind = ["input", "output"], af_input = { 1 = "VSPICLK", 3 = "HS1_DATA7" }, af_output = { 1 = "VSPICLK", 3 = "HS1_DATA7" } }, + { pin = 19, kind = ["input", "output"], af_input = { 1 = "VSPIQ", 3 = "U0CTS" }, af_output = { 1 = "VSPIQ", 5 = "EMAC_TXD0" } }, + { pin = 20, kind = ["input", "output"] }, + { pin = 21, kind = ["input", "output"], af_input = { 1 = "VSPIHD" }, af_output = { 1 = "VSPIHD", 5 = "EMAC_TX_EN" } }, + { pin = 22, kind = ["input", "output"], af_input = { 1 = "VSPIWP" }, af_output = { 1 = "VSPIWP", 3 = "U0RTS", 5 = "EMAC_TXD1" } }, + { pin = 23, kind = ["input", "output"], af_input = { 1 = "VSPID" }, af_output = { 1 = "VSPID", 3 = "HS1_STROBE" } }, + { pin = 24, kind = ["input", "output"] }, + { pin = 25, kind = ["input", "output", "analog", "rtc"], af_input = { 5 = "EMAC_RXD0" } }, + { pin = 26, kind = ["input", "output", "analog", "rtc"], af_input = { 5 = "EMAC_RXD1" } }, + { pin = 27, kind = ["input", "output", "analog", "rtc", "touch"], af_input = { 5 = "EMAC_RX_DV" } }, + { pin = 32, kind = ["input", "output", "analog", "rtc", "touch"] }, + { pin = 33, kind = ["input", "output", "analog", "rtc", "touch"] }, + { pin = 34, kind = ["input", "analog", "rtc"] }, + { pin = 35, kind = ["input", "analog", "rtc"] }, + { pin = 36, kind = ["input", "analog", "rtc"] }, + { pin = 37, kind = ["input", "analog", "rtc"] }, + { pin = 38, kind = ["input", "analog", "rtc"] }, + { pin = 39, kind = ["input", "analog", "rtc"] }, + ] }, +] [device.i2c_master] support_status = "supported" diff --git a/esp-metadata/devices/esp32c2.toml b/esp-metadata/devices/esp32c2.toml index 72f421c94..8d94e3c9b 100644 --- a/esp-metadata/devices/esp32c2.toml +++ b/esp-metadata/devices/esp32c2.toml @@ -72,6 +72,24 @@ instances = [ [device.gpio] support_status = "supported" +instances = [ + { name = "gpio", pins = [ + { pin = 0, kind = ["input", "output", "analog", "rtc"] }, + { pin = 1, kind = ["input", "output", "analog", "rtc"] }, + { pin = 2, kind = ["input", "output", "analog", "rtc"], af_input = { 2 = "FSPIQ" }, af_output = { 2 = "FSPIQ" } }, + { pin = 3, kind = ["input", "output", "analog", "rtc"] }, + { pin = 4, kind = ["input", "output", "analog", "rtc"], af_input = { 2 = "FSPIHD" }, af_output = { 2 = "FSPIHD" } }, + { pin = 5, kind = ["input", "output", "analog", "rtc"], af_input = { 2 = "FSPIWP" }, af_output = { 2 = "FSPIWP" } }, + { pin = 6, kind = ["input", "output"], af_input = { 2 = "FSPICLK" }, af_output = { 2 = "FSPICLK_MUX" } }, + { pin = 7, kind = ["input", "output"], af_input = { 2 = "FSPID" }, af_output = { 2 = "FSPID" } }, + { pin = 8, kind = ["input", "output"] }, + { pin = 9, kind = ["input", "output"] }, + { pin = 10, kind = ["input", "output"], af_input = { 2 = "FSPICS0" }, af_output = { 2 = "FSPICS0" } }, + { pin = 18, kind = ["input", "output"] }, + { pin = 19, kind = ["input", "output"] }, + { pin = 20, kind = ["input", "output"], af_input = { 0 = "U0RXD" } }, + ] }, +] [device.i2c_master] support_status = "supported" diff --git a/esp-metadata/devices/esp32c3.toml b/esp-metadata/devices/esp32c3.toml index a6069251b..d91f1f37f 100644 --- a/esp-metadata/devices/esp32c3.toml +++ b/esp-metadata/devices/esp32c3.toml @@ -87,6 +87,32 @@ instances = [ [device.gpio] support_status = "supported" +instances = [ + { name = "gpio", pins = [ + { pin = 0, kind = ["input", "output", "analog", "rtc"] }, + { pin = 1, kind = ["input", "output", "analog", "rtc"] }, + { pin = 2, kind = ["input", "output", "analog", "rtc"], af_input = { 2 = "FSPIQ" }, af_output = { 2 = "FSPIQ" } }, + { pin = 3, kind = ["input", "output", "analog", "rtc"] }, + { pin = 4, kind = ["input", "output", "analog", "rtc"], af_input = { 2 = "FSPIHD" }, af_output = { 0 = "USB_JTAG_TMS", 2 = "FSPIHD" } }, + { pin = 5, kind = ["input", "output", "analog", "rtc"], af_input = { 2 = "FSPIWP" }, af_output = { 0 = "USB_JTAG_TDI", 2 = "FSPIWP" } }, + { pin = 6, kind = ["input", "output"], af_input = { 2 = "FSPICLK" }, af_output = { 0 = "USB_JTAG_TCK", 2 = "FSPICLK_MUX" } }, + { pin = 7, kind = ["input", "output"], af_input = { 2 = "FSPID" }, af_output = { 0 = "USB_JTAG_TDO", 2 = "FSPID" } }, + { pin = 8, kind = ["input", "output"] }, + { pin = 9, kind = ["input", "output"] }, + { pin = 10, kind = ["input", "output"], af_input = { 2 = "FSPICS0" }, af_output = { 2 = "FSPICS0" } }, + { pin = 11, kind = ["input", "output"] }, + { pin = 12, kind = ["input", "output"], af_input = { 0 = "SPIHD" }, af_output = { 0 = "SPIHD" } }, + { pin = 13, kind = ["input", "output"], af_input = { 0 = "SPIWP" }, af_output = { 0 = "SPIWP" } }, + { pin = 14, kind = ["input", "output"], af_output = { 0 = "SPICS0" } }, + { pin = 15, kind = ["input", "output"], af_output = { 0 = "SPICLK_MUX" } }, + { pin = 16, kind = ["input", "output"], af_input = { 0 = "SPID" }, af_output = { 0 = "SPID" } }, + { pin = 17, kind = ["input", "output"], af_input = { 0 = "SPIQ" }, af_output = { 0 = "SPIQ" } }, + { pin = 18, kind = ["input", "output"] }, + { pin = 19, kind = ["input", "output"] }, + { pin = 20, kind = ["input", "output"], af_input = { 0 = "U0RXD" } }, + { pin = 21, kind = ["input", "output"], af_output = { 0 = "U0TXD" } }, + ] }, +] [device.i2c_master] support_status = "supported" diff --git a/esp-metadata/devices/esp32c6.toml b/esp-metadata/devices/esp32c6.toml index 00706e211..3d9e55926 100644 --- a/esp-metadata/devices/esp32c6.toml +++ b/esp-metadata/devices/esp32c6.toml @@ -115,6 +115,41 @@ instances = [ [device.gpio] support_status = "supported" +instances = [ + { name = "gpio", pins = [ + { pin = 0, kind = ["input", "output", "analog", "rtc"] }, + { pin = 1, kind = ["input", "output", "analog", "rtc"] }, + { pin = 2, kind = ["input", "output", "analog", "rtc"], af_input = { 2 = "FSPIQ" }, af_output = { 2 = "FSPIQ" } }, + { pin = 3, kind = ["input", "output", "analog", "rtc"] }, + { pin = 4, kind = ["input", "output", "analog", "rtc"], af_input = { 2 = "FSPIHD" }, af_output = { 0 = "USB_JTAG_TMS", 2 = "FSPIHD" } }, + { pin = 5, kind = ["input", "output", "analog", "rtc"], af_input = { 2 = "FSPIWP" }, af_output = { 0 = "USB_JTAG_TDI", 2 = "FSPIWP" } }, + { pin = 6, kind = ["input", "output"], af_input = { 2 = "FSPICLK" }, af_output = { 0 = "USB_JTAG_TCK", 2 = "FSPICLK_MUX" } }, + { pin = 7, kind = ["input", "output"], af_input = { 2 = "FSPID" }, af_output = { 0 = "USB_JTAG_TDO", 2 = "FSPID" } }, + { pin = 8, kind = ["input", "output"] }, + { pin = 9, kind = ["input", "output"] }, + { pin = 10, kind = ["input", "output"] }, + { pin = 11, kind = ["input", "output"] }, + { pin = 12, kind = ["input", "output"] }, + { pin = 13, kind = ["input", "output"] }, + { pin = 14, kind = ["input", "output"] }, + { pin = 15, kind = ["input", "output"] }, + { pin = 16, kind = ["input", "output"], af_input = { 0 = "U0RXD" }, af_output = { 2 = "FSPICS0" } }, + { pin = 17, kind = ["input", "output"], af_output = { 0 = "U0TXD", 2 = "FSPICS1" } }, + { pin = 18, kind = ["input", "output"], af_output = { 2 = "FSPICS2" } }, + { pin = 19, kind = ["input", "output"], af_output = { 2 = "FSPICS3" } }, + { pin = 20, kind = ["input", "output"], af_output = { 2 = "FSPICS4" } }, + { pin = 21, kind = ["input", "output"], af_output = { 2 = "FSPICS5" } }, + { pin = 22, kind = ["input", "output"] }, + { pin = 23, kind = ["input", "output"] }, + { pin = 24, kind = ["input", "output"], af_output = { 0 = "SPICS0" } }, + { pin = 25, kind = ["input", "output"], af_input = { 0 = "SPIQ" }, af_output = { 0 = "SPIQ" } }, + { pin = 26, kind = ["input", "output"], af_input = { 0 = "SPIWP" }, af_output = { 0 = "SPIWP" } }, + { pin = 27, kind = ["input", "output"] }, + { pin = 28, kind = ["input", "output"], af_input = { 0 = "SPIHD" }, af_output = { 0 = "SPIHD" } }, + { pin = 29, kind = ["input", "output"], af_output = { 0 = "SPICLK_MUX" } }, + { pin = 30, kind = ["input", "output"], af_input = { 0 = "SPID" }, af_output = { 0 = "SPID" } }, + ] }, +] [device.i2c_master] support_status = "supported" diff --git a/esp-metadata/devices/esp32h2.toml b/esp-metadata/devices/esp32h2.toml index 9c3cfc44d..edaceb14d 100644 --- a/esp-metadata/devices/esp32h2.toml +++ b/esp-metadata/devices/esp32h2.toml @@ -98,6 +98,38 @@ instances = [ [device.gpio] support_status = "supported" +instances = [ + { name = "gpio", pins = [ + { pin = 0, kind = ["input", "output", "analog"], af_input = { 2 = "FSPIQ" }, af_output = { 2 = "FSPIQ" } }, + { pin = 1, kind = ["input", "output", "analog"], af_input = { 2 = "FSPICS0" }, af_output = { 2 = "FSPICS0" } }, + { pin = 2, kind = ["input", "output", "analog"], af_input = { 2 = "FSPIWP" }, af_output = { 2 = "FSPIWP" } }, + { pin = 3, kind = ["input", "output", "analog"], af_input = { 2 = "FSPIHD" }, af_output = { 2 = "FSPIHD" } }, + { pin = 4, kind = ["input", "output", "analog"], af_input = { 2 = "FSPICLK" }, af_output = { 2 = "FSPICLK_MUX" } }, + { pin = 5, kind = ["input", "output", "analog"], af_input = { 2 = "FSPID" }, af_output = { 2 = "FSPID" } }, + { pin = 6, kind = ["input", "output"] }, + { pin = 7, kind = ["input", "output"] }, + { pin = 8, kind = ["input", "output"] }, + { pin = 9, kind = ["input", "output"] }, + { pin = 10, kind = ["input", "output"] }, + { pin = 11, kind = ["input", "output"] }, + { pin = 12, kind = ["input", "output"] }, + { pin = 13, kind = ["input", "output"] }, + { pin = 14, kind = ["input", "output"] }, + { pin = 15, kind = ["input", "output"], af_output = { 0 = "SPICS0" } }, + { pin = 16, kind = ["input", "output"], af_input = { 0 = "SPIQ" }, af_output = { 0 = "SPIQ" } }, + { pin = 17, kind = ["input", "output"], af_input = { 0 = "SPIWP" }, af_output = { 0 = "SPIWP" } }, + { pin = 18, kind = ["input", "output"], af_input = { 0 = "SPIHD" }, af_output = { 0 = "SPIHD" } }, + { pin = 19, kind = ["input", "output"], af_output = { 0 = "SPICLK" } }, + { pin = 20, kind = ["input", "output"], af_input = { 0 = "SPID" }, af_output = { 0 = "SPID" } }, + { pin = 21, kind = ["input", "output"] }, + { pin = 22, kind = ["input", "output"] }, + { pin = 23, kind = ["input", "output"], af_output = { 2 = "FSPICS1" } }, + { pin = 24, kind = ["input", "output"], af_output = { 2 = "FSPICS2" } }, + { pin = 25, kind = ["input", "output"], af_output = { 2 = "FSPICS3" } }, + { pin = 26, kind = ["input", "output"], af_output = { 2 = "FSPICS4" } }, + { pin = 27, kind = ["input", "output"], af_output = { 2 = "FSPICS5" } }, + ] }, +] [device.i2c_master] support_status = "supported" diff --git a/esp-metadata/devices/esp32s2.toml b/esp-metadata/devices/esp32s2.toml index b1e206b68..023c20cbc 100644 --- a/esp-metadata/devices/esp32s2.toml +++ b/esp-metadata/devices/esp32s2.toml @@ -104,7 +104,54 @@ instances = [ [device.gpio] support_status = "supported" has_bank_1 = true +instances = [ + { name = "gpio", pins = [ + { pin = 0, kind = ["input", "output", "analog", "rtc"] }, + { pin = 1, kind = ["input", "output", "analog", "rtc"] }, + { pin = 2, kind = ["input", "output", "analog", "rtc"] }, + { pin = 3, kind = ["input", "output", "analog", "rtc"] }, + { pin = 4, kind = ["input", "output", "analog", "rtc"] }, + { pin = 5, kind = ["input", "output", "analog", "rtc"] }, + { pin = 6, kind = ["input", "output", "analog", "rtc"] }, + { pin = 7, kind = ["input", "output", "analog", "rtc"] }, + { pin = 8, kind = ["input", "output", "analog", "rtc"], af_output = { 3 = "SUBSPICS1" } }, + { pin = 9, kind = ["input", "output", "analog", "rtc"], af_input = { 3 = "SUBSPIHD", 4 = "FSPIHD" }, af_output = { 3 = "SUBSPIHD", 4 = "FSPIHD" } }, + { pin = 10, kind = ["input", "output", "analog", "rtc"], af_input = { 2 = "FSPIIO4", 4 = "FSPICS0" }, af_output = { 2 = "FSPIIO4", 3 = "SUBSPICS0", 4 = "FSPICS0" } }, + { pin = 11, kind = ["input", "output", "analog", "rtc"], af_input = { 2 = "FSPIIO5", 3 = "SUBSPID", 4 = "FSPID" }, af_output = { 2 = "FSPIIO5", 3 = "SUBSPID", 4 = "FSPID" } }, + { pin = 12, kind = ["input", "output", "analog", "rtc"], af_input = { 2 = "FSPIIO6", 4 = "FSPICLK" }, af_output = { 2 = "FSPIIO6", 3 = "SUBSPICLK", 4 = "FSPICLK" } }, + { pin = 13, kind = ["input", "output", "analog", "rtc"], af_input = { 2 = "FSPIIO7", 3 = "SUBSPIQ", 4 = "FSPIQ" }, af_output = { 2 = "FSPIIO7", 3 = "SUBSPIQ", 4 = "FSPIQ" } }, + { pin = 14, kind = ["input", "output", "analog", "rtc"], af_input = { 3 = "SUBSPIWP", 4 = "FSPIWP" }, af_output = { 2 = "FSPIDQS", 3 = "SUBSPIWP", 4 = "FSPIWP" } }, + { pin = 15, kind = ["input", "output", "analog", "rtc"], af_output = { 2 = "U0RTS" } }, + { pin = 16, kind = ["input", "output", "analog", "rtc"], af_input = { 2 = "U0CTS" } }, + { pin = 17, kind = ["input", "output", "analog", "rtc"], af_output = { 2 = "U1TXD" } }, + { pin = 18, kind = ["input", "output", "analog", "rtc"], af_input = { 2 = "U1RXD" } }, + { pin = 19, kind = ["input", "output", "analog", "rtc"], af_output = { 2 = "U1RTS" } }, + { pin = 20, kind = ["input", "output", "analog", "rtc"], af_input = { 2 = "U1CTS" } }, + { pin = 21, kind = ["input", "output", "analog", "rtc"] }, + { pin = 26, kind = ["input", "output"] }, + { pin = 27, kind = ["input", "output"] }, + { pin = 28, kind = ["input", "output"] }, + { pin = 29, kind = ["input", "output"] }, + { pin = 30, kind = ["input", "output"] }, + { pin = 31, kind = ["input", "output"] }, + { pin = 32, kind = ["input", "output"] }, + { pin = 33, kind = ["input", "output"], af_input = { 2 = "FSPIHD", 3 = "SUBSPIHD" }, af_output = { 2 = "FSPIHD", 3 = "SUBSPIHD" } }, + { pin = 34, kind = ["input", "output"], af_input = { 2 = "FSPICS0" }, af_output = { 2 = "FSPICS0", 3 = "SUBSPICS0" } }, + { pin = 35, kind = ["input", "output"], af_input = { 2 = "FSPID", 3 = "SUBSPID" }, af_output = { 2 = "FSPID", 3 = "SUBSPID" } }, + { pin = 36, kind = ["input", "output"], af_input = { 2 = "FSPICLK" }, af_output = { 2 = "FSPICLK", 3 = "SUBSPICLK" } }, + { pin = 37, kind = ["input", "output"], af_input = { 2 = "FSPIQ", 3 = "SUBSPIQ", 4 = "SPIDQS" }, af_output = { 2 = "FSPIQ", 3 = "SUBSPIQ", 4 = "SPIDQS" } }, + { pin = 38, kind = ["input", "output"], af_input = { 2 = "FSPIWP", 3 = "SUBSPIWP" }, af_output = { 2 = "FSPIWP", 3 = "SUBSPIWP" } }, + { pin = 39, kind = ["input", "output"], af_output = { 3 = "SUBSPICS1" } }, + { pin = 40, kind = ["input", "output"] }, + { pin = 41, kind = ["input", "output"] }, + { pin = 42, kind = ["input", "output"] }, + { pin = 43, kind = ["input", "output"] }, + { pin = 44, kind = ["input", "output"] }, + { pin = 45, kind = ["input", "output"] }, + { pin = 46, kind = ["input", "output"] }, + ] }, +] [device.i2c_master] support_status = "supported" instances = [{ name = "i2c0" }, { name = "i2c1" }] diff --git a/esp-metadata/devices/esp32s3.toml b/esp-metadata/devices/esp32s3.toml index 3b21b083c..c399c8585 100644 --- a/esp-metadata/devices/esp32s3.toml +++ b/esp-metadata/devices/esp32s3.toml @@ -112,6 +112,56 @@ instances = [ [device.gpio] support_status = "supported" has_bank_1 = true +instances = [ + { name = "gpio", pins = [ + { pin = 0, kind = ["input", "output", "analog", "rtc"] }, + { pin = 1, kind = ["input", "output", "analog", "rtc"] }, + { pin = 2, kind = ["input", "output", "analog", "rtc"] }, + { pin = 3, kind = ["input", "output", "analog", "rtc"] }, + { pin = 4, kind = ["input", "output", "analog", "rtc"] }, + { pin = 5, kind = ["input", "output", "analog", "rtc"] }, + { pin = 6, kind = ["input", "output", "analog", "rtc"] }, + { pin = 7, kind = ["input", "output", "analog", "rtc"] }, + { pin = 8, kind = ["input", "output", "analog", "rtc"], af_output = { 3 = "SUBSPICS1" } }, + { pin = 9, kind = ["input", "output", "analog", "rtc"], af_input = { 3 = "SUBSPIHD", 4 = "FSPIHD" }, af_output = { 3 = "SUBSPIHD", 4 = "FSPIHD" } }, + { pin = 10, kind = ["input", "output", "analog", "rtc"], af_input = { 2 = "FSPIIO4", 4 = "FSPICS0" }, af_output = { 2 = "FSPIIO4", 3 = "SUBSPICS0", 4 = "FSPICS0" } }, + { pin = 11, kind = ["input", "output", "analog", "rtc"], af_input = { 2 = "FSPIIO5", 3 = "SUBSPID", 4 = "FSPID" }, af_output = { 2 = "FSPIIO5", 3 = "SUBSPID", 4 = "FSPID" } }, + { pin = 12, kind = ["input", "output", "analog", "rtc"], af_input = { 2 = "FSPIIO6", 4 = "FSPICLK" }, af_output = { 2 = "FSPIIO6", 3 = "SUBSPICLK", 4 = "FSPICLK" } }, + { pin = 13, kind = ["input", "output", "analog", "rtc"], af_input = { 2 = "FSPIIO7", 3 = "SUBSPIQ", 4 = "FSPIQ" }, af_output = { 2 = "FSPIIO7", 3 = "SUBSPIQ", 4 = "FSPIQ" } }, + { pin = 14, kind = ["input", "output", "analog", "rtc"], af_input = { 3 = "SUBSPIWP", 4 = "FSPIWP" }, af_output = { 2 = "FSPIDQS", 3 = "SUBSPIWP", 4 = "FSPIWP" } }, + { pin = 15, kind = ["input", "output", "analog", "rtc"], af_output = { 2 = "U0RTS" } }, + { pin = 16, kind = ["input", "output", "analog", "rtc"], af_input = { 2 = "U0CTS" } }, + { pin = 17, kind = ["input", "output", "analog", "rtc"], af_output = { 2 = "U1TXD" } }, + { pin = 18, kind = ["input", "output", "analog", "rtc"], af_input = { 2 = "U1RXD" } }, + { pin = 19, kind = ["input", "output", "analog", "rtc"], af_output = { 2 = "U1RTS" } }, + { pin = 20, kind = ["input", "output", "analog", "rtc"], af_input = { 2 = "U1CTS" } }, + { pin = 21, kind = ["input", "output", "analog", "rtc"] }, + + { pin = 26, kind = ["input", "output"] }, + { pin = 27, kind = ["input", "output"] }, + { pin = 28, kind = ["input", "output"] }, + { pin = 29, kind = ["input", "output"] }, + { pin = 30, kind = ["input", "output"] }, + { pin = 31, kind = ["input", "output"] }, + { pin = 32, kind = ["input", "output"] }, + { pin = 33, kind = ["input", "output"], af_input = { 2 = "FSPIHD", 3 = "SUBSPIHD" }, af_output = { 2 = "FSPIHD", 3 = "SUBSPIHD" } }, + { pin = 34, kind = ["input", "output"], af_input = { 2 = "FSPICS0" }, af_output = { 2 = "FSPICS0", 3 = "SUBSPICS0" } }, + { pin = 35, kind = ["input", "output"], af_input = { 2 = "FSPID", 3 = "SUBSPID" }, af_output = { 2 = "FSPID", 3 = "SUBSPID" } }, + { pin = 36, kind = ["input", "output"], af_input = { 2 = "FSPICLK" }, af_output = { 2 = "FSPICLK", 3 = "SUBSPICLK" } }, + { pin = 37, kind = ["input", "output"], af_input = { 2 = "FSPIQ", 3 = "SUBSPIQ", 4 = "SPIDQS" }, af_output = { 2 = "FSPIQ", 3 = "SUBSPIQ", 4 = "SPIDQS" } }, + { pin = 38, kind = ["input", "output"], af_input = { 2 = "FSPIWP", 3 = "SUBSPIWP" }, af_output = { 2 = "FSPIWP", 3 = "SUBSPIWP" } }, + { pin = 39, kind = ["input", "output"], af_output = { 3 = "SUBSPICS1" } }, + { pin = 40, kind = ["input", "output"] }, + { pin = 41, kind = ["input", "output"] }, + { pin = 42, kind = ["input", "output"] }, + { pin = 43, kind = ["input", "output"] }, + { pin = 44, kind = ["input", "output"] }, + { pin = 45, kind = ["input", "output"] }, + { pin = 46, kind = ["input", "output"] }, + { pin = 47, kind = ["input", "output"] }, + { pin = 48, kind = ["input", "output"] }, + ] }, +] [device.i2c_master] support_status = "supported" diff --git a/esp-metadata/src/cfg.rs b/esp-metadata/src/cfg.rs new file mode 100644 index 000000000..708b34edd --- /dev/null +++ b/esp-metadata/src/cfg.rs @@ -0,0 +1,577 @@ +/// Represents a value in the driver configuration. +pub(crate) enum Value { + Unset, + /// A numeric value. The generated macro will not include a type suffix + /// (i.e. will not be generated as `0u32`). + Number(u32), + /// A boolean value. If true, the value is included in the cfg symbols. + Boolean(bool), +} + +impl From for Value { + fn from(value: u32) -> Self { + Value::Number(value) + } +} +impl From> for Value { + fn from(value: Option) -> Self { + match value { + Some(v) => Value::Number(v), + None => Value::Unset, + } + } +} +impl From for Value { + fn from(value: bool) -> Self { + Value::Boolean(value) + } +} + +#[derive(Debug, Default, Clone, Copy, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "snake_case")] +pub(crate) enum SupportStatus { + NotSupported, + #[default] // Just the common option to reduce visual noise of "declare only" drivers. + Partial, + Supported, +} + +impl SupportStatus { + pub fn icon(self) -> &'static str { + match self { + SupportStatus::NotSupported => "❌", + SupportStatus::Partial => "⚒️", + SupportStatus::Supported => "✔️", + } + } + + pub fn status(self) -> &'static str { + match self { + SupportStatus::NotSupported => "Not supported", + SupportStatus::Partial => "Partial support", + SupportStatus::Supported => "Supported", + } + } +} + +/// An empty configuration, used when a driver just wants to declare that +/// it supports a peripheral, but does not have any configuration options. +#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)] +pub(crate) struct EmptyInstanceConfig {} + +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "snake_case")] +pub(crate) enum PinCapability { + Input, + Output, + Analog, + Rtc, + Touch, +} + +#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)] +pub(crate) struct AfMap { + #[serde(rename = "0")] + af0: Option, + #[serde(rename = "1")] + af1: Option, + #[serde(rename = "2")] + af2: Option, + #[serde(rename = "3")] + af3: Option, + #[serde(rename = "4")] + af4: Option, + #[serde(rename = "5")] + af5: Option, +} + +impl AfMap { + pub fn get(&self, af: usize) -> Option<&str> { + match af { + 0 => self.af0.as_deref(), + 1 => self.af1.as_deref(), + 2 => self.af2.as_deref(), + 3 => self.af3.as_deref(), + 4 => self.af4.as_deref(), + 5 => self.af5.as_deref(), + _ => None, + } + } +} + +#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)] +pub(crate) struct PinConfig { + /// The GPIO pin number. + pub pin: usize, + pub kind: Vec, + // Pin => InputSignal + #[serde(default)] + pub af_input: AfMap, + // Pin => OutputSignal + #[serde(default)] + pub af_output: AfMap, +} + +#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)] +pub(crate) struct GpioInstanceConfig { + pub pins: Vec, +} + +/// A peripheral instance for which a driver is implemented. +#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)] +pub(crate) struct PeriInstance { + /// The name of the instance + pub name: String, + #[serde(flatten)] + pub instance_config: I, +} + +pub(crate) struct SupportItem { + pub name: &'static str, + pub config_group: &'static str, + pub symbols: &'static [&'static str], +} + +/// Define driver configuration structs, and a PeriConfig struct +/// that contains all of them. +macro_rules! driver_configs { + // Creates a single struct + (@one + $struct:ident $(<$instance_config:ident>)? ($group:ident) { + $( + $(#[$meta:meta])? $config:ident: $ty:ty, + )* + } + ) => { + #[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] + pub(crate) struct $struct { + #[serde(default)] + pub support_status: SupportStatus, + // The list of peripherals for which this driver is implemented. + // If empty, the driver supports a single instance only. + #[serde(default)] + pub instances: Vec)?>, + $( + $(#[$meta])? + pub $config: $ty + ),* + } + + impl $struct { + fn properties(&self) -> impl Iterator { + [$( // for each property, generate a tuple + ( + /* name: */ concat!(stringify!($group), ".", stringify!($config)), + /* value: */ Value::from(self.$config), + ), + )*].into_iter() + } + } + }; + + // Repeat pattern for multiple structs + ($( + $struct:ident $(<$instance_config:ident>)? { + // This name will be emitted as a cfg symbol, to activate a driver. + driver: $driver:ident, + // Driver name, used in the generated documentation. + name: $name:literal, + // The list of peripheral symbols that this driver supports. For now this is used to + // double-check the configuration. + // TODO: remove once the metadata encodes which instances are supported. + peripherals: $symbols:expr, + properties: $tokens:tt + }, + )+) => { + // Implement the config driver and DriverConfig trait for each driver + $( + driver_configs!(@one $struct $(<$instance_config>)? ($driver) $tokens); + )+ + + // Generate a single PeriConfig struct that contains all the drivers. Each of the + // drivers is optional to support devices that may not have all peripherals. + #[derive(Default, Debug, Clone, serde::Deserialize, serde::Serialize)] + pub(crate) struct PeriConfig { + $( + // Each driver is an optional struct. + #[serde(default)] + pub(crate) $driver: Option<$struct>, + )+ + } + + impl PeriConfig { + pub fn drivers() -> &'static [SupportItem] { + &[ + $( + SupportItem { + name: $name, + config_group: stringify!($driver), + symbols: $symbols, + }, + )+ + ] + } + + /// Returns an iterator over all driver names, that are + /// available on the selected device. + pub fn driver_names(&self) -> impl Iterator { + [$( + self.$driver.as_ref().and_then(|d| { + match d.support_status { + SupportStatus::NotSupported => None, + _ => Some(stringify!($driver)), + } + }), + )*].into_iter().flatten() + } + + pub fn driver_instances(&self) -> impl Iterator { + // Collect into a vector. This compiles faster than chaining iterators. + let mut instances = vec![]; + $( + if let Some(driver) = &self.$driver { + instances.extend(driver.instances.iter().map(|i| { + format!("{}.{}", stringify!($driver), i.name) + })); + } + )* + instances.into_iter() + } + + /// Returns an iterator over all properties of all peripherals. + pub fn properties(&self) -> impl Iterator { + // Collect into a vector. This compiles faster than chaining iterators. + let mut properties = vec![]; + $( + if let Some(driver) = &self.$driver { + properties.extend(driver.properties()); + } + )* + properties.into_iter() + } + + /// Returns the support status of a peripheral by its name. + pub fn support_status(&self, peripheral: &str) -> Option { + // Find the driver by name and return its support status. + match peripheral { + $(stringify!($driver) => self.$driver.as_ref().map(|p| p.support_status),)* + _ => None, // If the peripheral is not found, return None. + } + } + } + }; +} + +// TODO: sort this similar to how the product portfolio is organized +driver_configs![ + AdcProperties { + driver: adc, + name: "ADC", + peripherals: &[], + properties: {} + }, + AesProperties { + driver: aes, + name: "AES", + peripherals: &["aes"], + properties: {} + }, + AssistDebugProperties { + driver: assist_debug, + name: "ASSIST_DEBUG", + peripherals: &["assist_debug"], + properties: {} + }, + DacProperties { + driver: dac, + name: "DAC", + peripherals: &["dac"], + properties: {} + }, + DmaProperties { + driver: dma, + name: "DMA", + peripherals: &["pdma", "gdma"], + properties: {} + }, + DsProperties { + driver: ds, + name: "DS", + peripherals: &["ds"], + properties: {} + }, + EccProperties { + driver: ecc, + name: "ECC", + peripherals: &["ecc"], + properties: {} + }, + EthernetProperties { + driver: ethernet, + name: "Ethernet", + peripherals: &["emac"], + properties: {} + }, + EtmProperties { + driver: etm, + name: "ETM", + peripherals: &["etm"], + properties: {} + }, + GpioProperties { + driver: gpio, + name: "GPIO", + peripherals: &["gpio"], + properties: { + #[serde(default)] + has_bank_1: bool, + } + }, + HmacProperties { + driver: hmac, + name: "HMAC", + peripherals: &["hmac"], + properties: {} + }, + I2cMasterProperties { + driver: i2c_master, + name: "I2C master", + peripherals: &["i2c0", "i2c1"], + properties: { + #[serde(default)] + has_fsm_timeouts: bool, + #[serde(default)] + has_hw_bus_clear: bool, + #[serde(default)] + has_bus_timeout_enable: bool, + #[serde(default)] + separate_filter_config_registers: bool, + #[serde(default)] + can_estimate_nack_reason: bool, + #[serde(default)] + has_conf_update: bool, + #[serde(default)] + has_reliable_fsm_reset: bool, + #[serde(default)] + has_arbitration_en: bool, + #[serde(default)] + has_tx_fifo_watermark: bool, + #[serde(default)] + bus_timeout_is_exponential: bool, + #[serde(default)] + i2c0_data_register_ahb_address: Option, + max_bus_timeout: u32, + ll_intr_mask: u32, + fifo_size: u32, + } + }, + I2cSlaveProperties { + driver: i2c_slave, + name: "I2C slave", + peripherals: &["i2c0", "i2c1"], + properties: {} + }, + I2sProperties { + driver: i2s, + name: "I2S", + peripherals: &["i2s0", "i2s1"], + properties: {} + }, + InterruptProperties { + driver: interrupts, + name: "Interrupts", + peripherals: &[], + properties: { + status_registers: u32, + } + }, + IoMuxProperties { + driver: io_mux, + name: "IOMUX", + peripherals: &["io_mux"], + properties: {} + }, + CameraProperties { + driver: camera, + name: "Camera interface", // LCD_CAM, ESP32 I2S, S2 SPI + peripherals: &[], + properties: {} + }, + RgbProperties { + driver: rgb_display, + name: "RGB display", // LCD_CAM, ESP32 I2S, S2 SPI + peripherals: &[], + properties: {} + }, + LedcProperties { + driver: ledc, + name: "LEDC", + peripherals: &["ledc"], + properties: {} + }, + McpwmProperties { + driver: mcpwm, + name: "MCPWM", + peripherals: &["mcpwm0", "mcpwm1"], + properties: {} + }, + ParlIoProperties { + driver: parl_io, + name: "PARL_IO", + peripherals: &["parl_io"], + properties: {} + }, + PcntProperties { + driver: pcnt, + name: "PCNT", + peripherals: &["pcnt"], + properties: {} + }, + PsramProperties { + driver: psram, + name: "PSRAM", + peripherals: &["psram"], + properties: {} + }, + RmtProperties { + driver: rmt, + name: "RMT", + peripherals: &["rmt"], + properties: { + ram_start: u32, + channel_ram_size: u32, + } + }, + RngProperties { + driver: rng, + name: "RNG", + peripherals: &["rng"], + properties: {} + }, + RsaProperties { + driver: rsa, + name: "RSA", + peripherals: &["rsa"], + properties: {} + }, + SdHostProperties { + driver: sd_host, + name: "SDIO host", + peripherals: &["sdhost"], + properties: {} + }, + SdSlaveProperties { + driver: sd_slave, + name: "SDIO slave", + peripherals: &["slchost"], + properties: {} + }, + SleepProperties { + driver: sleep, + name: "Light/deep sleep", + peripherals: &[], + properties: {} + }, + ShaProperties { + driver: sha, + name: "SHA", + peripherals: &["sha"], + properties: {} + }, + SpiMasterProperties { + driver: spi_master, + name: "SPI master", + peripherals: &["spi2", "spi3"], + properties: {} + }, + SpiSlaveProperties { + driver: spi_slave, + name: "SPI slave", + peripherals: &["spi2", "spi3"], + properties: {} + }, + SysTimerProperties { + driver: systimer, + name: "SYSTIMER", + peripherals: &["systimer"], + properties: {} + }, + TempProperties { + driver: temp_sensor, + name: "Temperature sensor", + peripherals: &[], + properties: {} + }, + TimersProperties { + driver: timergroup, + name: "Timers", + peripherals: &[], + properties: { + #[serde(default)] + timg_has_timer1: bool, + } + }, + TouchProperties { + driver: touch, + name: "Touch", + peripherals: &["touch"], + properties: {} + }, + TwaiProperties { + driver: twai, + name: "TWAI", + peripherals: &["twai0", "twai1"], + properties: {} + }, + UartProperties { + driver: uart, + name: "UART", + peripherals: &["uart0", "uart1", "uart2"], + properties: {} + }, + UlpFsmProperties { + driver: ulp_fsm, + name: "ULP (FSM)", + peripherals: &["ulp_supported"], + properties: {} + }, + UlpRiscvProperties { + driver: ulp_riscv, + name: "ULP (RISC-V)", + peripherals: &["ulp_riscv_core", "lp_core"], + properties: {} + }, + UsbOtgProperties { + driver: usb_otg, + name: "USB OTG FS", + peripherals: &["usb0"], + properties: {} + }, + UsbSerialJtagProperties { + driver: usb_serial_jtag, + name: "USB Serial/JTAG", + peripherals: &["usb_device"], + properties: {} + }, + WifiProperties { + driver: wifi, + name: "WIFI", + peripherals: &["wifi"], + properties: { + #[serde(default)] + has_wifi6: bool, + } + }, + BluetoothProperties { + driver: bt, + name: "Bluetooth", + peripherals: &["bt"], + properties: {} + }, + IeeeProperties { + driver: ieee802154, + name: "IEEE 802.15.4", + peripherals: &["ieee802154"], + properties: {} + }, +]; diff --git a/esp-metadata/src/generate_cfg.rs b/esp-metadata/src/generate_cfg.rs deleted file mode 100644 index bd4035b1e..000000000 --- a/esp-metadata/src/generate_cfg.rs +++ /dev/null @@ -1,1090 +0,0 @@ -use core::str::FromStr; -use std::{collections::HashMap, fmt::Write, sync::OnceLock}; - -use anyhow::{Result, bail, ensure}; -use proc_macro2::TokenStream; -use strum::IntoEnumIterator; - -macro_rules! include_toml { - (Config, $file:expr) => {{ - static LOADED_TOML: OnceLock = OnceLock::new(); - LOADED_TOML.get_or_init(|| { - let config: Config = basic_toml::from_str(include_str!($file)).unwrap(); - - config.validate().expect("Invalid device configuration"); - - config - }) - }}; -} - -/// Supported device architectures. -#[derive( - Debug, - Clone, - Copy, - PartialEq, - Eq, - PartialOrd, - Ord, - serde::Deserialize, - serde::Serialize, - strum::Display, - strum::EnumIter, - strum::EnumString, - strum::AsRefStr, -)] -#[serde(rename_all = "lowercase")] -#[strum(serialize_all = "lowercase")] -pub enum Arch { - /// RISC-V architecture - RiscV, - /// Xtensa architecture - Xtensa, -} - -/// Device core count. -#[derive( - Debug, - Clone, - Copy, - PartialEq, - Eq, - PartialOrd, - Ord, - serde::Deserialize, - serde::Serialize, - strum::Display, - strum::EnumIter, - strum::EnumString, - strum::AsRefStr, -)] -pub enum Cores { - /// Single CPU core - #[serde(rename = "single_core")] - #[strum(serialize = "single_core")] - Single, - /// Two or more CPU cores - #[serde(rename = "multi_core")] - #[strum(serialize = "multi_core")] - Multi, -} - -/// Supported devices. -#[derive( - Debug, - Clone, - Copy, - PartialEq, - Eq, - PartialOrd, - Ord, - Hash, - serde::Deserialize, - serde::Serialize, - strum::Display, - strum::EnumIter, - strum::EnumString, - strum::AsRefStr, -)] -#[cfg_attr(feature = "clap", derive(clap::ValueEnum))] -#[serde(rename_all = "kebab-case")] -#[strum(serialize_all = "kebab-case")] -pub enum Chip { - /// ESP32 - Esp32, - /// ESP32-C2, ESP8684 - Esp32c2, - /// ESP32-C3, ESP8685 - Esp32c3, - /// ESP32-C6 - Esp32c6, - /// ESP32-H2 - Esp32h2, - /// ESP32-S2 - Esp32s2, - /// ESP32-S3 - Esp32s3, -} - -impl Chip { - pub fn from_cargo_feature() -> Result { - let all_chips = Chip::iter().map(|c| c.to_string()).collect::>(); - - let mut chip = None; - for c in all_chips.iter() { - if std::env::var(format!("CARGO_FEATURE_{}", c.to_uppercase())).is_ok() { - if chip.is_some() { - bail!( - "Expected exactly one of the following features to be enabled: {}", - all_chips.join(", ") - ); - } - chip = Some(c); - } - } - - let Some(chip) = chip else { - bail!( - "Expected exactly one of the following features to be enabled: {}", - all_chips.join(", ") - ); - }; - - Ok(Self::from_str(chip.as_str()).unwrap()) - } - - pub fn target(&self) -> &'static str { - use Chip::*; - - match self { - Esp32 => "xtensa-esp32-none-elf", - Esp32c2 | Esp32c3 => "riscv32imc-unknown-none-elf", - Esp32c6 | Esp32h2 => "riscv32imac-unknown-none-elf", - Esp32s2 => "xtensa-esp32s2-none-elf", - Esp32s3 => "xtensa-esp32s3-none-elf", - } - } - - pub fn has_lp_core(&self) -> bool { - use Chip::*; - - matches!(self, Esp32c6 | Esp32s2 | Esp32s3) - } - - pub fn lp_target(&self) -> Result<&'static str> { - use Chip::*; - - match self { - Esp32c6 => Ok("riscv32imac-unknown-none-elf"), - Esp32s2 | Esp32s3 => Ok("riscv32imc-unknown-none-elf"), - _ => bail!("Chip does not contain an LP core: '{}'", self), - } - } - - pub fn pretty_name(&self) -> &str { - match self { - Chip::Esp32 => "ESP32", - Chip::Esp32c2 => "ESP32-C2", - Chip::Esp32c3 => "ESP32-C3", - Chip::Esp32c6 => "ESP32-C6", - Chip::Esp32h2 => "ESP32-H2", - Chip::Esp32s2 => "ESP32-S2", - Chip::Esp32s3 => "ESP32-S3", - } - } - - pub fn is_xtensa(&self) -> bool { - matches!(self, Chip::Esp32 | Chip::Esp32s2 | Chip::Esp32s3) - } - - pub fn is_riscv(&self) -> bool { - !self.is_xtensa() - } -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct MemoryRegion { - name: String, - start: u32, - end: u32, -} - -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -struct Device { - name: String, - arch: Arch, - cores: usize, - trm: String, - - peripherals: Vec, - // For now, this is only used to double-check the configuration. - virtual_peripherals: Vec, - symbols: Vec, - memory: Vec, - - // Peripheral driver configuration: - #[serde(flatten)] - peri_config: PeriConfig, -} - -/// Represents a value in the driver configuration. -enum Value { - Unset, - /// A numeric value. The generated macro will not include a type suffix - /// (i.e. will not be generated as `0u32`). - Number(u32), - /// A boolean value. If true, the value is included in the cfg symbols. - Boolean(bool), -} - -impl From for Value { - fn from(value: u32) -> Self { - Value::Number(value) - } -} -impl From> for Value { - fn from(value: Option) -> Self { - match value { - Some(v) => Value::Number(v), - None => Value::Unset, - } - } -} -impl From for Value { - fn from(value: bool) -> Self { - Value::Boolean(value) - } -} - -#[derive(Debug, Default, Clone, Copy, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "snake_case")] -enum SupportStatus { - NotSupported, - #[default] // Just the common option to reduce visual noise of "declare only" drivers. - Partial, - Supported, -} - -impl SupportStatus { - fn icon(self) -> &'static str { - match self { - SupportStatus::NotSupported => "❌", - SupportStatus::Partial => "⚒️", - SupportStatus::Supported => "✔️", - } - } - - fn status(self) -> &'static str { - match self { - SupportStatus::NotSupported => "Not supported", - SupportStatus::Partial => "Partial support", - SupportStatus::Supported => "Supported", - } - } -} - -/// An empty configuration, used when a driver just wants to declare that -/// it supports a peripheral, but does not have any configuration options. -#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)] -struct EmptyInstanceConfig {} - -/// A peripheral instance for which a driver is implemented. -#[derive(Debug, Default, Clone, serde::Deserialize, serde::Serialize)] -struct PeriInstance { - /// The name of the instance - name: String, - #[serde(flatten)] - instance_config: I, -} - -struct SupportItem { - name: &'static str, - config_group: &'static str, - symbols: &'static [&'static str], -} - -/// Define driver configuration structs, and a PeriConfig struct -/// that contains all of them. -macro_rules! driver_configs { - // Creates a single struct - (@one - $struct:tt($group:ident) { - $( - $(#[$meta:meta])? $config:ident: $ty:ty, - )* - } - ) => { - #[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] - struct $struct { - #[serde(default)] - support_status: SupportStatus, - // The list of peripherals for which this driver is implemented. - // If empty, the driver supports a single instance only. - #[serde(default)] - instances: Vec>, - $( - $(#[$meta])? - $config: $ty - ),* - } - - impl $struct { - fn properties(&self) -> impl Iterator { - [$( // for each property, generate a tuple - ( - /* name: */ concat!(stringify!($group), ".", stringify!($config)), - /* value: */ Value::from(self.$config), - ), - )*].into_iter() - } - } - }; - - // Repeat pattern for multiple structs - ($( - $struct:tt { - // This name will be emitted as a cfg symbol, to activate a driver. - driver: $driver:ident, - // Driver name, used in the generated documentation. - name: $name:literal, - // The list of peripheral symbols that this driver supports. For now this is used to - // double-check the configuration. - // TODO: remove once the metadata encodes which instances are supported. - peripherals: $symbols:expr, - properties: $tokens:tt - }, - )+) => { - // Implement the config driver and DriverConfig trait for each driver - $( - driver_configs!(@one $struct($driver) $tokens); - )+ - - // Generate a single PeriConfig struct that contains all the drivers. Each of the - // drivers is optional to support devices that may not have all peripherals. - #[derive(Default, Debug, Clone, serde::Deserialize, serde::Serialize)] - struct PeriConfig { - $( - // Each driver is an optional struct. - #[serde(default)] - $driver: Option<$struct>, - )+ - } - - impl PeriConfig { - fn drivers() -> &'static [SupportItem] { - &[ - $( - SupportItem { - name: $name, - config_group: stringify!($driver), - symbols: $symbols, - }, - )+ - ] - } - - /// Returns an iterator over all driver names, that are - /// available on the selected device. - fn driver_names(&self) -> impl Iterator { - [$( - self.$driver.as_ref().and_then(|d| { - match d.support_status { - SupportStatus::NotSupported => None, - _ => Some(stringify!($driver)), - } - }), - )*].into_iter().flatten() - } - - fn driver_instances(&self) -> impl Iterator { - // Collect into a vector. This compiles faster than chaining iterators. - let mut instances = vec![]; - $( - if let Some(driver) = &self.$driver { - instances.extend(driver.instances.iter().map(|i| { - format!("{}.{}", stringify!($driver), i.name) - })); - } - )* - instances.into_iter() - } - - /// Returns an iterator over all properties of all peripherals. - fn properties(&self) -> impl Iterator { - // Collect into a vector. This compiles faster than chaining iterators. - let mut properties = vec![]; - $( - if let Some(driver) = &self.$driver { - properties.extend(driver.properties()); - } - )* - properties.into_iter() - } - - /// Returns the support status of a peripheral by its name. - fn support_status(&self, peripheral: &str) -> Option { - // Find the driver by name and return its support status. - match peripheral { - $(stringify!($driver) => self.$driver.as_ref().map(|p| p.support_status),)* - _ => None, // If the peripheral is not found, return None. - } - } - } - }; -} - -// TODO: sort this similar to how the product portfolio is organized -driver_configs![ - AdcProperties { - driver: adc, - name: "ADC", - peripherals: &[], - properties: {} - }, - AesProperties { - driver: aes, - name: "AES", - peripherals: &["aes"], - properties: {} - }, - AssistDebugProperties { - driver: assist_debug, - name: "ASSIST_DEBUG", - peripherals: &["assist_debug"], - properties: {} - }, - DacProperties { - driver: dac, - name: "DAC", - peripherals: &["dac"], - properties: {} - }, - DmaProperties { - driver: dma, - name: "DMA", - peripherals: &["pdma", "gdma"], - properties: {} - }, - DsProperties { - driver: ds, - name: "DS", - peripherals: &["ds"], - properties: {} - }, - EccProperties { - driver: ecc, - name: "ECC", - peripherals: &["ecc"], - properties: {} - }, - EthernetProperties { - driver: ethernet, - name: "Ethernet", - peripherals: &["emac"], - properties: {} - }, - EtmProperties { - driver: etm, - name: "ETM", - peripherals: &["etm"], - properties: {} - }, - GpioProperties { - driver: gpio, - name: "GPIO", - peripherals: &["gpio"], - properties: { - #[serde(default)] - has_bank_1: bool, - } - }, - HmacProperties { - driver: hmac, - name: "HMAC", - peripherals: &["hmac"], - properties: {} - }, - I2cMasterProperties { - driver: i2c_master, - name: "I2C master", - peripherals: &["i2c0", "i2c1"], - properties: { - #[serde(default)] - has_fsm_timeouts: bool, - #[serde(default)] - has_hw_bus_clear: bool, - #[serde(default)] - has_bus_timeout_enable: bool, - #[serde(default)] - separate_filter_config_registers: bool, - #[serde(default)] - can_estimate_nack_reason: bool, - #[serde(default)] - has_conf_update: bool, - #[serde(default)] - has_reliable_fsm_reset: bool, - #[serde(default)] - has_arbitration_en: bool, - #[serde(default)] - has_tx_fifo_watermark: bool, - #[serde(default)] - bus_timeout_is_exponential: bool, - #[serde(default)] - i2c0_data_register_ahb_address: Option, - max_bus_timeout: u32, - ll_intr_mask: u32, - fifo_size: u32, - } - }, - I2cSlaveProperties { - driver: i2c_slave, - name: "I2C slave", - peripherals: &["i2c0", "i2c1"], - properties: {} - }, - I2sProperties { - driver: i2s, - name: "I2S", - peripherals: &["i2s0", "i2s1"], - properties: {} - }, - InterruptProperties { - driver: interrupts, - name: "Interrupts", - peripherals: &[], - properties: { - status_registers: u32, - } - }, - IoMuxProperties { - driver: io_mux, - name: "IOMUX", - peripherals: &["io_mux"], - properties: {} - }, - CameraProperties { - driver: camera, - name: "Camera interface", // LCD_CAM, ESP32 I2S, S2 SPI - peripherals: &[], - properties: {} - }, - RgbProperties { - driver: rgb_display, - name: "RGB display", // LCD_CAM, ESP32 I2S, S2 SPI - peripherals: &[], - properties: {} - }, - LedcProperties { - driver: ledc, - name: "LEDC", - peripherals: &["ledc"], - properties: {} - }, - McpwmProperties { - driver: mcpwm, - name: "MCPWM", - peripherals: &["mcpwm0", "mcpwm1"], - properties: {} - }, - ParlIoProperties { - driver: parl_io, - name: "PARL_IO", - peripherals: &["parl_io"], - properties: {} - }, - PcntProperties { - driver: pcnt, - name: "PCNT", - peripherals: &["pcnt"], - properties: {} - }, - PsramProperties { - driver: psram, - name: "PSRAM", - peripherals: &["psram"], - properties: {} - }, - RmtProperties { - driver: rmt, - name: "RMT", - peripherals: &["rmt"], - properties: { - ram_start: u32, - channel_ram_size: u32, - } - }, - RngProperties { - driver: rng, - name: "RNG", - peripherals: &["rng"], - properties: {} - }, - RsaProperties { - driver: rsa, - name: "RSA", - peripherals: &["rsa"], - properties: {} - }, - SdHostProperties { - driver: sd_host, - name: "SDIO host", - peripherals: &["sdhost"], - properties: {} - }, - SdSlaveProperties { - driver: sd_slave, - name: "SDIO slave", - peripherals: &["slchost"], - properties: {} - }, - SleepProperties { - driver: sleep, - name: "Light/deep sleep", - peripherals: &[], - properties: {} - }, - ShaProperties { - driver: sha, - name: "SHA", - peripherals: &["sha"], - properties: {} - }, - SpiMasterProperties { - driver: spi_master, - name: "SPI master", - peripherals: &["spi2", "spi3"], - properties: {} - }, - SpiSlaveProperties { - driver: spi_slave, - name: "SPI slave", - peripherals: &["spi2", "spi3"], - properties: {} - }, - SysTimerProperties { - driver: systimer, - name: "SYSTIMER", - peripherals: &["systimer"], - properties: {} - }, - TempProperties { - driver: temp_sensor, - name: "Temperature sensor", - peripherals: &[], - properties: {} - }, - TimersProperties { - driver: timergroup, - name: "Timers", - peripherals: &[], - properties: { - #[serde(default)] - timg_has_timer1: bool, - } - }, - TouchProperties { - driver: touch, - name: "Touch", - peripherals: &["touch"], - properties: {} - }, - TwaiProperties { - driver: twai, - name: "TWAI", - peripherals: &["twai0", "twai1"], - properties: {} - }, - UartProperties { - driver: uart, - name: "UART", - peripherals: &["uart0", "uart1", "uart2"], - properties: {} - }, - UlpFsmProperties { - driver: ulp_fsm, - name: "ULP (FSM)", - peripherals: &["ulp_supported"], - properties: {} - }, - UlpRiscvProperties { - driver: ulp_riscv, - name: "ULP (RISC-V)", - peripherals: &["ulp_riscv_core", "lp_core"], - properties: {} - }, - UsbOtgProperties { - driver: usb_otg, - name: "USB OTG FS", - peripherals: &["usb0"], - properties: {} - }, - UsbSerialJtagProperties { - driver: usb_serial_jtag, - name: "USB Serial/JTAG", - peripherals: &["usb_device"], - properties: {} - }, - WifiProperties { - driver: wifi, - name: "WIFI", - peripherals: &["wifi"], - properties: { - #[serde(default)] - has_wifi6: bool, - } - }, - BluetoothProperties { - driver: bt, - name: "Bluetooth", - peripherals: &["bt"], - properties: {} - }, - IeeeProperties { - driver: ieee802154, - name: "IEEE 802.15.4", - peripherals: &["ieee802154"], - properties: {} - }, -]; - -// Output a Display-able value as a TokenStream, intended to generate numbers -// without the type suffix. -fn number(n: impl std::fmt::Display) -> TokenStream { - TokenStream::from_str(&format!("{n}")).unwrap() -} - -/// Device configuration file format. -#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] -pub struct Config { - device: Device, - #[serde(skip)] - all_symbols: OnceLock>, -} - -impl Config { - /// The configuration for the specified chip. - pub fn for_chip(chip: &Chip) -> &Self { - match chip { - Chip::Esp32 => include_toml!(Config, "../devices/esp32.toml"), - Chip::Esp32c2 => include_toml!(Config, "../devices/esp32c2.toml"), - Chip::Esp32c3 => include_toml!(Config, "../devices/esp32c3.toml"), - Chip::Esp32c6 => include_toml!(Config, "../devices/esp32c6.toml"), - Chip::Esp32h2 => include_toml!(Config, "../devices/esp32h2.toml"), - Chip::Esp32s2 => include_toml!(Config, "../devices/esp32s2.toml"), - Chip::Esp32s3 => include_toml!(Config, "../devices/esp32s3.toml"), - } - } - - /// Create an empty configuration - pub fn empty() -> Self { - Self { - device: Device { - name: "".to_owned(), - arch: Arch::RiscV, - cores: 1, - trm: "".to_owned(), - peripherals: Vec::new(), - virtual_peripherals: Vec::new(), - symbols: Vec::new(), - memory: Vec::new(), - peri_config: PeriConfig::default(), - }, - all_symbols: OnceLock::new(), - } - } - - fn validate(&self) -> Result<()> { - for instance in self.device.peri_config.driver_instances() { - let (driver, peri) = instance.split_once('.').unwrap(); - ensure!( - self.device.peripherals.iter().any(|p| p == peri) - || self.device.virtual_peripherals.iter().any(|p| p == peri), - "Driver {driver} marks an implementation for '{peri}' but this peripheral is not defined for '{}'", - self.device.name - ); - } - - Ok(()) - } - - /// The name of the device. - pub fn name(&self) -> String { - self.device.name.clone() - } - - /// The CPU architecture of the device. - pub fn arch(&self) -> Arch { - self.device.arch - } - - /// The core count of the device. - pub fn cores(&self) -> Cores { - if self.device.cores > 1 { - Cores::Multi - } else { - Cores::Single - } - } - - /// The peripherals of the device. - pub fn peripherals(&self) -> &[String] { - &self.device.peripherals - } - - /// User-defined symbols for the device. - pub fn symbols(&self) -> &[String] { - &self.device.symbols - } - - /// Memory regions. - /// - /// Will be available as env-variables `REGION--START` / - /// `REGION--END` - pub fn memory(&self) -> &[MemoryRegion] { - &self.device.memory - } - - /// All configuration values for the device. - pub fn all(&self) -> &[String] { - self.all_symbols.get_or_init(|| { - let mut all = vec![ - self.device.name.clone(), - self.device.arch.to_string(), - match self.cores() { - Cores::Single => String::from("single_core"), - Cores::Multi => String::from("multi_core"), - }, - ]; - all.extend( - self.device - .peripherals - .iter() - .map(|p| format!("soc_has_{p}")), - ); - all.extend_from_slice(&self.device.symbols); - all.extend( - self.device - .peri_config - .driver_names() - .map(|name| name.to_string()), - ); - all.extend(self.device.peri_config.driver_instances()); - - all.extend(self.device.peri_config.properties().filter_map( - |(name, value)| match value { - Value::Boolean(true) => Some(name.to_string()), - Value::Number(value) => Some(format!("{name}=\"{value}\"")), - _ => None, - }, - )); - all - }) - } - - /// Does the configuration contain `item`? - pub fn contains(&self, item: &str) -> bool { - self.all().iter().any(|i| i == item) - } - - /// Define all symbols for a given configuration. - pub fn define_symbols(&self) { - define_all_possible_symbols(); - // Define all necessary configuration symbols for the configured device: - for symbol in self.all() { - println!("cargo:rustc-cfg={}", symbol.replace('.', "_")); - } - - // Define env-vars for all memory regions - for memory in self.memory() { - println!("cargo:rustc-cfg=has_{}_region", memory.name.to_lowercase()); - } - } - - pub fn generate_metadata(&self) { - let out_dir = std::env::var_os("OUT_DIR").unwrap(); - let out_dir = std::path::Path::new(&out_dir); - let out_file = out_dir.join("_generated.rs").to_string_lossy().to_string(); - - let mut g = TokenStream::new(); - - let chip_name = self.name(); - // Public API, can't use a private macro: - g.extend(quote::quote! { - /// The name of the chip as `&str` - #[macro_export] - macro_rules! chip { - () => { #chip_name }; - } - }); - - // Translate the chip properties into a macro that can be used in esp-hal: - let arch = self.device.arch.as_ref(); - let cores = number(self.device.cores); - let trm = &self.device.trm; - - let peripheral_properties = - self.device - .peri_config - .properties() - .flat_map(|(name, value)| match value { - Value::Unset => quote::quote! {}, - Value::Number(value) => { - let value = number(value); // ensure no numeric suffix is added - quote::quote! { - (#name) => { #value }; - (#name, str) => { stringify!(#value) }; - } - } - Value::Boolean(value) => quote::quote! { - (#name) => { #value }; - }, - }); - - // Not public API, can use a private macro: - g.extend(quote::quote! { - /// A link to the Technical Reference Manual (TRM) for the chip. - #[doc(hidden)] - #[macro_export] - macro_rules! property { - ("chip") => { #chip_name }; - ("arch") => { #arch }; - ("cores") => { #cores }; - ("cores", str) => { stringify!(#cores) }; - ("trm") => { #trm }; - #(#peripheral_properties)* - } - }); - - let region_branches = self.memory().iter().map(|region| { - let name = region.name.to_uppercase(); - let start = number(region.start as usize); - let end = number(region.end as usize); - - quote::quote! { - ( #name ) => { - #start .. #end - }; - } - }); - - g.extend(quote::quote! { - /// Macro to get the address range of the given memory region. - #[macro_export] - #[doc(hidden)] - macro_rules! memory_range { - #(#region_branches)* - } - }); - - std::fs::write(&out_file, g.to_string()).unwrap(); - } -} - -/// Defines all possible symbols that _could_ be output from this crate -/// regardless of the chosen configuration. -/// -/// This is required to avoid triggering the unexpected-cfgs lint. -fn define_all_possible_symbols() { - // Used by our documentation builds to prevent the huge red warning banner. - println!("cargo:rustc-check-cfg=cfg(not_really_docsrs)"); - - let mut cfg_values: HashMap> = HashMap::new(); - - for chip in Chip::iter() { - let config = Config::for_chip(&chip); - for symbol in config.all() { - if let Some((symbol_name, symbol_value)) = symbol.split_once('=') { - // cfg's with values need special syntax, so let's collect all - // of them separately. - let symbol_name = symbol_name.replace('.', "_"); - let entry = cfg_values.entry(symbol_name).or_default(); - // Avoid duplicates in the same cfg. - if !entry.contains(&symbol_value.to_string()) { - entry.push(symbol_value.to_string()); - } - } else { - // https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-check-cfg - println!("cargo:rustc-check-cfg=cfg({})", symbol.replace('.', "_")); - } - } - } - - // Now output all cfgs with values. - for (symbol_name, symbol_values) in cfg_values { - println!( - "cargo:rustc-check-cfg=cfg({symbol_name}, values({}))", - symbol_values.join(",") - ); - } -} - -pub fn generate_chip_support_status(output: &mut impl Write) -> std::fmt::Result { - let nothing = ""; - - // Calculate the width of the first column. - let driver_col_width = std::iter::once("Driver") - .chain(PeriConfig::drivers().iter().map(|i| i.name)) - .map(|c| c.len()) - .max() - .unwrap(); - - // Header - write!(output, "| {:driver_col_width$} |", "Driver")?; - for chip in Chip::iter() { - write!(output, " {} |", chip.pretty_name())?; - } - writeln!(output)?; - - // Header separator - write!(output, "| {nothing:- " ", - Some(status) => status.icon(), - }; - // VSCode displays emojis just a bit wider than 2 characters, making this - // approximation a bit too wide but good enough. - let support_cell_width = chip.pretty_name().len() - status.is_some() as usize; - write!(output, " {status_icon:support_cell_width$} |")?; - } - writeln!(output)?; - } - - writeln!(output)?; - - // Print legend - writeln!(output, " * Empty cell: not available")?; - for s in [ - SupportStatus::NotSupported, - SupportStatus::Partial, - SupportStatus::Supported, - ] { - writeln!(output, " * {}: {}", s.icon(), s.status())?; - } - - Ok(()) -} diff --git a/esp-metadata/src/lib.rs b/esp-metadata/src/lib.rs index 7b77d4728..6737918f7 100644 --- a/esp-metadata/src/lib.rs +++ b/esp-metadata/src/lib.rs @@ -1,4 +1,667 @@ //! Metadata for Espressif devices, primarily intended for use in build scripts. -mod generate_cfg; +mod cfg; -pub use generate_cfg::*; +use core::str::FromStr; +use std::{collections::HashMap, fmt::Write, path::Path, sync::OnceLock}; + +use anyhow::{Result, bail, ensure}; +use cfg::PeriConfig; +use proc_macro2::TokenStream; +use strum::IntoEnumIterator; + +use crate::cfg::{SupportItem, SupportStatus, Value}; + +macro_rules! include_toml { + (Config, $file:expr) => {{ + static LOADED_TOML: OnceLock = OnceLock::new(); + LOADED_TOML.get_or_init(|| { + let config: Config = basic_toml::from_str(include_str!($file)).unwrap(); + + config.validate().expect("Invalid device configuration"); + + config + }) + }}; +} + +/// Supported device architectures. +#[derive( + Debug, + Clone, + Copy, + PartialEq, + Eq, + PartialOrd, + Ord, + serde::Deserialize, + serde::Serialize, + strum::Display, + strum::EnumIter, + strum::EnumString, + strum::AsRefStr, +)] +#[serde(rename_all = "lowercase")] +#[strum(serialize_all = "lowercase")] +pub enum Arch { + /// RISC-V architecture + RiscV, + /// Xtensa architecture + Xtensa, +} + +/// Device core count. +#[derive( + Debug, + Clone, + Copy, + PartialEq, + Eq, + PartialOrd, + Ord, + serde::Deserialize, + serde::Serialize, + strum::Display, + strum::EnumIter, + strum::EnumString, + strum::AsRefStr, +)] +pub enum Cores { + /// Single CPU core + #[serde(rename = "single_core")] + #[strum(serialize = "single_core")] + Single, + /// Two or more CPU cores + #[serde(rename = "multi_core")] + #[strum(serialize = "multi_core")] + Multi, +} + +/// Supported devices. +#[derive( + Debug, + Clone, + Copy, + PartialEq, + Eq, + PartialOrd, + Ord, + Hash, + serde::Deserialize, + serde::Serialize, + strum::Display, + strum::EnumIter, + strum::EnumString, + strum::AsRefStr, +)] +#[cfg_attr(feature = "clap", derive(clap::ValueEnum))] +#[serde(rename_all = "kebab-case")] +#[strum(serialize_all = "kebab-case")] +pub enum Chip { + /// ESP32 + Esp32, + /// ESP32-C2, ESP8684 + Esp32c2, + /// ESP32-C3, ESP8685 + Esp32c3, + /// ESP32-C6 + Esp32c6, + /// ESP32-H2 + Esp32h2, + /// ESP32-S2 + Esp32s2, + /// ESP32-S3 + Esp32s3, +} + +impl Chip { + pub fn from_cargo_feature() -> Result { + let all_chips = Chip::iter().map(|c| c.to_string()).collect::>(); + + let mut chip = None; + for c in all_chips.iter() { + if std::env::var(format!("CARGO_FEATURE_{}", c.to_uppercase())).is_ok() { + if chip.is_some() { + bail!( + "Expected exactly one of the following features to be enabled: {}", + all_chips.join(", ") + ); + } + chip = Some(c); + } + } + + let Some(chip) = chip else { + bail!( + "Expected exactly one of the following features to be enabled: {}", + all_chips.join(", ") + ); + }; + + Ok(Self::from_str(chip.as_str()).unwrap()) + } + + pub fn target(&self) -> &'static str { + use Chip::*; + + match self { + Esp32 => "xtensa-esp32-none-elf", + Esp32c2 | Esp32c3 => "riscv32imc-unknown-none-elf", + Esp32c6 | Esp32h2 => "riscv32imac-unknown-none-elf", + Esp32s2 => "xtensa-esp32s2-none-elf", + Esp32s3 => "xtensa-esp32s3-none-elf", + } + } + + pub fn has_lp_core(&self) -> bool { + use Chip::*; + + matches!(self, Esp32c6 | Esp32s2 | Esp32s3) + } + + pub fn lp_target(&self) -> Result<&'static str> { + use Chip::*; + + match self { + Esp32c6 => Ok("riscv32imac-unknown-none-elf"), + Esp32s2 | Esp32s3 => Ok("riscv32imc-unknown-none-elf"), + _ => bail!("Chip does not contain an LP core: '{}'", self), + } + } + + pub fn pretty_name(&self) -> &str { + match self { + Chip::Esp32 => "ESP32", + Chip::Esp32c2 => "ESP32-C2", + Chip::Esp32c3 => "ESP32-C3", + Chip::Esp32c6 => "ESP32-C6", + Chip::Esp32h2 => "ESP32-H2", + Chip::Esp32s2 => "ESP32-S2", + Chip::Esp32s3 => "ESP32-S3", + } + } + + pub fn is_xtensa(&self) -> bool { + matches!(self, Chip::Esp32 | Chip::Esp32s2 | Chip::Esp32s3) + } + + pub fn is_riscv(&self) -> bool { + !self.is_xtensa() + } +} + +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +pub struct MemoryRegion { + name: String, + start: u32, + end: u32, +} + +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +struct Device { + name: String, + arch: Arch, + cores: usize, + trm: String, + + peripherals: Vec, + // For now, this is only used to double-check the configuration. + virtual_peripherals: Vec, + symbols: Vec, + memory: Vec, + + // Peripheral driver configuration: + #[serde(flatten)] + peri_config: PeriConfig, +} + +// Output a Display-able value as a TokenStream, intended to generate numbers +// without the type suffix. +fn number(n: impl std::fmt::Display) -> TokenStream { + TokenStream::from_str(&format!("{n}")).unwrap() +} + +/// Device configuration file format. +#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] +pub struct Config { + device: Device, + #[serde(skip)] + all_symbols: OnceLock>, +} + +impl Config { + /// The configuration for the specified chip. + pub fn for_chip(chip: &Chip) -> &Self { + match chip { + Chip::Esp32 => include_toml!(Config, "../devices/esp32.toml"), + Chip::Esp32c2 => include_toml!(Config, "../devices/esp32c2.toml"), + Chip::Esp32c3 => include_toml!(Config, "../devices/esp32c3.toml"), + Chip::Esp32c6 => include_toml!(Config, "../devices/esp32c6.toml"), + Chip::Esp32h2 => include_toml!(Config, "../devices/esp32h2.toml"), + Chip::Esp32s2 => include_toml!(Config, "../devices/esp32s2.toml"), + Chip::Esp32s3 => include_toml!(Config, "../devices/esp32s3.toml"), + } + } + + /// Create an empty configuration + pub fn empty() -> Self { + Self { + device: Device { + name: "".to_owned(), + arch: Arch::RiscV, + cores: 1, + trm: "".to_owned(), + peripherals: Vec::new(), + virtual_peripherals: Vec::new(), + symbols: Vec::new(), + memory: Vec::new(), + peri_config: PeriConfig::default(), + }, + all_symbols: OnceLock::new(), + } + } + + fn validate(&self) -> Result<()> { + for instance in self.device.peri_config.driver_instances() { + let (driver, peri) = instance.split_once('.').unwrap(); + ensure!( + self.device.peripherals.iter().any(|p| p == peri) + || self.device.virtual_peripherals.iter().any(|p| p == peri), + "Driver {driver} marks an implementation for '{peri}' but this peripheral is not defined for '{}'", + self.device.name + ); + } + + Ok(()) + } + + /// The name of the device. + pub fn name(&self) -> String { + self.device.name.clone() + } + + /// The CPU architecture of the device. + pub fn arch(&self) -> Arch { + self.device.arch + } + + /// The core count of the device. + pub fn cores(&self) -> Cores { + if self.device.cores > 1 { + Cores::Multi + } else { + Cores::Single + } + } + + /// The peripherals of the device. + pub fn peripherals(&self) -> &[String] { + &self.device.peripherals + } + + /// User-defined symbols for the device. + pub fn symbols(&self) -> &[String] { + &self.device.symbols + } + + /// Memory regions. + /// + /// Will be available as env-variables `REGION--START` / + /// `REGION--END` + pub fn memory(&self) -> &[MemoryRegion] { + &self.device.memory + } + + /// All configuration values for the device. + pub fn all(&self) -> &[String] { + self.all_symbols.get_or_init(|| { + let mut all = vec![ + self.device.name.clone(), + self.device.arch.to_string(), + match self.cores() { + Cores::Single => String::from("single_core"), + Cores::Multi => String::from("multi_core"), + }, + ]; + all.extend( + self.device + .peripherals + .iter() + .map(|p| format!("soc_has_{p}")), + ); + all.extend_from_slice(&self.device.symbols); + all.extend( + self.device + .peri_config + .driver_names() + .map(|name| name.to_string()), + ); + all.extend(self.device.peri_config.driver_instances()); + + all.extend(self.device.peri_config.properties().filter_map( + |(name, value)| match value { + Value::Boolean(true) => Some(name.to_string()), + Value::Number(value) => Some(format!("{name}=\"{value}\"")), + _ => None, + }, + )); + all + }) + } + + /// Does the configuration contain `item`? + pub fn contains(&self, item: &str) -> bool { + self.all().iter().any(|i| i == item) + } + + /// Define all symbols for a given configuration. + pub fn define_symbols(&self) { + define_all_possible_symbols(); + // Define all necessary configuration symbols for the configured device: + for symbol in self.all() { + println!("cargo:rustc-cfg={}", symbol.replace('.', "_")); + } + + // Define env-vars for all memory regions + for memory in self.memory() { + println!("cargo:rustc-cfg=has_{}_region", memory.name.to_lowercase()); + } + } + + pub fn generate_metadata(&self) { + let out_dir = std::env::var_os("OUT_DIR").unwrap(); + let out_dir = Path::new(&out_dir); + + self.generate_properties(out_dir, "_generated.rs"); + self.generate_gpios(out_dir, "_generated_gpio.rs"); + } + + fn generate_properties(&self, out_dir: &Path, file_name: &str) { + let out_file = out_dir.join(file_name).to_string_lossy().to_string(); + + let mut g = TokenStream::new(); + + let chip_name = self.name(); + // Public API, can't use a private macro: + g.extend(quote::quote! { + /// The name of the chip as `&str` + #[macro_export] + macro_rules! chip { + () => { #chip_name }; + } + }); + + // Translate the chip properties into a macro that can be used in esp-hal: + let arch = self.device.arch.as_ref(); + let cores = number(self.device.cores); + let trm = &self.device.trm; + + let peripheral_properties = + self.device + .peri_config + .properties() + .flat_map(|(name, value)| match value { + Value::Unset => quote::quote! {}, + Value::Number(value) => { + let value = number(value); // ensure no numeric suffix is added + quote::quote! { + (#name) => { #value }; + (#name, str) => { stringify!(#value) }; + } + } + Value::Boolean(value) => quote::quote! { + (#name) => { #value }; + }, + }); + + // Not public API, can use a private macro: + g.extend(quote::quote! { + /// A link to the Technical Reference Manual (TRM) for the chip. + #[doc(hidden)] + #[macro_export] + macro_rules! property { + ("chip") => { #chip_name }; + ("arch") => { #arch }; + ("cores") => { #cores }; + ("cores", str) => { stringify!(#cores) }; + ("trm") => { #trm }; + #(#peripheral_properties)* + } + }); + + let region_branches = self.memory().iter().map(|region| { + let name = region.name.to_uppercase(); + let start = number(region.start as usize); + let end = number(region.end as usize); + + quote::quote! { + ( #name ) => { + #start .. #end + }; + } + }); + + g.extend(quote::quote! { + /// Macro to get the address range of the given memory region. + #[macro_export] + #[doc(hidden)] + macro_rules! memory_range { + #(#region_branches)* + } + }); + + std::fs::write(&out_file, g.to_string()).unwrap(); + } + + fn generate_gpios(&self, out_dir: &Path, file_name: &str) { + let out_file = out_dir.join(file_name).to_string_lossy().to_string(); + + let pins = self.device.peri_config.gpio.as_ref().unwrap().instances[0] + .instance_config + .pins + .iter() + .map(|pin| { + let pin_number = number(pin.pin); + + struct PinAttrs { + input: bool, + output: bool, + analog: bool, + rtc_io: bool, + touch: bool, + } + + let mut pin_attrs = PinAttrs { + input: false, + output: false, + analog: false, + rtc_io: false, + touch: false, + }; + pin.kind.iter().for_each(|kind| match kind { + cfg::PinCapability::Input => pin_attrs.input = true, + cfg::PinCapability::Output => pin_attrs.output = true, + cfg::PinCapability::Analog => pin_attrs.analog = true, + cfg::PinCapability::Rtc => pin_attrs.rtc_io = true, + cfg::PinCapability::Touch => pin_attrs.touch = true, + }); + + let mut attrs = vec![]; + + if pin_attrs.input { + attrs.push(quote::quote! { Input }); + } + if pin_attrs.output { + attrs.push(quote::quote! { Output }); + } + if pin_attrs.analog { + attrs.push(quote::quote! { Analog }); + } + if pin_attrs.rtc_io { + if !pin_attrs.output { + attrs.push(quote::quote! { RtcIo }); + } else { + attrs.push(quote::quote! { RtcIoInput }); + } + } + if pin_attrs.touch { + attrs.push(quote::quote! { Touch }); + } + + let mut input_afs = vec![]; + let mut output_afs = vec![]; + + for af in 0..6 { + if let Some(signal) = pin.af_input.get(af) { + let af = quote::format_ident!("_{af}"); + let signal = TokenStream::from_str(signal).unwrap(); + input_afs.push(quote::quote! { #af => #signal }); + } + if let Some(signal) = pin.af_output.get(af) { + let af = quote::format_ident!("_{af}"); + let signal = TokenStream::from_str(signal).unwrap(); + output_afs.push(quote::quote! { #af => #signal }); + } + } + + quote::quote! { + ( #pin_number, [#(#attrs),*] ( #(#input_afs)* ) ( #(#output_afs)* ) ) + } + }); + + let g = quote::quote! { + crate::gpio! { + #(#pins)* + } + }; + + std::fs::write(&out_file, g.to_string()).unwrap(); + } +} + +/// Defines all possible symbols that _could_ be output from this crate +/// regardless of the chosen configuration. +/// +/// This is required to avoid triggering the unexpected-cfgs lint. +fn define_all_possible_symbols() { + // Used by our documentation builds to prevent the huge red warning banner. + println!("cargo:rustc-check-cfg=cfg(not_really_docsrs)"); + + let mut cfg_values: HashMap> = HashMap::new(); + + for chip in Chip::iter() { + let config = Config::for_chip(&chip); + for symbol in config.all() { + if let Some((symbol_name, symbol_value)) = symbol.split_once('=') { + // cfg's with values need special syntax, so let's collect all + // of them separately. + let symbol_name = symbol_name.replace('.', "_"); + let entry = cfg_values.entry(symbol_name).or_default(); + // Avoid duplicates in the same cfg. + if !entry.contains(&symbol_value.to_string()) { + entry.push(symbol_value.to_string()); + } + } else { + // https://doc.rust-lang.org/cargo/reference/build-scripts.html#rustc-check-cfg + println!("cargo:rustc-check-cfg=cfg({})", symbol.replace('.', "_")); + } + } + } + + // Now output all cfgs with values. + for (symbol_name, symbol_values) in cfg_values { + println!( + "cargo:rustc-check-cfg=cfg({symbol_name}, values({}))", + symbol_values.join(",") + ); + } +} + +pub fn generate_chip_support_status(output: &mut impl Write) -> std::fmt::Result { + let nothing = ""; + + // Calculate the width of the first column. + let driver_col_width = std::iter::once("Driver") + .chain(PeriConfig::drivers().iter().map(|i| i.name)) + .map(|c| c.len()) + .max() + .unwrap(); + + // Header + write!(output, "| {:driver_col_width$} |", "Driver")?; + for chip in Chip::iter() { + write!(output, " {} |", chip.pretty_name())?; + } + writeln!(output)?; + + // Header separator + write!(output, "| {nothing:- " ", + Some(status) => status.icon(), + }; + // VSCode displays emojis just a bit wider than 2 characters, making this + // approximation a bit too wide but good enough. + let support_cell_width = chip.pretty_name().len() - status.is_some() as usize; + write!(output, " {status_icon:support_cell_width$} |")?; + } + writeln!(output)?; + } + + writeln!(output)?; + + // Print legend + writeln!(output, " * Empty cell: not available")?; + for s in [ + SupportStatus::NotSupported, + SupportStatus::Partial, + SupportStatus::Supported, + ] { + writeln!(output, " * {}: {}", s.icon(), s.status())?; + } + + Ok(()) +}