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