/* # # This is code to run a home-made EPROM programmer # # $Id: 27c801.c,v 1.20 2002/09/05 00:38:43 nemesis Exp $ # # Copyright (C) 2002 Kees Cook # kees@outflux.net, http://outflux.net/ # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # http://www.gnu.org/copyleft/gpl.html # */ #include #include #include // memset #include // gettimeofday // scheduler prioriety #include #include // stat #include #include #include // global variables unsigned int par_port=0x378; unsigned char CTRL=0; unsigned char DATA=0; // configure the parallel port we're going to use #define PARALLEL_PORT (par_port) #define DATA_PORT (PARALLEL_PORT) #define STATUS_PORT (PARALLEL_PORT+1) #define CTRL_PORT (PARALLEL_PORT+2) #define C0 (1<<0) #define C1 (1<<1) #define C2 (1<<2) #define C3 (1<<3) #define DIR_READ (1<<5) #define SET_BITS(value) { CTRL |= (value); outb(CTRL,CTRL_PORT); } #define CLR_BITS(value) { CTRL &= ~(value); outb(CTRL,CTRL_PORT); } #define START_READ SET_BITS(DIR_READ); #define START_WRITE CLR_BITS(DIR_READ); // to pulse enable, we want to bring the LINE from high to low, hold, then // low to high // /C1: high is 0. low is 1. #define ENABLE_DEFAULT { CLR_BITS(C1); } #define ENABLE_ON { SET_BITS(C1); } #define ENABLE_OFF { CLR_BITS(C1); } #define ENABLE_PULSE { SET_BITS(C1); CLR_BITS(C1); } // to pulse reset, we want to bring the LINE from low to high, hold, then // high to low // C2: high is 1, low is 0 #define RESET_DEFAULT { CLR_BITS(C2); } #define RESET_PULSE { SET_BITS(C2); CLR_BITS(C2); } // to pulse the address clock, we want to bring the LINE from low to high, // hold, then high to low // /C0: high is 0, low is 1 #define ADDR_DEFAULT { SET_BITS(C0); } #define ADDR_PULSE { CLR_BITS(C0); SET_BITS(C0); } // to turn on programming voltage, we want to bring the LINE to low // /C3: high is 0, low is 1 #define PROGRAM_OFF { CLR_BITS(C3); START_READ; } #define PROGRAM_ON { START_WRITE; SET_BITS(C3); } #define DATA_GET (inb(DATA_PORT)) #define DATA_PUT(byte) { outb(byte,DATA_PORT); } #define INIT { \ PROGRAM_OFF; \ ENABLE_DEFAULT; \ ADDR_DEFAULT; \ RESET_DEFAULT; \ DATA_PUT(0); \ RESET_PULSE; \ } #define MAX_ADDRESS (1048576) unsigned char byte[MAX_ADDRESS]; void press() { char buf[1024]; printf("Press enter..."); fflush(NULL); fgets(buf,1024,stdin); } void write_from_file(char * filename) { FILE * fp; struct stat info; int n, i, k, prevk, verified, reburn; size_t bytes; // open the file if ((fp=fopen(filename,"r"))==NULL) { perror(filename); exit(1); } // find out how big it is if (fstat(fileno(fp),&info)<0) { perror("fstat"); exit(1); } bytes=info.st_size; // fill "memory" with 0xFF for smaller images memset(byte,0xFF,MAX_ADDRESS); // handle too-large files if (bytes>MAX_ADDRESS) { fprintf(stderr,"Image file too large! Cannot program past %d.\n", MAX_ADDRESS); fprintf(stderr,"Press enter to continue anyway...\n"); press(); bytes=MAX_ADDRESS; } if (bytes0) reburn++; // address valid PROGRAM_ON; // Vpp valid DATA_PUT(byte[i]); // data valid DATA_PUT(byte[i]); DATA_PUT(byte[i]); DATA_PUT(byte[i]); // take 2us (tQVEL) k=i>>10; if (k!=prevk) { printf("\r%dK",k); fflush(NULL); prevk=k; } ENABLE_ON; // CE valid usleep(45+n); // pause for 45us first // then work our way up to 55us (tELEH) ENABLE_OFF; ENABLE_OFF; ENABLE_OFF; // take 2us (tEHVPX) PROGRAM_OFF; PROGRAM_OFF; PROGRAM_OFF; // take 2us (tVPLEL) usleep(30+n); // this isn't in the spec, but the read // back always failed. I suspect this // is needed because the chip voltage // is out of spec (datasheet requires // 6.75V, we're only giving it ~5V) ENABLE_ON; ENABLE_ON; ENABLE_ON; // take >1us (tELQV) readback=DATA_GET; ENABLE_OFF; if (readback==byte[i]) verified=1; n++; if (n>20) { printf("Write verify failed after %d attempts @ %x (%02X should be %02X)\n", n,i,readback,byte[i]); INIT; exit(1); } } ENABLE_OFF; // make SURE ADDR_PULSE; // address-to-enable time is handled by the data line timings } PROGRAM_OFF; printf("\r%dK\n",MAX_ADDRESS>>10); printf("Wrote to EEPROM from %s\n",filename); printf("Had to over-program %d byte%s.\n",reburn,reburn==1?"":"s"); } int read_to_file(char * filename) { FILE * fp=NULL; int i, k, prevk; int allblank=1; if ((filename) && (fp=fopen(filename,"w"))==NULL) { perror(filename); exit(1); } // read cycle PROGRAM_OFF; RESET_PULSE; prevk=0; for (i=0;i>14; if (k!=prevk) { printf("\r%dK",k<<4); fflush(NULL); prevk=k; } ENABLE_ON; ENABLE_ON; ENABLE_ON; #undef DOUBLE_CHECK_DATA #ifdef DOUBLE_CHECK_DATA { unsigned char check[0xf]; int j; int bad=0; check[0]=DATA_GET; for (j=1;j<0xF;j++) { check[j]=DATA_GET; if (check[0]!=check[j]) bad=1; } if (bad) { printf("read failure @ %x\n",i); for (j=0;j<0xF;j++) { printf("0x%X: 0x%02X\n",j,check[j]); } INIT; exit(1); } } #endif byte[i]=DATA_GET; if (byte[i]!=0xFF) allblank=0; ENABLE_OFF; ADDR_PULSE; } printf("\r%dK\n",MAX_ADDRESS>>10); if (fp) { if (fwrite(byte,sizeof(unsigned char),MAX_ADDRESS,fp)!=MAX_ADDRESS) { perror("fwrite"); INIT; exit(1); } printf("Dumped EEPROM to %s\n",filename); fclose(fp); } return allblank; } void do_tests() { int i; struct timeval before; struct timeval now; long diff; gettimeofday(&before,NULL); for (i=0;i<1000;i++) { usleep(50); // 50us } gettimeofday(&now,NULL); // should have slept for 50,000us, or 50ms diff=((now.tv_sec-before.tv_sec)*1000000)+(now.tv_usec-before.tv_usec); printf("50000us worth of 50us delays took %luus\n",diff); press(); PROGRAM_ON; printf("program on\n"); press(); PROGRAM_OFF; printf("program off\n"); press(); // address test RESET_PULSE; printf("address all zero\n"); press(); //11111111111111111111 //1048575 for (i=0;i-1) { switch (c) { case 't': need_startup=1; need_tests=1; break; case 'b': need_startup=1; need_blankcheck=1; break; case 'r': need_startup=1; read_filename=optarg; break; case 'w': need_startup=1; write_filename=optarg; break; case 's': need_realtime=0; break; case 'p': if (sscanf(optarg,"%x",&par_port)!=1) Usage(argv); printf("Using parallel port base 0x%03X\n",par_port); break; default: printf("Unknown argument '%c'\n",c); case 'h': Usage(argv); } } if (need_startup) { if (need_realtime) get_realtime(); startup(); } else { printf("You gotta tell me what to do...\n"); Usage(argv); } if (need_tests) do_tests(); if (need_blankcheck) { if (read_to_file(NULL)) { printf("EPROM is blank\n"); } else { printf("EPROM is NOT blank!\n"); } } if (read_filename) read_to_file(read_filename); if (write_filename) write_from_file(write_filename); return 1; }