-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path28c256.ino
499 lines (427 loc) · 12.5 KB
/
28c256.ino
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
// EEPROM (28C256) datasheet: https://eater.net/datasheets/28c256.pdf
// This is an EEPROM read/writer. :)
#define USE_CAMINO
#ifdef USE_CAMINO
#include <Camino.h>
#endif
#include "vec/vec.h"
#include "vec/vec.c"
// EEPROM pins to Arduino pins:
// The numbers on the right are the Arduino pin numbers.
#define EEPROM01 22
#define EEPROM02 23
#define EEPROM03 24
#define EEPROM04 25
#define EEPROM05 26
#define EEPROM06 27
#define EEPROM07 28
#define EEPROM08 29
#define EEPROM09 30
#define EEPROM10 31
#define EEPROM11 32
#define EEPROM12 33
#define EEPROM13 34
// EEPROM14 ~~
#define EEPROM15 47
#define EEPROM16 46
#define EEPROM17 45
#define EEPROM18 44
#define EEPROM19 43
#define EEPROM20 42
#define EEPROM21 41
#define EEPROM22 40
#define EEPROM23 39
#define EEPROM24 38
#define EEPROM25 37
#define EEPROM26 36
#define EEPROM27 35
// EEPROM28 ~~
// NOP instruction :) for nanosecond delays
// Each should be 62.5 ns @ 16MHz clock
// (according to the internet)
#define NOP __asm__("nop\n\t")
// The maximum valid value for an address.
#define ADDR_MAX 0x7FFF
// To be able to write to the EEPROM, the write enable pin must be low
// Reference the datasheet for more information.
#define WRITE_ENABLE EEPROM27
// This pin must be low for the chip to work.
// Reference the datasheet for more information.
#define CHIP_ENABLE EEPROM20
// For the EEPROM to output anything to the IO pins, this pin must be low.
// Reference the datasheet for more information.
#define OUTPUT_ENABLE EEPROM22
// A list of all address pins in order from A0 to A14
byte ADDR_PINS[] = {
EEPROM10, EEPROM09, EEPROM08, EEPROM07, EEPROM06, EEPROM05, EEPROM04, EEPROM03,
EEPROM25, EEPROM24, EEPROM21, EEPROM23, EEPROM02, EEPROM26, EEPROM01
};
#define ADDR_PINS_LEN 15
// This is a list of All IO pins that are part of one group (an array :) )
// in order from IO0 to IO7
byte IO_PINS[] = {
EEPROM11, EEPROM12, EEPROM13, EEPROM15, EEPROM16, EEPROM17, EEPROM18, EEPROM19
};
#define IO_PINS_LEN 8
// Stores the current mode of the IO pins
byte IO_PINS_MODE = INPUT;
typedef struct {
// signals if this write job is a page write.
bool is_page_write;
// if is_page_write, then address must be divisible by 64,
// regardless, it must be less than 0x8000.
unsigned int address;
// if is_page_write, then data is a 64 byte array.
// otherwise, it is an array with a single byte.
byte* data;
} write_job;
// A vector of write jobs.
#define vec_write_job write_job*
bool has_write_job = false;
vec_write_job write_jobs = vector_create();
void hexdump() {
#ifdef USE_CAMINO
// Hexdump uses the serial port, therefor
// it is not available while using camino
return;
#else
for (unsigned int i = 0U; i < 0x0100U; i += 16U) {
char* msg = hexdump16(i);
Serial.println(msg);
free(msg);
}
Serial.println("=-------------------------------------------------------------------------=");
#endif
}
// Returns 80 byte (allocated) string formatted in a hexdump manner :)
char* hexdump16(unsigned int address) {
byte data[16];
char visualized[16];
for (unsigned int j = 0U; j < 16U; j++) {
byte b = readEEPROM(address + j);
data[j] = b;
visualized[j] = (isPrintable(b) && !isControl(b)) ? b : '.';
}
char* msg = malloc(80);
sprintf(msg,
"%04x: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x |%.16s|",
address,
data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15], visualized);
return msg;
}
// To keep from interference its best if the data wires (IO0-IO3) on the left side of the chip are NOT
// in line (that is, parallel) with the address pins (A0-A7,A12,A14). :) The closer wires are more important.
void setup() {
// Default control pin settings: (set before pins are enabled)
digitalWrite(CHIP_ENABLE, LOW); // enable the chip
digitalWrite(OUTPUT_ENABLE, LOW); // enable output from the chip
digitalWrite(WRITE_ENABLE, HIGH); // disable writing to the chip
// digitalWrite(LED_BUILTIN, HIGH); // extra 5v pin
// Control pins: always output
pinMode(CHIP_ENABLE, OUTPUT);
pinMode(OUTPUT_ENABLE, OUTPUT);
pinMode(WRITE_ENABLE, OUTPUT);
// pinMode(LED_BUILTIN, OUTPUT);
// Address pins: always output
for (int i = 0; i < ADDR_PINS_LEN; i++) {
pinMode(ADDR_PINS[i], OUTPUT);
}
// Initialize data pins for reading
for (int i = 0; i < IO_PINS_LEN; i++) {
pinMode(IO_PINS[i], INPUT);
}
IO_PINS_MODE = INPUT;
// byte msg[4] = {0xde, 0xad, 0xbe, 0xef};
// for (unsigned int i = 0; i < 0x300U; i++) {
// writeEEPROM(i, msg[i%4]);
// }
// Delay to allow for the chip to power up completely
// and do whatever it is that it does.
// Otherwise, the first 28 or so writes with a page write don't go
// through.
delay(100);
#ifdef USE_CAMINO
camino.begin(115200);
#else
Serial.begin(115200);
#define num_msgs 8
byte msg[num_msgs][4] = {
{ 0xA5, 0xA5, 0xA5, 0xA5 },
{ 0x00, 0xFA, 0xCA, 0xDE },
{ 0xC0, 0xFF, 0xEE, 0x00 },
{ 0xDE, 0xAD, 0xBE, 0xEF },
{ 0xBE, 0xEF, 0xDE, 0xAD },
{ 0xCA, 0xFE, 0xD0, 0x0D },
{ 0xBA, 0xAA, 0xAA, 0xAD },
{ 0x8B, 0xAD, 0xF0, 0x0D }
};
// Cycles through one message per trial
#define TRIAL_COUNT 64
unsigned int count = 0;
for (unsigned int i = 0; i < TRIAL_COUNT; i++) {
byte data[64];
for (int j = 0; j < 64; j++) {
data[j] = msg[i % num_msgs][j % 4];
}
writeEEPROMPage(0x00, data);
writeEEPROMPage(0x40, data);
writeEEPROMPage(0x80, data);
writeEEPROMPage(0xc0, data);
// hexdump();
for (unsigned int a = 0; a < 0x100; a++) {
byte got = readEEPROM(a);
byte expected = msg[i % num_msgs][a % 4];
if (got != expected) {
count++;
char text[40];
byte flipped = got ^ expected;
sprintf(text, "%04x: Expected %02x, got %02x.", a, expected, got);
Serial.print(text);
Serial.print(" (");
for (int i = 0; i < 8; i++) {
Serial.print(flipped & 0x80 ? '1' : '0');
flipped <<= 1;
}
Serial.println(" flipped)");
}
}
}
Serial.print("Done! ");
Serial.print((double(count) / (TRIAL_COUNT * 64.0)) * 100);
Serial.print("% (");
Serial.print(count);
Serial.print("/");
Serial.print(TRIAL_COUNT * 64);
Serial.print(") errors avg over ");
Serial.print(TRIAL_COUNT);
Serial.println(" trials.");
#endif
}
void loop() {
if (!has_write_job) return;
int num_items = vector_size(write_jobs);
if (num_items == 0) {
has_write_job = false;
return;
}
// Disable interrupts while messing with write_jobs array
// because jobs are added through interrupts, and we don't want that to
// happen in the middle
cli(); // disable interrupts ("cli()" == "clear" the "interrupts" flag)
write_job job = write_jobs[0];
vector_remove(&write_jobs, 0);
sei(); // re-enable interrupts ("sei()" == "set" the "interrupts" flag)
if (job.is_page_write) {
writeEEPROMPage(job.address, job.data);
} else {
writeEEPROM(job.address, job.data[0]);
}
free(job.data);
}
// Sets the address pins to the address provided
void writeAddress(unsigned int addr) {
for (int i = 0; i < ADDR_PINS_LEN; i++) {
digitalWrite(ADDR_PINS[i], addr & 1);
addr >>= 1;
}
}
void pinModeIO(byte mode) {
if (IO_PINS_MODE == mode) return;
for (int i = 0; i < IO_PINS_LEN; i++) {
pinMode(IO_PINS[i], mode);
}
IO_PINS_MODE = mode;
}
byte readEEPROM(unsigned int address) {
// Go into read mode
digitalWrite(OUTPUT_ENABLE, LOW);
digitalWrite(WRITE_ENABLE, HIGH);
// Data pins: input for reading
pinModeIO(INPUT);
// Set the address
writeAddress(address);
// Wait >=350 ns before reading (~375 ns)
NOP;
NOP;
NOP; // 3 * 62.5 ns @ 16,000,000hz
NOP;
NOP;
NOP; // 3 * 62.5 ns @ 16,000,000hz
// Serial.print("Reading: ");
byte data;
// Read the IO pins
for (int i = IO_PINS_LEN - 1; i >= 0; i--) {
byte bit = digitalRead(IO_PINS[i]);
data = (data << 1) | bit;
// Serial.print(bit);
}
// Serial.println();
return data;
}
void writeEEPROM(unsigned int address, byte data) {
// Configure initial control pins
digitalWrite(OUTPUT_ENABLE, HIGH);
digitalWrite(WRITE_ENABLE, HIGH);
cli(); // No interrupts can happen, because reads will mess this up.
// Set the address
writeAddress(address);
// Data pins: output for writing
pinModeIO(OUTPUT);
// Write the data
for (int i = 0; i < IO_PINS_LEN; i++) {
digitalWrite(IO_PINS[i], data & 1);
data >>= 1;
}
sei();
digitalWrite(WRITE_ENABLE, LOW);
delay(1);
digitalWrite(WRITE_ENABLE, HIGH);
delay(10);
}
void writeEEPROMPage(unsigned int address, byte* data) {
if (address % 0x40 != 0) {
return; // idk if this is a good idea
}
cli();
// Configure initial control pins
digitalWrite(OUTPUT_ENABLE, HIGH);
digitalWrite(WRITE_ENABLE, HIGH);
// Data pins: output for writing
pinModeIO(OUTPUT);
for (int i = 0; i < 64; i++) {
byte currentData = data[i];
writeAddress(address + i);
digitalWrite(WRITE_ENABLE, LOW);
// Write data
for (int i = 0; i < IO_PINS_LEN; i++) {
digitalWrite(IO_PINS[i], currentData & 1);
currentData >>= 1;
}
// wait >= 50ns
NOP;
NOP;
NOP;
digitalWrite(WRITE_ENABLE, HIGH);
// High pulse must be >= 50ns
}
sei();
// Wait up to 10 ms for the write cycle to complete
delay(10);
}
#ifdef USE_CAMINO
void readEEPROM_callable(byte dataLength, byte data[]) {
returns((byte)readEEPROM(data[0] | data[1] << 8));
}
void writeEEPROM_callable(byte dataLength, byte dataArray[]) {
// Copy a single tiny byte to its own array, how sweet.
// This is needed because the same memory is used for all callables
byte* data = malloc(1);
if (data == NULL) {
// OOM
returns("[arduino error] OOM");
return;
}
data[0] = dataArray[2];
// Note: the lifetime of temp is not guaranteed to be long
// don't use this pointer after instantiation
write_job* temp = vector_add_asg(&write_jobs);
temp->address = dataArray[0] | dataArray[1] << 8;
temp->data = data;
temp->is_page_write = false;
// temp = NULL; // commented out for performance (does it matter tho?)
has_write_job = true;
}
void hexdump16_callable(byte dataLength, byte data[]) {
unsigned int address = data[0] | data[1] << 8;
if (address % 16 != 0) {
returns("[arduino error] address must be divisible by 16!");
return;
}
char* msg = hexdump16(address);
returns(msg);
free(msg); // BE FREE LITTLE ONES
}
void hexdump32_callable(byte dataLength, byte data[]) {
unsigned int address = data[0] | data[1] << 8;
if (address % 32 != 0) {
returns("[arduino error] address must be divisible by 32!");
return;
}
char* msg1 = hexdump16(address);
char* msg2 = hexdump16(address + 16);
// Join the two strings with newline.
char big_msg[160];
// Copy string 1
int i;
for (i = 0; msg1[i] != 0; i++) {
big_msg[i] = msg1[i];
}
free(msg1);
// Newline
big_msg[i] = '\n';
i++;
// Copy string 2
int j;
for (j = 0; msg2[j] != 0; j++) {
big_msg[i + j] = msg2[j];
}
free(msg2);
// Null termination
big_msg[i + j] = 0;
returns(big_msg);
}
void readEEPROMPage_callable(byte dataLength, byte dataArray[]) {
unsigned int address = dataArray[0] | dataArray[1] << 8;
if (address % 64 != 0) {
// No data should cause some sort of error on the other side.
return;
}
byte outgoing[64];
for (int i = 0; i < 64; i++) {
outgoing[i] = readEEPROM(address + i);
}
returns(64, outgoing);
}
// Note: has been tested
void writeEEPROMPage_callable(byte dataLength, byte dataArray[]) {
unsigned int address = dataArray[0] | dataArray[1] << 8;
if (address % 64 != 0) {
// TODO: Find a better way to express there was an error
return;
}
// This is needed because the same memory is used for all callables
byte* data = malloc(64);
if (data == NULL) {
// OOM
returns("arduino OOM");
return;
}
byte* incoming = &dataArray[2];
for (int i = 0; i < 64; i++) {
data[i] = incoming[i];
}
// Note: the lifetime of temp is not guaranteed to be long
// don't use this pointer after instantiation
write_job* temp = vector_add_asg(&write_jobs);
temp->address = address;
temp->data = data;
temp->is_page_write = true;
// temp = NULL; // commented out for performance (does it matter tho?)
has_write_job = true;
}
// temp
void noop(byte dataLength, byte data[]) {}
BEGIN_CALLABLES{
// read takes in address (2 bytes) and returns data (1 byte)
{ "read", readEEPROM_callable },
// write takes in address (2 bytes) and data (1 byte)
{ "write", writeEEPROM_callable },
// readPage takes in address (2 bytes, must be divisible by 64) and returns 64 bytes of data
{ "read_page", readEEPROMPage_callable },
// writePage takes in address (2 bytes, must be divisible by 64) and 64 bytes of data
{ "write_page", writeEEPROMPage_callable },
{ "hexdump16", hexdump16_callable },
{ "hexdump32", hexdump32_callable },
} END_CALLABLES;
#endif