91 lines
2.5 KiB
C
91 lines
2.5 KiB
C
|
|
/*
|
|||
|
|
* 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");
|
|||
|
|
}
|