build/sim: improve timebase calculation (strict checks) and update modules
[litex.git] / litex / build / sim / core / modules / spdeeprom / spdeeprom.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include "error.h"
5 #include "modules.h"
6
7 /*
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.
12 *
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
17 */
18
19 #define SPD_EEPROM_ADDR 0b000
20
21 #ifdef DEBUG_SPD_EEPROM
22 #define DBG(...) do{ fprintf(stderr, __VA_ARGS__); } while(0)
23 #else
24 #define DBG(...) do{ } while (0)
25 #endif
26
27 // state of the serial-to-parallel FSM
28 enum SerialState {
29 IDLE,
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
35 };
36
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
43 };
44
45 // module state
46 struct session_s {
47 // DUT pads (need separate SDA io/out as Verilator does not support tristate pins)
48 char *sys_clk;
49 char *sda_in;
50 char *sda_out;
51 char *scl;
52 // SPD EEPROM memory contents
53 unsigned char mem[256];
54 // state machine
55 enum TransactionState state_transaction;
56 enum SerialState state_serial;
57 unsigned int byte_in;
58 unsigned int byte_out;
59 unsigned int bit_counter;
60 unsigned int devaddr;
61 unsigned int addr;
62 };
63
64 // Module interface
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);
69 // EEPROM simulation
70 static void fsm_tick(struct session_s *s);
71 static enum SerialState state_serial_next(struct session_s *s);
72 // Helper functions
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);
75
76 /*** Module interface *****************************************************************************/
77
78 static struct ext_module_s ext_mod = {
79 "spdeeprom",
80 spdeeprom_start,
81 spdeeprom_new,
82 spdeeprom_add_pads,
83 NULL,
84 spdeeprom_tick
85 };
86
87 int litex_sim_ext_module_init(int (*register_module)(struct ext_module_s *))
88 {
89 int ret = RC_OK;
90 ret = register_module(&ext_mod);
91 return ret;
92 }
93
94 static int spdeeprom_start()
95 {
96 printf("[spdeeprom] loaded (addr = 0x%01x)\n", SPD_EEPROM_ADDR);
97 return RC_OK;
98 }
99
100 static int spdeeprom_new(void **sess, char *args)
101 {
102 int ret = RC_OK;
103 int i;
104 char *spd_filename;
105 FILE *spd_file;
106
107 struct session_s *s=NULL;
108
109 if(!sess) {
110 ret = RC_INVARG;
111 goto out;
112 }
113
114 s = (struct session_s*) malloc(sizeof(struct session_s));
115 if(!s) {
116 ret=RC_NOENMEM;
117 goto out;
118 }
119 memset(s, 0, sizeof(struct session_s));
120
121 spd_filename = getenv("SPD_EEPROM_FILE");
122 if (spd_filename != NULL) {
123 spd_file = fopen(spd_filename, "r");
124 }
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);
128 fclose(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;
132 }
133 }
134
135 out:
136 *sess = (void*) s;
137 return ret;
138 }
139
140 static int spdeeprom_add_pads(void *sess, struct pad_list_s *plist)
141 {
142 int ret = RC_OK;
143 struct session_s *s = (struct session_s*) sess;
144 struct pad_s *pads;
145
146 if(!sess || !plist) {
147 ret = RC_INVARG;
148 goto out;
149 }
150 pads = plist->pads;
151
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);
156 }
157
158 if(!strcmp(plist->name, "sys_clk"))
159 litex_sim_module_pads_get(pads, "sys_clk", (void**) &s->sys_clk);
160
161 out:
162 return ret;
163 }
164
165 static int spdeeprom_tick(void *sess, uint64_t time_ps)
166 {
167 static struct clk_edge_t edge;
168 struct session_s *s = (struct session_s*) sess;
169
170 if (s->sda_in == 0 || s->sda_out == 0 || s->scl == 0) {
171 return RC_OK;
172 }
173
174 if(!clk_pos_edge(&edge, *s->sys_clk)) {
175 return RC_OK;
176 }
177
178 fsm_tick(s);
179
180 return RC_OK;
181 }
182
183 /*** Simulation ***********************************************************************************/
184
185 #ifdef DEBUG_SPD_EEPROM
186 static inline const char *state_serial_str(enum SerialState s)
187 {
188 switch (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";
195 default: return "_";
196 }
197 }
198
199 static inline const char *state_transaction_str(enum TransactionState s)
200 {
201 switch (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";
206 default: return "_";
207 }
208 }
209 #endif
210
211 static void fsm_tick(struct session_s *s)
212 {
213 static int sda_last = 1;
214 static int scl_last = 1;
215
216 enum SerialState last_state_serial;
217 int sda_rising_edge;
218 int sda_falling_edge;
219 int start_cond;
220 int stop_cond;
221 int scl_rising;
222 int scl_falling;
223
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;
230
231 sda_last = *s->sda_out;
232 scl_last = *s->scl;
233
234 if (start_cond) {
235 DBG("[spdeeprom] START condition\n");
236 s->state_serial = READ;
237 s->state_transaction = DEV_ADDR;
238 s->bit_counter = 0;
239 }
240 if (stop_cond) {
241 DBG("[spdeeprom] STOP condition\n");
242 s->state_serial = IDLE;
243 s->state_transaction = DEV_ADDR;
244 }
245
246 last_state_serial = s->state_serial;
247
248 switch (s->state_serial) {
249 case IDLE:
250 *s->sda_in = 1;
251 break;
252 case READ:
253 if (s->bit_counter == 0) {
254 s->byte_in = 0;
255 }
256 if (scl_rising) {
257 s->byte_in <<= 1;
258 s->byte_in |= *s->sda_out & 1;
259 s->bit_counter++;
260 }
261 if (s->bit_counter >= 8) {
262 s->bit_counter = 0;
263 s->state_serial = RACK_0;
264 }
265 break;
266 case WRITE:
267 if (scl_rising) {
268 *s->sda_in = (s->byte_out & (1 << 7)) != 0;
269 s->byte_out <<= 1;
270 s->bit_counter++;
271 }
272 if (s->bit_counter >= 8) {
273 s->bit_counter = 0;
274 s->state_serial = WACK;
275 }
276 break;
277 case RACK_0: // first falling edge
278 if (scl_falling) {
279 *s->sda_in = 0;
280 s->state_serial = RACK_1;
281 }
282 break;
283 case RACK_1: // second falling edge
284 if (scl_falling) {
285 *s->sda_in = 1;
286 s->state_serial = state_serial_next(s);
287 }
288 break;
289 case WACK:
290 if (scl_rising) {
291 if ((*s->sda_out) != 0) {
292 DBG("[spdeeprom] No ACK from master!\n");
293 }
294 s->state_serial = state_serial_next(s);
295 }
296 break;
297 default: DBG("[spdeeprom] unknown state_serial\n"); break;
298 }
299
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));
303 }
304
305 }
306
307 static enum SerialState state_serial_next(struct session_s *s)
308 {
309 enum TransactionState state_transaction_last = s->state_transaction;
310 enum SerialState state_serial = IDLE;
311
312 switch (s->state_transaction) {
313 case DEV_ADDR:
314 if (s->state_serial != RACK_1) {
315 DBG("[spdeeprom] ERROR: DEV_ADDR during WACK\n");
316 }
317 s->devaddr = s->byte_in;
318 if (((s->devaddr & 0b1110) >> 1) != SPD_EEPROM_ADDR) {
319 DBG("[spdeeprom] ERROR: read wrong address\n");
320 state_serial = IDLE;
321 } else {
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;
332 state_serial = READ;
333 }
334 }
335 break;
336 case WRITE_ADDR:
337 if (s->state_serial != RACK_1) {
338 DBG("[spdeeprom] ERROR: WRITE_ADDR during WACK\n");
339 }
340 s->addr = s->byte_in;
341 s->state_transaction = WRITE_DATA;
342 DBG("[spdeeprom] addr = 0x%02x\n", s->addr);
343 state_serial = READ;
344 break;
345 case WRITE_DATA:
346 if (s->state_serial != RACK_1) {
347 DBG("[spdeeprom] ERROR: WRITE_DATA during WACK\n");
348 }
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);
353 state_serial = READ;
354 break;
355 case READ_DATA:
356 if (s->state_serial != WACK) {
357 DBG("[spdeeprom] ERROR: READ_DATA during RACK\n");
358 }
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;
363 break;
364 default:
365 DBG("[spdeeprom] ERROR: wrong state_transaction!\n");
366 break;
367 }
368
369 if (state_serial == IDLE) {
370 DBG("[spdeeprom] ERROR: unhandled state_serial_next\n");
371 }
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));
374 }
375 return state_serial;
376 }
377
378 /*** Helper functions *****************************************************************************/
379
380 static void spdeeprom_from_file(struct session_s *s, FILE *file)
381 {
382 size_t bufsize = 0;
383 ssize_t n_read;
384 char *line = NULL;
385 char *c;
386 unsigned int byte;
387 int i;
388
389 for (i = 0; i < sizeof(s->mem) / sizeof(s->mem[0]); ++i) {
390 if ((n_read = getline(&line, &bufsize, file)) < 0) {
391 break;
392 }
393 byte = strtoul(line, &c, 16);
394 if (c == line) {
395 DBG("[spdeeprom] Incorrect value at line %d\n", i);
396 } else {
397 s->mem[i] = byte;
398 }
399 }
400
401 if (line != NULL)
402 free(line);
403 }
404
405 static int litex_sim_module_pads_get(struct pad_s *pads, char *name, void **signal)
406 {
407 int ret = RC_OK;
408 void *sig=NULL;
409 int i;
410
411 if(!pads || !name || !signal) {
412 ret = RC_INVARG;
413 goto out;
414 }
415
416 i = 0;
417 while(pads[i].name) {
418 if(!strcmp(pads[i].name, name))
419 {
420 sig = (void*) pads[i].signal;
421 break;
422 }
423 i++;
424 }
425
426 out:
427 *signal = sig;
428 return ret;
429 }