test_simm30_dram

annotate test.c @ 2:42d26388b709

memory detection works
author John Tsiombikas <nuclear@member.fsf.org>
date Thu, 09 Mar 2017 01:40:31 +0200
parents 318a758ede82
children bd6ad00cb1bc
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 }