Skip to content

Commit 1d3f3d5

Browse files
transceivers: fix I2C read bug (#1787)
#1768 did not properly account for the FIFO behavior of the FPGA's data buffers. The "check the status byte" portion of the loop happened outside the part where we read the buffer, and since the buffer was just memory-mapped registers it could be repeatedly without consequence. Since the data was now in a FIFO, I was inadvertently draining the FIFO before the transaction was done. This PR consolidates the "is I2C done yet" logic into the `get_i2c_status_and_read_buffer` so calling code can just deal with the status register and the data buffer. Fixes #1786
1 parent 561b577 commit 1d3f3d5

File tree

3 files changed

+79
-82
lines changed

3 files changed

+79
-82
lines changed

drv/sidecar-front-io/src/transceivers.rs

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -677,13 +677,22 @@ impl core::ops::Index<LogicalPort> for LogicalPortFailureTypes {
677677

678678
#[derive(Copy, Clone)]
679679
pub struct PortI2CStatus {
680+
pub rdata_fifo_empty: bool,
681+
pub wdata_fifo_empty: bool,
680682
pub done: bool,
681683
pub error: FpgaI2CFailure,
682684
}
683685

684686
impl PortI2CStatus {
685687
pub fn new(status: u8) -> Self {
688+
// Use QSFP::PORT0 for constants, since they're all identical
686689
Self {
690+
rdata_fifo_empty: (status
691+
& Reg::QSFP::PORT0_STATUS::RDATA_FIFO_EMPTY)
692+
!= 0,
693+
wdata_fifo_empty: (status
694+
& Reg::QSFP::PORT0_STATUS::WDATA_FIFO_EMPTY)
695+
!= 0,
687696
done: (status & Reg::QSFP::PORT0_STATUS::BUSY) == 0,
688697
error: FpgaI2CFailure::try_from(status).unwrap_lite(),
689698
}
@@ -1028,13 +1037,13 @@ impl Transceivers {
10281037
.read(Addr::QSFP_PORT0_STATUS as u16 + port_loc.port.0 as u16)
10291038
}
10301039

1031-
/// Get `buf.len()` bytes of data, where the first byte is port status and
1032-
/// trailing bytes are the I2C read buffer for a `port`. The buffer stores
1033-
/// data from the last I2C read transaction done and thus only the number of
1034-
/// bytes read will be valid in the buffer.
1040+
/// Returns a PortI2CStatus and will fill `buf.len()` bytes of data with the
1041+
/// I2C read buffer for a `port`. The buffer stores data from the last I2C
1042+
/// read transaction done and thus only the number of bytes read will be
1043+
/// valid in the buffer.
10351044
///
10361045
/// Poll the status register for a port until an I2C transaction is done.
1037-
/// Upon completion, pass the status byte back alongside the read data. The
1046+
/// Upon completion, return the status struct back and the read data. The
10381047
/// read data buffer stores I2C read data from the last transaction so the
10391048
/// FIFO will only contain that many bytes. The FPGA automatically clears
10401049
/// the read data FIFO before starting a new read, so there's no need for
@@ -1043,14 +1052,23 @@ impl Transceivers {
10431052
&self,
10441053
port: P,
10451054
buf: &mut [u8],
1046-
) -> Result<(), FpgaError> {
1055+
) -> Result<PortI2CStatus, FpgaError> {
10471056
let port_loc = port.into();
1048-
buf[0] = self.get_i2c_status(port_loc)?;
1049-
self.fpga(port_loc.controller).read_bytes(
1050-
ReadOp::ReadNoAddrIncr,
1051-
Addr::QSFP_PORT0_I2C_DATA as u16 + port_loc.port.0 as u16,
1052-
&mut buf[1..],
1053-
)
1057+
loop {
1058+
let status_byte = self.get_i2c_status(port_loc)?;
1059+
let status = PortI2CStatus::new(status_byte);
1060+
1061+
if status.done {
1062+
self.fpga(port_loc.controller).read_bytes(
1063+
ReadOp::ReadNoAddrIncr,
1064+
Addr::QSFP_PORT0_I2C_DATA as u16 + port_loc.port.0 as u16,
1065+
buf,
1066+
)?;
1067+
return Ok(status);
1068+
}
1069+
1070+
userlib::hl::sleep_for(1);
1071+
}
10541072
}
10551073

10561074
/// Write `buf.len()` bytes of data into the I2C write buffer of the `mask`

drv/transceivers-server/src/main.rs

Lines changed: 25 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ use drv_fpga_api::FpgaError;
1616
use drv_i2c_devices::pca9956b::Error;
1717
use drv_sidecar_front_io::{
1818
leds::{FullErrorSummary, Leds},
19-
transceivers::{LogicalPort, LogicalPortMask, Transceivers},
19+
transceivers::{
20+
FpgaI2CFailure, LogicalPort, LogicalPortMask, Transceivers,
21+
},
2022
Reg,
2123
};
2224
use drv_sidecar_seq_api::{SeqError, Sequencer};
@@ -253,28 +255,24 @@ impl ServerImpl {
253255

254256
#[derive(Copy, Clone, FromBytes, AsBytes)]
255257
#[repr(C)]
256-
struct StatusAndTemperature {
257-
status: u8,
258+
struct Temperature {
258259
temperature: zerocopy::I16<zerocopy::BigEndian>,
259260
}
260261

261-
loop {
262-
let mut out = StatusAndTemperature::new_zeroed();
263-
self.transceivers
264-
.get_i2c_status_and_read_buffer(port, out.as_bytes_mut())?;
265-
if out.status & Reg::QSFP::PORT0_STATUS::BUSY == 0 {
266-
if out.status & Reg::QSFP::PORT0_STATUS::ERROR != 0 {
267-
return Err(FpgaError::ImplError(out.status));
268-
} else {
269-
// "Internally measured free side device temperatures are
270-
// represented as a 16-bit signed twos complement value in
271-
// increments of 1/256 degrees Celsius"
272-
//
273-
// - SFF-8636 rev 2.10a, Section 6.2.4
274-
return Ok(Celsius(out.temperature.get() as f32 / 256.0));
275-
}
276-
}
277-
userlib::hl::sleep_for(1);
262+
let mut out = Temperature::new_zeroed();
263+
let status = self
264+
.transceivers
265+
.get_i2c_status_and_read_buffer(port, out.as_bytes_mut())?;
266+
267+
if status.error == FpgaI2CFailure::NoError {
268+
// "Internally measured free side device temperatures are
269+
// represented as a 16-bit signed twos complement value in
270+
// increments of 1/256 degrees Celsius"
271+
//
272+
// - SFF-8636 rev 2.10a, Section 6.2.4
273+
Ok(Celsius(out.temperature.get() as f32 / 256.0))
274+
} else {
275+
Err(FpgaError::ImplError(status.error as u8))
278276
}
279277
}
280278

@@ -287,28 +285,23 @@ impl ServerImpl {
287285
return Err(FpgaError::CommsError);
288286
}
289287

290-
let mut out = [0u8; 2]; // [status, SFF8024Identifier]
288+
let mut out = [0u8; 1]; // [SFF8024Identifier]
291289

292290
// Wait for the I2C transaction to complete
293-
loop {
294-
self.transceivers
295-
.get_i2c_status_and_read_buffer(port, &mut out)?;
296-
if out[0] & Reg::QSFP::PORT0_STATUS::BUSY == 0 {
297-
break;
298-
}
299-
userlib::hl::sleep_for(1);
300-
}
291+
let status = self
292+
.transceivers
293+
.get_i2c_status_and_read_buffer(port, &mut out)?;
301294

302-
if out[0] & Reg::QSFP::PORT0_STATUS::ERROR == 0 {
303-
match out[1] {
295+
if status.error == FpgaI2CFailure::NoError {
296+
match out[0] {
304297
0x1E => Ok(ManagementInterface::Cmis),
305298
0x0D | 0x11 => Ok(ManagementInterface::Sff8636),
306299
i => Ok(ManagementInterface::Unknown(i)),
307300
}
308301
} else {
309302
// TODO: how should we handle this?
310303
// Right now, we'll retry on the next pass through the loop.
311-
Err(FpgaError::ImplError(out[0]))
304+
Err(FpgaError::ImplError(status.error as u8))
312305
}
313306
}
314307

drv/transceivers-server/src/udp.rs

Lines changed: 24 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use userlib::UnwrapLite;
1919
use crate::{FrontIOStatus, ServerImpl};
2020
use drv_sidecar_front_io::transceivers::{
2121
FpgaI2CFailure, LogicalPort, LogicalPortFailureTypes, LogicalPortMask,
22-
ModuleResult, ModuleResultNoFailure, ModuleResultSlim, PortI2CStatus,
22+
ModuleResult, ModuleResultNoFailure, ModuleResultSlim,
2323
};
2424
use task_net_api::*;
2525
use transceiver_messages::{
@@ -1156,48 +1156,34 @@ impl ServerImpl {
11561156
let mut failure_types = LogicalPortFailureTypes::default();
11571157

11581158
for port in result.success().to_indices() {
1159-
// The status register is contiguous with the output buffer, so
1160-
// we'll read them all in a single pass. This should normally
1161-
// terminate with a single read, since I2C is faster than Hubris
1162-
// IPC.
1163-
let mut buf = [0u8; 129];
1159+
let mut buf = [0u8; 128];
11641160
let port_loc = port.get_physical_location();
1165-
loop {
1166-
// If we have not encountered any errors, keep pulling full
1167-
// status + buffer payloads.
1168-
if self
1169-
.transceivers
1170-
.get_i2c_status_and_read_buffer(
1171-
port_loc,
1172-
&mut buf[0..(buf_len + 1)],
1173-
)
1174-
.is_err()
1175-
{
1176-
error.set(port);
1177-
break;
1178-
};
1179-
1180-
let status = PortI2CStatus::new(buf[0]);
11811161

1182-
// Use QSFP::PORT0 for constants, since they're all identical
1183-
if status.done {
1184-
// Check error mask
1185-
if status.error != FpgaI2CFailure::NoError {
1186-
// Record which port the error ocurred at so we can
1187-
// give the host a more meaningful error.
1188-
failure.set(port);
1189-
failure_types.0[port.0 as usize] = status.error
1190-
} else {
1191-
// Add data to payload
1192-
success.set(port);
1193-
let end_idx = idx + buf_len;
1194-
out[idx..end_idx].copy_from_slice(&buf[1..][..buf_len]);
1195-
idx = end_idx;
1196-
}
1162+
// If we have not encountered any errors, keep pulling full
1163+
// status + buffer payloads.
1164+
let status = match self
1165+
.transceivers
1166+
.get_i2c_status_and_read_buffer(port_loc, &mut buf[0..buf_len])
1167+
{
1168+
Ok(status) => status,
1169+
Err(_) => {
1170+
error.set(port);
11971171
break;
11981172
}
1173+
};
11991174

1200-
userlib::hl::sleep_for(1);
1175+
// Check error mask
1176+
if status.error != FpgaI2CFailure::NoError {
1177+
// Record which port the error ocurred at so we can
1178+
// give the host a more meaningful error.
1179+
failure.set(port);
1180+
failure_types.0[port.0 as usize] = status.error
1181+
} else {
1182+
// Add data to payload
1183+
success.set(port);
1184+
let end_idx = idx + buf_len;
1185+
out[idx..end_idx].copy_from_slice(&buf[..buf_len]);
1186+
idx = end_idx;
12011187
}
12021188
}
12031189
let mut final_result =

0 commit comments

Comments
 (0)