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