Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate PARL_IO driver to newer DMA API #3033

Merged
merged 9 commits into from
Feb 3, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions esp-hal/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `Rng` and `Trng` now implement `Peripheral<P = Self>` (#2992)
- SPI, UART, I2C: `with_<pin>` functions of peripheral drivers now disconnect the previously assigned pins from the peripheral. (#3012)
- SPI, UART, I2C: Dropping a driver now disconnects pins from their peripherals. (#3012)
- Migrate PARL_IO driver to DMA move API (#3033)
- `Async` drivers are no longer `Send` (#2980)
- GPIO drivers now take configuration structs (#2990, #3029)
- `flip-link` feature is now a config option (`ESP_HAL_CONFIG_FLIP_LINK`) (#3001)
29 changes: 29 additions & 0 deletions esp-hal/MIGRATING-0.23.md
Original file line number Diff line number Diff line change
@@ -174,6 +174,35 @@ config/config.toml
+ ESP_HAL_CONFIG_PSRAM_MODE = "octal"
```

## PARL_IO changes
Parallel IO now uses the newer DMA Move API.

Changes on the TX side
```diff
let (_, _, tx_buffer, tx_descriptors) = dma_buffers!(0, 32000);
+ let mut dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap();

- let transfer = parl_io_tx.write_dma(&tx_buffer).unwrap();
- transfer.wait().unwrap();
+ let transfer = parl_io_tx.write(dma_tx_buf.len(), dma_tx_buf).unwrap();
+ (result, parl_io_tx, dma_tx_buf) = transfer.wait();
+ result.unwrap();
```

Changes on the RX side
```diff
let (rx_buffer, rx_descriptors, _, _) = dma_buffers!(32000, 0);
+ let mut dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap();
- let transfer = parl_io_rx.read_dma(&mut rx_buffer).unwrap();
- transfer.wait().unwrap();
+ let transfer = parl_io_rx.read(Some(dma_rx_buf.len()), dma_rx_buf).unwrap();
+ (_, parl_io_rx, dma_rx_buf) = transfer.wait();
```

On the RX side, the `EofMode` is now decided at transfer time, rather than config time.
- `EofMode::ByteLen` -> `Some(<number of bytes to receive>)`
- `EofMode::EnableSignal` -> `None`

## GPIO changes

GPIO drivers now take configuration structs.
586 changes: 322 additions & 264 deletions esp-hal/src/parl_io.rs

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions hil-test/Cargo.toml
Original file line number Diff line number Diff line change
@@ -115,6 +115,10 @@ harness = false
name = "spi_slave"
harness = false

[[test]]
name = "parl_io"
harness = false

[[test]]
name = "parl_io_tx"
harness = false
121 changes: 121 additions & 0 deletions hil-test/tests/parl_io.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
//! PARL_IO test
//% CHIPS: esp32c6 esp32h2
//% FEATURES: unstable

#![no_std]
#![no_main]

use esp_hal::{
dma::{DmaChannel0, DmaRxBuf, DmaTxBuf},
dma_buffers,
gpio::{AnyPin, Pin},
parl_io::{
BitPackOrder,
ClkOutPin,
EnableMode,
ParlIoFullDuplex,
RxClkInPin,
RxFourBits,
RxPinConfigWithValidPin,
SampleEdge,
TxFourBits,
TxPinConfigWithValidPin,
},
peripherals::PARL_IO,
time::RateExtU32,
};
use hil_test as _;

struct Context {
parl_io: PARL_IO,
dma_channel: DmaChannel0,
clock_pin: AnyPin,
valid_pin: AnyPin,
data_pins: [AnyPin; 4],
}

#[cfg(test)]
#[embedded_test::tests(default_timeout = 3)]
mod tests {
use super::*;

#[init]
fn init() -> Context {
let peripherals = esp_hal::init(esp_hal::Config::default());

let dma_channel = peripherals.DMA_CH0;

let parl_io = peripherals.PARL_IO;

Context {
parl_io,
dma_channel,
clock_pin: peripherals.GPIO11.degrade(),
valid_pin: peripherals.GPIO10.degrade(),
data_pins: [
peripherals.GPIO1.degrade(),
peripherals.GPIO0.degrade(),
peripherals.GPIO14.degrade(),
peripherals.GPIO23.degrade(),
],
}
}

#[test]
fn test_parl_io_rx_can_read_tx(ctx: Context) {
const BUFFER_SIZE: usize = 64;

let (rx_buffer, rx_descriptors, tx_buffer, tx_descriptors) = dma_buffers!(BUFFER_SIZE);
let mut dma_rx_buf = DmaRxBuf::new(rx_descriptors, rx_buffer).unwrap();
let mut dma_tx_buf = DmaTxBuf::new(tx_descriptors, tx_buffer).unwrap();

let (clock_rx, clock_tx) = ctx.clock_pin.split();
let (valid_rx, valid_tx) = ctx.valid_pin.split();
let [(d0_rx, d0_tx), (d1_rx, d1_tx), (d2_rx, d2_tx), (d3_rx, d3_tx)] =
ctx.data_pins.map(|pin| pin.split());

let tx_pins = TxFourBits::new(d0_tx, d1_tx, d2_tx, d3_tx);
let rx_pins = RxFourBits::new(d0_rx, d1_rx, d2_rx, d3_rx);

let tx_pins = TxPinConfigWithValidPin::new(tx_pins, valid_tx);
let mut rx_pins = RxPinConfigWithValidPin::new(rx_pins, valid_rx, EnableMode::HighLevel);

let clock_out_pin = ClkOutPin::new(clock_tx);
let mut clock_in_pin = RxClkInPin::new(clock_rx, SampleEdge::Normal);

let pio = ParlIoFullDuplex::new(ctx.parl_io, ctx.dma_channel, 40.MHz()).unwrap();

let pio_tx = pio
.tx
.with_config(
tx_pins,
clock_out_pin,
0,
SampleEdge::Invert,
BitPackOrder::Lsb,
)
.unwrap();
let pio_rx = pio
.rx
.with_config(&mut rx_pins, &mut clock_in_pin, BitPackOrder::Lsb, None)
.unwrap();

for (i, b) in dma_tx_buf.as_mut_slice().iter_mut().enumerate() {
*b = i as u8;
}

let rx_transfer = pio_rx
.read(Some(dma_rx_buf.len()), dma_rx_buf)
.map_err(|e| e.0)
.unwrap();
let tx_transfer = pio_tx
.write(dma_tx_buf.len(), dma_tx_buf)
.map_err(|e| e.0)
.unwrap();
(_, _, dma_tx_buf) = tx_transfer.wait();
(_, _, dma_rx_buf) = rx_transfer.wait();

assert_eq!(dma_rx_buf.as_slice(), dma_tx_buf.as_slice());
}
}
32 changes: 18 additions & 14 deletions hil-test/tests/parl_io_tx.rs
Original file line number Diff line number Diff line change
@@ -9,7 +9,8 @@
#[cfg(esp32c6)]
use esp_hal::parl_io::{TxPinConfigWithValidPin, TxSixteenBits};
use esp_hal::{
dma::DmaChannel0,
dma::{DmaChannel0, DmaTxBuf},
dma_tx_buffer,
gpio::{
interconnect::{InputSignal, OutputSignal},
NoPin,
@@ -78,8 +79,8 @@ mod tests {
#[test]
fn test_parl_io_tx_16bit_valid_clock_count(ctx: Context) {
const BUFFER_SIZE: usize = 64;
let tx_buffer = [0u16; BUFFER_SIZE];
let (_, tx_descriptors) = esp_hal::dma_descriptors!(0, 2 * BUFFER_SIZE);

let mut dma_tx_buf: DmaTxBuf = dma_tx_buffer!(2 * BUFFER_SIZE).unwrap();

let pins = TxSixteenBits::new(
NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin,
@@ -88,8 +89,7 @@ mod tests {
let mut pins = TxPinConfigIncludingValidPin::new(pins);
let mut clock_pin = ClkOutPin::new(ctx.clock);

let pio =
ParlIoTxOnly::new(ctx.parl_io, ctx.dma_channel, tx_descriptors, 10.MHz()).unwrap();
let pio = ParlIoTxOnly::new(ctx.parl_io, ctx.dma_channel, 10.MHz()).unwrap();

let mut pio = pio
.tx
@@ -102,7 +102,7 @@ mod tests {
)
.unwrap(); // TODO: handle error

// use a PCNT unit to count the negitive clock edges only when valid is high
// use a PCNT unit to count the negative clock edges only when valid is high
let clock_unit = ctx.pcnt_unit;
clock_unit.channel0.set_edge_signal(ctx.clock_loopback);
clock_unit.channel0.set_ctrl_signal(ctx.valid_loopback);
@@ -115,8 +115,11 @@ mod tests {

for _ in 0..100 {
clock_unit.clear();
let xfer = pio.write_dma(&tx_buffer).unwrap();
xfer.wait().unwrap();
let xfer = pio
.write(dma_tx_buf.len(), dma_tx_buf)
.map_err(|e| e.0)
.unwrap();
(_, pio, dma_tx_buf) = xfer.wait();
info!("clock count: {}", clock_unit.value());
assert_eq!(clock_unit.value(), BUFFER_SIZE as _);
}
@@ -125,8 +128,7 @@ mod tests {
#[test]
fn test_parl_io_tx_8bit_valid_clock_count(ctx: Context) {
const BUFFER_SIZE: usize = 64;
let tx_buffer = [0u8; BUFFER_SIZE];
let (_, tx_descriptors) = esp_hal::dma_descriptors!(0, 2 * BUFFER_SIZE);
let mut dma_tx_buf: DmaTxBuf = dma_tx_buffer!(BUFFER_SIZE).unwrap();

let pins = TxEightBits::new(
NoPin,
@@ -149,8 +151,7 @@ mod tests {

let mut clock_pin = ClkOutPin::new(ctx.clock);

let pio =
ParlIoTxOnly::new(ctx.parl_io, ctx.dma_channel, tx_descriptors, 10.MHz()).unwrap();
let pio = ParlIoTxOnly::new(ctx.parl_io, ctx.dma_channel, 10.MHz()).unwrap();

let mut pio = pio
.tx
@@ -176,8 +177,11 @@ mod tests {

for _ in 0..100 {
clock_unit.clear();
let xfer = pio.write_dma(&tx_buffer).unwrap();
xfer.wait().unwrap();
let xfer = pio
.write(dma_tx_buf.len(), dma_tx_buf)
.map_err(|e| e.0)
.unwrap();
(_, pio, dma_tx_buf) = xfer.wait();
info!("clock count: {}", clock_unit.value());
assert_eq!(clock_unit.value(), BUFFER_SIZE as _);
}
28 changes: 19 additions & 9 deletions hil-test/tests/parl_io_tx_async.rs
Original file line number Diff line number Diff line change
@@ -9,7 +9,8 @@
#[cfg(esp32c6)]
use esp_hal::parl_io::{TxPinConfigWithValidPin, TxSixteenBits};
use esp_hal::{
dma::DmaChannel0,
dma::{DmaChannel0, DmaTxBuf},
dma_tx_buffer,
gpio::{
interconnect::{InputSignal, OutputSignal},
NoPin,
@@ -78,8 +79,7 @@ mod tests {
#[test]
async fn test_parl_io_tx_async_16bit_valid_clock_count(ctx: Context) {
const BUFFER_SIZE: usize = 64;
let tx_buffer = [0u16; BUFFER_SIZE];
let (_, tx_descriptors) = esp_hal::dma_descriptors!(0, 2 * BUFFER_SIZE);
let mut dma_tx_buf: DmaTxBuf = dma_tx_buffer!(2 * BUFFER_SIZE).unwrap();

let pins = TxSixteenBits::new(
NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin, NoPin,
@@ -88,7 +88,7 @@ mod tests {
let mut pins = TxPinConfigIncludingValidPin::new(pins);
let mut clock_pin = ClkOutPin::new(ctx.clock);

let pio = ParlIoTxOnly::new(ctx.parl_io, ctx.dma_channel, tx_descriptors, 10.MHz())
let pio = ParlIoTxOnly::new(ctx.parl_io, ctx.dma_channel, 10.MHz())
.unwrap()
.into_async();

@@ -116,7 +116,12 @@ mod tests {

for _ in 0..100 {
clock_unit.clear();
pio.write_dma_async(&tx_buffer).await.unwrap();
let mut xfer = pio
.write(dma_tx_buf.len(), dma_tx_buf)
.map_err(|e| e.0)
.unwrap();
xfer.wait_for_done().await;
(_, pio, dma_tx_buf) = xfer.wait();
info!("clock count: {}", clock_unit.value());
assert_eq!(clock_unit.value(), BUFFER_SIZE as _);
}
@@ -125,8 +130,8 @@ mod tests {
#[test]
async fn test_parl_io_tx_async_8bit_valid_clock_count(ctx: Context) {
const BUFFER_SIZE: usize = 64;
let tx_buffer = [0u8; BUFFER_SIZE];
let (_, tx_descriptors) = esp_hal::dma_descriptors!(0, 2 * BUFFER_SIZE);

let mut dma_tx_buf: DmaTxBuf = dma_tx_buffer!(BUFFER_SIZE).unwrap();

let pins = TxEightBits::new(
NoPin,
@@ -149,7 +154,7 @@ mod tests {

let mut clock_pin = ClkOutPin::new(ctx.clock);

let pio = ParlIoTxOnly::new(ctx.parl_io, ctx.dma_channel, tx_descriptors, 10.MHz())
let pio = ParlIoTxOnly::new(ctx.parl_io, ctx.dma_channel, 10.MHz())
.unwrap()
.into_async();

@@ -178,7 +183,12 @@ mod tests {

for _ in 0..100 {
clock_unit.clear();
pio.write_dma_async(&tx_buffer).await.unwrap();
let mut xfer = pio
.write(dma_tx_buf.len(), dma_tx_buf)
.map_err(|e| e.0)
.unwrap();
xfer.wait_for_done().await;
(_, pio, dma_tx_buf) = xfer.wait();
info!("clock count: {}", clock_unit.value());
assert_eq!(clock_unit.value(), BUFFER_SIZE as _);
}