velxio/test/test_custom_chips/sdk/examples/eeprom-24c01.c

91 lines
2.5 KiB
C
Raw Normal View History

/*
* eeprom-24c01.c Velxio custom chip implementing a 24C01-class I2C EEPROM.
*
* 128 bytes (datasheet says 1 Kbit = 128 × 8). Address pins A0..A2 set the
* three low bits of the 7-bit I2C address (base 0x50).
*
* Protocol on the I2C bus:
* write 1 byte set the read/write address pointer
* write 2+ bytes first sets address pointer, rest stores data sequentially
* read returns memory at pointer, autoincrements
*
* Build:
* bash scripts/compile-chip.sh sdk/examples/eeprom-24c01.c fixtures/eeprom-24c01.wasm
*/
#include "velxio-chip.h"
#include <stdlib.h>
#include <string.h>
#define EEPROM_BASE_ADDR 0x50
#define EEPROM_SIZE 128
typedef enum {
ST_IDLE, /* before any byte after addressing */
ST_HAS_POINTER, /* received the pointer byte; further writes are data */
} ee_state;
typedef struct {
vx_pin a0, a1, a2;
uint8_t pointer;
uint8_t mem[EEPROM_SIZE];
ee_state state;
} chip_state_t;
static bool i2c_connect(void* ud, uint8_t address, bool is_read) {
chip_state_t* s = (chip_state_t*)ud;
/* On a fresh transaction, reads use the existing pointer; writes restart. */
if (!is_read) s->state = ST_IDLE;
return true; /* ACK */
}
static uint8_t i2c_read(void* ud) {
chip_state_t* s = (chip_state_t*)ud;
uint8_t b = s->mem[s->pointer & (EEPROM_SIZE - 1)];
s->pointer = (s->pointer + 1) & (EEPROM_SIZE - 1);
return b;
}
static bool i2c_write(void* ud, uint8_t byte) {
chip_state_t* s = (chip_state_t*)ud;
if (s->state == ST_IDLE) {
s->pointer = byte & (EEPROM_SIZE - 1);
s->state = ST_HAS_POINTER;
} else {
s->mem[s->pointer & (EEPROM_SIZE - 1)] = byte;
s->pointer = (s->pointer + 1) & (EEPROM_SIZE - 1);
}
return true; /* ACK */
}
static void i2c_stop(void* ud) {
chip_state_t* s = (chip_state_t*)ud;
s->state = ST_IDLE;
}
void chip_setup(void) {
chip_state_t* s = (chip_state_t*)calloc(1, sizeof(chip_state_t));
s->a0 = vx_pin_register("A0", VX_INPUT);
s->a1 = vx_pin_register("A1", VX_INPUT);
s->a2 = vx_pin_register("A2", VX_INPUT);
uint8_t addr = EEPROM_BASE_ADDR
| ((vx_pin_read(s->a2) & 1) << 2)
| ((vx_pin_read(s->a1) & 1) << 1)
| ((vx_pin_read(s->a0) & 1));
vx_i2c_config cfg = {
.address = addr,
.scl = vx_pin_register("SCL", VX_INPUT),
.sda = vx_pin_register("SDA", VX_INPUT),
.on_connect = i2c_connect,
.on_read = i2c_read,
.on_write = i2c_write,
.on_stop = i2c_stop,
.user_data = s,
};
vx_i2c_attach(&cfg);
vx_log("24C01 EEPROM ready");
}