Skip to content

Commit

Permalink
Mostly refactored again
Browse files Browse the repository at this point in the history
  • Loading branch information
Pokechu22 committed Jan 14, 2024
1 parent 422eee8 commit 5f20644
Show file tree
Hide file tree
Showing 2 changed files with 163 additions and 80 deletions.
230 changes: 153 additions & 77 deletions Source/Core/Common/I2C.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,44 +32,154 @@ void I2CBusBase::Reset()
m_slaves.clear();
}

int I2CBusSimple::BusRead(u8 slave_addr, u8 addr, int count, u8* data_out)
bool I2CBusBase::StartWrite(u8 slave_addr)
{
if (slave != nullptr)
bool got_ack = false;
for (I2CSlave* slave : m_slaves)
{
for (int i = 0; i < count; i++)
if (slave->StartWrite(slave_addr))
{
// TODO: Does this make sense? The transmitter can't NACK a read... it's the receiver that
// does that
auto byte = slave->ReadByte();
if (byte.has_value())
data_out[i] = byte.value();
else
return i;
if (got_ack)
{
WARN_LOG_FMT(WII_IPC, "Multiple I2C slaves ACKed starting write for I2C addr {:02x}",
slave_addr);
}
got_ack = true;
}
return count;
}
else
return got_ack;
}

bool I2CBusBase::StartRead(u8 slave_addr)
{
bool got_ack = false;
for (I2CSlave* slave : m_slaves)
{
return 0;
if (slave->StartRead(slave_addr))
{
if (got_ack)
{
WARN_LOG_FMT(WII_IPC, "Multiple I2C slaves ACKed starting read for I2C addr {:02x}",
slave_addr);
}
got_ack = true;
}
}
return got_ack;
}

void I2CBusBase::Stop()
{
for (I2CSlave* slave : m_slaves)
slave->Stop();
}

std::optional<u8> I2CBusBase::ReadByte()
{
std::optional<u8> byte = std::nullopt;
for (I2CSlave* slave : m_slaves)
{
std::optional<u8> byte2 = slave->ReadByte();
if (byte.has_value() && byte2.has_value())
{
WARN_LOG_FMT(WII_IPC, "Multiple slaves responded to read: {:02x} vs {:02x}", *byte, *byte2);
}
else if (byte2.has_value())
{
INFO_LOG_FMT(WII_IPC, "I2C: Read {:02x}", byte2.value());
byte = byte2;
}
}
return byte;
}

bool I2CBusBase::WriteByte(u8 value)
{
bool got_ack = false;
for (I2CSlave* slave : m_slaves)
{
if (slave->WriteByte(value))
{
if (got_ack)
{
WARN_LOG_FMT(WII_IPC, "Multiple I2C slaves ACKed write of {:02x}", value);
}
got_ack = true;
}
}
return got_ack;
}

int I2CBusSimple::BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in)
{
I2CSlave* slave = GetSlave(slave_addr);
if (slave != nullptr)
if (!StartWrite(slave_addr))
{
WARN_LOG_FMT(WII_IPC, "I2C: Failed to start write to {:02x} ({:02x}, {:02x})", slave_addr, addr,
count);
Stop();
return 0;
}
if (!WriteByte(addr))
{
for (int i = 0; i < count; i++)
WARN_LOG_FMT(WII_IPC, "I2C: Failed to write device address {:02x} to {:02x} ({:02x})", addr,
slave_addr, count);
Stop();
return 0;
}
for (int i = 0; i < count; i++)
{
if (!WriteByte(data_in[i]))
{
if (!slave->WriteByte(data_in[i]))
return i;
WARN_LOG_FMT(WII_IPC,
"I2C: Failed to byte {} of {} starting at device address {:02x} to {:02x}", i,
count, addr, slave_addr);
Stop();
return i;
}
return count;
}
else
Stop();
return count;
}

int I2CBusSimple::BusRead(u8 slave_addr, u8 addr, int count, u8* data_out)
{
if (!StartWrite(slave_addr))
{
WARN_LOG_FMT(WII_IPC, "I2C: Failed to start write for read from {:02x} ({:02x}, {:02x})",
slave_addr, addr, count);
Stop();
return 0;
}
if (!WriteByte(addr))
{
WARN_LOG_FMT(WII_IPC, "I2C: Failed to write device address {:02x} to {:02x} ({:02x})", addr,
slave_addr, count);
Stop();
return 0;
}
// Note: No Stop() call before StartRead.
if (!StartRead(slave_addr))
{
WARN_LOG_FMT(WII_IPC, "I2C: Failed to start read from {:02x} ({:02x}, {:02x})",
slave_addr, addr, count);
Stop();
return 0;
}
for (int i = 0; i < count; i++)
{
const std::optional<u8> byte = ReadByte();
if (!byte.has_value())
{
WARN_LOG_FMT(WII_IPC,
"I2C: Failed to byte {} of {} starting at device address {:02x} from {:02x}", i,
count, addr, slave_addr);
Stop();
return i;
}
data_out[i] = byte.value();
}
Stop();
return count;
}

bool I2CBus::GetSCL() const
Expand Down Expand Up @@ -120,10 +230,7 @@ void I2CBus::Start()
void I2CBus::Stop()
{
INFO_LOG_FMT(WII_IPC, "AVE: Stop I2C");
for (I2CSlave* slave : m_slaves)
{
slave->Stop();
}
I2CBusBase::Stop();

state = State::Inactive;
bit_counter = 0;
Expand Down Expand Up @@ -178,19 +285,7 @@ void I2CBus::SCLRisingEdge(const bool sda)
if (state == State::ReadFromDevice && bit_counter == 0)
{
// Start of a read.
std::optional<u8> byte = std::nullopt;
for (I2CSlave* slave : m_slaves)
{
std::optional<u8> byte2 = slave->ReadByte();
if (byte.has_value() && byte2.has_value())
{
WARN_LOG_FMT(WII_IPC, "Multiple slaves responded to read: {:02x} vs {:02x}", *byte, *byte2);
}
else if (byte2.has_value())
{
byte = byte2;
}
}
const std::optional<u8> byte = ReadByte();
if (!byte.has_value())
{
WARN_LOG_FMT(WII_IPC, "No slaves responded to I2C read");
Expand Down Expand Up @@ -233,61 +328,42 @@ void I2CBus::SCLFallingEdge(const bool sda)
// Write finished.
if (state == State::SetI2CAddress)
{
bool got_ack = false;
for (I2CSlave* slave : m_slaves)
const u8 slave_addr = current_byte >> 1;
if (current_byte & 1)
{
if (current_byte & 1)
if (StartRead(slave_addr))
{
if (slave->StartRead(current_byte >> 1))
{
if (got_ack)
{
WARN_LOG_FMT(WII_IPC, "Multiple slaves ACKed starting read for I2C addr {:02x}",
current_byte);
}
got_ack = true;
}
// State transition handled by bit_counter >= 8, as we still need to handle the ACK
INFO_LOG_FMT(WII_IPC, "I2C: Start read from {:02x}", slave_addr);
}
else // (current_byte & 1) == 0
else
{
if (slave->StartWrite(current_byte >> 1))
{
if (got_ack)
{
WARN_LOG_FMT(WII_IPC, "Multiple slaves ACKed starting write for I2C addr {:02x}",
current_byte);
}
got_ack = true;
}
state = State::Inactive; // NACK
WARN_LOG_FMT(WII_IPC, "I2C: No device responded to read from {:02x}", current_byte);
}
}

if (!got_ack)
{
state = State::Inactive; // NACK
WARN_LOG_FMT(WII_IPC, "AVE: Unknown I2C address {:02x}", current_byte);
}
else
{
// State transition handled by bit_counter >= 8, as we still need to handle the ACK
INFO_LOG_FMT(WII_IPC, "AVE: I2C address is {:02x}", current_byte);
if (StartWrite(slave_addr))
{
// State transition handled by bit_counter >= 8, as we still need to handle the ACK
INFO_LOG_FMT(WII_IPC, "I2C: Start write to {:02x}", slave_addr);
}
else
{
state = State::Inactive; // NACK
WARN_LOG_FMT(WII_IPC, "I2C: No device responded to write to {:02x}", current_byte);
}
}
}
else
{
// Actual write
ASSERT(state == State::WriteToDevice);
bool got_ack = false;
for (I2CSlave* slave : m_slaves)
if (!WriteByte(current_byte))
{
if (slave->WriteByte(current_byte))
{
if (got_ack)
{
WARN_LOG_FMT(WII_IPC, "Multiple slaves responded to write of {:02x}", current_byte);
}
got_ack = true;
}
state = State::Inactive;
WARN_LOG_FMT(WII_IPC, "I2C: Write of {:02x} NACKed", current_byte);
}
}
}
Expand Down
13 changes: 10 additions & 3 deletions Source/Core/Common/I2C.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ class I2CSlave
virtual bool StartWrite(u8 slave_addr) = 0;
virtual bool StartRead(u8 slave_addr) = 0;
virtual void Stop() = 0;
// NOTE: std::optional is for ease of implementation. I2C does not provide a way for the
// transmitting device to NACK a read; only the receiver can NACK.
virtual std::optional<u8> ReadByte() = 0;
virtual bool WriteByte(u8 value) = 0;
};
Expand Down Expand Up @@ -61,9 +63,14 @@ class I2CBusBase
void Reset();

protected:
// Returns nullptr if there is no match
I2CSlave* GetSlave(u8 slave_addr);
// Signals all slaves on the bus
bool StartWrite(u8 slave_addr);
bool StartRead(u8 slave_addr);
void Stop();
std::optional<u8> ReadByte();
bool WriteByte(u8 value);

private:
// Pointers are unowned:
std::vector<I2CSlave*> m_slaves;
};
Expand All @@ -72,8 +79,8 @@ class I2CBusSimple : public I2CBusBase
{
public:
// TODO: change int to u16 or something
int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out);
int BusWrite(u8 slave_addr, u8 addr, int count, const u8* data_in);
int BusRead(u8 slave_addr, u8 addr, int count, u8* data_out);
};

// An I²C bus implementation accessed via bit-banging.
Expand Down

0 comments on commit 5f20644

Please sign in to comment.