test_simm30_dram

annotate test.c @ 5:7d9b129a5791

convert to 30pin
author John Tsiombikas <nuclear@member.fsf.org>
date Sun, 04 Jun 2017 02:56:55 +0300
parents bd6ad00cb1bc
children
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 }