SPI EEPROM boot for 6502

For discussing the 65xx hardware itself or electronics projects.
barnacle
Posts: 1831
Joined: 19 Jan 2004
Location: Potsdam, DE
Contact:

Re: SPI EEPROM boot for 6502

Post by barnacle »

Honeywell Aerospace SPI/Parallel eeprom. For Aerospace, Deep Earth, and High Temperature applications and (was) priced accordingly... https://aerospace.honeywell.com/content ... asheet.pdf

(I used to work in the Deep Earth development, making steering robots for oil drills. It gets a bit warm down there; we used to design to operate from -40 to +150C, and were looking at +225C when I left.)

Neil
fachat
Posts: 1123
Joined: 05 Jul 2005
Location: near Heidelberg, Germany
Contact:

Re: SPI EEPROM boot for 6502

Post by fachat »

I see you're all way more creative than I am.

I used a large CPLD in the Micro-PET and part of it boots from an SPI ROM, by copying over the first page of the ROM into 6502 page $ffxx, while keepinh phi2 stopped. After that the SPI interface - incl ROM is available as peripheral to the CPU.

My approach would still be a CPLD to do just that. If you want a smaller one you could generate the lower 8 address bits only and pull the upper 8 bits high using other means.

André
Author of the GeckOS multitasking operating system, the usb65 stack, designer of the Micro-PET and many more 6502 content: http://6502.org/users/andre/
fachat
Posts: 1123
Joined: 05 Jul 2005
Location: near Heidelberg, Germany
Contact:

Re: SPI EEPROM boot for 6502

Post by fachat »

If you want to do away with the address line generation, you could stream out the exact code sequence the CPU requires while booting - completely ignoring the address lines.

However that quickly gets complicated. Stores need to go to parallel RAM. But I don't think you can use zeropage or stack as this would need detection when to allow reading it instead of getting the value from SPI ROM.
Also you cannot use the same SPI ROM to load the second stage boot code from - as it is busy streaming out the initial boot code to the CPU, you would need a second storage medium.
Also, no wait loops are allowed as the executed code is fixed in ROM. - no dynamic second stage boot program storage can be used. You might get away with an SD card but I don't know enough of its protocol. An SD Card actually requires a second SPI core as the first one is busy streaming the initial boot code...

So, would maybe work, in the category 'because I can', but I doubt it would less resources than a small CPLD that counts addresses, and can be even used to hold the second stage boot code.
Author of the GeckOS multitasking operating system, the usb65 stack, designer of the Micro-PET and many more 6502 content: http://6502.org/users/andre/
fachat
Posts: 1123
Joined: 05 Jul 2005
Location: near Heidelberg, Germany
Contact:

Re: SPI EEPROM boot for 6502

Post by fachat »

Michael wrote:
gfoot wrote:
Michael wrote:
Ok. Since you've pretty much dis'ed using a microcontroller, how about using an 8K 74AHCTXL18426 "boot loader" IC in a DIP-14 package (~$1.50)?
I'm afraid I don't know what you mean - yes I'm not keen on using a microcontroller for this, and I'm not sure what 74AHCTXL18426 is, did you mean PIC16F18426, or is it the same thing?
Yeah, it's a PIC microcontroller and it simply takes advantage of a simple novel interface method to appear to the 6502 as a 'smart' phantom ROM of sorts. Using it to copy an image of your boot loader into memory at startup is one of many ways you might use it. I use it to copy a full 64K image from a slow ROM at a nice leisurely 1-MHz rate into RAM on a 64K RAM system before switching to a fast clock.

Good luck on your project...
How does the copying work with only 14 pins minus two for power supply, to generate addresses to store data to?

Edit: that's probably exactly what described in my post above when ignoring address lines completely. And using a microcontroller you are more flexible - you can even run a 'shadow ' program to see if data read from IO causes branches and then just stream out the right code branch. Nice!
Author of the GeckOS multitasking operating system, the usb65 stack, designer of the Micro-PET and many more 6502 content: http://6502.org/users/andre/
barnacle
Posts: 1831
Joined: 19 Jan 2004
Location: Potsdam, DE
Contact:

Re: SPI EEPROM boot for 6502

Post by barnacle »

Same approach as mine, I think: let the processor generate the addresses it wants, but ignore them except when writing.

Though I wouldn't expect to execute loops and branches; no need if all you're doing is loading the ram with a bootloader.

Neil
User avatar
Alarm Siren
Posts: 363
Joined: 25 Oct 2016

Re: SPI EEPROM boot for 6502

Post by Alarm Siren »

Yes, a CPLD would be "easier" - if you already have the knowledge and infrastructure to program one. Not everyone does, nor is it trivial to get to that position. For this reason I feel like booting from an SPI EEPROM still has a place if it can be made to work. The solution is far from ideal, but it has less "up-front barriers" than a CPLD does.
Want to design a PCB for your project? I strongly recommend KiCad. Its free, its multiplatform, and its easy to learn!
Also, I maintain KiCad libraries of Retro Computing and Arduino components you might find useful.
User avatar
Michael
Posts: 633
Joined: 13 Feb 2013
Location: Michigan, USA

Re: SPI EEPROM boot for 6502

Post by Michael »

fachat wrote:
Michael wrote:
gfoot wrote:
Michael wrote:
Ok. Since you've pretty much dis'ed using a microcontroller, how about using an 8K 74AHCTXL18426 "boot loader" IC in a DIP-14 package (~$1.50)?
I'm afraid I don't know what you mean - yes I'm not keen on using a microcontroller for this, and I'm not sure what 74AHCTXL18426 is, did you mean PIC16F18426, or is it the same thing?
Yeah, it's a PIC microcontroller and it simply takes advantage of a simple novel interface method to appear to the 6502 as a 'smart' phantom ROM of sorts. Using it to copy an image of your boot loader into memory at startup is one of many ways you might use it. I use it to copy a full 64K image from a slow ROM at a nice leisurely 1-MHz rate into RAM on a 64K RAM system before switching to a fast clock.

Good luck on your project...
Quote:

How does the copying work with only 14 pins minus two for power supply, to generate addresses to store data to?

Edit: that's probably exactly what described in my post above when ignoring address lines completely. And using a microcontroller you are more flexible - you can even run a 'shadow ' program to see if data read from IO causes branches and then just stream out the right code branch. Nice!
The microcontroller presents to the 6502 as a smart ROM of sorts. It simply provides LDA <imm>, LDA <abs>, STA <abs>, and JMP <abs> instructions while turning on ROM or RAM during the correct cycle within each instruction.

A hardware analogy might look something like the drawing below where you're setting clock and data switches during each half clock cycle to mimick the microcontroller single-cycle Push() and Pull() functions. I'm working on a demo project.
blind interface #2.png

Code: Select all

  /******************************************************************************
   *  core 'blind interface' functions                                          *
   *                                                                            */
   void uReset()                      // ****************************************
   { clk(1);                          // clock = 1                              *
     res(0);                          // reset = 0  ~~~~~~~~~~~~~~~~~~~~~~~~~~  *
     _delay_us(100);                  //                                        *
     uPull(ram);                      //                                   (1)  *
     uPull(ram);                      //                                   (2)  *
     res(1);                          // reset = 1  ~~~~~~~~~~~~~~~~~~~~~~~~~~  *
     uPull(ram);                      //                                   (1)  *
     uPull(ram);                      //                                   (2)  *
     uPull(ram);                      //                                   (3)  *
     uPull(ram);                      //                                   (4)  *
     uPull(ram);                      //                                   (5)  *
     uPush(lo(0x8000));               // address $FFFC (reset vector lo)   (6)  *
     uPush(hi(0x8000));               // address $FFFD (reset vector hi)   (7)  *
   }                                  // ****************************************

   void wrROM(u16 addr,byte data)     // ****************************************
   { uPush(0xA9);                     //  lda <imm>                             *
     uPush(data);                     //   "                                    *
     uPush(0x8D);                     //  sta <abs>                             *
     uPush(lo(addr));                 //   "         address lo                 *
     uPush(hi(addr));                 //   "         address hi                 *
     uPull(rom);                      //   "         6502 write op'             *
   }                                  // ****************************************

   void wrRAM(byte data)              // ****************************************
   { uPush(0xA9);                     //  lda <imm>                             *
     uPush(data);                     //   "                                    *
     uPush(0x8D);                     //  sta <abs>                             *
     uPush(lo(addr));                 //   "         abs address lo             *
     uPush(hi(addr));                 //   "         abs address hi             *
     uPull(ram);                      //   "         6502 write op'             *
   }                                  // ****************************************

   byte rdMEM(byte mem)               // ****************************************
   { uPush(0x4C);                     //  jmp $8000  reset PC (avoid I/O area)  *
     uPush(lo(0x8000));               //   "         abs address lo             *
     uPush(hi(0x8000));               //   "         abs address hi             *
     uPush(0xAD);                     //  lda <abs>                             *
     uPush(lo(addr));                 //   "         abs address lo             *
     uPush(hi(addr));                 //   "         abs address hi             *
     return uPull(mem);               //   "         6502 read op'              *
   }                                  // ****************************************
Simply build off of those core functions (loader, programmer, etc.)...

Code: Select all

  /******************************************************************************
   *  load 64K RAM from 64K 'A' or 'B' half of ROM at 'power-up' or 'reset' at  *
   *  a nice liesurely 1-MHz rate.                                              *
   *                                                                            */
   void loader()                      // ****************************************
   { addr = 0x0000; x15(0);           // start address $0000, ROM A15 = 0       *
     uReset();                        // reset CPU (synchronize micro to cpu)   *
     do                               // copy 64K ROM minus 8K I/O area to RAM  *
     { wrRAM(rdMEM(rom));             // ROM -> RAM (0000..5FFF & 8000..FFFF)   *
       if(++addr == 0x6000)           // if I/O area ($6000..$7FFF)             *
       { addr = 0x8000; x15(1);       //   skip, ROM A15 = 1 ($8000..$FFFF)     *
       }                              //                                        *
     } while(addr);                   // until roll-over to 0 (full 64K range)  *
   }                                  // ****************************************

Code: Select all

  /******************************************************************************
   *  programming 64K RAM image onto 64K 'A' or 'B' half of the 128K Flash ROM  *
   *  takes about 7 seconds.                                                    */
   void flash()                       // ****************************************
   { addr = 0x0000; x15(0);           // start address $0000, ROM A15 = 0       *
     uReset();                        // reset 6502 (synchronize uC to cpu)     *
     do                               //                                        *
     { if((addr & 0x0FFF) == 0)       // if 4K sector boundary                  *
       { wrROM(0x5555,0xAA);          // erase sector sequence                  *
         wrROM(0x2AAA,0x55);          //  "                                     *
         wrROM(0x5555,0x80);          //  "                                     *
         wrROM(0x5555,0xAA);          //  "                                     *
         wrROM(0x2AAA,0x55);          //  "                                     *
         wrROM(addr,0x30);            //  "                                     *
         _delay_ms(25);               // required delay                         *
         putSer('.');                 // indicate progress                      *
       }                              //                                        *
       wrROM(0x5555,0xAA);            // write byte 'unlock' sequence           *
       wrROM(0x2AAA,0x55);            //  "                                     *
       wrROM(0x5555,0xA0);            //  "                                     *
       wrROM(addr,rdMEM(ram));        // write byte                             *
       _delay_us(20);                 // required write delay                   *
       if(++addr == 0x6000)           // if I/O area ($6000..$7FFF)             *
       { addr = 0x8000; x15(1);       //   skip, ROM A15 = 1 ($8000..$FFFF)     *
       }                              //                                        *
     } while(addr);                   // until roll-over to 0 (full 64K range)  *
   }                                  // ****************************************

Code: Select all

  /******************************************************************************
   *  hex file download to RAM (ROM emulator mode, 115200 baud)                 *
   *                                                                            */
   char loadHex()                     // ****************************************
   { char bytecnt;                    // hex record length                      *
     char type;                       // hex record type                        *
     char data;                       //                                        *
     char cksum;                      // hex record checksum                    *
     char error = 0;                  //                                        *
     do                               // process hex file                       *
     { do                             // process hex record                     *
       { putSer(data = getSer());     // echo & flush <cr> and <lf> chars       *
       } while(data != ':');          // until we see a ':' record header       *
       cksum = (bytecnt = getByte()); // get 'byte' (not character) count       *
       cksum += (data = getByte());   // get address hi                         *
       addr = (data << 8);            //  "                                     *
       cksum += (data = getByte());   // get address lo                         *
       addr += data;                  //  "                                     *
       cksum += (type = getByte());   // get record type (0, 1, or 4)           *
       if(type == 4)                  // if <extended address> record           *
       { cksum += getByte();          //                                        *
         cksum += getByte();          //                                        *
       }                              //                                        *
       if(type == 0)                  // if <data> record                       *
       { while(bytecnt--)             //                                        *
         { data = getByte();          //                                        *
           cksum += data;             //                                        *
           wrRAM(data); addr++;       // write byte to RAM, bump address        *
         }                            //                                        *
       }                              //                                        *
       data = getByte();              // get record checksum byte               *
       if(-data != cksum) error = 1;  // test for error                         *
     } while(type != 1);              // loop until <end-of-file> record        *
     return error;                    //                                        *
   }                                  // ****************************************
Last edited by Michael on Sat Aug 12, 2023 10:20 am, edited 5 times in total.
User avatar
GARTHWILSON
Forum Moderator
Posts: 8773
Joined: 30 Aug 2002
Location: Southern California
Contact:

Re: SPI EEPROM boot for 6502

Post by GARTHWILSON »

What I did was similar to what barnacle describes.  It's described at viewtopic.php?p=91876#p91876 and my next two posts on that page.  It's finished and tests fine, but I've been slow to make the system it's made to plug into.  I can provide them, and I do show them on the front page of my site, but I have not finished the documentation.
http://WilsonMinesCo.com/ lots of 6502 resources
The "second front page" is http://wilsonminesco.com/links.html .
What's an additional VIA among friends, anyhow?
Post Reply