use core::marker::PhantomData;
use core::task::Poll;
use crate::iomuxc::{consts, lpspi};
use crate::ral;
pub use eh02::spi::{Mode, Phase, Polarity, MODE_0, MODE_1, MODE_2, MODE_3};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Direction {
Tx,
Rx,
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum BitOrder {
#[default]
Msb,
Lsb,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SamplePoint {
Edge,
DelayedEdge,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LpspiError {
FrameSize,
Fifo(Direction),
Busy,
NoData,
}
pub struct Transaction {
pub byte_swap: bool,
pub bit_order: BitOrder,
pub receive_data_mask: bool,
pub transmit_data_mask: bool,
pub continuous: bool,
pub continuing: bool,
frame_size: u16,
}
impl Transaction {
pub fn new_u32s(data: &[u32]) -> Result<Self, LpspiError> {
Transaction::new_words(data)
}
fn new_words<W>(data: &[W]) -> Result<Self, LpspiError> {
if let Ok(frame_size) = u16::try_from(8 * core::mem::size_of_val(data)) {
Transaction::new(frame_size)
} else {
Err(LpspiError::FrameSize)
}
}
fn frame_size_valid(frame_size: u16) -> bool {
const MIN_FRAME_SIZE: u16 = 8;
const MAX_FRAME_SIZE: u16 = 1 << 12;
const WORD_SIZE: u16 = 32;
let last_frame_size = frame_size % WORD_SIZE;
(MIN_FRAME_SIZE..=MAX_FRAME_SIZE).contains(&frame_size) && (1 != last_frame_size)
}
pub fn new(frame_size: u16) -> Result<Self, LpspiError> {
if Self::frame_size_valid(frame_size) {
Ok(Self {
byte_swap: false,
bit_order: Default::default(),
receive_data_mask: false,
transmit_data_mask: false,
frame_size: frame_size - 1,
continuing: false,
continuous: false,
})
} else {
Err(LpspiError::FrameSize)
}
}
}
fn set_spi_clock(source_clock_hz: u32, spi_clock_hz: u32, reg: &ral::lpspi::RegisterBlock) {
let half_div =
u32::try_from(1 + u64::from(source_clock_hz - 1) / (u64::from(spi_clock_hz) * 2)).unwrap();
let half_div = half_div.clamp(3, 128);
let sckdiv = 2 * (half_div - 1);
ral::write_reg!(ral::lpspi, reg, CCR,
DBT: half_div - 1,
PCSSCK: half_div - 1,
SCKPCS: half_div - 1,
SCKDIV: sckdiv
);
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct ClockConfigs {
pub sckpcs: u8,
pub pcssck: u8,
pub dbt: u8,
pub sckdiv: u8,
}
pub struct Lpspi<P, const N: u8> {
lpspi: ral::lpspi::Instance<N>,
pins: P,
bit_order: BitOrder,
mode: Mode,
}
pub struct Pins<SDO, SDI, SCK, PCS0> {
pub sdo: SDO,
pub sdi: SDI,
pub sck: SCK,
pub pcs0: PCS0,
}
impl<SDO, SDI, SCK, PCS0, const N: u8> Lpspi<Pins<SDO, SDI, SCK, PCS0>, N>
where
SDO: lpspi::Pin<Module = consts::Const<N>, Signal = lpspi::Sdo>,
SDI: lpspi::Pin<Module = consts::Const<N>, Signal = lpspi::Sdi>,
SCK: lpspi::Pin<Module = consts::Const<N>, Signal = lpspi::Sck>,
PCS0: lpspi::Pin<Module = consts::Const<N>, Signal = lpspi::Pcs0>,
{
pub fn new(lpspi: ral::lpspi::Instance<N>, mut pins: Pins<SDO, SDI, SCK, PCS0>) -> Self {
lpspi::prepare(&mut pins.sdo);
lpspi::prepare(&mut pins.sdi);
lpspi::prepare(&mut pins.sck);
lpspi::prepare(&mut pins.pcs0);
Self::init(lpspi, pins)
}
}
impl<const N: u8> Lpspi<(), N> {
pub fn without_pins(lpspi: ral::lpspi::Instance<N>) -> Self {
Self::init(lpspi, ())
}
}
impl<P, const N: u8> Lpspi<P, N> {
pub const N: u8 = N;
fn init(lpspi: ral::lpspi::Instance<N>, pins: P) -> Self {
let spi = Lpspi {
lpspi,
pins,
bit_order: BitOrder::default(),
mode: MODE_0,
};
ral::modify_reg!(ral::lpspi, spi.lpspi, CR, MEN: MEN_0, RST: RST_1);
while spi.is_enabled() {}
ral::modify_reg!(ral::lpspi, spi.lpspi, CR, RST: RST_0);
ral::modify_reg!(ral::lpspi, spi.lpspi, CR, RTF: RTF_1, RRF: RRF_1);
ral::write_reg!(
ral::lpspi,
spi.lpspi,
CFGR1,
MASTER: MASTER_1,
SAMPLE: SAMPLE_1
);
let tx_fifo_size = spi.max_watermark(Direction::Tx);
ral::write_reg!(ral::lpspi, spi.lpspi, FCR,
RXWATER: 0, TXWATER: u32::from(tx_fifo_size) / 2 );
ral::write_reg!(ral::lpspi, spi.lpspi, CR, MEN: MEN_1);
spi
}
pub fn is_enabled(&self) -> bool {
ral::read_reg!(ral::lpspi, self.lpspi, CR, MEN == MEN_1)
}
pub fn set_enable(&mut self, enable: bool) {
ral::modify_reg!(ral::lpspi, self.lpspi, CR, MEN: enable as u32)
}
pub fn reset(&mut self) {
ral::modify_reg!(ral::lpspi, self.lpspi, CR, RST: RST_1);
while ral::read_reg!(ral::lpspi, self.lpspi, CR, RST == RST_1) {
ral::modify_reg!(ral::lpspi, self.lpspi, CR, RST: RST_0);
}
}
pub fn release(self) -> (ral::lpspi::Instance<N>, P) {
(self.lpspi, self.pins)
}
pub fn bit_order(&self) -> BitOrder {
self.bit_order
}
pub fn set_bit_order(&mut self, bit_order: BitOrder) {
self.bit_order = bit_order;
}
pub fn disabled<R>(&mut self, func: impl FnOnce(&mut Disabled<N>) -> R) -> R {
let mut disabled = Disabled::new(&mut self.lpspi, &mut self.mode);
func(&mut disabled)
}
pub fn status(&self) -> Status {
Status::from_bits_truncate(ral::read_reg!(ral::lpspi, self.lpspi, SR))
}
pub fn clear_status(&self, flags: Status) {
let flags = flags & Status::W1C;
ral::write_reg!(ral::lpspi, self.lpspi, SR, flags.bits());
}
pub fn interrupts(&self) -> Interrupts {
Interrupts::from_bits_truncate(ral::read_reg!(ral::lpspi, self.lpspi, IER))
}
pub fn set_interrupts(&self, interrupts: Interrupts) {
ral::write_reg!(ral::lpspi, self.lpspi, IER, interrupts.bits());
}
#[inline]
pub fn clear_fifo(&mut self, direction: Direction) {
match direction {
Direction::Tx => ral::modify_reg!(ral::lpspi, self.lpspi, CR, RTF: RTF_1),
Direction::Rx => ral::modify_reg!(ral::lpspi, self.lpspi, CR, RRF: RRF_1),
}
}
pub fn clear_fifos(&mut self) {
ral::modify_reg!(ral::lpspi, self.lpspi, CR, RTF: RTF_1, RRF: RRF_1);
}
#[inline]
pub fn watermark(&self, direction: Direction) -> u8 {
(match direction {
Direction::Rx => ral::read_reg!(ral::lpspi, self.lpspi, FCR, RXWATER),
Direction::Tx => ral::read_reg!(ral::lpspi, self.lpspi, FCR, TXWATER),
}) as u8
}
#[inline]
pub fn fifo_status(&self) -> FifoStatus {
let (rxcount, txcount) = ral::read_reg!(ral::lpspi, self.lpspi, FSR, RXCOUNT, TXCOUNT);
FifoStatus {
rxcount: rxcount as u16,
txcount: txcount as u16,
}
}
fn read_data_unchecked(&self) -> u32 {
ral::read_reg!(ral::lpspi, self.lpspi, RDR)
}
pub fn read_data(&mut self) -> Option<u32> {
if ral::read_reg!(ral::lpspi, self.lpspi, RSR, RXEMPTY == RXEMPTY_0) {
Some(self.read_data_unchecked())
} else {
None
}
}
pub fn enqueue_data(&self, word: u32) {
ral::write_reg!(ral::lpspi, self.lpspi, TDR, word);
}
pub(crate) async fn spin_for_fifo_space(&self) -> Result<(), LpspiError> {
core::future::poll_fn(|_| {
let status = self.status();
if status.intersects(Status::TRANSMIT_ERROR) {
return Poll::Ready(Err(LpspiError::Fifo(Direction::Tx)));
}
let fifo_status = self.fifo_status();
if !fifo_status.is_full(Direction::Tx) {
Poll::Ready(Ok(()))
} else {
Poll::Pending
}
})
.await
}
pub(crate) fn wait_for_transmit_fifo_space(&self) -> Result<(), LpspiError> {
crate::spin_on(self.spin_for_fifo_space())
}
async fn spin_for_word(&self) -> Result<u32, LpspiError> {
core::future::poll_fn(|_| {
let status = self.status();
if status.intersects(Status::RECEIVE_ERROR) {
return Poll::Ready(Err(LpspiError::Fifo(Direction::Rx)));
}
let fifo_status = self.fifo_status();
if !fifo_status.is_empty(Direction::Rx) {
let data = self.read_data_unchecked();
Poll::Ready(Ok(data))
} else {
Poll::Pending
}
})
.await
}
async fn spin_transmit(
&self,
mut data: impl TransmitData,
len: usize,
) -> Result<(), LpspiError> {
for _ in 0..len {
self.spin_for_fifo_space().await?;
let word = data.next_word(self.bit_order);
self.enqueue_data(word);
}
Ok(())
}
async fn spin_receive(&self, mut data: impl ReceiveData, len: usize) -> Result<(), LpspiError> {
for _ in 0..len {
let word = self.spin_for_word().await?;
data.next_word(self.bit_order, word);
}
Ok(())
}
pub fn set_mode(&mut self, mode: Mode) {
self.mode = mode;
}
pub fn enqueue_transaction(&mut self, transaction: &Transaction) {
ral::write_reg!(ral::lpspi, self.lpspi, TCR,
CPOL: if self.mode.polarity == Polarity::IdleHigh { CPOL_1 } else { CPOL_0 },
CPHA: if self.mode.phase == Phase::CaptureOnSecondTransition { CPHA_1 } else { CPHA_0 },
PRESCALE: PRESCALE_0,
PCS: PCS_0,
WIDTH: WIDTH_0,
LSBF: transaction.bit_order as u32,
BYSW: transaction.byte_swap as u32,
RXMSK: transaction.receive_data_mask as u32,
TXMSK: transaction.transmit_data_mask as u32,
FRAMESZ: transaction.frame_size as u32,
CONT: transaction.continuous as u32,
CONTC: transaction.continuing as u32
);
}
pub fn flush(&mut self) -> Result<(), LpspiError> {
loop {
let status = self.status();
if status.intersects(Status::RECEIVE_ERROR) {
return Err(LpspiError::Fifo(Direction::Rx));
}
if status.intersects(Status::TRANSMIT_ERROR) {
return Err(LpspiError::Fifo(Direction::Tx));
}
if !status.intersects(Status::BUSY) && self.fifo_status().is_empty(Direction::Tx) {
return Ok(());
}
}
}
fn exchange<W: Word>(&mut self, data: &mut [W]) -> Result<(), LpspiError> {
if data.is_empty() {
return Ok(());
}
let mut transaction = Transaction::new_words(data)?;
transaction.bit_order = self.bit_order();
self.wait_for_transmit_fifo_space()?;
self.enqueue_transaction(&transaction);
let word_count = word_count(data);
let (tx, rx) = transfer_in_place(data);
crate::spin_on(futures::future::try_join(
self.spin_transmit(tx, word_count),
self.spin_receive(rx, word_count),
))
.inspect_err(|_| self.recover_from_error())?;
self.flush()?;
Ok(())
}
fn write_no_read<W: Word>(&mut self, data: &[W]) -> Result<(), LpspiError> {
if data.is_empty() {
return Ok(());
}
let mut transaction = Transaction::new_words(data)?;
transaction.receive_data_mask = true;
transaction.bit_order = self.bit_order();
self.wait_for_transmit_fifo_space()?;
self.enqueue_transaction(&transaction);
let word_count = word_count(data);
let tx = TransmitBuffer::new(data);
crate::spin_on(self.spin_transmit(tx, word_count)).inspect_err(|_| {
self.recover_from_error();
})?;
self.flush()?;
Ok(())
}
pub fn enable_dma_receive(&mut self) {
ral::modify_reg!(ral::lpspi, self.lpspi, FCR, RXWATER: 0); ral::modify_reg!(ral::lpspi, self.lpspi, DER, RDDE: 1);
}
pub fn disable_dma_receive(&mut self) {
while ral::read_reg!(ral::lpspi, self.lpspi, DER, RDDE == 1) {
ral::modify_reg!(ral::lpspi, self.lpspi, DER, RDDE: 0);
}
}
pub fn enable_dma_transmit(&mut self) {
ral::modify_reg!(ral::lpspi, self.lpspi, FCR, TXWATER: 0); ral::modify_reg!(ral::lpspi, self.lpspi, DER, TDDE: 1);
}
pub fn disable_dma_transmit(&mut self) {
while ral::read_reg!(ral::lpspi, self.lpspi, DER, TDDE == 1) {
ral::modify_reg!(ral::lpspi, self.lpspi, DER, TDDE: 0);
}
}
pub fn rdr(&self) -> *const ral::RORegister<u32> {
core::ptr::addr_of!(self.lpspi.RDR)
}
pub fn tdr(&self) -> *const ral::WORegister<u32> {
core::ptr::addr_of!(self.lpspi.TDR)
}
fn max_watermark(&self, direction: Direction) -> u8 {
(match direction {
Direction::Rx => 1 << ral::read_reg!(ral::lpspi, self.lpspi, PARAM, RXFIFO),
Direction::Tx => 1 << ral::read_reg!(ral::lpspi, self.lpspi, PARAM, TXFIFO),
}) as u8
}
pub fn soft_reset(&mut self) {
let ier = ral::read_reg!(ral::lpspi, self.lpspi, IER);
let der = ral::read_reg!(ral::lpspi, self.lpspi, DER);
let cfgr0 = ral::read_reg!(ral::lpspi, self.lpspi, CFGR0);
let cfgr1 = ral::read_reg!(ral::lpspi, self.lpspi, CFGR1);
let dmr0 = ral::read_reg!(ral::lpspi, self.lpspi, DMR0);
let dmr1 = ral::read_reg!(ral::lpspi, self.lpspi, DMR1);
let ccr = ral::read_reg!(ral::lpspi, self.lpspi, CCR);
let fcr = ral::read_reg!(ral::lpspi, self.lpspi, FCR);
let enabled = self.is_enabled();
ral::modify_reg!(ral::lpspi, self.lpspi, CR, MEN: MEN_0, RST: RST_1);
while self.is_enabled() {}
ral::modify_reg!(ral::lpspi, self.lpspi, CR, RST: RST_0);
ral::modify_reg!(ral::lpspi, self.lpspi, CR, RTF: RTF_1, RRF: RRF_1);
ral::write_reg!(ral::lpspi, self.lpspi, IER, ier);
ral::write_reg!(ral::lpspi, self.lpspi, DER, der);
ral::write_reg!(ral::lpspi, self.lpspi, CFGR0, cfgr0);
ral::write_reg!(ral::lpspi, self.lpspi, CFGR1, cfgr1);
ral::write_reg!(ral::lpspi, self.lpspi, DMR0, dmr0);
ral::write_reg!(ral::lpspi, self.lpspi, DMR1, dmr1);
ral::write_reg!(ral::lpspi, self.lpspi, CCR, ccr);
ral::write_reg!(ral::lpspi, self.lpspi, FCR, fcr);
self.set_enable(enabled);
}
#[inline]
pub fn set_watermark(&mut self, direction: Direction, watermark: u8) -> u8 {
set_watermark(&self.lpspi, direction, watermark)
}
fn recover_from_error(&mut self) {
self.soft_reset();
self.clear_status(Status::TRANSMIT_ERROR | Status::RECEIVE_ERROR);
}
pub fn clock_configs(&self) -> ClockConfigs {
let (sckpcs, pcssck, dbt, sckdiv) =
ral::read_reg!(ral::lpspi, self.lpspi, CCR, SCKPCS, PCSSCK, DBT, SCKDIV);
ClockConfigs {
sckpcs: sckpcs as u8,
pcssck: pcssck as u8,
dbt: dbt as u8,
sckdiv: sckdiv as u8,
}
}
}
bitflags::bitflags! {
pub struct Status : u32 {
const BUSY = 1 << 24;
const DATA_MATCH = 1 << 13;
const RECEIVE_ERROR = 1 << 12;
const TRANSMIT_ERROR = 1 << 11;
const TRANSFER_COMPLETE = 1 << 10;
const FRAME_COMPLETE = 1 << 9;
const WORD_COMPLETE = 1 << 8;
const RECEIVE_DATA = 1 << 1;
const TRANSMIT_DATA = 1 << 0;
}
}
impl Status {
const W1C: Self = Self::from_bits_truncate(
Self::DATA_MATCH.bits()
| Self::RECEIVE_ERROR.bits()
| Self::TRANSMIT_ERROR.bits()
| Self::TRANSFER_COMPLETE.bits()
| Self::FRAME_COMPLETE.bits()
| Self::WORD_COMPLETE.bits(),
);
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FifoStatus {
pub rxcount: u16,
pub txcount: u16,
}
impl FifoStatus {
#[inline]
pub const fn is_full(self, direction: Direction) -> bool {
const MAX_FIFO_SIZE: u16 = 16;
let count = match direction {
Direction::Tx => self.txcount,
Direction::Rx => self.rxcount,
};
count >= MAX_FIFO_SIZE
}
#[inline]
const fn is_empty(self, direction: Direction) -> bool {
0 == match direction {
Direction::Tx => self.txcount,
Direction::Rx => self.rxcount,
}
}
}
bitflags::bitflags! {
pub struct Interrupts : u32 {
const DATA_MATCH = 1 << 13;
const RECEIVE_ERROR = 1 << 12;
const TRANSMIT_ERROR = 1 << 11;
const TRANSMIT_COMPLETE = 1 << 10;
const FRAME_COMPLETE = 1 << 9;
const WORD_COMPLETE = 1 << 8;
const RECEIVE_DATA = 1 << 1;
const TRANSMIT_DATA = 1 << 0;
}
}
#[inline]
fn set_watermark(lpspi: &ral::lpspi::RegisterBlock, direction: Direction, watermark: u8) -> u8 {
let max_watermark = match direction {
Direction::Rx => 1 << ral::read_reg!(ral::lpspi, lpspi, PARAM, RXFIFO),
Direction::Tx => 1 << ral::read_reg!(ral::lpspi, lpspi, PARAM, TXFIFO),
};
let watermark = watermark.min(max_watermark - 1);
match direction {
Direction::Rx => {
ral::modify_reg!(ral::lpspi, lpspi, FCR, RXWATER: watermark as u32)
}
Direction::Tx => {
ral::modify_reg!(ral::lpspi, lpspi, FCR, TXWATER: watermark as u32)
}
}
watermark
}
pub struct Disabled<'a, const N: u8> {
lpspi: &'a ral::lpspi::Instance<N>,
mode: &'a mut Mode,
men: bool,
}
impl<'a, const N: u8> Disabled<'a, N> {
fn new(lpspi: &'a mut ral::lpspi::Instance<N>, mode: &'a mut Mode) -> Self {
let men = ral::read_reg!(ral::lpspi, lpspi, CR, MEN == MEN_1);
ral::modify_reg!(ral::lpspi, lpspi, CR, MEN: MEN_0);
while ral::read_reg!(ral::lpspi, lpspi, CR, MEN == MEN_1) {}
Self { lpspi, mode, men }
}
#[deprecated(
since = "0.5.5",
note = "Use Lpspi::set_mode to change modes while enabled."
)]
pub fn set_mode(&mut self, mode: Mode) {
*self.mode = mode;
}
pub fn set_clock_hz(&mut self, source_clock_hz: u32, clock_hz: u32) {
set_spi_clock(source_clock_hz, clock_hz, self.lpspi);
}
pub fn set_clock_configs(&mut self, timing: ClockConfigs) {
ral::write_reg!(ral::lpspi, self.lpspi, CCR,
SCKPCS: timing.sckpcs as u32,
PCSSCK: timing.pcssck as u32,
DBT: timing.dbt as u32,
SCKDIV: timing.sckdiv as u32,
);
}
#[inline]
#[deprecated(
since = "0.5.5",
note = "Use Lpspi::set_watermark to change watermark while enabled"
)]
pub fn set_watermark(&mut self, direction: Direction, watermark: u8) -> u8 {
set_watermark(self.lpspi, direction, watermark)
}
#[inline]
pub fn set_sample_point(&mut self, sample_point: SamplePoint) {
match sample_point {
SamplePoint::Edge => ral::modify_reg!(ral::lpspi, self.lpspi, CFGR1, SAMPLE: SAMPLE_0),
SamplePoint::DelayedEdge => {
ral::modify_reg!(ral::lpspi, self.lpspi, CFGR1, SAMPLE: SAMPLE_1)
}
}
}
#[inline]
pub fn set_peripheral_enable(&mut self, enable: bool) {
ral::modify_reg!(ral::lpspi, self.lpspi, CFGR1, MASTER: !enable as u32);
}
}
impl<const N: u8> Drop for Disabled<'_, N> {
fn drop(&mut self) {
ral::modify_reg!(ral::lpspi, self.lpspi, CR, MEN: self.men as u32);
}
}
impl<P, const N: u8> eh02::blocking::spi::Transfer<u8> for Lpspi<P, N> {
type Error = LpspiError;
fn transfer<'a>(&mut self, words: &'a mut [u8]) -> Result<&'a [u8], Self::Error> {
self.exchange(words)?;
Ok(words)
}
}
impl<P, const N: u8> eh02::blocking::spi::Transfer<u16> for Lpspi<P, N> {
type Error = LpspiError;
fn transfer<'a>(&mut self, words: &'a mut [u16]) -> Result<&'a [u16], Self::Error> {
self.exchange(words)?;
Ok(words)
}
}
impl<P, const N: u8> eh02::blocking::spi::Transfer<u32> for Lpspi<P, N> {
type Error = LpspiError;
fn transfer<'a>(&mut self, words: &'a mut [u32]) -> Result<&'a [u32], Self::Error> {
self.exchange(words)?;
Ok(words)
}
}
impl<P, const N: u8> eh02::blocking::spi::Write<u8> for Lpspi<P, N> {
type Error = LpspiError;
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
self.write_no_read(words)
}
}
impl<P, const N: u8> eh02::blocking::spi::Write<u16> for Lpspi<P, N> {
type Error = LpspiError;
fn write(&mut self, words: &[u16]) -> Result<(), Self::Error> {
self.write_no_read(words)
}
}
impl<P, const N: u8> eh02::blocking::spi::Write<u32> for Lpspi<P, N> {
type Error = LpspiError;
fn write(&mut self, words: &[u32]) -> Result<(), Self::Error> {
self.write_no_read(words)
}
}
trait Word: Copy + Into<u32> + TryFrom<u32> {
fn pack_word(bit_order: BitOrder, provider: impl FnMut() -> Option<Self>) -> u32;
fn unpack_word(word: u32, bit_order: BitOrder, valid_bytes: usize, sink: impl FnMut(Self));
}
impl Word for u8 {
fn pack_word(bit_order: BitOrder, mut provider: impl FnMut() -> Option<Self>) -> u32 {
let mut word = 0;
match bit_order {
BitOrder::Msb => {
for _ in 0..4 {
if let Some(byte) = provider() {
word <<= 8;
word |= u32::from(byte);
}
}
}
BitOrder::Lsb => {
for offset in 0..4 {
if let Some(byte) = provider() {
word |= u32::from(byte) << (8 * offset);
}
}
}
}
word
}
fn unpack_word(word: u32, bit_order: BitOrder, valid_bytes: usize, mut sink: impl FnMut(Self)) {
let mut offsets = [0usize, 8, 16, 24];
let valid = &mut offsets[..valid_bytes];
if matches!(bit_order, BitOrder::Msb) {
valid.reverse();
}
for offset in valid {
sink((word >> *offset) as u8);
}
}
}
impl Word for u16 {
fn pack_word(bit_order: BitOrder, mut provider: impl FnMut() -> Option<Self>) -> u32 {
let mut word = 0;
match bit_order {
BitOrder::Msb => {
for _ in 0..2 {
if let Some(half) = provider() {
word <<= 16;
word |= u32::from(half);
}
}
}
BitOrder::Lsb => {
for offset in 0..2 {
if let Some(half) = provider() {
word |= u32::from(half) << (16 * offset);
}
}
}
}
word
}
fn unpack_word(word: u32, bit_order: BitOrder, valid_bytes: usize, mut sink: impl FnMut(Self)) {
let mut offsets = [0usize, 16];
let valid = &mut offsets[..valid_bytes / 2];
if matches!(bit_order, BitOrder::Msb) {
valid.reverse();
}
for offset in valid {
sink((word >> *offset) as u16);
}
}
}
impl Word for u32 {
fn pack_word(_: BitOrder, mut provider: impl FnMut() -> Option<Self>) -> u32 {
provider().unwrap_or(0)
}
fn unpack_word(word: u32, _: BitOrder, _: usize, mut sink: impl FnMut(Self)) {
sink(word)
}
}
trait TransmitData {
fn next_word(&mut self, bit_order: BitOrder) -> u32;
}
trait ReceiveData {
fn next_word(&mut self, bit_order: BitOrder, word: u32);
}
struct TransmitBuffer<'a, W> {
ptr: *const W,
end: *const W,
_buffer: PhantomData<&'a [W]>,
}
impl<'a, W> TransmitBuffer<'a, W>
where
W: Word,
{
fn new(buffer: &'a [W]) -> Self {
unsafe { Self::from_raw(buffer.as_ptr(), buffer.len()) }
}
unsafe fn from_raw(ptr: *const W, len: usize) -> Self {
Self {
ptr,
end: unsafe { ptr.add(len) },
_buffer: PhantomData,
}
}
fn next_read(&mut self) -> Option<W> {
unsafe {
(!core::ptr::eq(self.ptr, self.end)).then(|| {
let word = self.ptr.read();
self.ptr = self.ptr.add(1);
word
})
}
}
}
impl<W> TransmitData for TransmitBuffer<'_, W>
where
W: Word,
{
fn next_word(&mut self, bit_order: BitOrder) -> u32 {
W::pack_word(bit_order, || self.next_read())
}
}
struct ReceiveBuffer<'a, W> {
ptr: *mut W,
end: *const W,
_buffer: PhantomData<&'a [W]>,
}
impl<W> ReceiveBuffer<'_, W>
where
W: Word,
{
#[cfg(test)] fn new(buffer: &mut [W]) -> Self {
unsafe { Self::from_raw(buffer.as_mut_ptr(), buffer.len()) }
}
unsafe fn from_raw(ptr: *mut W, len: usize) -> Self {
Self {
ptr,
end: unsafe { ptr.cast_const().add(len) },
_buffer: PhantomData,
}
}
fn next_write(&mut self, elem: W) {
unsafe {
if !core::ptr::eq(self.ptr.cast_const(), self.end) {
self.ptr.write(elem);
self.ptr = self.ptr.add(1);
}
}
}
fn array_len(&self) -> usize {
unsafe { self.end.byte_offset_from(self.ptr) as _ }
}
}
impl<W> ReceiveData for ReceiveBuffer<'_, W>
where
W: Word,
{
fn next_word(&mut self, bit_order: BitOrder, word: u32) {
let valid_bytes = self.array_len().min(size_of_val(&word));
W::unpack_word(word, bit_order, valid_bytes, |elem| self.next_write(elem));
}
}
const fn per_word<W: Word>() -> usize {
core::mem::size_of::<u32>() / core::mem::size_of::<W>()
}
const fn word_count<W: Word>(words: &[W]) -> usize {
words.len().div_ceil(per_word::<W>())
}
fn transfer_in_place<W: Word>(buffer: &mut [W]) -> (TransmitBuffer<'_, W>, ReceiveBuffer<'_, W>) {
unsafe {
let len = buffer.len();
let ptr = buffer.as_mut_ptr();
(
TransmitBuffer::from_raw(ptr, len),
ReceiveBuffer::from_raw(ptr, len),
)
}
}
#[cfg(test)]
mod tests {
#[test]
fn transfer_in_place_interleaved_read_write_u32() {
const BUFFER: [u32; 9] = [42u32, 43, 44, 45, 46, 47, 48, 49, 50];
let mut buffer = BUFFER;
let (mut tx, mut rx) = super::transfer_in_place(&mut buffer);
for elem in BUFFER {
assert_eq!(elem, tx.next_read().unwrap());
rx.next_write(elem + 1);
}
assert_eq!(buffer, [43, 44, 45, 46, 47, 48, 49, 50, 51]);
}
#[test]
fn transfer_in_place_interleaved_write_read_u32() {
const BUFFER: [u32; 9] = [42u32, 43, 44, 45, 46, 47, 48, 49, 50];
let mut buffer = BUFFER;
let (mut tx, mut rx) = super::transfer_in_place(&mut buffer);
for elem in BUFFER {
rx.next_write(elem + 1);
assert_eq!(elem + 1, tx.next_read().unwrap());
}
assert_eq!(buffer, [43, 44, 45, 46, 47, 48, 49, 50, 51]);
}
#[test]
fn transfer_in_place_bulk_read_write_u32() {
const BUFFER: [u32; 9] = [42u32, 43, 44, 45, 46, 47, 48, 49, 50];
let mut buffer = BUFFER;
let (mut tx, mut rx) = super::transfer_in_place(&mut buffer);
for elem in BUFFER {
assert_eq!(elem, tx.next_read().unwrap());
}
for elem in BUFFER {
rx.next_write(elem + 1);
}
assert_eq!(buffer, [43, 44, 45, 46, 47, 48, 49, 50, 51]);
}
#[test]
fn transfer_in_place_bulk_write_read_u32() {
const BUFFER: [u32; 9] = [42u32, 43, 44, 45, 46, 47, 48, 49, 50];
let mut buffer = BUFFER;
let (mut tx, mut rx) = super::transfer_in_place(&mut buffer);
for elem in BUFFER {
rx.next_write(elem + 1);
}
for elem in BUFFER {
assert_eq!(elem + 1, tx.next_read().unwrap());
}
assert_eq!(buffer, [43, 44, 45, 46, 47, 48, 49, 50, 51]);
}
#[test]
fn transmit_buffer() {
use super::{BitOrder::*, TransmitBuffer, TransmitData};
let mut tx = TransmitBuffer::new(&[0xDEADBEEFu32, 0xAD1CAC1D]);
assert_eq!(tx.next_word(Msb), 0xDEADBEEF);
assert_eq!(tx.next_word(Msb), 0xAD1CAC1D);
assert_eq!(tx.next_word(Msb), 0);
let mut tx = TransmitBuffer::new(&[0xDEADBEEFu32, 0xAD1CAC1D]);
assert_eq!(tx.next_word(Lsb), 0xDEADBEEF);
assert_eq!(tx.next_word(Lsb), 0xAD1CAC1D);
assert_eq!(tx.next_word(Lsb), 0);
let mut tx = TransmitBuffer::new(&[0xDEu8, 0xAD, 0xBE, 0xEF, 0xA5, 0x00, 0x1D]);
assert_eq!(tx.next_word(Msb), 0xDEADBEEF);
assert_eq!(tx.next_word(Msb), 0x00A5001D);
assert_eq!(tx.next_word(Msb), 0);
assert_eq!(tx.next_word(Msb), 0);
let mut tx = TransmitBuffer::new(&[0xDEu8, 0xAD, 0xBE, 0xEF, 0xA5, 0x00, 0x1D]);
assert_eq!(tx.next_word(Lsb), 0xEFBEADDE);
assert_eq!(tx.next_word(Lsb), 0x001D00A5);
assert_eq!(tx.next_word(Lsb), 0);
assert_eq!(tx.next_word(Lsb), 0);
let mut tx = TransmitBuffer::new(&[0xDEu8, 0xAD, 0xBE, 0xEF]);
assert_eq!(tx.next_word(Msb), 0xDEADBEEF);
assert_eq!(tx.next_word(Msb), 0);
assert_eq!(tx.next_word(Msb), 0);
let mut tx = TransmitBuffer::new(&[0xDEu8, 0xAD, 0xBE, 0xEF]);
assert_eq!(tx.next_word(Lsb), 0xEFBEADDE);
assert_eq!(tx.next_word(Lsb), 0);
assert_eq!(tx.next_word(Lsb), 0);
let mut tx = TransmitBuffer::new(&[0xDEu8, 0xAD, 0xBE]);
assert_eq!(tx.next_word(Msb), 0x00DEADBE);
assert_eq!(tx.next_word(Msb), 0);
assert_eq!(tx.next_word(Msb), 0);
let mut tx = TransmitBuffer::new(&[0xDEu8, 0xAD, 0xBE]);
assert_eq!(tx.next_word(Lsb), 0x00BEADDE);
assert_eq!(tx.next_word(Lsb), 0);
assert_eq!(tx.next_word(Lsb), 0);
let mut tx = TransmitBuffer::new(&[0xDEADu16, 0xBEEF, 0xA5A5]);
assert_eq!(tx.next_word(Msb), 0xDEADBEEF);
assert_eq!(tx.next_word(Msb), 0x0000A5A5);
assert_eq!(tx.next_word(Msb), 0);
assert_eq!(tx.next_word(Msb), 0);
let mut tx = TransmitBuffer::new(&[0xDEADu16, 0xBEEF, 0xA5A5]);
assert_eq!(tx.next_word(Lsb), 0xBEEFDEAD);
assert_eq!(tx.next_word(Lsb), 0x0000A5A5);
assert_eq!(tx.next_word(Lsb), 0);
assert_eq!(tx.next_word(Lsb), 0);
let mut tx = TransmitBuffer::new(&[0xDEADu16, 0xBEEF]);
assert_eq!(tx.next_word(Msb), 0xDEADBEEF);
assert_eq!(tx.next_word(Msb), 0);
assert_eq!(tx.next_word(Msb), 0);
let mut tx = TransmitBuffer::new(&[0xDEADu16, 0xBEEF]);
assert_eq!(tx.next_word(Lsb), 0xBEEFDEAD);
assert_eq!(tx.next_word(Lsb), 0);
assert_eq!(tx.next_word(Lsb), 0);
let mut tx = TransmitBuffer::new(&[0xDEADu16]);
assert_eq!(tx.next_word(Msb), 0x0000DEAD);
assert_eq!(tx.next_word(Msb), 0);
assert_eq!(tx.next_word(Msb), 0);
let mut tx = TransmitBuffer::new(&[0xDEADu16]);
assert_eq!(tx.next_word(Lsb), 0x0000DEAD);
assert_eq!(tx.next_word(Lsb), 0);
assert_eq!(tx.next_word(Lsb), 0);
}
#[test]
fn receive_buffer() {
use super::{BitOrder::*, ReceiveBuffer, ReceiveData};
let mut buffer = [0u8; 9];
let mut rx = ReceiveBuffer::new(&mut buffer);
rx.next_word(Msb, 0xDEADBEEF);
rx.next_word(Msb, 0xAD1CAC1D);
rx.next_word(Msb, 0x04030201);
rx.next_word(Msb, 0x55555555);
assert_eq!(
buffer,
[0xDE, 0xAD, 0xBE, 0xEF, 0xAD, 0x1C, 0xAC, 0x1D, 0x01]
);
let mut buffer = [0u8; 9];
let mut rx = ReceiveBuffer::new(&mut buffer);
rx.next_word(Lsb, 0xDEADBEEF);
rx.next_word(Lsb, 0xAD1CAC1D);
rx.next_word(Lsb, 0x04030201);
rx.next_word(Lsb, 0x55555555);
assert_eq!(
buffer,
[0xEF, 0xBE, 0xAD, 0xDE, 0x1D, 0xAC, 0x1C, 0xAD, 0x01]
);
let mut buffer = [0u16; 5];
let mut rx = ReceiveBuffer::new(&mut buffer);
rx.next_word(Msb, 0xDEADBEEF);
rx.next_word(Msb, 0xAD1CAC1D);
rx.next_word(Msb, 0x04030201);
rx.next_word(Msb, 0x55555555);
assert_eq!(buffer, [0xDEAD, 0xBEEF, 0xAD1C, 0xAC1D, 0x0201]);
let mut buffer = [0u16; 5];
let mut rx = ReceiveBuffer::new(&mut buffer);
rx.next_word(Lsb, 0xDEADBEEF);
rx.next_word(Lsb, 0xAD1CAC1D);
rx.next_word(Lsb, 0x04030201);
rx.next_word(Lsb, 0x55555555);
assert_eq!(buffer, [0xBEEF, 0xDEAD, 0xAC1D, 0xAD1C, 0x0201]);
let mut buffer = [0u32; 3];
let mut rx = ReceiveBuffer::new(&mut buffer);
rx.next_word(Msb, 0xDEADBEEF);
rx.next_word(Msb, 0xAD1CAC1D);
rx.next_word(Msb, 0x77777777);
rx.next_word(Msb, 0x55555555);
assert_eq!(buffer, [0xDEADBEEF, 0xAD1CAC1D, 0x77777777]);
let mut buffer = [0u32; 3];
let mut rx = ReceiveBuffer::new(&mut buffer);
rx.next_word(Lsb, 0xDEADBEEF);
rx.next_word(Lsb, 0xAD1CAC1D);
rx.next_word(Lsb, 0x77777777);
rx.next_word(Lsb, 0x55555555);
assert_eq!(buffer, [0xDEADBEEF, 0xAD1CAC1D, 0x77777777]);
}
#[test]
fn transaction_frame_sizes() {
assert!(super::Transaction::new_words(&[1u8]).is_ok());
assert!(super::Transaction::new_words(&[1u8, 2]).is_ok());
assert!(super::Transaction::new_words(&[1u8, 2, 3]).is_ok());
assert!(super::Transaction::new_words(&[1u8, 2, 3, 4]).is_ok());
assert!(super::Transaction::new_words(&[1u8, 2, 3, 4, 5]).is_ok());
assert!(super::Transaction::new_words(&[1u16]).is_ok());
assert!(super::Transaction::new_words(&[1u16, 2]).is_ok());
assert!(super::Transaction::new_words(&[1u16, 2, 3]).is_ok());
assert!(super::Transaction::new_words(&[1u16, 2, 3, 4]).is_ok());
assert!(super::Transaction::new_words(&[1u16, 2, 3, 4, 5]).is_ok());
assert!(super::Transaction::new_words(&[1u32]).is_ok());
assert!(super::Transaction::new_words(&[1u32, 2]).is_ok());
assert!(super::Transaction::new_words(&[1u32, 2, 3]).is_ok());
assert!(super::Transaction::new_words(&[1u32, 2, 3, 4]).is_ok());
assert!(super::Transaction::new_words(&[1u32, 2, 3, 4, 5]).is_ok());
assert!(super::Transaction::new(7).is_err());
assert!(super::Transaction::new(8).is_ok());
assert!(super::Transaction::new(9).is_ok());
assert!(super::Transaction::new(31).is_ok());
assert!(super::Transaction::new(32).is_ok());
assert!(super::Transaction::new(33).is_err());
assert!(super::Transaction::new(34).is_ok());
assert!(super::Transaction::new(95).is_ok());
assert!(super::Transaction::new(96).is_ok());
assert!(super::Transaction::new(97).is_err());
assert!(super::Transaction::new(98).is_ok());
}
}