rev |
line source |
nuclear@2
|
1 #define F_CPU XTAL
|
nuclear@2
|
2
|
nuclear@0
|
3 #include <stdio.h>
|
nuclear@1
|
4 #include <stdlib.h>
|
nuclear@0
|
5 #include <string.h>
|
nuclear@0
|
6 #include <ctype.h>
|
nuclear@2
|
7 #include <stdint.h>
|
nuclear@0
|
8 #include <avr/io.h>
|
nuclear@0
|
9 #include <avr/interrupt.h>
|
nuclear@2
|
10 #include <util/delay.h>
|
nuclear@0
|
11 #include "serial.h"
|
nuclear@0
|
12
|
nuclear@1
|
13 /* pin assignments:
|
nuclear@1
|
14 * A[0,7] data
|
nuclear@1
|
15 * B[0,7] A0 - A7
|
nuclear@1
|
16 * C[0,3] A8 - A11
|
nuclear@1
|
17 * D7 CAS3
|
nuclear@1
|
18 * D6 RAS2
|
nuclear@1
|
19 * D5 RAS3
|
nuclear@1
|
20 */
|
nuclear@1
|
21
|
nuclear@1
|
22 #define CAS3_BIT 0x80
|
nuclear@1
|
23 #define RAS2_BIT 0x40
|
nuclear@1
|
24 #define RAS3_BIT 0x20
|
nuclear@1
|
25 #define WE_BIT 0x04
|
nuclear@1
|
26
|
nuclear@1
|
27 void proc_cmd(char *cmd);
|
nuclear@1
|
28 void cmd_read(char *buf);
|
nuclear@1
|
29 void cmd_write(char *buf);
|
nuclear@1
|
30 void cmd_setcfg(char *buf);
|
nuclear@2
|
31 void cmd_test(char *buf);
|
nuclear@2
|
32 void cmd_detect(void);
|
nuclear@1
|
33
|
nuclear@1
|
34 void dram_init(void);
|
nuclear@1
|
35 void dram_detect(void);
|
nuclear@2
|
36 int memtest(uint32_t addr);
|
nuclear@1
|
37 void dram_refresh(void);
|
nuclear@2
|
38 void dram_write(uint32_t addr, unsigned char val);
|
nuclear@2
|
39 unsigned char dram_read(uint32_t addr);
|
nuclear@0
|
40
|
nuclear@0
|
41 #define MAX_INPUT_SIZE 128
|
nuclear@0
|
42 static char input[MAX_INPUT_SIZE];
|
nuclear@0
|
43 static unsigned char inp_cidx;
|
nuclear@0
|
44
|
nuclear@1
|
45 /* SIMM access config */
|
nuclear@1
|
46 static int addr_bits;
|
nuclear@2
|
47 static uint32_t addr_mask;
|
nuclear@1
|
48 static int ras_lines = 1;
|
nuclear@1
|
49 static long memsize_kb; /* derived from the above */
|
nuclear@1
|
50
|
nuclear@0
|
51 int main(void)
|
nuclear@0
|
52 {
|
nuclear@1
|
53 dram_init();
|
nuclear@0
|
54 init_serial(38400);
|
nuclear@0
|
55 sei();
|
nuclear@0
|
56
|
nuclear@2
|
57 printf("\n72pin SIMM DRAM tester by John Tsiombikas <nuclear@member.fsf.org>\n");
|
nuclear@1
|
58
|
nuclear@2
|
59 cmd_detect();
|
nuclear@1
|
60
|
nuclear@1
|
61 fputs("> ", stdout);
|
nuclear@0
|
62
|
nuclear@0
|
63 for(;;) {
|
nuclear@2
|
64 if(have_input()) {
|
nuclear@0
|
65 int c = getchar();
|
nuclear@0
|
66 putchar(c);
|
nuclear@0
|
67
|
nuclear@0
|
68 if(c == '\r' || c == '\n') {
|
nuclear@0
|
69 input[inp_cidx] = 0;
|
nuclear@1
|
70 proc_cmd(input);
|
nuclear@0
|
71 inp_cidx = 0;
|
nuclear@1
|
72
|
nuclear@1
|
73 fputs("> ", stdout);
|
nuclear@0
|
74 } else if(inp_cidx < sizeof input - 1) {
|
nuclear@0
|
75 input[inp_cidx++] = c;
|
nuclear@0
|
76 }
|
nuclear@0
|
77 }
|
nuclear@1
|
78
|
nuclear@1
|
79 dram_refresh();
|
nuclear@1
|
80 }
|
nuclear@1
|
81 return 0;
|
nuclear@1
|
82 }
|
nuclear@1
|
83
|
nuclear@1
|
84 void proc_cmd(char *cmd)
|
nuclear@1
|
85 {
|
nuclear@1
|
86 switch(cmd[0]) {
|
nuclear@1
|
87 case 'w':
|
nuclear@1
|
88 cmd_write(cmd + 1);
|
nuclear@1
|
89 break;
|
nuclear@1
|
90
|
nuclear@1
|
91 case 'r':
|
nuclear@1
|
92 cmd_read(cmd + 1);
|
nuclear@1
|
93 break;
|
nuclear@1
|
94
|
nuclear@1
|
95 case 's':
|
nuclear@1
|
96 cmd_setcfg(cmd + 1);
|
nuclear@1
|
97 break;
|
nuclear@1
|
98
|
nuclear@2
|
99 case 't':
|
nuclear@2
|
100 cmd_test(cmd + 1);
|
nuclear@2
|
101 break;
|
nuclear@2
|
102
|
nuclear@2
|
103 case 'd':
|
nuclear@2
|
104 cmd_detect();
|
nuclear@2
|
105 break;
|
nuclear@2
|
106
|
nuclear@1
|
107 case 'h':
|
nuclear@1
|
108 printf("commands:\n");
|
nuclear@1
|
109 printf(" w <addr> <value> - write byte to address\n");
|
nuclear@1
|
110 printf(" r <addr> - read byte from address\n");
|
nuclear@1
|
111 printf(" s <addr_bits|ras_lines> <value>\n");
|
nuclear@2
|
112 printf(" t <addr> - test address\n");
|
nuclear@2
|
113 printf(" d - detect DRAM\n");
|
nuclear@1
|
114 printf(" h - help\n");
|
nuclear@1
|
115 break;
|
nuclear@1
|
116 }
|
nuclear@1
|
117 }
|
nuclear@1
|
118
|
nuclear@1
|
119 void cmd_read(char *buf)
|
nuclear@1
|
120 {
|
nuclear@1
|
121 char *endp;
|
nuclear@2
|
122 uint32_t addr;
|
nuclear@1
|
123 unsigned char data;
|
nuclear@1
|
124
|
nuclear@1
|
125 addr = strtol(buf, &endp, 0);
|
nuclear@1
|
126 if(endp == buf) {
|
nuclear@1
|
127 fprintf(stderr, "invalid argument to write command: %s\n", buf);
|
nuclear@1
|
128 return;
|
nuclear@1
|
129 }
|
nuclear@1
|
130 buf = endp;
|
nuclear@1
|
131
|
nuclear@1
|
132 data = dram_read(addr);
|
nuclear@2
|
133 printf("%04lx: %02x (%u)\n", (unsigned long)addr, (unsigned int)data, (unsigned int)data);
|
nuclear@1
|
134 }
|
nuclear@1
|
135
|
nuclear@1
|
136 void cmd_write(char *buf)
|
nuclear@1
|
137 {
|
nuclear@1
|
138 char *endp;
|
nuclear@2
|
139 uint32_t addr;
|
nuclear@1
|
140 unsigned char data;
|
nuclear@1
|
141
|
nuclear@1
|
142 addr = strtol(buf, &endp, 0);
|
nuclear@1
|
143 if(endp == buf) {
|
nuclear@1
|
144 fprintf(stderr, "invalid address argument to read command: %s\n", buf);
|
nuclear@1
|
145 return;
|
nuclear@1
|
146 }
|
nuclear@1
|
147 buf = endp;
|
nuclear@1
|
148
|
nuclear@1
|
149 data = strtol(buf, &endp, 0);
|
nuclear@1
|
150 if(endp == buf) {
|
nuclear@1
|
151 fprintf(stderr, "invalid data argument to read command: %s\n", buf);
|
nuclear@1
|
152 return;
|
nuclear@1
|
153 }
|
nuclear@1
|
154 buf = endp;
|
nuclear@1
|
155
|
nuclear@1
|
156 dram_write(addr, data);
|
nuclear@2
|
157 printf("%04lx: %02x (%u)\n", (unsigned long)addr, (unsigned int)data, (unsigned int)data);
|
nuclear@1
|
158 }
|
nuclear@1
|
159
|
nuclear@1
|
160 void cmd_setcfg(char *buf)
|
nuclear@1
|
161 {
|
nuclear@1
|
162 char *endp;
|
nuclear@1
|
163 char *name, *valstr;
|
nuclear@1
|
164 long value;
|
nuclear@1
|
165
|
nuclear@1
|
166 name = buf;
|
nuclear@1
|
167 while(*name && isspace(*name)) ++name;
|
nuclear@1
|
168 if(!*name) {
|
nuclear@1
|
169 fprintf(stderr, "invalid or missing variable name\n");
|
nuclear@1
|
170 return;
|
nuclear@1
|
171 }
|
nuclear@1
|
172 endp = name;
|
nuclear@1
|
173 while(*endp && !isspace(*endp)) ++endp;
|
nuclear@1
|
174 *endp = 0;
|
nuclear@1
|
175
|
nuclear@1
|
176 valstr = endp + 1;
|
nuclear@1
|
177 value = strtol(valstr, &endp, 0);
|
nuclear@1
|
178 if(endp == valstr) {
|
nuclear@1
|
179 fprintf(stderr, "invalid or missing variable value: %s\n", valstr);
|
nuclear@1
|
180 return;
|
nuclear@1
|
181 }
|
nuclear@1
|
182
|
nuclear@1
|
183 if(strcmp(name, "addr_bits") == 0) {
|
nuclear@1
|
184 if(value > 0 && value <= 12) {
|
nuclear@1
|
185 addr_bits = value;
|
nuclear@2
|
186 addr_mask = ((uint32_t)1 << addr_bits) - 1;
|
nuclear@2
|
187 printf("Address bits: %ld (mask: %lx)\n", value, (unsigned long)addr_mask);
|
nuclear@1
|
188 } else {
|
nuclear@1
|
189 fprintf(stderr, "invalid address bits value: %ld\n", value);
|
nuclear@1
|
190 }
|
nuclear@1
|
191 } else if(strcmp(name, "ras_lines") == 0) {
|
nuclear@1
|
192 if(value > 0 && value <= 2) {
|
nuclear@1
|
193 ras_lines = value;
|
nuclear@1
|
194 printf("RAS lines: %d\n", ras_lines);
|
nuclear@1
|
195 } else {
|
nuclear@1
|
196 fprintf(stderr, "invalid RAS lines value: %ld\n", value);
|
nuclear@1
|
197 }
|
nuclear@1
|
198 } else {
|
nuclear@1
|
199 fprintf(stderr, "unknown variable: %s\n", name);
|
nuclear@1
|
200 }
|
nuclear@1
|
201 }
|
nuclear@1
|
202
|
nuclear@2
|
203 void cmd_test(char *buf)
|
nuclear@2
|
204 {
|
nuclear@2
|
205 char *endp;
|
nuclear@2
|
206 uint32_t addr;
|
nuclear@2
|
207
|
nuclear@2
|
208 addr = strtol(buf, &endp, 0);
|
nuclear@2
|
209 if(endp == buf) {
|
nuclear@2
|
210 fprintf(stderr, "invalid argument to test command: %s\n", buf);
|
nuclear@2
|
211 return;
|
nuclear@2
|
212 }
|
nuclear@2
|
213
|
nuclear@2
|
214 if(memtest(addr) == 0) {
|
nuclear@2
|
215 printf("success!\n");
|
nuclear@2
|
216 }
|
nuclear@2
|
217 }
|
nuclear@2
|
218
|
nuclear@2
|
219
|
nuclear@2
|
220 void cmd_detect(void)
|
nuclear@2
|
221 {
|
nuclear@2
|
222 printf("Detecting memory ...\n");
|
nuclear@2
|
223 dram_detect();
|
nuclear@2
|
224
|
nuclear@2
|
225 memsize_kb = ((uint32_t)1 << (addr_bits * 2)) * 4 * ras_lines;
|
nuclear@2
|
226
|
nuclear@2
|
227 printf("Address lines: %d\n", addr_bits);
|
nuclear@2
|
228 printf("RAS lines: %d\n", ras_lines);
|
nuclear@2
|
229 printf("Memory size: %ldmb (%ldkb)\n", memsize_kb >> 20, memsize_kb >> 10);
|
nuclear@2
|
230 }
|
nuclear@2
|
231
|
nuclear@2
|
232
|
nuclear@1
|
233 void dram_set_data(unsigned char val)
|
nuclear@1
|
234 {
|
nuclear@1
|
235 DDRA = 0xff;
|
nuclear@1
|
236 PORTA = val;
|
nuclear@1
|
237 }
|
nuclear@1
|
238
|
nuclear@1
|
239 void dram_release_data(void)
|
nuclear@1
|
240 {
|
nuclear@1
|
241 DDRA = 0;
|
nuclear@1
|
242 PORTA = 0;
|
nuclear@1
|
243 }
|
nuclear@1
|
244
|
nuclear@1
|
245 void dram_set_addr(unsigned long addr)
|
nuclear@1
|
246 {
|
nuclear@1
|
247 PORTB = addr & 0xff;
|
nuclear@1
|
248 PORTC = (addr >> 8) & 3;
|
nuclear@1
|
249 }
|
nuclear@1
|
250
|
nuclear@1
|
251 void dram_assert_cas(void)
|
nuclear@1
|
252 {
|
nuclear@1
|
253 PORTD &= ~CAS3_BIT;
|
nuclear@1
|
254 }
|
nuclear@1
|
255
|
nuclear@1
|
256 void dram_release_cas(void)
|
nuclear@1
|
257 {
|
nuclear@1
|
258 PORTD |= CAS3_BIT;
|
nuclear@1
|
259 }
|
nuclear@1
|
260
|
nuclear@1
|
261 void dram_assert_ras(unsigned char bits)
|
nuclear@1
|
262 {
|
nuclear@1
|
263 PORTD &= ~bits;
|
nuclear@1
|
264 }
|
nuclear@1
|
265
|
nuclear@1
|
266 void dram_release_ras(unsigned char bits)
|
nuclear@1
|
267 {
|
nuclear@1
|
268 PORTD |= bits;
|
nuclear@1
|
269 }
|
nuclear@1
|
270
|
nuclear@1
|
271 void dram_set_we(void)
|
nuclear@1
|
272 {
|
nuclear@1
|
273 PORTD &= ~WE_BIT;
|
nuclear@1
|
274 }
|
nuclear@1
|
275
|
nuclear@1
|
276 void dram_clear_we(void)
|
nuclear@1
|
277 {
|
nuclear@1
|
278 PORTD |= WE_BIT;
|
nuclear@1
|
279 }
|
nuclear@1
|
280
|
nuclear@1
|
281 void dram_init(void)
|
nuclear@1
|
282 {
|
nuclear@1
|
283 DDRA = 0; /* port A is the data bus */
|
nuclear@1
|
284 PORTA = 0; /* no pullups when A is input */
|
nuclear@1
|
285 DDRB = 0xff; /* port B is A0-A7 */
|
nuclear@1
|
286 DDRC = 0xff; /* port C (low nibble) is A8-A11 */
|
nuclear@1
|
287 DDRD = 0xff; /* port D are the control lines CAS/RAS/WR */
|
nuclear@1
|
288
|
nuclear@1
|
289 PORTD = 0xff; /* deassert all control signals */
|
nuclear@2
|
290
|
nuclear@2
|
291 /* it seems like nothing works until we do one refresh cycle... */
|
nuclear@2
|
292 dram_refresh();
|
nuclear@1
|
293 }
|
nuclear@1
|
294
|
nuclear@1
|
295 void dram_detect(void)
|
nuclear@1
|
296 {
|
nuclear@2
|
297 uint32_t addr = 0;
|
nuclear@2
|
298
|
nuclear@1
|
299 /* detect how many address bits we've got */
|
nuclear@1
|
300 addr_bits = 12;
|
nuclear@1
|
301 while(addr_bits > 8) {
|
nuclear@2
|
302 addr_mask = ((uint32_t)1 << (uint32_t)addr_bits) - 1;
|
nuclear@2
|
303 addr = ((uint32_t)1 << ((uint32_t)addr_bits * 2)) - 1;
|
nuclear@2
|
304 if(memtest(addr) == 0) {
|
nuclear@1
|
305 break;
|
nuclear@1
|
306 }
|
nuclear@1
|
307 --addr_bits;
|
nuclear@1
|
308 }
|
nuclear@1
|
309 if(addr_bits < 1) {
|
nuclear@1
|
310 fprintf(stderr, "Failed to detect DRAM configuration (address lines)...\n");
|
nuclear@1
|
311 return;
|
nuclear@1
|
312 }
|
nuclear@2
|
313
|
nuclear@2
|
314 /* now detect if there's a second ras pair */
|
nuclear@2
|
315 ++addr; /* addr was already the highest of the first bank, see if there's a second */
|
nuclear@2
|
316 ras_lines = 2;
|
nuclear@2
|
317 if(memtest(addr) != 0) {
|
nuclear@2
|
318 ras_lines = 1;
|
nuclear@2
|
319 }
|
nuclear@1
|
320 }
|
nuclear@1
|
321
|
nuclear@2
|
322 int memtest(uint32_t addr)
|
nuclear@1
|
323 {
|
nuclear@1
|
324 int i;
|
nuclear@1
|
325 unsigned char pat[] = { 0xf0, 0x0f, 0xaa, 0x55, 0xc0, 0x30, 0x0c, 0x03 };
|
nuclear@1
|
326 unsigned char val;
|
nuclear@1
|
327
|
nuclear@2
|
328 printf("testing address: %lx (a:%d,r:%d)\n", (unsigned long)addr, addr_bits, ras_lines);
|
nuclear@1
|
329
|
nuclear@1
|
330 for(i=0; i<sizeof pat / sizeof *pat; i++) {
|
nuclear@1
|
331 dram_write(addr, pat[i]);
|
nuclear@1
|
332 if((val = dram_read(addr)) != pat[i]) {
|
nuclear@2
|
333 printf("pattern %x failed, got: %x\n", (unsigned int)pat[i], (unsigned int)val);
|
nuclear@1
|
334 return -1;
|
nuclear@1
|
335 }
|
nuclear@0
|
336 }
|
nuclear@0
|
337 return 0;
|
nuclear@0
|
338 }
|
nuclear@0
|
339
|
nuclear@1
|
340 void dram_refresh(void)
|
nuclear@0
|
341 {
|
nuclear@1
|
342 dram_assert_cas();
|
nuclear@1
|
343 dram_assert_ras(RAS2_BIT | RAS3_BIT);
|
nuclear@1
|
344 dram_release_cas();
|
nuclear@1
|
345 dram_release_ras(RAS2_BIT | RAS3_BIT);
|
nuclear@0
|
346 }
|
nuclear@1
|
347
|
nuclear@2
|
348 void dram_write(uint32_t addr, unsigned char val)
|
nuclear@1
|
349 {
|
nuclear@2
|
350 uint32_t row_addr = (addr >> addr_bits) & addr_mask;
|
nuclear@2
|
351 uint32_t col_addr = addr & addr_mask;
|
nuclear@2
|
352 unsigned char ras = (addr >> (addr_bits * 2)) ? RAS3_BIT : RAS2_BIT;
|
nuclear@2
|
353
|
nuclear@1
|
354 dram_set_data(val);
|
nuclear@1
|
355 dram_set_we();
|
nuclear@1
|
356 /* set row address */
|
nuclear@2
|
357 dram_set_addr(row_addr);
|
nuclear@2
|
358 dram_assert_ras(ras);
|
nuclear@1
|
359 /* set column address */
|
nuclear@2
|
360 dram_set_addr(col_addr);
|
nuclear@1
|
361 dram_assert_cas();
|
nuclear@2
|
362 dram_release_ras(ras);
|
nuclear@1
|
363 dram_release_cas();
|
nuclear@1
|
364 dram_release_data();
|
nuclear@1
|
365 dram_clear_we();
|
nuclear@1
|
366 }
|
nuclear@1
|
367
|
nuclear@2
|
368 unsigned char dram_read(uint32_t addr)
|
nuclear@1
|
369 {
|
nuclear@1
|
370 unsigned char val;
|
nuclear@2
|
371 uint32_t row_addr = (addr >> addr_bits) & addr_mask;
|
nuclear@2
|
372 uint32_t col_addr = addr & addr_mask;
|
nuclear@2
|
373 unsigned char ras = (addr >> (addr_bits * 2)) ? RAS3_BIT : RAS2_BIT;
|
nuclear@1
|
374
|
nuclear@1
|
375 dram_clear_we();
|
nuclear@2
|
376 /* this is necessary to remove previous data from the lines when no-one is driving them
|
nuclear@2
|
377 * in case we're trying to detect the presence of a RAS line which doesn't exist
|
nuclear@2
|
378 */
|
nuclear@2
|
379 dram_set_data(0);
|
nuclear@1
|
380 dram_release_data();
|
nuclear@1
|
381
|
nuclear@1
|
382 /* set row address */
|
nuclear@2
|
383 dram_set_addr(row_addr);
|
nuclear@2
|
384 dram_assert_ras(ras);
|
nuclear@1
|
385 /* set column address */
|
nuclear@2
|
386 dram_set_addr(col_addr);
|
nuclear@1
|
387 dram_assert_cas();
|
nuclear@1
|
388
|
nuclear@1
|
389 val = PINA;
|
nuclear@1
|
390
|
nuclear@2
|
391 dram_release_ras(ras);
|
nuclear@1
|
392 dram_release_cas();
|
nuclear@1
|
393
|
nuclear@1
|
394 return val;
|
nuclear@1
|
395 }
|