How i2c reads DDR5 SPD?
At present, I have achieved reading DDR3 and DDR4, but I have no clue about DDR5.
As you showed in I2c read DDR4 SPD.
I'm tempted to merge the topics but decided against it because it's now about DDR5. It would however have been useful if you would have posted the code from that topic here.
It's outside my knowledge so can't help.
This code is in the GitHub - 1a2m3/SPD-Reader-Writer: SPD Reader & Writer with Software Write Protection capabilities supporting Arduino and SMBus project, you can refer to the code to get the DDR5 SPD reading method, but my level is limited, most of the code can not be understood, do you have a friend to help you see?
/*
Arduino based EEPROM SPD reader and writer
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
For overclockers and PC hardware enthusiasts
Repos: https://github.com/1a2m3/SPD-Reader-Writer
Support: https://forums.evga.com/FindPost/3053544
Donate: https://paypal.me/mik4rt3m
PS: DO NOT EDIT THIS FILE UNLESS YOU KNOW WHAT YOU ARE DOING!
CONFIGURABLE SETTINGS ARE IN "SpdReaderWriterSettings.h" FILE
*/
#include <Wire.h>
#include <EEPROM.h>
#include "SpdReaderWriterSettings.h" // Settings
#define FW_VER 20231207 // Firmware version number (YYYYMMDD)
#define DDR5 _BV(5) // Offline mode
#define DDR4 _BV(4) // VHV control
#define DDR3 _BV(3) // VHV+SA1 controls
#define MR0 0x00 // Device Type; Most Significant Byte
#define MR1 0x01 // Device Type; Least Significant Byte
#define MR6 0x06 // Device Write Recovery Time Capability
#define MR11 0x0B // I2C Legacy Mode Device Configuration
#define MR12 0x0C // Write Protection For NVM Blocks [7:0]
#define MR13 0x0D // Write Protection for NVM Blocks [15:8]
#define MR14 0x0E // Device Configuration - Host and Local Interface IO
#define MR18 0x12 // Device Configuration
#define MR20 0x14 // Clear Register MR52 Error Status Command
#define MR48 0x30 // Device Status
#define MR52 0x34 // Hub, Thermal and NVM Error Status
#define PMIC 0b1001 << 3 // PMIC local device type ID
#define SPD5_NO 0x5108 // SPD5 Hub Device
#define SPD5_TS 0x5118 // SPD5 Hub Device w/ Temp Sensor
#define SPA0 0x6C // Set Page Address 0
#define SPA1 0x6E // Set Page Address 1
#define RPA 0x6D // Read Page Address
#define RPS0 0x63 // Read SWP0 status (offsets 0-127) (DDR4/DDR3/DDR2)
#define RPS1 0x69 // Read SWP1 status (offsets 128-255) (DDR4)
#define RPS2 0x6B // Read SWP2 status (offsets 256-383) (DDR4)
#define RPS3 0x61 // Read SWP3 status (offsets 384-511) (DDR4)
#define SWP0 0x62 // Set RSWP for block 0 (offsets 0-127) (DDR4/DDR3/DDR2) *
#define SWP1 0x68 // Set RSWP for block 1 (offsets 128-255) (DDR4)
#define SWP2 0x6A // Set RSWP for block 2 (offsets 256-383) (DDR4)
#define SWP3 0x60 // Set RSWP for block 3 (offsets 384-511) (DDR4) *
#define CWP 0x66 // Clear RSWP (DDR4/DDR3/DDR2) *
#define PWPB 0b0110 // PSWP Device Type Identifier Control Code (bits 7-4) (DDR3/DDR2)
#define DNC 0x00 // "Do not care" byte
#define RESPONSE '&'
#define ALERT '@'
#define UNKNOWN '?'
#define A1_MASK 0b11001100 // ScanBus() bitmask response when SA1 is high: 82-83, 86-87
#define SLAVEINC '+'
#define SLAVEDEC '-'
#define CLOCKINC '/'
#define CLOCKDEC '\\'
#define NAMELENGTH 16
char deviceName[NAMELENGTH];
#define DEVICESETTINGS 0x20 // EEPROM location to store device settings
#define CLOCKMODE 0 // Bit position for I2C clock settings
#define FASTMODE true
#define STDMODE false
int32_t clock[] = { 100000, 400000 };
uint32_t i2cClock = clock[0]; // Initial I2C clock
uint8_t eepromPageAddress; // Initial EEPROM page address
uint8_t slaveCountCurrent; // Current number of slave addresses on I2C bus
uint8_t slaveCountLast; // Last number of slave addresses on I2C bus
bool i2cClockCurrent; // Current I2C clock mode
bool i2cClockLast; // Last I2C clock mode
bool cmdExecuting; // Indicates an input command is being executed
uint8_t responseBuffer[32]; // Response body buffer
uint8_t responseLength; // Output response body length and index
enum Command : uint8_t {
Get = ((uint8_t)-1), // Gets current value
Disable = 0, // Resets variable value to default
Enable, // Modifies variable value
ReadByte, // Read byte
WriteByte, // Write byte
WritePage, // Write page
WriteTest, // Write protection test
Ddr4Detect, // DDR4 detection
Ddr5Detect, // DDR5 detection
Spd5HubReg, // Access SPD5 Hub register space
Size, // Get EEPROM size
ScanBus, // Scan I2C bus
BusClock, // I2C clock control
ProbeAddress, // Probe I2C address
PinControl, // Config pin control
PinReset, // Reset config pins state to defaults
Rswp, // RSWP operation
Pswp, // PSWP operation
RswpReport, // Report current RSWP capabilities
Version, // Get Firmware version
Test, // Device Communication Test
Name, // Name controls
FactoryReset, // Restore device settings to default
};
enum pin {
HV_FEEDBACK = -1, // VHV feedback pin
HV_SWITCH, // Pin to toggle VHV on SA0 pin
SA1_SWITCH, // Pin to toggle SA1 state
};
typedef struct {
pin number;
const int name;
bool defaultState;
uint8_t mode;
} pinData;
pinData ConfigPin[] = {
{ HV_SWITCH, HV_EN, false, OUTPUT }, // HV control
{ SA1_SWITCH, SA1_EN, true, OUTPUT }, // SA1 control
{ HV_FEEDBACK, HV_FB, false, INPUT }, // HV feedback
};
size_t pinCount = sizeof(ConfigPin) / sizeof(ConfigPin[0]);
void setup() {
for (uint8_t i = 0; i < pinCount; i++) {
pinMode(ConfigPin[i].name, ConfigPin[i].mode);
}
resetPins();
Wire.begin();
Wire.setWireTimeout(10000, true);
Wire.setClock(clock[getI2cClockMode()]);
i2cClockCurrent = clock[getI2cClockMode()];
i2cClockLast = i2cClockCurrent;
slaveCountCurrent = getQuantity();
slaveCountLast = slaveCountCurrent;
setDdr4PageAddress(0);
PORT.begin(BAUD_RATE);
PORT.setTimeout(100); // Input timeout in ms
while (!PORT) {}
#ifndef __AVR__
PORT.write(UNKNOWN);
while (true) {}
#endif
Respond(true);
OutputResponse();
}
void loop() {
resetPinsInternal();
if (PORT.available()) {
parseCommand();
}
i2cMonitor();
}
void parseCommand() {
if (!PORT.available()) {
cmdExecuting = false;
return;
}
cmdExecuting = true;
switch ((uint8_t)PORT.read()) {
case Command::ReadByte:
cmdRead();
break;
case Command::WriteByte:
cmdWriteByte();
break;
case Command::WritePage:
cmdWritePage();
break;
case Command::ScanBus:
cmdScanBus();
break;
case Command::ProbeAddress:
cmdProbeBusAddress();
break;
case Command::BusClock:
cmdBusClock();
break;
case Command::PinControl:
cmdPinControl();
break;
case Command::PinReset:
cmdPinReset();
break;
case Command::Rswp:
cmdRSWP();
break;
case Command::Pswp:
cmdPSWP();
break;
case Command::WriteTest:
cmdWriteTest();
break;
case Command::Version:
cmdVersion();
break;
case Command::Test:
cmdTest();
break;
case Command::RswpReport:
cmdRswpRespond();
break;
case Command::Ddr4Detect:
cmdDdr4Detect();
break;
case Command::Ddr5Detect:
cmdDdr5Detect();
break;
case Command::Spd5HubReg:
cmdSpd5Hub();
break;
case Command::Size:
cmdSize();
break;
case Command::Name:
cmdName();
break;
case Command::FactoryReset:
cmdFactoryReset();
break;
}
OutputResponse();
cmdExecuting = false;
}
/* -= Response handlers =- */
void Respond(uint8_t inputData) {
responseBuffer[responseLength] = inputData;
responseLength++;
}
void Respond(uint8_t* inputData, size_t length) {
for (uint8_t i = 0; i < length; i++) {
Respond(inputData[i]);
}
}
void Respond(String inputData) {
for (uint8_t i = 0; i < inputData.length(); i++) {
Respond(inputData[i]);
}
}
void OutputResponse() {
if (responseLength > 0) {
uint8_t checkSum = 0;
for (uint8_t i = 0; i < responseLength; i++) {
checkSum += responseBuffer[i];
}
PORT.write(RESPONSE);
PORT.write(responseLength);
PORT.write(responseBuffer, responseLength);
PORT.write(checkSum);
PORT.flush();
responseLength = 0;
memset(responseBuffer, 0x00, sizeof(responseBuffer));
}
}
/* -= Command handlers =- */
void cmdRead() {
uint8_t buffer[4] = { 0 };
PORT.readBytes(buffer, sizeof(buffer));
uint8_t address = buffer[0];
uint16_t offset = buffer[1] << 8 | buffer[2];
uint8_t length = buffer[3];
uint8_t data[length];
readByte(address, offset, length, data);
Respond(data, sizeof(data));
}
void cmdWriteByte() {
uint8_t buffer[4] = { 0 };
PORT.readBytes(buffer, sizeof(buffer));
uint8_t address = buffer[0];
uint16_t offset = buffer[1] << 8 | buffer[2];
uint8_t data = buffer[3];
Respond(writeByte(address, offset, data));
}
void cmdWritePage() {
uint8_t buffer[4] = { 0 };
PORT.readBytes(buffer, sizeof(buffer));
uint8_t address = buffer[0];
uint16_t offset = buffer[1] << 8 | buffer[2];
uint8_t length = buffer[3];
if (length == 0) {
Respond(0);
return;
}
uint8_t data[length];
PORT.readBytes(data, sizeof(data));
if (length > 16) {
Respond(false);
return;
}
Respond(writePage(address, offset, length, data));
}
void cmdScanBus() {
Respond(scanBus());
}
void cmdTest() {
Respond(true);
}
void cmdRswpRespond() {
Respond(rswpSupportTest());
}
void cmdDdr4Detect() {
uint8_t buffer[1] = { 0 };
PORT.readBytes(buffer, sizeof(buffer));
uint8_t address = buffer[0]; // I2C address
Respond(ddr4Detect(address));
}
void cmdDdr5Detect() {
uint8_t buffer[1] = { 0 };
PORT.readBytes(buffer, sizeof(buffer));
uint8_t address = buffer[0]; // I2C address
Respond(ddr5Detect(address));
}
void cmdSpd5Hub() {
uint8_t buffer[3] = { 0 };
PORT.readBytes(buffer, sizeof(buffer));
uint8_t address = buffer[0]; // I2C address
uint8_t memReg = buffer[1]; // register
uint8_t command = buffer[2]; // command
if (!ddr5Detect(address)) {
Respond(false);
}
if (command == Command::Enable) {
uint8_t data[1] = { 0 }; // Byte value
PORT.readBytes(data, sizeof(data));
Respond(writeReg(address, memReg, data[0]));
}
else if (command == Command::Get) {
Respond(readReg(address, memReg));
}
else {
Respond(false);
}
}
void cmdSize() {
uint8_t buffer[1] = { 0 };
PORT.readBytes(buffer, sizeof(buffer));
uint8_t address = buffer[0]; // I2C address
uint16_t size = 0; // 0
if (probeBusAddress(address)) {
if (validateEepromAddress(address)) { // EEPROM
if (ddr5Detect(address)) {
size = 1024; // 3
}
else {
if (getQuantity() == 1) {
if (ddr4Detect()) {
size = 512; // 2
}
else {
size = 256; // 1
}
}
else if (getQuantity() > 1) {
if (!ddr4Detect()) {
size = 256; // 1
}
}
}
}
else if (validatePmicAddress(address)) { // PMIC
size = 256; // 1
}
}
if (!size) {
uint8_t keyByte[1] = { 0 };
readByte(address, 0x02, 1, keyByte);
if (0x0C <= keyByte[0] && keyByte[0] <= 0x11) {
size = 512; // 2
}
}
for (uint8_t i = 0; i <= 3; i++) {
if(bitRead(highByte(size), i)) {
Respond(i + 1);
return;
}
}
Respond(0);
}
void cmdVersion() {
uint8_t verLength = sizeof(FW_VER);
uint8_t data[verLength];
for (int8_t i = verLength; i > 0; i--) {
data[i - 1] = FW_VER >> (8 * (i - 1));
}
Respond(data, verLength);
}
void cmdName() {
uint8_t buffer[1] = { 0 };
PORT.readBytes(buffer, sizeof(buffer));
if (buffer[0] == Command::Get) {
Respond(getName());
}
else if (buffer[0] > 0 && buffer[0] <= NAMELENGTH) {
char name[buffer[0] + 1];
PORT.readBytes(name, buffer[0]);
name[buffer[0]] = 0;
Respond(setName(name));
}
else {
Respond(false);
}
}
void cmdProbeBusAddress() {
uint8_t buffer[1] = { 0 };
PORT.readBytes(buffer, sizeof(buffer));
uint8_t address = buffer[0]; // I2C address
Respond(probeBusAddress(address));
}
void cmdBusClock() {
uint8_t buffer[1] = { 0 };
PORT.readBytes(buffer, sizeof(buffer));
if (buffer[0] == FASTMODE || buffer[0] == STDMODE) {
setI2cClockMode(buffer[0]);
Respond(getI2cClockMode() == buffer[0]);
}
else if (buffer[0] == Command::Get) {
Respond(getI2cClockMode());
}
else {
Respond(false);
}
}
void cmdFactoryReset() {
Respond(factoryReset());
}
void cmdRSWP() {
uint8_t buffer[3] = { 0 };
PORT.readBytes(buffer, sizeof(buffer));
uint8_t address = buffer[0];
uint8_t block = buffer[1];
char state = buffer[2];
if (state == Command::Enable) {
Respond(setRswp(address, block));
}
else if (state == Command::Disable) {
Respond(clearRswp(address));
}
else if (state == Command::Get) {
Respond(getRswp(address, block));
}
else {
Respond(false);
}
}
void cmdPSWP() {
uint8_t buffer[2] = { 0 };
PORT.readBytes(buffer, sizeof(buffer));
uint8_t address = buffer[0];
char state = buffer[1];
if (state == Command::Enable) {
Respond(setPswp(address));
}
else if (state == Command::Get) {
Respond(getPswp(address));
}
else {
Respond(false);
}
}
void cmdWriteTest() {
uint8_t buffer[3] = { 0 };
PORT.readBytes(buffer, sizeof(buffer));
uint8_t address = buffer[0];
uint16_t offset = buffer[1] << 8 | buffer[2];
uint8_t data[1];
Respond(readByte(address, offset, 1, data) && writeByte(address, offset, data[0]));
}
void cmdPinControl() {
uint8_t buffer[2] = { 0 };
PORT.readBytes(buffer, sizeof(buffer));
uint8_t pin = buffer[0];
uint8_t state = buffer[1];
if (pin == SA1_SWITCH) {
if (state == Command::Enable || state == Command::Disable) {
Respond(setConfigPin(SA1_EN, state));
}
else if (state == Command::Get) {
Respond(getConfigPin(SA1_EN));
}
else {
Respond(false);
}
}
else if (pin == HV_SWITCH) {
if (state == Command::Enable || state == Command::Disable) {
Respond(setHighVoltage(state));
}
else if (state == Command::Get) {
Respond(getHighVoltage());
}
else {
Respond(0);
}
}
else {
Respond(false);
}
}
void cmdPinReset() {
Respond(resetPins());
}
/* -= Read/Write functions =- */
bool readByte(uint8_t address, uint16_t offset, uint8_t length, uint8_t* data) {
uint8_t _offset = lowByte(offset);
if (ddr5Detect(address)) {
_offset |= 0x80;
}
adjustPageAddress(address, offset);
Wire.beginTransmission(address);
Wire.write(_offset);
if (Wire.endTransmission(false) != 0) {
return false;
}
Wire.requestFrom(address, length);
while (Wire.available() < length) {}
for (uint8_t i = 0; i < length; i++) {
while (!Wire.available()) {}
data[i] = Wire.read();
}
return true;
}
bool writeByte(uint8_t address, uint16_t offset, uint8_t data) {
uint8_t input[1] = { data };
return writePage(address, offset, 1, input);
}
bool writePage(uint8_t address, uint16_t offset, uint8_t length, uint8_t* data) {
if ((offset % 16 + length) > 16) {
return false;
}
if (ddr5Detect(address) && ddr5GetOfflineMode()) {
uint8_t block = offset / 64;
if (getRswp(address, block)) {
return false;
}
}
uint16_t _offset = offset;
if (ddr5Detect(address)) {
_offset |= 0x80;
while (bitRead(readReg(address, MR48), 3)) {}
}
adjustPageAddress(address, offset);
Wire.beginTransmission(address);
Wire.write((uint8_t)(_offset));
Wire.write(data, length);
uint8_t status = Wire.endTransmission();
delay(10);
return status == 0;
}
uint8_t readReg(uint8_t address, uint8_t memReg) {
return readReg(address, memReg, true);
}
uint8_t readReg(uint8_t address, uint8_t memReg, bool resetPage) {
if (memReg >= 128) {
return false;
}
if (resetPage) {
adjustPageAddress(address, 0);
}
Wire.beginTransmission(address);
Wire.write(memReg & 0x7F);
uint8_t status = Wire.endTransmission(false);
if (status != 0) {
return false;
}
Wire.requestFrom(address, (uint8_t)1);
while (!Wire.available()) {}
uint8_t output = Wire.read();
return output;
}
bool writeReg(uint8_t address, uint8_t memReg, uint8_t value) {
if ( memReg >= 128 || !(MR11 <= memReg && memReg <= MR13)) {
return false;
}
Wire.beginTransmission(address);
Wire.write(memReg);
Wire.write(value);
uint8_t status = Wire.endTransmission();
delay(memReg == MR11 ? 0 : 10);
return status == 0;
}
/* -= RSWP functions =- */
bool setRswp(uint8_t address, uint8_t block) {
if (block > 15) {
return false;
}
if (ddr5Detect(address)) {
uint8_t memReg = MR12 + bitRead(block, 3);
uint8_t currentValue = readReg(address, memReg);
uint8_t updatedValue = 1 << (block & 0b111);
return writeReg(address, memReg, currentValue | updatedValue);
}
uint8_t commands[] = { SWP0, SWP1, SWP2, SWP3 };
uint8_t cmd = commands[(0 < block && block <= 3) ? block : 0];
bool result = false;
if (setHighVoltage(true)) {
if (block == 0) {
setConfigPin(SA1_EN, false); // Required for pre-DDR4
}
if (block > 0 && !ddr4Detect()) {
result = false;
}
else {
result = probeDeviceTypeId(cmd);
}
resetPins();
}
return result;
}
bool getRswp(uint8_t address, uint8_t block) {
if (ddr5Detect(address)) {
return (block <= 15)
? readReg(address, MR12 + bitRead(block, 3)) & (1 << (block & 0b111))
: false;
}
uint8_t commands[] = { RPS0, RPS1, RPS2, RPS3 };
uint8_t cmd = (0 < block && block <= 3) ? commands[block] : commands[0];
if (block == 0 && !ddr4Detect()) {
setHighVoltage(true);
}
bool status = probeDeviceTypeId(cmd); // true/ack = not protected
resetPins();
return !status; // true = protected or rswp not supported; false = unprotected
}
bool clearRswp(uint8_t address) {
if (ddr5Detect(address)) {
return ddr5GetOfflineMode()
? writeReg(address, MR12, 0) && readReg(address, MR12) == 0 &&
writeReg(address, MR13, 0) && readReg(address, MR13) == 0
: false;
}
if (!ddr4Detect(address)) {
setConfigPin(SA1_EN, true); // Required for pre-DDR4
}
if (setHighVoltage(true)) {
bool result = probeDeviceTypeId(CWP);
resetPins();
return result;
}
return false;
}
uint8_t rswpSupportTest() {
resetPins();
if (!scanBus()) {
return 0;
}
uint8_t rswpSupport = 0;
if (ddr5GetOfflineMode()) {
rswpSupport |= DDR5;
}
if (setHighVoltage(true)) {
rswpSupport |= DDR4;
if ((setConfigPin(SA1_EN, true) && setConfigPin(SA1_EN, false))) {
rswpSupport |= DDR3;
}
}
resetPins();
return rswpSupport;
}
/* -= High Voltage (9V) functions =- */
bool setHighVoltage(bool state) {
digitalWrite(HV_EN, state);
uint64_t timeout = millis() + 25;
while (millis() < timeout) {
if (getHighVoltage() == state) {
return true;
}
}
return false;
}
bool getHighVoltage() {
return getConfigPin(HV_FB);
}
/* -= PSWP functions =- */
bool setPswp(uint8_t address) {
if (ddr4Detect(address) || ddr5Detect(address)) {
return false;
}
uint8_t cmd = (address & 0b111) | (PWPB << 3);
Wire.beginTransmission(cmd);
Wire.write(DNC);
Wire.write(DNC);
int status = Wire.endTransmission();
return status == 0;
}
bool getPswp(uint8_t address) {
uint8_t cmd = (address & 0b111) | (PWPB << 3);
Wire.beginTransmission(cmd);
Wire.write(DNC);
int status = Wire.endTransmission();
return status == 0; // returns true if PSWP is not set
}
/* -= EEPROM Page functions =- */
uint8_t getPageAddress(bool lowLevel = false) {
if (!lowLevel) {
return eepromPageAddress;
}
int8_t status = -1;
TWCR = _BV(TWEN) | _BV(TWINT) | _BV(TWEA) | _BV(TWSTA);
while (!(TWCR & (_BV(TWINT)))) {}
while ((TWSR & 0xF8) != 0x08) {}
TWDR = RPA;
TWCR = _BV(TWEN) | _BV(TWEA) | _BV(TWINT);
while (!(TWCR & (_BV(TWINT)))) {}
status = (TWSR & 0xF8);
if (status == 0x40) {
for (int i = 0; i < 2; i++) {
TWDR = DNC;
TWCR = _BV(TWEN) | _BV(TWEA) | _BV(TWINT);
while (!(TWCR & (_BV(TWINT)))) {}
}
}
TWCR = _BV(TWEN) | _BV(TWINT) | _BV(TWEA) | _BV(TWSTO);
switch (status) {
case 0x40: return 0;
case 0x48: return 1;
default: return status;
}
}
void setDdr4PageAddress(uint8_t pageNumber) {
probeDeviceTypeId((pageNumber == 0) ? SPA0 : SPA1);
eepromPageAddress = pageNumber;
}
void adjustPageAddress(uint8_t address, uint16_t offset) {
if (!validateEepromAddress(address) || offset >= 1024) {
return;
}
int8_t page;
if (ddr5Detect(address)) {
page = offset >> 7; // DDR5 page
writeReg(address, MR11, page);
return;
}
if (offset < 512) {
page = bitRead(offset, 8); // DDR4 page
if (getPageAddress() != page) {
setDdr4PageAddress(page);
eepromPageAddress = page;
}
}
}
/* -= Device settings functions =- */
bool setName(String name) {
for (uint8_t i = 0; i < name.length(); i++) {
EEPROM.update(i, name[i]);
}
EEPROM.update(name.length(), 0);
return name == getName();
}
String getName() {
char deviceNameChar[NAMELENGTH + 1];
for (uint8_t i = 0; i < NAMELENGTH; i++) {
deviceNameChar[i] = EEPROM.read(i);
}
deviceNameChar[NAMELENGTH] = 0;
return deviceNameChar;
}
bool getSettings(uint8_t name) {
return bitRead(EEPROM.read(DEVICESETTINGS), name);
}
bool saveSettings(uint8_t name, uint8_t value) {
uint8_t currentSettings = EEPROM.read(DEVICESETTINGS);
EEPROM.update(DEVICESETTINGS, bitWrite(currentSettings, name, value));
return getSettings(name) == value;
}
/* -= I2C bus functions =- */
bool setI2cClockMode(bool mode) {
saveSettings(CLOCKMODE, mode ? FASTMODE : STDMODE);
Wire.setClock(clock[mode]);
return getI2cClockMode() == mode;
}
bool getI2cClockMode() {
return getSettings(CLOCKMODE);
}
uint8_t scanBus() {
return scanBus(80, 87);
}
uint8_t scanBus(uint8_t startAddress, uint8_t endAddress) {
uint8_t totalAddresses = endAddress - startAddress;
if (totalAddresses > 7) {
return 0;
}
uint8_t response = 0;
for (uint8_t i = 0; i <= totalAddresses; i++) {
if (probeBusAddress(i + startAddress)) {
response |= 1 << i;
}
}
return response;
}
uint8_t getQuantity() {
return bitCount(scanBus());
}
uint8_t bitCount(uint8_t bitMask) {
if (bitMask == 0) {
return 0;
}
uint8_t quantity = 0;
for (uint8_t i = 0; i <= 7; i++) {
if (bitRead(bitMask, i)) {
quantity++;
}
}
return quantity;
}
void i2cMonitor() {
if (cmdExecuting) {
return;
}
bool i2cPause = false;
if (!digitalRead(HV_EN) && !digitalRead(HV_FB)) {
slaveCountCurrent = getQuantity();
if (slaveCountCurrent != slaveCountLast) {
uint8_t buffer[] = { ALERT, (uint8_t)(slaveCountCurrent < slaveCountLast ? SLAVEDEC : SLAVEINC) };
PORT.write(buffer, sizeof(buffer));
slaveCountLast = slaveCountCurrent;
i2cPause = true;
}
}
i2cClockCurrent = getI2cClockMode();
if (i2cClockCurrent != i2cClockLast) {
uint8_t buffer[] = { ALERT, (uint8_t)(i2cClockCurrent < i2cClockLast ? CLOCKDEC : CLOCKINC) };
PORT.write(buffer, sizeof(buffer));
i2cClockLast = i2cClockCurrent;
}
if (i2cPause) {
delay(10);
}
PORT.flush();
}
bool setConfigPin(uint8_t pin, bool state) {
digitalWrite(pin, state);
if (pin == SA1_EN) {
uint64_t timeout = millis() + 10;
while (millis() < timeout) {
if (scanBus() & (state ? A1_MASK : ~A1_MASK)) {
return true;
}
}
return false;
}
return getConfigPin(pin) == state;
}
bool getConfigPin(uint8_t pin) {
return pin == SA1_EN ? scanBus() & A1_MASK : digitalRead(pin);
}
bool resetPins() {
return setHighVoltage(ConfigPin[HV_SWITCH].defaultState) &&
setConfigPin(ConfigPin[SA1_SWITCH].name, ConfigPin[SA1_SWITCH].defaultState);
}
void resetPinsInternal() {
for (uint8_t i = 0; i < pinCount; i++) {
if (ConfigPin[i].mode == OUTPUT) {
digitalWrite(ConfigPin[i].name, ConfigPin[i].defaultState);
}
}
}
bool ddr5GetOfflineMode() {
return ddr5Detect(80) && bitRead(readReg(80, MR48), 2);
}
bool probeBusAddress(uint8_t address) {
Wire.beginTransmission(address);
return Wire.endTransmission(false) == 0;
}
bool probeDeviceTypeId(uint8_t deviceSelectCode) {
uint8_t status = 0;
bool writeBit = !bitRead(deviceSelectCode, 0);
uint8_t cmd = deviceSelectCode >> 1;
Wire.beginTransmission(cmd);
if (writeBit) {
Wire.write(DNC);
Wire.write(DNC);
}
status = Wire.endTransmission();
if (writeBit) {
return status == 0;
}
return Wire.requestFrom(cmd, (uint8_t)1) > 0; // true when ACK is received after control byte
}
bool ddr4Detect(uint8_t address) {
if (!address) {
return ddr4Detect();
}
return probeBusAddress(address) && ddr4Detect() && !ddr5Detect(address);
}
bool ddr4Detect() {
setDdr4PageAddress(0);
return getQuantity() > 0 && getPageAddress(true) == 0;
}
bool ddr5Detect(uint8_t address) {
if (!validateEepromAddress(address) ||
!probeBusAddress(address) ||
!probeBusAddress((address & 0b111) | PMIC)) {
return false;
}
writeReg(address, MR11, 0);
if (readReg(address, MR0, false) == highByte(SPD5_TS)) {
uint8_t mr1 = readReg(address, MR1, false);
return mr1 == lowByte(SPD5_TS) || mr1 == lowByte(SPD5_NO);
}
return false;
}
bool validateEepromAddress(uint8_t address) {
return address >> 3 == 0b1010;
}
bool validatePmicAddress(uint8_t address) {
return address >> 3 == PMIC >> 3;
}
bool factoryReset() {
for (uint8_t i = 0; i <= 32; i++) {
EEPROM.update(i, 0);
}
for (uint8_t i = 0; i <= 32; i++) {
if (EEPROM.read(i) != 0) {
return false;
}
}
return true;
}