/* * scenix2.cc * * for use with the jpb programmer or the 2nd Steele programmer schematic * * orig code by Dan Steele * * April 1999 - Dave2 * * - corrected method of writing fusex by preserving 5 highorder bits * both in the erase and program modes. This significantly improves the * reliability of the programmer. * * * April 1999 - modified by Allan Meyer * * - default (without parameter specified) now gives help screen, not erase * - removed case sensitivity on command-line parameters * - added /E parameter for erase * - added support for multiple parallel ports using env variable SXLPT * - added port detect and separated error messages specific to port or programmer * - changed dump parameter from /T to /D * - changed dump routine to intel hex format * - added parameter /T for Hardware Test mode * - added parameters /V and /S for verify * * * * * THIS SOFTWARE IS PROVIDED AS IS AND WITHOUT WARRANTY OF ANY KIND, * EITHER EXPRESSED OR IMPLIED. * */ extern "C" delay (int); #include #include #include #include #include #include #include int ReadTimeStampCounter (void) { int a; asm volatile ("RDTSC" /* asm statements */ :"=eax" (a) /* output */ : /* input */ :"edx"); /* registers-modified */ return a; } #define Vdd 0x10 /* Parallel 6 - Controls Vdd Main Power */ #define V12 0x08 /* Parallel 5 - 12 volts - OSC2 */ #define V5 0x04 /* Parallel 4 - 5 volts - OSC1 */ #define V0 0x02 /* Parallel 3 - 0 volts - OSC1 */ #define VZ 0x01 /* Parallel 2 - OSC2 */ #define VOff 0x00 /* All lines off -release OSC2 */ #define VTest1 0x40 /* test output to determine if lpt port exists */ #define VTest2 0x0a0 /* test output to determine if lpt port exists */ #define OK 0 #define INVALID -1 #define NOLOOP -2 int d_reg; /* port address of LPT data register */ int s_reg; /* LPT status register */ int c_reg; /* LPT control register */ int ByteMem[16384]; int mem1[8192]; int mem2[8192]; int lpinit (void); int p_rep = 200; /* prog repeats - 200=100ms/word and 20=10msec/word */ void lpreset (void); void givehelp (void); char *version = "scenix2 Version 0.1j Not for use with Scenix Rev 5 Chips"; class Scenix { public: int T; int D; void Error (const char *s) { for (int a = 0; a < 20; ++a) { delay (5); outportb (d_reg, V0); /* Parallel 3 on - OSC1 to zero */ delay (5); outportb (d_reg, V5); /* Parallel 4 on - OSC1 to 5volts */ delay (5); } outportb (d_reg, VOff); /* All lines off */ cout << s << endl; exit (1); } Scenix (void) { lpinit(); /* Step 1. Drive OSC1 low to stop clock */ outportb (d_reg, V0 | Vdd); /* Pin 3 on - OSC1 = 0 v */ delay (5); /* Step 2. Drive OSC2 low and toggle OSC1 at least 9 times to start ISP */ outportb (d_reg, V0 | VZ | Vdd); /* Pin 3/2 on - O1=0/O2=lo */ delay (5); for (int a = 0; a < 20; ++a) { outportb (d_reg, V5 | VZ | Vdd); /* Pin 4/2 on - O1-hi/O2-lo */ delay (5); outportb (d_reg, V0 | VZ | Vdd); /* Pin 3/2 on - O1=0/O2=lo */ delay (5); } /* Step 3. Release the OSC2 Pin */ outportb (d_reg, V0 | Vdd); /* Pin 3 on - OSC1= 0 v(OSC2=float??) */ delay (5); /* 4. Apply VPP Programming Voltage to OSC1 */ outportb (d_reg, V12 | Vdd); /* Pin 5 on - OSC1 = 12 v */ delay (20); int count = 0; /* now check for status port toggling */ for (int a = 0; a < 100; ++a) { int a = inportb (s_reg) & 0x40; delay (1); if (a != (inportb (s_reg) & 0x40)) ++count; } if (count < 10) Error ("ISP Osc Not Detected!"); else cout << "ISP Osc Detected" << endl << endl; delay (100); D = 0; asm volatile ("cli"); while (!inportb (s_reg) & 0x40); while (inportb (s_reg) & 0x40); T = ReadTimeStampCounter (); for (int a = 0; a < 1600; ++a) { while (!(inportb (s_reg) & 0x40)); while (inportb (s_reg) & 0x40); } D = (ReadTimeStampCounter () - T) / (1700 * 4); asm volatile ("sti"); delay (100); } /* RDTSC—Read Time-Stamp Counter Description Loads the current value of the processor's time-stamp counter into the EDX:EAX registers. The time-stamp counter is contained in a 64-bit MSR. The high-order 32 bits of the MSR are loaded into the EDX register, and the low-order 32 bits are loaded into the EAX register. The processor increments the time-stamp counter MSR every clock cycle and resets it to 0 whenever the processor is reset. The time stamp disable (TSD) flag in register CR4 restricts the use of the RDTSC instruction. When the TSD flag is clear, the RDTSC instruction can be executed at any privilege level; when the flag is set, the instruction can only be executed at privilege level 0. The time-stamp counter can also be read with the RDMSR instruction, when executing at privilege level 0. The RDTSC instruction is not a serializing instruction. Thus, it does not necessarily wait until all previous instructions have been executed before reading the counter. Similarly, subsequent instructions may begin execution before the read operation is performed. This instruction was introduced into the Intel Architecture in the Pentium processor. Operation IF (CR4.TSD = 0) OR ((CR4.TSD = 1) AND (CPL=0)) THEN EDX:EAX ¬ TimeStampCounter; ELSE (* CR4 is 1 and CPL is 1, 2, or 3 *) #GP(0) FI; Flags Affected None. Protected Mode Exceptions #GP(0) If the TSD flag in register CR4 is set and the CPL is greater than 0. Real-Address Mode Exceptions #GP If the TSD flag in register CR4 is set. Virtual-8086 Mode Exceptions #GP(0) If the TSD flag in register CR4 is set. Opcode Instruction Description 0F 31 RDTSC Read time-stamp counter into EDX:EAX */ ~Scenix (void) { outportb (d_reg, VOff); /* All lines off */ } int Read (int x) { asm volatile ("cli"); for (int done = 0; !done;) { int z = ReadTimeStampCounter (); while (inportb (s_reg) & 0x40); T = ReadTimeStampCounter (); if ((T - z) > (D * 5)) done = 1; } for (int a = 15; a >= 12; --a) { while ((T + D - ReadTimeStampCounter ()) > 0); if (!(x & (1 << a))) outportb (d_reg, V12 | VZ | Vdd); while ((T + D + D + (D / 2) - ReadTimeStampCounter ()) > 0); outportb (d_reg, V12 | Vdd); while ((T + D + D + D + (D / 4) - ReadTimeStampCounter ()) > 0); while (inportb (s_reg) & 0x40); T = ReadTimeStampCounter (); } int y = 0; for (int a = 11; a >= 0; --a) { while ((T + D + D - ReadTimeStampCounter ()) > 0); if (inportb (s_reg) & 0x40) y |= 1 << a; while ((T + D + D + D + (D / 4) - ReadTimeStampCounter ()) > 0); while (inportb (s_reg) & 0x40); T = ReadTimeStampCounter (); } asm volatile ("sti"); return y; } void Write (int x) { asm volatile ("cli"); for (int done = 0; !done;) { int z = ReadTimeStampCounter (); while (inportb (s_reg) & 0x40); T = ReadTimeStampCounter (); if ((T - z) > (D * 5)) done = 1; } for (int a = 15; a >= 0; --a) { while ((T + D - ReadTimeStampCounter ()) > 0); if (!(x & (1 << a))) outportb (d_reg, V12 | VZ | Vdd); while ((T + D + D + (D / 2) - ReadTimeStampCounter ()) > 0); outportb (d_reg, V12 | Vdd); while ((T + D + D + D + (D / 4) - ReadTimeStampCounter ()) > 0); while (inportb (s_reg) & 0x40); T = ReadTimeStampCounter (); } asm volatile ("sti"); } void WriteX (int x, int i) { asm volatile ("cli"); for (int done = 0; !done;) { int z = ReadTimeStampCounter (); while (inportb (s_reg) & 0x40); T = ReadTimeStampCounter (); if ((T - z) > (D * 5)) done = 1; } for (int a = 0; a < i; ++a) for (int a = 15; a >= 0; --a) { while ((T + D - ReadTimeStampCounter ()) > 0); if (!(x & (1 << a))) outportb (d_reg, V12 | VZ | Vdd); while ((T + D + D + (D / 2) - ReadTimeStampCounter ()) > 0); outportb (d_reg, V12 | Vdd); while ((T + D + D + D + (D / 4) - ReadTimeStampCounter ()) > 0); while (inportb (s_reg) & 0x40); T = ReadTimeStampCounter (); } asm volatile ("sti"); } int ReadDevice (void) { return Read (0x1000); } int ReadFuseX (void) { return Read (0x2000); } void LoadData (int x) { Write (0x4000 + x); } int ReadData (void) { return Read (0x6000); } void ProgramFuseX (int x) { LoadData (x); WriteX (0x3000, 1000); } void ProgramData (int x) { LoadData (x); WriteX (0x5000, p_rep); } void IncrementAddress (void) { Write (0x7000); } int Erase (void) { int X = (ReadFuseX () | 0x7f); cout << "Erasing " << endl; WriteX (0x0000, 2000); ProgramFuseX (X); return X; } }; void lpreset (void) { /* power down the port and wait a few */ outportb (d_reg, VOff); /* All off */ delay (200); outportb (c_reg, 0); /* All off */ delay (20); } void givehelp (void) { cout << endl << "Usage: scenix filename.sxh [/D][/E][/H][/S][/T][/V][/Z][/?][/Fxxx][/Xxxx]" << endl << endl << " /Fxxx - Fuse Word (must be hex and 3 digits long)" << endl << " /Xxxx - FuseX Word (must be hex and 3 digits long)" << endl << endl << " /D - Dump contents of chip " << endl << " /E - Erase chip " << endl << " /H or /? - Display this screen " << endl << " /S - Show me the Verify " << endl << " /T - Test hardware WITHOUT CHIP" << endl << " /V - Verify chip with file " << endl << " /Z - Use 10msec Program Time " << endl << endl << " Default is LPT1 (0x378) " << endl << " To change this create an env " << endl << " variable named SXLPT " << endl << " " << endl << " At the dos prompt type: " << endl << " Set SXLPT=1 for adr=0x378 " << endl << " Set SXLPT=2 for adr=0x278 " << endl << " Set SXLPT=3 for adr=0x3BC " << endl; lpreset(); exit (1); } int lpinit (void) { char *s = 0; int lpt = 1; d_reg = 0x378; /* default port adr 0x378 */ if ((s = getenv ("SXLPT")) != NULL) /* look for env variable */ if ((lpt = atoi (s)) < 1 || lpt > 3) lpt = 1; if (s != NULL) cout << "Found SXLPT=" << s << endl; switch (lpt) /* assign selected data register address */ { case 1: d_reg = 0x378; cout << "using base adr=" << hex << setw (4) << d_reg << endl; break; case 2: d_reg = 0x278; cout << "using base adr=" << hex << setw (4) << d_reg << endl; break; case 3: d_reg = 0x3BC; cout << "using base adr=" << hex << setw (4) << d_reg << endl; break; default: return INVALID; } s_reg = d_reg + 1; c_reg = s_reg + 1; switch (d_reg) /* check port address is valid - really here if user def added */ { case 0x3BC: case 0x378: case 0x278: break; default: return INVALID; } lpreset (); delay (20); outportb (d_reg, VTest1 ); delay (20); int a = inportb (d_reg); outportb (d_reg, VTest2 ); delay (20); int b = inportb (d_reg); delay (20); outportb (d_reg, V0 | Vdd); /* power on to board */ delay(100); if ((a == VTest1) & (b == VTest2)) cout << "Parallel Port " << hex << setw (3) << d_reg << " detected" << endl; else { cout << "Warning! - Check that you are using the correct printer address," << endl << " that the port exists on your machine and is enabled " << endl << " in the bios. " << endl; return INVALID; } return OK; } int CtoI (char c) { if ((c >= '0') && (c <= '9')) return c - '0'; if ((c >= 'A') && (c <= 'F')) return c - 'A' + 10; if ((c >= 'a') && (c <= 'f')) return c - 'a' + 10; return -1; } int ReadByte (ifstream & f) { char c; int i; f >> c; i = CtoI (c) << 4; f >> c; i += CtoI (c); return i; } int main (int argc, char **argv) { cout << version << endl; int Fuse = -1; int FuseX = -1; int htest = 0; int erase = 0; int verify = 0; int verishow = 0; int help = 0; int dump = 0; int csum = 0; /* holds checksum value */ int buffer[16]; /* buffer for dump */ char ch = 0; if (argc == 1) /* if no parameters specified */ givehelp (); for (int a = 0; a < 8192; ++a) mem2[a] = 0x0fff; char c; char *s; for (int a = 0; a < 16384; a += 2) ByteMem[a] = 0xff; for (int a = 1; a < 16384; a += 2) ByteMem[a] = 0x0f; for (int a = 0; a < 8192; ++a) mem1[a] = 0x0fff; for (int a = 1; a < argc; ++a) { int error = 0; if (argv[a][0] != '/') { ifstream f (argv[a]); int done = 0; do { f >> c; if (c != ':') { cout << "Invalid HEX File!" << endl; lpreset(); exit (1); } int Size = ReadByte (f); int Address = (ReadByte (f) << 8) + ReadByte (f); int Type = ReadByte (f); for (int a = 0; a < Size; ++a) ByteMem[Address + a] = ReadByte (f); ReadByte (f); done = (Type == 0x01); } while (!done); for (int a = 0; a < 8192; ++a) mem1[a] = ByteMem[2 * a] + (ByteMem[(2 * a) + 1] << 8); if (mem1[1010] != -1) Fuse = mem1[0x1010]; if (mem1[1011] != -1) FuseX = mem1[0x1011]; } else switch (toupper (argv[a][1])) { case 'F': /* Fuse bit */ Fuse = strtol (&(argv[a][2]), &s, 16); if (*s) error = 1; break; case 'X': /* FuseX */ FuseX = strtol (&(argv[a][2]), &s, 16); if (*s) error = 1; break; case 'D': /* Dump code and fuse bits */ dump = 1; break; case 'T': /* Hardware test mode */ htest = 1; break; case 'E': /* Erase Chip Contents */ erase = 1; break; case 'V': /* Verify Chip Contents */ verify = 1; break; case 'Z': /* Change Programming Time to 20msec - only newest chips */ p_rep = 20; break; case 'S': /* Show Verify Information */ verishow = 1; break; case 'H': /* Help - Display help screen */ help = 1; break; case '?': /* Help - Display help screen */ help = 1; break; default: help = 1; } if (help) { givehelp (); } if (htest) /* Hardware test mode */ { lpinit(); cout << endl; cout << " WARNING! WARNING! WARNING! WARNING! " << endl << "========================================" << endl << "The scenix chip should be removed from " << endl << "the programmer for these tests. " << endl << " " << endl << "Parallel port pins 2, 3, 4 and 5 will " << endl << "be turned on individually allowing you " << endl << "time to physically check your hardware. " << endl << " " << endl << "When you are ready to begin, " << endl << " Press the ENTER key " << endl; ch = getchar (); /* wait for a return from kb */ outportb (d_reg, VZ | Vdd); /* Pin 2 on */ cout << "Parallel pin 2 active - Led D3 On " << endl << "Press ENTER to continue " << endl; ch = getchar (); /* wait for a return from kb */ outportb (d_reg, V0 | Vdd); /* Pin 3 on - OSC1= 0 v */ cout << "Parallel pin 3 active - Led D4 On " << endl << "Press ENTER to continue " << endl; ch = getchar (); /* wait for a return from kb */ outportb (d_reg, V5 | Vdd); /* Pin 4 on - OSC1 = 5 v */ cout << "Parallel pin 4 active - Led D5 On " << endl << "Press ENTER to continue " << endl; ch = getchar (); /* wait for a return from kb */ outportb (d_reg, V12 | Vdd); /* Pin 5 on - OSC1 = 12 v */ cout << "Parallel pin 5 active - Led D6 On " << endl << "Press ENTER to end test mode " << endl; ch = getchar (); /* wait for a return from kb */ lpreset (); exit (1); } } Scenix S; if (dump | verify | verishow) { csum = 0; long flags = cout.flags (ios::uppercase); int rdata = 0; int address = 0; int k = 0; int Size; int i = 0; int fuse = 0; int fusex = 0; int device = 0; char *v = "0"; flags = cout.flags (ios::uppercase); fuse = S.ReadData (); fusex = S.ReadFuseX (); device = S.ReadDevice (); cout << "Fuse : " << hex << setw (3) << setfill (*v) << fuse << endl << "FuseX : " << hex << setw (3) << setfill (*v) << fusex << endl << "Device : " << hex << setw (3) << setfill (*v) << device << endl << "Time Constant : " << dec << S.D << endl << endl; for (int a = 0; a < 8192; ++a) mem2[a] = 0x0fff; for (int a = 0; a < 2048; a++) { S.IncrementAddress (); rdata = S.ReadData () & 0xFFF; if (rdata == 0xFFF) break; mem2[a] = rdata; mem2[a + 1] = 0xfff; } if (mem1[1010] != -1) Fuse = mem1[0x1010]; if (mem1[1011] != -1) FuseX = mem1[0x1011]; int FsX = (fusex & 0xf80) | (FuseX & 0x7f); FuseX = FsX; if (verify | verishow) { int progfailed = 0; int fusefailed = 0; if (verishow) { cout << "Chip fuse " << hex << setw (4) << fuse << endl; cout << "File Fuse " << hex << setw (4) << Fuse << endl << endl; cout << "Chip fusex " << hex << setw (4) << fusex << endl; cout << "File FuseX " << hex << setw (4) << FuseX << " (After Preserving factory presets)" << endl << endl; } if (Fuse != -1) { if (Fuse != fuse) { fusefailed = 1; cout << "Fuse verify failed" << endl; } else cout << "Fuse Passed" << endl; } if (FuseX != -1) { if (FuseX != fusex) { fusefailed = 1; cout << "Fusex verify failed" << endl << endl; } else cout << "Fusex Passed" << endl << endl; } for (int a = 0; a < 2048; a++) { if (mem1[a] == 0x0fff) break; if (verishow) { cout << hex << setw (4) << setfill (*v) << mem1[a] << " " << hex << setw (4) << setfill (*v) << mem2[a] << endl; } if (mem1[a] != mem2[a]) { progfailed = 1; cout << "Program Verify Failed!" << endl; } } lpreset(); if (!progfailed) cout << "Program Data Passes Verify" << endl << endl; if (fusefailed | progfailed){ exit (1); } exit (0); } address = 0; Size = 0x010; int memloc = 0; int done = 0; do { csum = 0; k = 0; for (i = 0; i < Size; i += 2) { rdata = mem2[memloc++]; if (rdata == 0x0fff) break; rdata = (((rdata) >> 8) + (((rdata) & 0xFF) << 8)); csum += rdata >> 8; csum += (rdata & 0xff); buffer[k++] = rdata; } if (i) { csum += address >> 8; /* Checksum high address byte. */ csum += address & 0xff; /* Checksum low address byte. */ csum += (i); /* Checksum record byte count. */ csum = ((0 - csum) & 0xff); cout << ":" << setbase (16) << setfill (*v) << setw (2) << (i) << setw (4) << address << "00"; for (int l = 0; l < k; l++) /* Then the actual data. */ { cout << setbase (16) << setfill (*v) << setw (4) << buffer[l]; } cout << setbase (16) << setfill (*v) << setw (2) << csum << endl; } address += i; /* increment address */ done = (rdata == 0x0fff); } while (!done); k = 0; csum = 0; fuse = (((fuse) >> 8) + (((fuse) & 0xFF) << 8)); csum += fuse >> 8; csum += (fuse & 0xff); buffer[k++] = fuse; fusex = (((fusex) >> 8) + (((fusex) & 0xFF) << 8)); csum += fusex >> 8; csum += (fusex & 0xff); buffer[k++] = fusex; address = 0x2020; i = 4; csum += address >> 8; csum += address & 0xff; csum += (i); csum = ((0 - csum) & 0xff); cout << ":" << setbase (16) << setfill (*v) << setw (2) << (i) << setw (4) << address << "00"; for (int l = 0; l < k; l++) { cout << setbase (16) << setfill (*v) << setw (4) << buffer[l]; } cout << setbase (16) << setfill (*v) << setw (2) << csum << endl; cout << ":00000001FF" << endl; /* end of file record */ lpreset(); } else if (erase) { S.Erase (); lpreset (); exit (1); } else { int FsX = (S.Erase() & 0xf80) | (FuseX & 0x7f); FuseX = FsX; cout << "Programming " << endl; S.ProgramFuseX (FuseX); S.ProgramData (Fuse); S.ProgramData (Fuse); S.ProgramData (Fuse); S.ProgramData (Fuse); S.ProgramData (Fuse); S.IncrementAddress (); for (int a = 0; a < 2048; ++a) { if (mem1[a] != 0xfff) S.ProgramData (mem1[a]); S.IncrementAddress (); if ((a % 32) == 0) { cout << '.'; cout.flush (); } } cout << endl << "Done" << endl; } lpreset(); }