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

Reintroduce SlaveClient, size-optimise EEPROM #100

Merged
merged 3 commits into from
Aug 17, 2023
Merged
Show file tree
Hide file tree
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
4 changes: 2 additions & 2 deletions src/dc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::{
fmt,
pdu_loop::CheckWorkingCounter,
register::RegisterAddress,
slave::{ports::Topology, Slave, SlaveRef},
slave::{ports::Topology, slave_client::SlaveClient, Slave},
Client,
};

Expand All @@ -25,7 +25,7 @@ async fn latch_dc_times(client: &Client<'_>, slaves: &mut [Slave]) -> Result<(),

// Read receive times for all slaves and store on slave structs
for slave in slaves.iter_mut().filter(|slave| slave.flags.dc_supported) {
let sl = SlaveRef::new(client, slave.configured_address, ());
let sl = SlaveClient::new(client, slave.configured_address);

// NOTE: Defined as a u64, not i64, in the spec
// TODO: Remember why this is i64 here. SOEM uses i64 I think, and I seem to remember things
Expand Down
69 changes: 34 additions & 35 deletions src/eeprom/reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::{
error::{EepromError, Error},
fmt,
register::RegisterAddress,
slave::SlaveRef,
slave::slave_client::SlaveClient,
};
use core::ops::Deref;

Expand All @@ -30,8 +30,8 @@ impl EepromSectionReader {
/// `EepromSectionReader` will either return [`EepromError::SectionOverrun`] or
/// [`EepromError::SectionUnderrun`] errors if the section cannot be completely read as this is
/// often an indicator of a bug in either the slave's EEPROM or EtherCrab.
pub async fn new<S>(
slave: &SlaveRef<'_, S>,
pub async fn new(
slave: &SlaveClient<'_>,
category: CategoryType,
) -> Result<Option<Self>, Error> {
let mut start_word = SII_FIRST_CATEGORY_START;
Expand Down Expand Up @@ -79,8 +79,8 @@ impl EepromSectionReader {
}
}

async fn read_eeprom_raw<S>(
slave: &SlaveRef<'_, S>,
async fn read_eeprom_raw(
slave: &SlaveClient<'_>,
eeprom_address: u16,
) -> Result<[u8; 8], Error> {
let status = slave
Expand Down Expand Up @@ -116,14 +116,15 @@ impl EepromSectionReader {
let data = match status.read_size {
// If slave uses 4 octet reads, do two reads so we can always return a chunk of 8 bytes
SiiReadSize::Octets4 => {
let chunk1 = slave
let mut data = slave
.read_slice(RegisterAddress::SiiData.into(), 4, "Read SII data")
.await?;
.await
.map(|sl| fmt::unwrap!(heapless::Vec::<u8, 8>::from_slice(sl.deref())))?;

// Move on to next chunk
{
// NOTE: We must compute offset in 16 bit words, not bytes, hence the divide by 2
let setup = SiiRequest::read(eeprom_address + (chunk1.len() / 2) as u16);
let setup = SiiRequest::read(eeprom_address + (data.len() / 2) as u16);

slave
.write_slice(
Expand All @@ -140,29 +141,27 @@ impl EepromSectionReader {
.read_slice(RegisterAddress::SiiData.into(), 4, "SII data 2")
.await?;

let mut data = [0u8; 8];

data[0..4].copy_from_slice(&chunk1);
data[4..8].copy_from_slice(&chunk2);
fmt::unwrap!(data.extend_from_slice(&chunk2));

data
}
SiiReadSize::Octets8 => slave
.read_slice(RegisterAddress::SiiData.into(), 8, "SII data")
.await
.and_then(|sl| sl.deref().try_into().map_err(|_| Error::Internal))?,
.map(|sl| fmt::unwrap!(heapless::Vec::<u8, 8>::from_slice(&sl)))?,
};

#[cfg(not(feature = "defmt"))]
fmt::trace!("Read {:#04x} {:02x?}", eeprom_address, data);
#[cfg(feature = "defmt")]
fmt::trace!("Read {:#04x} {=[u8]}", eeprom_address, data);

Ok(data)
data.into_array()
.map_err(|_| Error::Eeprom(EepromError::SectionUnderrun))
}

/// Wait for EEPROM read or write operation to finish and clear the busy flag.
async fn wait<S>(slave: &SlaveRef<'_, S>) -> Result<(), Error> {
async fn wait(slave: &SlaveClient<'_>) -> Result<(), Error> {
crate::timer_factory::timeout(slave.timeouts().eeprom, async {
loop {
let control = slave
Expand All @@ -182,7 +181,7 @@ impl EepromSectionReader {
/// Read the next byte from the EEPROM.
///
/// Internally, this method reads the EEPROM in chunks of 4 or 8 bytes (depending on the slave).
pub async fn next<S>(&mut self, slave: &SlaveRef<'_, S>) -> Result<Option<u8>, Error> {
pub async fn next(&mut self, slave: &SlaveClient<'_>) -> Result<Option<u8>, Error> {
if self.read.is_empty() {
let read = Self::read_eeprom_raw(slave, self.start).await?;

Expand All @@ -191,11 +190,12 @@ impl EepromSectionReader {
self.read_length = slice.len();

for byte in slice.iter() {
self.read.push_back(*byte).map_err(|_| {
fmt::error!("EEPROM read queue is full");

Error::Eeprom(EepromError::SectionOverrun)
})?;
// SAFETY:
// - The queue is empty at this point
// - The read chunk is 8 bytes long
// - So is the queue
// - So all 8 bytes will push into the 8 byte queue successfully
unsafe { self.read.push_back_unchecked(*byte) };
}

self.start += (self.read.len() / 2) as u16;
Expand All @@ -215,7 +215,7 @@ impl EepromSectionReader {
}

/// Skip a given number of addresses (note: not bytes).
pub async fn skip<S>(&mut self, slave: &SlaveRef<'_, S>, skip: u16) -> Result<(), Error> {
pub async fn skip(&mut self, slave: &SlaveClient<'_>, skip: u16) -> Result<(), Error> {
// TODO: Optimise by calculating new skip address instead of just iterating through chunks
for _ in 0..skip {
self.next(slave).await?;
Expand All @@ -225,39 +225,38 @@ impl EepromSectionReader {
}

/// Try reading the next chunk in the current section.
pub async fn try_next<S>(&mut self, slave: &SlaveRef<'_, S>) -> Result<u8, Error> {
match self.next(slave).await {
Ok(Some(value)) => Ok(value),
Ok(None) => Err(Error::Eeprom(EepromError::SectionOverrun)),
Err(e) => Err(e),
pub async fn try_next(&mut self, slave: &SlaveClient<'_>) -> Result<u8, Error> {
match self.next(slave).await? {
Some(value) => Ok(value),
None => Err(Error::Eeprom(EepromError::SectionOverrun)),
}
}

/// Attempt to read exactly `N` bytes. If not enough data could be read, this method returns an
/// error.
pub async fn take_vec_exact<const N: usize, S>(
pub async fn take_vec_exact<const N: usize>(
&mut self,
slave: &SlaveRef<'_, S>,
slave: &SlaveClient<'_>,
) -> Result<heapless::Vec<u8, N>, Error> {
self.take_vec(slave)
.await?
.ok_or(Error::Eeprom(EepromError::SectionUnderrun))
}

/// Read up to `N` bytes. If not enough data could be read, this method will return `Ok(None)`.
pub async fn take_vec<const N: usize, S>(
pub async fn take_vec<const N: usize>(
&mut self,
slave: &SlaveRef<'_, S>,
slave: &SlaveClient<'_>,
) -> Result<Option<heapless::Vec<u8, N>>, Error> {
self.take_vec_len(slave, N).await
}

/// Try to take `len` bytes, returning an error if the buffer length `N` is too small.
///
/// If not enough data could be read, this method returns an error.
pub async fn take_vec_len_exact<const N: usize, S>(
pub async fn take_vec_len_exact<const N: usize>(
&mut self,
slave: &SlaveRef<'_, S>,
slave: &SlaveClient<'_>,
len: usize,
) -> Result<heapless::Vec<u8, N>, Error> {
self.take_vec_len(slave, len)
Expand All @@ -268,9 +267,9 @@ impl EepromSectionReader {
/// Try to take `len` bytes, returning an error if the buffer length `N` is too small.
///
/// If not enough data can be read to fill the buffer, this method will return `Ok(None)`.
pub async fn take_vec_len<const N: usize, S>(
pub async fn take_vec_len<const N: usize>(
&mut self,
slave: &SlaveRef<'_, S>,
slave: &SlaveClient<'_>,
len: usize,
) -> Result<Option<heapless::Vec<u8, N>>, Error> {
let mut buf = heapless::Vec::new();
Expand Down
39 changes: 21 additions & 18 deletions src/slave/configuration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ where

fmt::debug!(
"Slave {:#06x} mailbox SMs configured. Transitioning to PRE-OP",
self.configured_address
self.client.configured_address
);

self.request_slave_state(SlaveState::PreOp).await?;
Expand Down Expand Up @@ -94,7 +94,7 @@ where

fmt::debug!(
"Slave {:#06x} found sync managers {:?}",
self.configured_address,
self.client.configured_address,
sms
);

Expand Down Expand Up @@ -239,14 +239,15 @@ where
},
};

self.write_slice(
RegisterAddress::sync_manager(sync_manager_index).into(),
&fmt::unwrap!(sm_config
.pack()
.map_err(crate::error::WrappedPackingError::from)),
"SM config",
)
.await?;
self.client
.write_slice(
RegisterAddress::sync_manager(sync_manager_index).into(),
&fmt::unwrap!(sm_config
.pack()
.map_err(crate::error::WrappedPackingError::from)),
"SM config",
)
.await?;

fmt::debug!(
"Slave {:#06x} SM{}: {}",
Expand Down Expand Up @@ -477,6 +478,7 @@ where
) -> Result<(), Error> {
// Multiple SMs may use the same FMMU, so we'll read the existing config from the slave
let mut fmmu_config = self
.client
.read_slice(
RegisterAddress::fmmu(fmmu_index as u8).into(),
16,
Expand Down Expand Up @@ -507,14 +509,15 @@ where
}
};

self.write_slice(
RegisterAddress::fmmu(fmmu_index as u8).into(),
&fmt::unwrap!(fmmu_config
.pack()
.map_err(crate::error::WrappedPackingError::from)),
"PDI FMMU",
)
.await?;
self.client
.write_slice(
RegisterAddress::fmmu(fmmu_index as u8).into(),
&fmt::unwrap!(fmmu_config
.pack()
.map_err(crate::error::WrappedPackingError::from)),
"PDI FMMU",
)
.await?;
fmt::debug!(
"Slave {:#06x} FMMU{}: {}",
self.state.configured_address,
Expand Down
Loading