avr_test_simm72_dram

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