Skip to content

Commit e5c9931

Browse files
committed
sdio: qa-test integration tests
Adds a split `qa-test` SPI-mode SDIO integration test. The tests are split with the intention to flash each half of the test to separate devices, allowing for concurrent execution.
1 parent 70733b3 commit e5c9931

File tree

4 files changed

+332
-2
lines changed

4 files changed

+332
-2
lines changed

esp-hal/src/sdio.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ mod slchost;
3636
mod state;
3737
mod timing;
3838

39-
pub use config::Config;
39+
pub use config::{Config, SpiMode};
4040
pub use direction::Direction;
4141
pub use hinf::{AnyHinf, HinfInfo, HinfInstance};
4242
pub use interrupt::{DeviceInterrupt, HostInterrupt};
@@ -408,7 +408,7 @@ impl<'d> Sdio<'d> {
408408
}
409409

410410
/// Waits for a clock edge transition to indicate when to read/write data.
411-
// TODO: configure SPI modes
411+
// TODO: add timeout parameter
412412
pub(crate) fn wait_for_clock_edge(&self, direction: SpiDirection) -> Result<(), Error> {
413413
match self.pins.mode() {
414414
Mode::Spi => {

esp-hal/src/sdio/config.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
use super::Timing;
2+
pub use crate::spi::Mode as SpiMode;
23

34
/// Represents SDIO configuration parameters.
45
#[repr(C)]
56
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
67
pub struct Config {
78
hs: bool,
89
timing: Timing,
10+
spi_mode: SpiMode,
911
}
1012

1113
impl Config {
@@ -14,6 +16,7 @@ impl Config {
1416
Self {
1517
hs: false,
1618
timing: Timing::new(),
19+
spi_mode: SpiMode::_0,
1720
}
1821
}
1922

@@ -46,6 +49,21 @@ impl Config {
4649
pub fn with_timing(self, timing: Timing) -> Self {
4750
Self { timing, ..self }
4851
}
52+
53+
/// Gets the SPI mode setting.
54+
pub const fn spi_mode(&self) -> SpiMode {
55+
self.spi_mode
56+
}
57+
58+
/// Sets the SPI mode seting.
59+
pub fn set_spi_mode(&mut self, spi_mode: SpiMode) {
60+
self.spi_mode = spi_mode;
61+
}
62+
63+
/// Builder funciton that sets the SPI mode setting.
64+
pub fn with_spi_mode(self, spi_mode: SpiMode) -> Self {
65+
Self { spi_mode, ..self }
66+
}
4967
}
5068

5169
impl Default for Config {

qa-test/src/bin/sdio-spi-device.rs

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
#![no_std]
2+
#![no_main]
3+
4+
use esp_backtrace as _;
5+
use esp_hal::{
6+
dma::{DescriptorFlagFields, Owner},
7+
sdio::{
8+
Config,
9+
Mode,
10+
Pins,
11+
Sdio,
12+
SpiMode,
13+
dma::{
14+
AtomicBuffer,
15+
AtomicDmaDescriptor,
16+
AtomicDmaDescriptors,
17+
DmaDescriptor,
18+
DmaDescriptorFlags,
19+
},
20+
},
21+
};
22+
23+
esp_bootloader_esp_idf::esp_app_desc!();
24+
25+
// Use 10KB of data to fit inside SRAM
26+
// One RX + one TX buffer
27+
const DATA_SIZE: usize = 1024 * 5;
28+
// Use 6KB of descriptors to fit inside SRAM
29+
const DESC_SIZE: usize = 1024 * 6;
30+
// Represents the default byte length of a SDIO block.
31+
const BLOCK_LEN: usize = 512;
32+
// Total number of DMA descriptors.
33+
const DESC_NUM: usize = DESC_SIZE / core::mem::size_of::<AtomicDmaDescriptor>() / 2;
34+
// Represents the set of Receive DMA descriptors.
35+
static RX_DESCRIPTORS: AtomicDmaDescriptors<DESC_NUM> = AtomicDmaDescriptors::new();
36+
// Represents the set of Transmit DMA descriptors.
37+
static TX_DESCRIPTORS: AtomicDmaDescriptors<DESC_NUM> = AtomicDmaDescriptors::new();
38+
// Represents the Receive DMA buffer.
39+
static RX_BUFFER: AtomicBuffer<DATA_SIZE> = AtomicBuffer::new();
40+
// Represents the Transmit DMA buffer.
41+
static TX_BUFFER: AtomicBuffer<DATA_SIZE> = AtomicBuffer::new();
42+
43+
struct Context {
44+
sdio: Sdio<'static>,
45+
}
46+
47+
impl Context {
48+
/// Creates a new context for the SDIO SPI slave controller.
49+
pub fn new() -> Self {
50+
let peripherals = esp_hal::init(esp_hal::Config::default());
51+
52+
cfg_if::cfg_if! {
53+
if #[cfg(feature = "esp32")] {
54+
// GPIO Slot 1 config
55+
let pins = Pins::new(
56+
Mode::Spi,
57+
peripherals.GPIO6, // CLK/SCK
58+
peripherals.GPIO11, // CMD/MOSI
59+
peripherals.GPIO7, // DAT0/MISO
60+
peripherals.GPIO8, // DAT1/IRQ
61+
peripherals.GPIO9, // DAT2
62+
peripherals.GPIO10, // DAT3/#CS
63+
);
64+
65+
// GPIO Slot 2 config
66+
//let pins = Pins::new(
67+
// Mode::Spi,
68+
// peripherals.GPIO14, // CLK/SCK
69+
// peripherals.GPIO15, // CMD/MOSI
70+
// peripherals.GPIO2, // DAT0/MISO
71+
// peripherals.GPIO4, // DAT1/IRQ
72+
// peripherals.GPIO12, // DAT2
73+
// peripherals.GPIO13, // DAT3/#CS
74+
//);
75+
} else if #[cfg(feature = "esp32c6")] {
76+
let pins = Pins::new(
77+
Mode::Spi,
78+
peripherals.GPIO19, // CLK/SCLK
79+
peripherals.GPIO18, // CMD/MOSI
80+
peripherals.GPIO20, // DAT0/MISO
81+
peripherals.GPIO21, // DAT1/IRQ
82+
peripherals.GPIO22, // DAT2
83+
peripherals.GPIO23, // DAT3/#CS
84+
);
85+
} else {
86+
panic!("unsupported platform");
87+
}
88+
}
89+
90+
let config = Config::new().with_spi_mode(SpiMode::_2);
91+
92+
let sdio = Sdio::new(
93+
peripherals.SLC,
94+
peripherals.SLCHOST,
95+
peripherals.HINF,
96+
pins,
97+
config,
98+
);
99+
100+
Self { sdio }
101+
}
102+
}
103+
104+
#[esp_hal::main]
105+
fn main() -> ! {
106+
let ctx = Context::new();
107+
108+
// TODO: perform data transfer
109+
// indicate a transfer of a single SDIO block
110+
let mut flags = DmaDescriptorFlags::new();
111+
flags.set_owner(Owner::Dma);
112+
flags.set_suc_eof(true);
113+
flags.set_size(BLOCK_LEN);
114+
flags.set_len(BLOCK_LEN);
115+
116+
let tx_descriptor = &TX_DESCRIPTORS[0];
117+
118+
let packet = b"test RX packet";
119+
RX_BUFFER.write(packet.as_slice());
120+
121+
let buffer = unsafe { RX_BUFFER.as_ptr_mut() };
122+
tx_descriptor.update(DmaDescriptor {
123+
flags,
124+
buffer,
125+
next: core::ptr::null_mut(),
126+
});
127+
128+
let slc = unsafe { &*ctx.sdio.slc().register_block };
129+
130+
// configure SLC RX descriptors
131+
slc.slc1rx_link_addr()
132+
.write(|w| unsafe { w.bits(RX_DESCRIPTORS.address()) });
133+
slc.slc1rx_link()
134+
.modify(|_, w| w.sdio_slc1_rxlink_start().set_bit());
135+
136+
loop {
137+
core::hint::spin_loop()
138+
}
139+
}

qa-test/src/bin/sdio-spi-host.rs

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
//! SDIO SPI mode master driver example.
2+
3+
#![no_std]
4+
#![no_main]
5+
6+
use esp_backtrace as _;
7+
use esp_hal::{
8+
Blocking,
9+
sdio::{
10+
command::{Cmd0, Cmd5, Cmd52, Cmd53, FunctionNumber, RwFlag},
11+
response::{
12+
R1,
13+
spi::{R4, R5},
14+
},
15+
},
16+
spi::{Mode, master as spi},
17+
time::Rate,
18+
};
19+
use esp_println::println;
20+
21+
esp_bootloader_esp_idf::esp_app_desc!();
22+
23+
// Represents the byte length of a SDIO block.
24+
const BLOCK_LEN: usize = 512;
25+
26+
struct Context {
27+
spi: spi::Spi<'static, Blocking>,
28+
}
29+
30+
impl Context {
31+
/// Creates a new context for the SDIO SPI master controller.
32+
pub fn new() -> Self {
33+
let peripherals = esp_hal::init(esp_hal::Config::default());
34+
35+
let config = spi::Config::default()
36+
.with_mode(Mode::_2)
37+
.with_frequency(Rate::from_mhz(40));
38+
39+
cfg_if::cfg_if! {
40+
if #[cfg(feature = "esp32")] {
41+
// Create SPI master for mock SDIO host
42+
// HSPI config
43+
let spi = spi::Spi::new(
44+
peripherals.SPI2,
45+
config,
46+
)
47+
.unwrap()
48+
.with_sck(peripherals.GPIO14)
49+
.with_mosi(peripherals.GPIO13)
50+
.with_miso(peripherals.GPIO12)
51+
.with_cs(peripherals.GPIO15);
52+
53+
// Create SPI master for mock SDIO host
54+
// VSPI config
55+
//let spi = spi::Spi::new(
56+
// peripherals.SPI3,
57+
// spi::Config::default().with_frequency(Rate::from_mhz(40)),
58+
//)
59+
//.unwrap()
60+
//.with_sck(peripherals.GPIO18)
61+
//.with_mosi(peripherals.GPIO23)
62+
//.with_miso(peripherals.GPIO19)
63+
//.with_cs(peripherals.GPIO5);
64+
} else if #[cfg(feature = "esp32c6")] {
65+
// Create SPI master for mock SDIO host
66+
let spi = spi::Spi::new(
67+
peripherals.SPI2,
68+
config,
69+
)
70+
.unwrap()
71+
.with_sck(peripherals.GPIO6)
72+
.with_mosi(peripherals.GPIO7)
73+
.with_miso(peripherals.GPIO2)
74+
.with_cs(peripherals.GPIO16);
75+
} else {
76+
panic!("unsupported platform");
77+
}
78+
}
79+
80+
Self { spi }
81+
}
82+
}
83+
84+
#[esp_hal::main]
85+
fn main() -> ! {
86+
let mut ctx = Context::new();
87+
88+
ctx.spi
89+
.write(&[0u8; 10])
90+
.expect("error sending SPI init cycles");
91+
92+
let mut poll = true;
93+
94+
// continually send CMD0 until an IDLE R1 response
95+
while poll {
96+
let mut packet = Cmd0::new().into_bytes();
97+
ctx.spi
98+
.transfer(&mut packet)
99+
.expect("error writing SDIO from host");
100+
101+
// expect R1 response from SDIO device
102+
match R1::try_from(packet.as_ref()) {
103+
Ok(r1) if r1.idle() && !r1.is_err() => {
104+
println!("response: {r1}");
105+
poll = false;
106+
}
107+
_ => (),
108+
}
109+
}
110+
111+
println!("received R1 IDLE response ...");
112+
poll = true;
113+
114+
while poll {
115+
let mut packet = Cmd5::new().into_bytes();
116+
117+
// expect R4 response from SDIO device
118+
ctx.spi
119+
.transfer(&mut packet)
120+
.expect("error transfer CMD5 from SPI host");
121+
122+
match R4::try_from(packet.as_ref()) {
123+
Ok(r4) if r4.ready() && !r4.is_err() => {
124+
poll = false;
125+
println!("response: {r4:x?}");
126+
}
127+
Ok(r4) if r4.is_err() => {
128+
println!("error response: {}", r4.modified_r1());
129+
}
130+
_ => (),
131+
}
132+
}
133+
134+
println!("received R4 READY response ...");
135+
poll = true;
136+
137+
let mut packet = [0u8; Cmd53::LEN + BLOCK_LEN];
138+
139+
while poll {
140+
let mut cmd = Cmd53::new();
141+
142+
cmd.set_function_number(FunctionNumber::Io2);
143+
cmd.set_read_write_flag(RwFlag::ReadOnly);
144+
// Fixed-address transfer
145+
cmd.set_register_address(0x0);
146+
cmd.set_crc();
147+
148+
packet[..Cmd53::LEN].copy_from_slice(cmd.into_bytes().as_slice());
149+
150+
ctx.spi
151+
.transfer(&mut packet)
152+
.expect("error transfer CMD53 from SPI host");
153+
154+
match R5::try_from(packet.as_ref()) {
155+
Ok(r5) if r5.idle() && !r5.is_err() => {
156+
poll = false;
157+
158+
println!("response: {r5}");
159+
160+
let data = &packet[R5::LEN..];
161+
println!("data: {data:x?}");
162+
}
163+
Ok(r5) if r5.is_err() => {
164+
println!("error response: {r5}");
165+
}
166+
_ => (),
167+
}
168+
}
169+
170+
loop {
171+
core::hint::spin_loop()
172+
}
173+
}

0 commit comments

Comments
 (0)