sfp: read the EEPROM in blocks instead of a byte at a time

The I2C controller transfers up to sixteen bytes per transaction and
page_impl.c already used that for sfp_send_data(), while sfp_read_reg()
asked for one byte and every caller looped. Reading a module therefore
cost one address phase per byte: 87 transactions when a module is
inserted, 52 for the sfp command, 36 for the vendor block in status.json.

sfp_read_block() replaces sfp_read_reg() and the callers that already
wanted a run of registers ask for it once: the vendor fields as three
16 byte pages, the diagnostics as one transfer, rate and encoding
together. That drops the three paths above to 8, 6 and 3 transactions,
and sfp_send_data() loses its copy of the transfer.

The vendor loops now run over 16..63 rather than 20..59 so the page base
is a multiple of sixteen and the index into the buffer is a single AND.
The four extra bytes at each end are read and discarded. The diagnostics
read asks for 16 bytes rather than the 15 it uses, because 16 is a width
the shipped firmware already exercises and 15 is not.

The device address, the bus selection and the start bit go into the
control register in one write now that the memory address is written
first, so a transfer touches that register once instead of three times.
The register reads take their result from the SFRs directly rather than
through the sfr_data mirror. The result is a bool and the destination is
sfp_buf, so a caller that cares about a failed transfer looks at the
return value instead of a flag.

Every caller gives up on the first failed read rather than carrying a
flag to the end, which is why the module read moved out of handle_sfp
into a function of its own. A module whose read fails is left marked as
absent, so the next poll retries it instead of configuring the SerDes
from bytes that never arrived.

BANK1 -194 bytes, BANK2 +382, common segment +44, xdata +15 for the
buffer, and one byte more of internal RAM free than before the series.
Built for all 25 machine definitions on sdcc 4.5.0; the tightest common
segment is 98 bytes free on SWTG024AS_V2_0, against 54 before this
series.
This commit is contained in:
d00f
2026-08-25 01:09:20 +02:00
parent 9a96c80af2
commit a0628c7df5
5 changed files with 140 additions and 108 deletions
+24 -16
View File
@@ -748,18 +748,25 @@ void parse_mtu(void)
write_char('\n');
}
void sfp_print_measurements(uint8_t sfp)
bool sfp_print_measurements(uint8_t sfp)
{
print_string("Options: "); print_byte(sfp_read_reg(sfp, 92)); write_char('\n');
if (!sfp_read_block(sfp, 92, 1))
return false;
print_string("Options: "); print_byte(sfp_buf[0]); write_char('\n');
if (!(sfp_options[sfp] & 0x40))
return;
print_string("Temp: "); print_byte(sfp_read_reg(sfp, 224)); print_byte(sfp_read_reg(sfp, 225)); write_char('\n');
print_string("Vcc: "); print_byte(sfp_read_reg(sfp, 226)); print_byte(sfp_read_reg(sfp, 227)); write_char('\n');
print_string("TX Bias: "); print_byte(sfp_read_reg(sfp, 228)); print_byte(sfp_read_reg(sfp, 229)); write_char('\n');
print_string("TX Power: "); print_byte(sfp_read_reg(sfp, 230)); print_byte(sfp_read_reg(sfp, 231)); write_char('\n');
print_string("RX Power: "); print_byte(sfp_read_reg(sfp, 232)); print_byte(sfp_read_reg(sfp, 233)); write_char('\n');
print_string("Laser: "); print_byte(sfp_read_reg(sfp, 234)); print_byte(sfp_read_reg(sfp, 235)); write_char('\n');
print_string("State: "); print_byte(sfp_read_reg(sfp, 238)); write_char('\n');
return true;
if (!sfp_read_block(sfp, 224, 16))
return false;
print_string("Temp: "); print_byte(sfp_buf[0]); print_byte(sfp_buf[1]); write_char('\n');
print_string("Vcc: "); print_byte(sfp_buf[2]); print_byte(sfp_buf[3]); write_char('\n');
print_string("TX Bias: "); print_byte(sfp_buf[4]); print_byte(sfp_buf[5]); write_char('\n');
print_string("TX Power: "); print_byte(sfp_buf[6]); print_byte(sfp_buf[7]); write_char('\n');
print_string("RX Power: "); print_byte(sfp_buf[8]); print_byte(sfp_buf[9]); write_char('\n');
print_string("Laser: "); print_byte(sfp_buf[10]); print_byte(sfp_buf[11]); write_char('\n');
print_string("State: "); print_byte(sfp_buf[14]); write_char('\n');
return true;
}
@@ -777,13 +784,14 @@ void parse_sfp(void)
print_string(" - empty\n");
continue;
}
sfp_i2c_fail = 0;
print_string(" - Rate: "); print_byte(sfp_read_reg(slot, 12));
print_string(" Encoding: "); print_byte(sfp_read_reg(slot, 11));
if (!sfp_read_block(slot, 11, 2)) {
print_string(" - I2C read failed on this slot\n");
continue;
}
print_string(" - Rate: "); print_byte(sfp_buf[1]);
print_string(" Encoding: "); print_byte(sfp_buf[0]);
write_char('\n');
sfp_print_info(slot);
sfp_print_measurements(slot);
if (sfp_i2c_fail)
if (!sfp_print_info(slot) || !sfp_print_measurements(slot))
print_string("I2C read failed on this slot\n");
}
return;
+8 -28
View File
@@ -177,10 +177,12 @@ void reg_to_html_long(register uint16_t reg)
void send_sfp_info(uint8_t sfp)
{
// This loops over the Vendor-name, Vendor OUI, Vendor PN and Vendor rev ASCII fields
for (uint8_t i = 20; i < 60; i++) {
if (i >= 36 && i < 40) // Skip Non-ASCII codes
for (uint8_t i = 16; i < 64; i++) {
if (!(i & 0xf))
sfp_read_block(sfp, i, 16);
if (i < 20 || i >= 60 || (i >= 36 && i < 40)) // Skip Non-ASCII codes
continue;
uint8_t c = sfp_read_reg(sfp, i);
uint8_t c = sfp_buf[i & 0xf];
if (c && c != 0xa0) // a0 is the byte read from a non-existant I2C EEPROM
char_to_html(c);
}
@@ -193,32 +195,10 @@ void sfp_send_data(uint8_t slot, uint8_t reg, uint8_t len)
if (len > 16)
return;
if (reg & 0x80) { // Configure SFP readings address (0x51) as I2C device address
reg &= 0x7f;
REG_WRITE(RTL837X_REG_I2C_CTRL, 0x00, 0x1 << (I2C_MEM_ADDR_WIDTH-16) | (len - 1) & 0xf, 0x51 >> 5, (0x51 << 3) & 0xff);
} else {
REG_WRITE(RTL837X_REG_I2C_CTRL, 0x00, 0x1 << (I2C_MEM_ADDR_WIDTH-16) | (len - 1) & 0xf, 0x50 >> 5, (0x50 << 3) & 0xff);
}
sfp_read_block(slot, reg, len);
reg_read_m(RTL837X_REG_I2C_CTRL);
sfr_mask_data(1, 0xfc, i2c_bus_from_scl_pin(machine.sfp_port[slot].i2c.scl) << 5 | i2c_bus_from_sda_pin(machine.sfp_port[slot].i2c.sda) << 2);
reg_write_m(RTL837X_REG_I2C_CTRL);
REG_WRITE(RTL837X_REG_I2C_IN, 0, 0, 0, reg);
// Execute I2C Read
reg_bit_set(RTL837X_REG_I2C_CTRL, 0);
// Wait for execution to finish
do {
reg_read_m(RTL837X_REG_I2C_CTRL);
} while (sfr_data[3] & 0x1);
for (uint8_t i = 0; i < len; i++) {
if (!(i & 0x3))
reg_read_m(RTL837X_REG_I2C_OUT + i);
byte_to_html(sfr_data[3 - (i & 0x3)]);
}
for (uint8_t i = 0; i < len; i++)
byte_to_html(sfp_buf[i]);
}
+3 -3
View File
@@ -149,8 +149,8 @@ void sleep(uint16_t t);
void write_char_no_syslog(char c);
void write_char(char c);
void print_reg(uint16_t reg);
uint8_t sfp_read_reg(uint8_t slot, uint8_t reg) __banked;
extern __xdata uint8_t sfp_i2c_fail;
bool sfp_read_block(uint8_t slot, uint8_t reg, uint8_t len) __banked __reentrant;
extern __xdata uint8_t sfp_buf[16];
void reg_bit_set(uint16_t reg_addr, char bit);
void reg_bit_clear(uint16_t reg_addr, char bit);
uint8_t reg_bit_test(uint16_t reg_addr, char bit);
@@ -169,7 +169,7 @@ void tcpip_output(void);
uint8_t read_flash(uint8_t bank, __code uint8_t *addr);
void get_random_32(void);
void read_reg_timer(__xdata uint32_t * tmr);
void sfp_print_info(uint8_t sfp);
bool sfp_print_info(uint8_t sfp);
bool gpio_pin_test(uint8_t pin);
void set_sys_led_state(uint8_t state);
void sds_read(uint8_t sds_id, uint8_t page, uint8_t reg);
+38 -24
View File
@@ -129,39 +129,53 @@ void gpio_output_setup(uint8_t pin, __xdata uint8_t initial_val) __banked{
/*
* Read a register of the EEPROM via I2C
* Read up to 16 consecutive registers of the EEPROM via I2C into sfp_buf
*/
uint8_t sfp_read_reg(uint8_t slot, uint8_t reg) __banked
bool sfp_read_block(uint8_t slot, uint8_t reg, uint8_t len) __banked __reentrant
{
if (reg & 0x80) { // Configure SFP readings address (0x51) as I2C device address
reg &= 0x7f;
REG_WRITE(RTL837X_REG_I2C_CTRL, 0x00, 0x1 << (I2C_MEM_ADDR_WIDTH-16) | 0, 0x51 >> 5, (0x51 << 3) & 0xff);
} else {
REG_WRITE(RTL837X_REG_I2C_CTRL, 0x00, 0x1 << (I2C_MEM_ADDR_WIDTH-16) | 0, 0x50 >> 5, (0x50 << 3) & 0xff);
}
uint8_t dev;
uint8_t val;
reg_read_m(RTL837X_REG_I2C_CTRL);
sfr_mask_data(1, 0xfc, i2c_bus_from_scl_pin(machine.sfp_port[slot].i2c.scl) << 5 | i2c_bus_from_sda_pin(machine.sfp_port[slot].i2c.sda) << 2);
reg_write_m(RTL837X_REG_I2C_CTRL);
len--;
if (len > 15)
return false;
dev = (reg & 0x80) ? 0x51 : 0x50; // 0x51 holds the diagnostics, 0x50 the module data
reg &= 0x7f;
REG_WRITE(RTL837X_REG_I2C_IN, 0, 0, 0, reg);
// Execute I2C Read
reg_bit_set(RTL837X_REG_I2C_CTRL, 0);
REG_WRITE(RTL837X_REG_I2C_CTRL, 0x00,
0x1 << (I2C_MEM_ADDR_WIDTH - 16) | len,
(dev >> 5) | i2c_bus_from_scl_pin(machine.sfp_port[slot].i2c.scl) << 5
| i2c_bus_from_sda_pin(machine.sfp_port[slot].i2c.sda) << 2,
((dev << 3) & 0xff) | 0x1);
// Wait for execution to finish
do {
reg_read_m(RTL837X_REG_I2C_CTRL);
} while (sfr_data[3] & 0x1);
reg_read(RTL837X_REG_I2C_CTRL);
} while (SFR_DATA_0 & 0x1);
/* Bit 1 is the controller's own failure indication, which the vendor SDK
* looks at and this did not. Without it an unacknowledged address comes
* back as an ordinary byte and the caller cannot tell it from data. */
if (sfr_data[3] & 0x2) {
sfp_i2c_fail = 1;
return 0xff;
if (SFR_DATA_0 & 0x2)
return false;
for (uint8_t i = 0; i <= len; i++) {
switch (i & 0x3) {
case 0:
reg_read(RTL837X_REG_I2C_OUT + i);
val = SFR_DATA_0;
break;
case 1:
val = SFR_DATA_8;
break;
case 2:
val = SFR_DATA_16;
break;
default:
val = SFR_DATA_24;
break;
}
sfp_buf[i] = val;
}
reg_read_m(RTL837X_REG_I2C_OUT);
return sfr_data[3];
return true;
}
+67 -37
View File
@@ -139,7 +139,7 @@ __xdata char sfp_module_vendor[2][17];
__xdata char sfp_module_model[2][17];
__xdata char sfp_module_serial[2][17];
__xdata uint8_t sfp_options[2];
__xdata uint8_t sfp_i2c_fail; /* set by sfp_read_reg() when the controller flags a failed transfer */
__xdata uint8_t sfp_buf[16]; /* scratch for one I2C transaction, the controller reads at most 16 bytes */
__xdata uint8_t sfp_speed[2];
__xdata uint8_t sfp_quirks[2];
__xdata bool button_last;
@@ -1188,36 +1188,46 @@ static inline uint8_t sfp_rate_to_sds_config(register uint8_t rate)
}
void sfp_print_info(uint8_t sfp)
bool sfp_print_info(uint8_t sfp)
{
// This loops over the Vendor-name, Vendor OUI, Vendor PN and Vendor rev ASCII fields
for (uint8_t i = 20; i < 60; i++) {
if (i >= 36 && i < 40) // Skip Non-ASCII codes
for (uint8_t i = 16; i < 64; i++) {
if (!(i & 0xf) && !sfp_read_block(sfp, i, 16))
return false;
if (i < 20 || i >= 60 || (i >= 36 && i < 40)) // Skip Non-ASCII codes
continue;
uint8_t c = sfp_read_reg(sfp, i);
uint8_t c = sfp_buf[i & 0xf];
if (c)
write_char(c);
}
print_string("\n");
return true;
}
// Normalize strings from EEPROM by removing any trailing spaces; this allows simpler comparisons
void sfp_read_field(__xdata char *dst, uint8_t sfp, uint8_t start, uint8_t length) __reentrant
bool sfp_read_field(__xdata char *dst, uint8_t sfp, uint8_t start, uint8_t length) __reentrant
{
dst[length] = '\0';
if (!sfp_read_block(sfp, start, length))
return false;
for (uint8_t i = 0; i < length; i++)
dst[i] = sfp_read_reg(sfp, start + i);
dst[length] = '\0';
memcpy(dst, sfp_buf, length);
while (length > 0 && dst[--length] == ' ')
dst[length] = '\0';
return true;
}
void sfp_get_info(uint8_t sfp)
bool sfp_get_info(uint8_t sfp)
{
sfp_read_field(sfp_module_vendor[sfp], sfp, 20, 16);
sfp_read_field(sfp_module_model[sfp], sfp, 40, 16);
sfp_read_field(sfp_module_serial[sfp], sfp, 68, 16);
if (!sfp_read_field(sfp_module_vendor[sfp], sfp, 20, 16))
return false;
if (!sfp_read_field(sfp_module_model[sfp], sfp, 40, 16))
return false;
return sfp_read_field(sfp_module_serial[sfp], sfp, 68, 16);
}
void sfp_apply_quirks(uint8_t sfp) __reentrant
@@ -1236,7 +1246,7 @@ void sfp_apply_quirks(uint8_t sfp) __reentrant
if (!(sfp_options[sfp] & 0x40)) {
// The module reports that DDM is not implemented, but try a dummy read to confirm
// 0xff would mean a failed I2C read or an impossible (per spec) voltage greater than 6.5V
if (sfp_read_reg(sfp, 226) != 0xff) {
if (sfp_read_block(sfp, 226, 1) && sfp_buf[0] != 0xff) {
sfp_options[sfp] |= 0x40;
}
}
@@ -1260,6 +1270,45 @@ void setup_sfp_gpio(void)
}
}
static bool sfp_module_read(uint8_t sfp)
{
uint8_t rate;
// Read Reg 11: Encoding, see SFF-8472 and SFF-8024
// Read Reg 12: Signalling rate (including overhead) in 100Mbit: 0xd: 1Gbit, 0x67:10Gbit
delay(100); // Delay, because some modules need time to wake up
if (!sfp_read_block(sfp, 11, 2))
return false;
rate = sfp_buf[1];
if (sfp_speed[sfp] == SFP_SPEED_100M)
rate = 0x1;
else if (sfp_speed[sfp] == SFP_SPEED_1G)
rate = 0xc;
else if (sfp_speed[sfp] == SFP_SPEED_2G5)
rate = 0x19;
else if (sfp_speed[sfp] == SFP_SPEED_10G)
rate = 0x69;
print_string(" Rate: "); print_byte(rate); // Normally 1, but 0 for DAC, can be ignored?
print_string(" Encoding: "); print_byte(sfp_buf[0]);
print_string(" Module: ");
if (!sfp_print_info(sfp))
return false;
print_string("\n");
if (!sfp_read_block(sfp, 92, 1))
return false;
sfp_options[sfp] = sfp_buf[0];
if (!sfp_get_info(sfp))
return false;
sfp_apply_quirks(sfp);
sds_config(machine.sfp_port[sfp].sds, sfp_rate_to_sds_config(rate));
return true;
}
void handle_sfp(void)
{
for (uint8_t sfp = 0; sfp < machine.n_sfp; sfp++) {
@@ -1267,29 +1316,10 @@ void handle_sfp(void)
if (sfp_pins_last & (0x1 << (sfp << 2))) {
sfp_pins_last &= ~(0x01 << (sfp << 2));
print_string("\n<MODULE INSERTED> Slot: "); write_char('1' + sfp);
// Read Reg 11: Encoding, see SFF-8472 and SFF-8024
// Read Reg 12: Signalling rate (including overhead) in 100Mbit: 0xd: 1Gbit, 0x67:10Gbit
delay(100); // Delay, because some modules need time to wake up
sfp_i2c_fail = 0;
uint8_t rate = sfp_read_reg(sfp, 12);
if (sfp_speed[sfp] == SFP_SPEED_100M)
rate = 0x1;
else if (sfp_speed[sfp] == SFP_SPEED_1G)
rate = 0xc;
else if (sfp_speed[sfp] == SFP_SPEED_2G5)
rate = 0x19;
else if (sfp_speed[sfp] == SFP_SPEED_10G)
rate = 0x69;
print_string(" Rate: "); print_byte(rate); // Normally 1, but 0 for DAC, can be ignored?
print_string(" Encoding: "); print_byte(sfp_read_reg(sfp, 11));
print_string(" Module: "); sfp_print_info(sfp);
print_string("\n");
sfp_options[sfp] = sfp_read_reg(sfp, 92);
sfp_get_info(sfp);
sfp_apply_quirks(sfp);
if (sfp_i2c_fail)
print_string("SFP: an I2C read failed, the module data above may be wrong\n");
sds_config(machine.sfp_port[sfp].sds, sfp_rate_to_sds_config(rate));
if (!sfp_module_read(sfp)) {
print_string("SFP: an I2C read failed, retrying on the next poll\n");
sfp_pins_last |= 0x01 << (sfp << 2);
}
}
} else {
if (!(sfp_pins_last & (0x1 << (sfp << 2)))) {