Skip to content
Open
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
9 changes: 6 additions & 3 deletions sw/device/examples/i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,13 @@ int main(void)
while (true) {
timer_busy_sleep_us(timer, 1000u);

uint8_t w_data = 0;
// Read current temperature from an AS6212 I^2C-bus sensor and print the value
if (i2c_write_byte(i2c, 0x48u, 0u)) { // select TVAL reg; also a presence check
uint16_t sensor_reading = i2c_read_byte(i2c, 0x48u); // read TVAl reg
if (sensor_reading != 0xFF) { // only print if we get a non-error value
i2c_write_bytes(i2c, 0x48u, &w_data, 1);
if (i2c_wait_write_finish(i2c)) { // select TVAL reg; also a presence check
i2c_read_bytes(i2c, 0x48u, 1);
if (i2c_wait_read_finish(i2c)) {
uint16_t sensor_reading = i2c_rdata_byte(i2c); // read TVAl reg
uprintf(uart, "Temperature: 0x%x degC\n",
(sensor_reading << 1)); // no decimal printf
}
Expand Down
93 changes: 66 additions & 27 deletions sw/device/lib/hal/i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,10 @@ void i2c_init(i2c_t i2c)
VOLATILE_WRITE(i2c->timing4, t4_reg);
}

bool i2c_write_byte(i2c_t i2c, uint8_t addr, uint8_t data)
void i2c_write_bytes(i2c_t i2c, uint8_t addr, const uint8_t *data, uint8_t len_bytes)
{
// Reset FMT FIFO (because we currently don't clean-up after errors)
// Reset the FMT FIFO as a precautionary step in case something goes wrong when controller's FSM
// is halted and the SW didn't manage to clear the FIFO during that scenario.
i2c_fifo_ctrl fifo_ctrl_reg = { .fmtrst = 1u };
VOLATILE_WRITE(i2c->fifo_ctrl, fifo_ctrl_reg);

Expand All @@ -74,31 +75,30 @@ bool i2c_write_byte(i2c_t i2c, uint8_t addr, uint8_t data)
fdata_reg.start = 1u;
VOLATILE_WRITE(i2c->fdata, fdata_reg);

// Send stop bit and data
fdata_reg.fbyte = data;
fdata_reg.start = 0;
fdata_reg.stop = 1u;
VOLATILE_WRITE(i2c->fdata, fdata_reg);

// Wait for transaction to complete and report simple succeed/fail
for (uint32_t ii = 0; ii < 10000000ul /*arbitrary number*/; ii++) {
i2c_intr i2c_intr_state_reg = VOLATILE_READ(i2c->intr_state);
if (i2c_intr_state_reg & i2c_intr_controller_halt) {
return false; // transaction failed
for (uint8_t i = 0; i < len_bytes; i++) {
// Check the overflow condition before writing to the FMT FIFO.
i2c_status i2c_status_reg = VOLATILE_READ(i2c->status);

// Wait until FMT FIFO has some space
while (i2c_status_reg & i2c_status_fmtfull) {
i2c_status_reg = VOLATILE_READ(i2c->status);
}
if (i2c_intr_state_reg & i2c_intr_cmd_complete) {
i2c_status i2c_status_reg = VOLATILE_READ(i2c->status);
if (i2c_status_reg & i2c_status_fmtempty) {
return true; // transaction succeeded
}

// Send all data bytes; assert STOP only on the last byte
fdata_reg.fbyte = data[i];
if (i == (len_bytes - 1u)) {
fdata_reg.stop = 1u;
}
VOLATILE_WRITE(i2c->fdata, fdata_reg);
Comment thread
KinzaQamar marked this conversation as resolved.
}
return false; // timeout
}

uint8_t i2c_read_byte(i2c_t i2c, uint8_t addr)
void i2c_read_bytes(i2c_t i2c, uint8_t addr, uint8_t len_bytes)
{
// Reset FMT FIFO (because we currently don't clean-up after errors)
// Reset the FMT FIFO as a precautionary step in case something goes wrong when controller's FSM
// is halted and the SW didn't manage to clear the FIFO during that scenario.
i2c_fifo_ctrl fifo_ctrl_reg = { .fmtrst = 1u };
VOLATILE_WRITE(i2c->fifo_ctrl, fifo_ctrl_reg);
Comment on lines +100 to 103
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@elliotb-lowrisc Do we still need to keep this precautionary step before the start of every read and write no that we are clearing on errors?


Expand All @@ -112,28 +112,67 @@ uint8_t i2c_read_byte(i2c_t i2c, uint8_t addr)

// Send stop bit, read bit and number of bytes to read
fdata_reg.readb = 1u;
fdata_reg.fbyte = 1u; // If readb = 1 then fbyte contains the number of bytes to read
fdata_reg.fbyte = len_bytes; // If readb = 1 then fbyte contains the number of bytes to read
fdata_reg.start = 0;
fdata_reg.stop = 1u;
VOLATILE_WRITE(i2c->fdata, fdata_reg);
}

bool i2c_wait_write_finish(i2c_t i2c)
{
// Wait for transaction to complete and report simple succeed / fail
while (true) {
i2c_intr i2c_intr_state_reg = VOLATILE_READ(i2c->intr_state);
if (i2c_intr_state_reg & i2c_intr_controller_halt) {
// Reset FMT FIFO as controller's FSM is in halt
i2c_fifo_ctrl fifo_ctrl_reg = { .fmtrst = 1u };
VOLATILE_WRITE(i2c->fifo_ctrl, fifo_ctrl_reg);

// Wait for transaction to complete and return either read data or 0xFF
for (uint32_t ii = 0; ii < 10000000ul /*arbitrary number*/; ii++) {
// According to programmer's guide, the CONTROLLER_EVENTS register would be cleared
// here to acknowledge the controller halt interrupt. However, since we want to
// treat a halt event as a failure, we intentionally skip clearing it.
return false; // Transaction failed
}
if (i2c_intr_state_reg & i2c_intr_cmd_complete) {
i2c_status i2c_status_reg = VOLATILE_READ(i2c->status);
if (i2c_status_reg & i2c_status_fmtempty) {
return true; // Transaction succeeded
}
}
}
return false; // Timeout
}

bool i2c_wait_read_finish(i2c_t i2c)
{
// Wait for transaction to complete and report simple succeed / fail
while (true) {
i2c_intr i2c_intr_state_reg = VOLATILE_READ(i2c->intr_state);
if (i2c_intr_state_reg & i2c_intr_controller_halt) {
return 0xFF; // transaction failed
// Reset FMT FIFO as controller's FSM is in halt
i2c_fifo_ctrl fifo_ctrl_reg = { .fmtrst = 1u };
VOLATILE_WRITE(i2c->fifo_ctrl, fifo_ctrl_reg);

// According to programmer's guide, the CONTROLLER_EVENTS register would be cleared
// here to acknowledge the controller halt interrupt. However, since we want to
// treat a halt event as a failure, we intentionally skip clearing it.
return false; // Transaction failed
}
i2c_status i2c_status_reg = VOLATILE_READ(i2c->status);
if (i2c_status_reg & i2c_status_fmtempty) {
// transaction succeeded, return read data
i2c_rdata rdata_reg = VOLATILE_READ(i2c->rdata);
return rdata_reg.rdata;
return true;
}
}
return 0xFF; // timeout
return false; // Timeout
}

void enable_controller_mode(i2c_t i2c)
{
VOLATILE_WRITE(i2c->ctrl, i2c_ctrl_enablehost);
}

uint8_t i2c_rdata_byte(i2c_t i2c)
{
i2c_rdata rdata_reg = VOLATILE_READ(i2c->rdata);
return rdata_reg.rdata;
}
16 changes: 14 additions & 2 deletions sw/device/lib/hal/i2c.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,20 @@

void i2c_init(i2c_t i2c);

bool i2c_write_byte(i2c_t i2c, uint8_t addr, uint8_t data);
uint8_t i2c_read_byte(i2c_t i2c, uint8_t addr);
// Transmits multiple bytes to the target
void i2c_write_bytes(i2c_t i2c, uint8_t addr, const uint8_t *data, uint8_t len_bytes);

// Receive multiple bytes from the target
void i2c_read_bytes(i2c_t i2c, uint8_t addr, uint8_t len_bytes);

// Wait for the read transfer to complete by checking interrupt state and status register fields
bool i2c_wait_read_finish(i2c_t i2c);

// Wait for the write transfer to complete by checking interrupt state and status register fields
bool i2c_wait_write_finish(i2c_t i2c);

// Enable I2C in controller mode
void enable_controller_mode(i2c_t i2c);

// Return the data in the target's tx fifo
uint8_t i2c_rdata_byte(i2c_t i2c);
18 changes: 15 additions & 3 deletions sw/device/tests/i2c/smoketest.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,27 @@
// Read temperature from an AS6212 sensor (default address 0x48)
static bool as6212_test(i2c_t i2c)
{
uint8_t w_data = 0;

// Write the desired register index
if (!i2c_write_byte(i2c, 0x48u, 0u)) {
i2c_write_bytes(i2c, 0x48u, &w_data, 1);

// Check if the write was successful
if (!i2c_wait_write_finish(i2c)) {
return false;
}

// Read current temperature
uint8_t byte = i2c_read_byte(i2c, 0x48u);
if (byte == 0xFFu /* error value */) {
i2c_read_bytes(i2c, 0x48u, 1);

// Check if the read was successful
if (!i2c_wait_read_finish(i2c)) {
return false;
}

// If the read was successful, then retrieve the data from the fifo.
uint8_t byte = i2c_rdata_byte(i2c);

int16_t tval = byte; // signed, as temperature can be negative
tval <<= 8; // first byte is the most-significant byte of two
tval >>= 7; // convert from units of 1/128 degC to 1 degC
Expand Down