8 * This is a simulation of SPD EEPROM I2C slave.
9 * It only supports basic read/write commands.
10 * Although it has been written with SPD EEPROM chips in mind, it should be
11 * compatible with some other EEPROM chips that use single byte addressing.
13 * Some details can be controlled using defines/environmental variables:
14 * #define SPD_EEPROM_ADDR 7bit address of I2C slave
15 * #define DEBUG_SPD_EEPROM print debug messages
16 * env SPD_EEPROM_FILE load memory contents from file
19 #define SPD_EEPROM_ADDR 0b000
21 #ifdef DEBUG_SPD_EEPROM
22 #define DBG(...) do{ fprintf(stderr, __VA_ARGS__); } while(0)
24 #define DBG(...) do{ } while (0)
27 // state of the serial-to-parallel FSM
30 WRITE
, // slave writing a byte to master
31 READ
, // slave reading a byte from master
32 RACK_0
, // slave starts sending ACK
33 RACK_1
, // slave finishes sending ACK
34 WACK
, // slave reads ACK
37 // state of the transaction FSM
38 enum TransactionState
{
39 DEV_ADDR
, // reading slave device address
40 WRITE_ADDR
, // master writes address
41 WRITE_DATA
, // master writes data
42 READ_DATA
, // master reads data
47 // DUT pads (need separate SDA io/out as Verilator does not support tristate pins)
52 // SPD EEPROM memory contents
53 unsigned char mem
[256];
55 enum TransactionState state_transaction
;
56 enum SerialState state_serial
;
58 unsigned int byte_out
;
59 unsigned int bit_counter
;
65 static int spdeeprom_start();
66 static int spdeeprom_new(void **sess
, char *args
);
67 static int spdeeprom_add_pads(void *sess
, struct pad_list_s
*plist
);
68 static int spdeeprom_tick(void *sess
, uint64_t time_ps
);
70 static void fsm_tick(struct session_s
*s
);
71 static enum SerialState
state_serial_next(struct session_s
*s
);
73 static void spdeeprom_from_file(struct session_s
*s
, FILE *file
);
74 static int litex_sim_module_pads_get(struct pad_s
*pads
, char *name
, void **signal
);
76 /*** Module interface *****************************************************************************/
78 static struct ext_module_s ext_mod
= {
87 int litex_sim_ext_module_init(int (*register_module
)(struct ext_module_s
*))
90 ret
= register_module(&ext_mod
);
94 static int spdeeprom_start()
96 printf("[spdeeprom] loaded (addr = 0x%01x)\n", SPD_EEPROM_ADDR
);
100 static int spdeeprom_new(void **sess
, char *args
)
107 struct session_s
*s
=NULL
;
114 s
= (struct session_s
*) malloc(sizeof(struct session_s
));
119 memset(s
, 0, sizeof(struct session_s
));
121 spd_filename
= getenv("SPD_EEPROM_FILE");
122 if (spd_filename
!= NULL
) {
123 spd_file
= fopen(spd_filename
, "r");
125 if (spd_filename
!= NULL
&& spd_file
!= NULL
) {
126 printf("[spdeeprom] loading EEPROM contents from file: %s\n", spd_filename
);
127 spdeeprom_from_file(s
, spd_file
);
129 } else { // fill in the memory with some data
130 for (i
= 0; i
< sizeof(s
->mem
) / sizeof(s
->mem
[0]); ++i
) {
131 s
->mem
[i
] = i
& 0xff;
140 static int spdeeprom_add_pads(void *sess
, struct pad_list_s
*plist
)
143 struct session_s
*s
= (struct session_s
*) sess
;
146 if(!sess
|| !plist
) {
152 if(!strcmp(plist
->name
, "i2c")) {
153 litex_sim_module_pads_get(pads
, "sda_in", (void**) &s
->sda_in
);
154 litex_sim_module_pads_get(pads
, "sda_out", (void**) &s
->sda_out
);
155 litex_sim_module_pads_get(pads
, "scl", (void**) &s
->scl
);
158 if(!strcmp(plist
->name
, "sys_clk"))
159 litex_sim_module_pads_get(pads
, "sys_clk", (void**) &s
->sys_clk
);
165 static int spdeeprom_tick(void *sess
, uint64_t time_ps
)
167 static struct clk_edge_t edge
;
168 struct session_s
*s
= (struct session_s
*) sess
;
170 if (s
->sda_in
== 0 || s
->sda_out
== 0 || s
->scl
== 0) {
174 if(!clk_pos_edge(&edge
, *s
->sys_clk
)) {
183 /*** Simulation ***********************************************************************************/
185 #ifdef DEBUG_SPD_EEPROM
186 static inline const char *state_serial_str(enum SerialState s
)
189 case IDLE
: return "IDLE";
190 case WRITE
: return "WRITE";
191 case READ
: return "READ";
192 case RACK_0
: return "RACK_0";
193 case RACK_1
: return "RACK_1";
194 case WACK
: return "WACK";
199 static inline const char *state_transaction_str(enum TransactionState s
)
202 case DEV_ADDR
: return "DEV_ADDR";
203 case WRITE_ADDR
: return "WRITE_ADDR";
204 case WRITE_DATA
: return "WRITE_DATA";
205 case READ_DATA
: return "READ_DATA";
211 static void fsm_tick(struct session_s
*s
)
213 static int sda_last
= 1;
214 static int scl_last
= 1;
216 enum SerialState last_state_serial
;
218 int sda_falling_edge
;
224 sda_rising_edge
= !sda_last
&& *s
->sda_out
;
225 sda_falling_edge
= sda_last
&& !*s
->sda_out
;
226 start_cond
= sda_falling_edge
&& *s
->scl
;
227 stop_cond
= sda_rising_edge
&& *s
->scl
;
228 scl_rising
= !scl_last
&& *s
->scl
;
229 scl_falling
= scl_last
&& !*s
->scl
;
231 sda_last
= *s
->sda_out
;
235 DBG("[spdeeprom] START condition\n");
236 s
->state_serial
= READ
;
237 s
->state_transaction
= DEV_ADDR
;
241 DBG("[spdeeprom] STOP condition\n");
242 s
->state_serial
= IDLE
;
243 s
->state_transaction
= DEV_ADDR
;
246 last_state_serial
= s
->state_serial
;
248 switch (s
->state_serial
) {
253 if (s
->bit_counter
== 0) {
258 s
->byte_in
|= *s
->sda_out
& 1;
261 if (s
->bit_counter
>= 8) {
263 s
->state_serial
= RACK_0
;
268 *s
->sda_in
= (s
->byte_out
& (1 << 7)) != 0;
272 if (s
->bit_counter
>= 8) {
274 s
->state_serial
= WACK
;
277 case RACK_0
: // first falling edge
280 s
->state_serial
= RACK_1
;
283 case RACK_1
: // second falling edge
286 s
->state_serial
= state_serial_next(s
);
291 if ((*s
->sda_out
) != 0) {
292 DBG("[spdeeprom] No ACK from master!\n");
294 s
->state_serial
= state_serial_next(s
);
297 default: DBG("[spdeeprom] unknown state_serial\n"); break;
300 if (s
->state_serial
!= last_state_serial
) {
301 DBG("[spdeeprom] state_serial: %s -> %s\n",
302 state_serial_str(last_state_serial
), state_serial_str(s
->state_serial
));
307 static enum SerialState
state_serial_next(struct session_s
*s
)
309 enum TransactionState state_transaction_last
= s
->state_transaction
;
310 enum SerialState state_serial
= IDLE
;
312 switch (s
->state_transaction
) {
314 if (s
->state_serial
!= RACK_1
) {
315 DBG("[spdeeprom] ERROR: DEV_ADDR during WACK\n");
317 s
->devaddr
= s
->byte_in
;
318 if (((s
->devaddr
& 0b1110) >> 1) != SPD_EEPROM_ADDR
) {
319 DBG("[spdeeprom] ERROR: read wrong address\n");
322 DBG("[spdeeprom] devaddr = 0x%02x\n", s
->devaddr
);
323 if ((s
->devaddr
& 1) != 0) { // read command
324 DBG("[spdeeprom] registered READ cmd\n");
325 s
->state_transaction
= READ_DATA
;
326 s
->byte_out
= s
->mem
[s
->addr
++];
327 s
->addr
%= sizeof(s
->mem
);
328 state_serial
= WRITE
;
329 } else { // write command
330 DBG("[spdeeprom] registered WRITE cmd\n");
331 s
->state_transaction
= WRITE_ADDR
;
337 if (s
->state_serial
!= RACK_1
) {
338 DBG("[spdeeprom] ERROR: WRITE_ADDR during WACK\n");
340 s
->addr
= s
->byte_in
;
341 s
->state_transaction
= WRITE_DATA
;
342 DBG("[spdeeprom] addr = 0x%02x\n", s
->addr
);
346 if (s
->state_serial
!= RACK_1
) {
347 DBG("[spdeeprom] ERROR: WRITE_DATA during WACK\n");
349 s
->mem
[s
->addr
++] = s
->byte_in
;
350 s
->addr
%= sizeof(s
->mem
);
351 s
->state_transaction
= WRITE_DATA
;
352 DBG("[spdeeprom] wdata = 0x%02x\n", s
->byte_in
);
356 if (s
->state_serial
!= WACK
) {
357 DBG("[spdeeprom] ERROR: READ_DATA during RACK\n");
359 s
->state_transaction
= READ_DATA
;
360 s
->byte_out
= s
->mem
[s
->addr
++];
361 DBG("[spdeeprom] rdata = 0x%02x\n", s
->byte_out
);
362 state_serial
= WRITE
;
365 DBG("[spdeeprom] ERROR: wrong state_transaction!\n");
369 if (state_serial
== IDLE
) {
370 DBG("[spdeeprom] ERROR: unhandled state_serial_next\n");
372 if (state_transaction_last
!= s
->state_transaction
) {
373 DBG("[spdeeprom] state_transaction: %s -> %s\n", state_transaction_str(state_transaction_last
), state_transaction_str(s
->state_transaction
));
378 /*** Helper functions *****************************************************************************/
380 static void spdeeprom_from_file(struct session_s
*s
, FILE *file
)
389 for (i
= 0; i
< sizeof(s
->mem
) / sizeof(s
->mem
[0]); ++i
) {
390 if ((n_read
= getline(&line
, &bufsize
, file
)) < 0) {
393 byte
= strtoul(line
, &c
, 16);
395 DBG("[spdeeprom] Incorrect value at line %d\n", i
);
405 static int litex_sim_module_pads_get(struct pad_s
*pads
, char *name
, void **signal
)
411 if(!pads
|| !name
|| !signal
) {
417 while(pads
[i
].name
) {
418 if(!strcmp(pads
[i
].name
, name
))
420 sig
= (void*) pads
[i
].signal
;