Skip to content

Commit a294d64

Browse files
committed
Utilise the SPI FIFO in the Transfer and Write implementations
1 parent 76f626d commit a294d64

File tree

2 files changed

+72
-48
lines changed

2 files changed

+72
-48
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
* ethernet: `ethernet::DesRing` and `ethernet::EthernetDMA` require generic
1818
constants to specify how many transmit / receive buffers to include in
1919
`ethernet::DesRing`. To replicate the previous behaviour, use `DesRing<4, 4>`
20+
* spi: Utilise FIFO in `Transfer` and `Write` implementations
2021

2122
## [v0.10.0] 2021-07-xx
2223

src/spi.rs

Lines changed: 71 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,74 +1087,76 @@ macro_rules! spi {
10871087
}
10881088
}
10891089

1090-
impl hal::blocking::spi::Transfer<$TY> for Spi<$SPIX, Enabled, $TY> {
1091-
type Error = Error;
1092-
1093-
fn transfer<'w>(&mut self, words: &'w mut [$TY]) -> Result<&'w [$TY], Self::Error> {
1094-
use embedded_hal::spi::FullDuplex;
1095-
1096-
if words.is_empty() {
1097-
return Ok(words);
1090+
impl Spi<$SPIX, Enabled, $TY>
1091+
{
1092+
/// Internal implementation for blocking::spi::Transfer and
1093+
/// blocking::spi::Write
1094+
fn transfer_internal<'w>(&mut self,
1095+
write_words: &'w [$TY],
1096+
read_words: Option<&'w mut [$TY]>
1097+
) -> Result<(), Error> {
1098+
use hal::spi::FullDuplex;
1099+
1100+
// both buffers are the same length
1101+
if let Some(ref read) = read_words {
1102+
debug_assert!(write_words.len() == read.len());
1103+
}
1104+
if write_words.is_empty() {
1105+
return Ok(());
10981106
}
10991107

11001108
// Are we in frame mode?
11011109
if matches!(self.hardware_cs_mode, HardwareCSMode::FrameTransaction) {
11021110
const MAX_WORDS: usize = 0xFFFF;
11031111

11041112
// Can we send
1105-
if words.len() > MAX_WORDS {
1113+
if write_words.len() > MAX_WORDS {
11061114
return Err(Error::BufferTooBig { max_size: MAX_WORDS });
11071115
}
11081116

11091117
// Setup that we're going to send this amount of bits
1110-
// SAFETY: We already checked that `words` is not empty
1111-
self.setup_transaction(unsafe{ core::num::NonZeroU16::new_unchecked(words.len() as u16) })?;
1118+
// SAFETY: We already checked that `write_words` is not empty
1119+
self.setup_transaction(unsafe {
1120+
core::num::NonZeroU16::new_unchecked(write_words.len() as u16)
1121+
})?;
11121122
}
11131123

1114-
// Send the data
1115-
for word in words.iter_mut() {
1116-
nb::block!(self.send(word.clone()))?;
1117-
*word = nb::block!(self.read())?;
1118-
}
1124+
// Depth of FIFO to use. All current SPI implementations
1125+
// have a FIFO depth of at least 8 (see RM0433 Rev 7
1126+
// Tabel 409.) but pick 4 as a conservative value.
1127+
const FIFO_WORDS: usize = 4;
11191128

1120-
// Are we in frame mode?
1121-
if matches!(self.hardware_cs_mode, HardwareCSMode::FrameTransaction) {
1122-
// Clean up
1123-
self.end_transaction()?;
1129+
// Fill the first half of the write FIFO
1130+
let len = write_words.len();
1131+
let mut write = write_words.iter();
1132+
for _ in 0..core::cmp::min(FIFO_WORDS, len) {
1133+
nb::block!(self.send(*write.next().unwrap()))?;
11241134
}
11251135

1126-
Ok(words)
1127-
}
1128-
}
1136+
if let Some(read) = read_words {
1137+
let mut read = read.iter_mut();
11291138

1130-
impl hal::blocking::spi::Write<$TY> for Spi<$SPIX, Enabled, $TY> {
1131-
type Error = Error;
1132-
1133-
fn write(&mut self, words: &[$TY]) -> Result<(), Self::Error> {
1134-
use embedded_hal::spi::FullDuplex;
1135-
1136-
if words.is_empty() {
1137-
return Ok(());
1138-
}
1139-
1140-
// Are we in frame mode?
1141-
if matches!(self.hardware_cs_mode, HardwareCSMode::FrameTransaction) {
1142-
const MAX_WORDS: usize = 0xFFFF;
1143-
1144-
// Can we send
1145-
if words.len() > MAX_WORDS {
1146-
return Err(Error::BufferTooBig { max_size: MAX_WORDS });
1139+
// Continue filling write FIFO and emptying read FIFO
1140+
for word in write {
1141+
nb::block!(self.send(*word))?;
1142+
*read.next().unwrap() = nb::block!(self.read())?;
11471143
}
11481144

1149-
// Setup that we're going to send this amount of bits
1150-
// SAFETY: We already checked that `words` is not empty
1151-
self.setup_transaction(unsafe{ core::num::NonZeroU16::new_unchecked(words.len() as u16) })?;
1152-
}
1145+
// Finish emptying the read FIFO
1146+
for word in read {
1147+
*word = nb::block!(self.read())?;
1148+
}
1149+
} else {
1150+
// Continue filling write FIFO and emptying read FIFO
1151+
for word in write {
1152+
nb::block!(self.send(*word))?;
1153+
let _ = nb::block!(self.read())?;
1154+
}
11531155

1154-
// Send the data
1155-
for word in words {
1156-
nb::block!(self.send(word.clone()))?;
1157-
nb::block!(self.read())?;
1156+
// Dummy read from the read FIFO
1157+
for _ in 0..core::cmp::min(FIFO_WORDS, len) {
1158+
let _ = nb::block!(self.read())?;
1159+
}
11581160
}
11591161

11601162
// Are we in frame mode?
@@ -1166,6 +1168,27 @@ macro_rules! spi {
11661168
Ok(())
11671169
}
11681170
}
1171+
impl hal::blocking::spi::Transfer<$TY> for Spi<$SPIX, Enabled, $TY> {
1172+
type Error = Error;
1173+
1174+
fn transfer<'w>(&mut self, words: &'w mut [$TY]) -> Result<&'w [$TY], Self::Error> {
1175+
// SAFETY: transfer_internal always writes out bytes
1176+
// before modifying them
1177+
let write = unsafe {
1178+
core::slice::from_raw_parts(words.as_ptr(), words.len())
1179+
};
1180+
self.transfer_internal(write, Some(words))?;
1181+
1182+
Ok(words)
1183+
}
1184+
}
1185+
impl hal::blocking::spi::Write<$TY> for Spi<$SPIX, Enabled, $TY> {
1186+
type Error = Error;
1187+
1188+
fn write(&mut self, words: &[$TY]) -> Result<(), Self::Error> {
1189+
self.transfer_internal(words, None)
1190+
}
1191+
}
11691192
)+
11701193
)+
11711194
}

0 commit comments

Comments
 (0)