Serial EEPROMs as Mass Storage
Intro
Forth without mass storage (blocks, screens) is a not complete. A SD-Card interface could be a reasonable solution but it is an overkill for a small Forth system. Small serial EEPROMs are for my opinion more suitable.
SPI EEPROMs

SPI EEPROMs

The Serial Peripheral Interface SPI or four-wire serial bus is easy to use. The CDP1802 (MC) is the SPI master, the EEPROM is the slave. There are many different EEPROM types and sizes available e.g. AT25M02 (2Mbit, 256 KiB, $3), 25LC1024 (128 KiB, $2), or 25LC512 (64 KiB, $1.50). All available in DIL8 packages. 64/128/256 KiB seems very small for today's standards where storage is quantified in GiB, but I think it's more than enough for a small Forth system, the size is similar to early floppy disks. If you want more memory there are 16 MiB serial Flash chips e.g. from WINBOND W25Q128J or IS25LP128F, please note they are 3.3 V devices and in SMT!

CLK   MC ->- host
MOSI  MC ->- host
MISO  MC -<- host
SS    MC ->- host or other peripherals (optional)

A high-to-low transition on the CS pin is required to start an operation and a low-to-high transition is required to end an operation.

Invalid Opcode: If an invalid opcode is received, no data will be shifted into AT25M02 and the Serial Data Output (SO) pin will remain in a high impedance state until the falling edge of CS is detected again. This will reinitialize the serial communication.

While in Hold mode, the SO pin will be in a high impedance state. In addition, both the SI pin and the SCK pin will be ignored.

From 1024 Kibit up there are 24 address bits, 8 Kibit to 512 Kibit have 16 address bits. 1, 2, and 4 Kibit have 8 bit address bits.

SPI Serial EEPROM Family Data Sheet 25AAXXXX/25LCXXXX.

25LCxxxx Instruction Set

Name Format Description
READ 0000 0011 Read data from memory array beginning at selected address
WRITE 0000 0010 Write data to memory array beginning at selected address
WREN 0000 0110 Set the write enable latch (enable write operations)
WRDI 0000 0100 Reset the write enable latch (disable write operations)
RDSR 0000 0101 Read STATUS register
WRSR 0000 0001 Write STATUS register
PE 0100 0010 Page Erase erase one page in memory array
SE 1101 1000 Sector Erase erase one sector in memory array
CE 1100 0111 Chip Erase erase all sectors in memory array
RDID 1010 1011 Release from Deep power-down and read electronic signature
DPD 1011 1001 Deep Power-Dow

AT25M02 Instruction Set

Name Format Description
READ 0000 0011 Read from Memory Array
WRITE 0000 0010 Write to Memory Array
WREN 0000 0110 Set Write Enable Latch (WEL)
WRDI 0000 0100 Reset Write Enable Latch (WEL)
RDSR 0000 0101 Read Status Register (SR)
WRSR 0000 0001 Write Status Register (SR)
LPWP 0000 1000 Low Power Write Poll

EEPROM Connected to MC's Centronics DB25 Connector (Switches and LEDs)

EEPROM Connected to MC's Centronics DB25 Connector (Switches and LEDs)

Sharing the LED and Switch port, you loose three LEDs and one switch or IN. Possible conflict with the bootstrap loader, if there is a read sequence (CS and read pattern 0000 0011). To prevent this, set the EEPROM into HOLD state e.g. with the WAIT signal.

SPI MC (Master) 25LCxxxx (Slave) Interface
MISO J2.1 IN EF4 2 SO diode e.g. 1N4148
MOSI J2.11 O7 LED7 5 SI direct
CLK J2.10 O6 LED6 6 SCK direct
CS J2.12 O5 LED5 1 CS direct
  P4.3 VDD 8 VCC +5V capacitor 100 nF to GND
  " 3 WP +5V
  J2.14 WAIT 7 HOLD direct
  21 GND 4 GND GND

mc-eeprom-conn.png
Schematic
db25-eeprom-top.jpg
EEPROM on Veroboard
db25-eeprom-bottom.jpg
EEPROM on Veroboard

Raspberry Pi can emulate SPI EEPROM. On RaspiElf the switches/LEDs are already connected to Raspi's GPIOs. No need for additional hardware. But I have to write an SPI server for the Raspberry Pi. Raspi's SPI interfaces can't be used because of conflicting port usage.

Read Byte

                LDI     01H
                PLO     R4              ; for the carry          
                PLO     R5              ; reset all bits
                LDI     8               ; counting down 8 times
                PLO     R6              ; bit counter
RDBITLOOP       GLO     R4              ; set CARRY
                SHR
                GLO     R5              ; get bits
                BN4     SETBIT          ; branch if bit set (EF4 == 0)
                SHL                     ; bit not set
                SKP                     ; BR SAVEBIT
SETBIT          SHLC                    ; set bit
SAVEBIT         OUT     P4              ; CLK on for SPI
                BYTE    01000000B
                OUT     P4              ; CLK off
                BYTE    00000000b
                PLO     R5              ; save bits
                DEC     R6
                GLO     R6
                BNZ     RDBITLOOP

about 230 cycles for one byte -> 1 ms -> 1 KiB takes about 1 s @ 1.79 MHz

Write Byte

WRITEBYTE       PLO     R5              ; save transmit byte
                LDI     8               ; counter 8 bits
                PLO     R6
WRBITLOOP       GLO     R5              ; get the next bit
                SHL                     ; next bit is in the carry
                PLO     R5
                BDF     BITSET
                OUT     P4              ; bit cleared
                BYTE    00000000B
                OUT     P4              ; clock on
                BYTE    01000000B       
                OUT     P4              ; clock off
                BYTE    00000000B
                BR      WRTEST
BITSET          OUT     P4              ; bit set
                BYTE    10000000B
                OUT     P4              ; clock on
                BYTE    11000000B
                OUT     P4              ; clock off
                BYTE    10000000B 
WRTEST          DEC     R6
                GLO     R6

EEPROM patched on MC PCB

EEPROM patched on MC PCB

SPI Mode 0, data is always latched in on the rising edge of SCK and always output on the falling edge of SCK. For CS one output port bis is needed e.g. O7 or N0 (INP1) to start/end operation (A high-to-low transition on the CS pin is required to start an operation and a low-to-high transition is required to end an operation).

SPI MC (Master) 25LCxxxx (Slave) Interface
MISO EF2 2 SO direct
MOSI D0 5 SI direct
CLK TPB & N1 (OUT2) 6 SCK wired AND; Pullup 10 k, 2 1N4148
CS N0 1 CS direct
    8 VCC +5V
    3 WP +5V
  J2.14 WAIT 7 HOLD direct
    4 GND GND

mc-eeprom-u1.png
Schematic
eeprom-mc.jpg
EEPROM on Veroboard

Read Byte

                LDI     01
                PLO     R4              ; for the carry
                GHI     R0              ; D = 0
                PLO     R5              ; reset all bits
                LDI     0 - 8           ; counting up 8 times
                PLO     R6              ; bit counter
RDBITLOOP       GLO     R4              ; set CARRY
                SHR
                GLO     R5
                BN2     SETBIT          ; branch if bit set (EF2 == 0)
                SHL                     ; bit not set
                SKP                     ; BR SAVEBIT      
SETBIT          SHLC
SAVEBIT         OUT     P2              ; CLK for SPI, INC Rx   
                PLO     R5
                GLO     R6
                BNZ     RDBITLOOP

about 200 cycles for one byte -> 1 ms -> 1 KiB takes about 1 s

Write Byte

WRITEBYTE       PLO     R5              ; save transmit byte
                LDI     8               ; counter 8 bits
                PLO     R6
WRBITLOOP       GLO     R5              ; get the next bit
                SHL                     ; next bit is in the carry
                PLO     R5
                LSNF                    ; skip if bit is 0
                OUT     P2
                BYTE    00000001B       ; CLK for SPI with data bit cleared
                LSDF                    ; skip if bit is 1
                OUT     P2
                BYTE    00000000B       ; CLK for SPI with data bit set 
                DEC     R6
                GLO     R6
                BNZ     WRBITLOOP

EEPROM Connected to Raspberry Pi

EEPROM Connected to Raspberry Pi

I use a Raspberry Pi Zero (about $20) as an EEPROM programmer.

SPI Function BCM/GPIO RaspiElf
SPI0 MISO 9 IN4
SPI0 MOSI 10 IN3
SPI0 SCLK 11 IN6
SPI0 CS0 8 IN7
SPI0 CS1 7 O0
EEPROM CS 5 O1
SPI1 MISO 19 O5
SPI1 MOSI 20 -
SPI1 SCLK 21 shutdown
SPI1 CS0 18 CLR
SPI1 CS1 17 WAIT

raspi-eeprom.png
Schematic
raspi-zero-eeprom.jpg
Raspberry Pi Zero and EEPROM interface

eeprom2bin (download tool)

NAME
eeprom2bin - Copies the EEPROM memory to a binary file on the Raspberry Pi.

SYNOPSIS
eeprom2bin [-s hexadr] [-e hexadr] [-k size] [-p page_size] [-a address_bits] [file]

DESCRIPTION
Copies the EEPROM memory to a binary file (or stdout) on the Raspberry Pi. The Raspberry Pi GPIO SPI0.1 is used as interface to the SPI EEPROM (e.g. 25LC1024 has 24 bit address and 256 byte page). The generated data is written to the standard output stream or to a file. Caution: Overwrite file if it exists. Use > for redirecting (save the file) or | for piping to another command (e.g. hexdump).
size [Kibit] address bits page size [byte]
1, 2 8 16
4, 8, 16 16 16 (some 32)
32, 64 16 32
128, 256 16 64
512 16 256
1024, 2048 24 256

OPTIONS
Non argument options that are duplicated on the command line are not harmful. For options that require an argument, each duplication will override the previous argument value.
-s hexadr
start address in hex (0 is default)
-e hexadr
end adress in hex (0x1FFFF is default)
-p number
page size in bytes (256 is default)
-a number
address bits (8, 16, or 24; 24 is default)
-k number
size in Kibit (1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048; 1024 is default)

bin2eeprom (upload tool)

NAME
bin2eeprom - Copies the content of binary file on the Raspberry Pi to EEPROM.

SYNOPSIS
bin2eeprom [-s hexadr] [-e hexadr] [-k size] [-p page_size] [-a address_bits] [file]

DESCRIPTION
Copies the content of binary file on the Raspberry Pi to EEPROM memory. The Raspberry Pi GPIO SPI0.1 is used as interface to the SPI EEPROM (e.g. 25LC1024 has 24 bit address and 256 byte page). Use < for redirecting or | for piping from another command.
size [Kibit] address bits page size [byte]
1, 2 8 16
4, 8, 16 16 16 (some 32)
32, 64 16 32
128, 256 16 64
512 16 256
1024, 2048 24 256

OPTIONS
Non argument options that are duplicated on the command line are not harmful. For options that require an argument, each duplication will override the previous argument value.
-s hexadr
start address in hex (0 is default)
-e hexadr
end adress in hex (0x1FFFF is default)
-p number
page size in bytes (256 is default)
-a number
address bits (8, 16, or 24; 24 is default)
-k number
size in Kibit (1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048; 1024 is default)

How to get and build the EEPROM tools

Get the source from the GIT repository (if you have not installed GIT yet, then install it with sudo apt-get install git), type only the bold text after the $ sign:

pi@cosmac:~/elf $ git clone https://github.com/spyren/RaspiElf
Cloning into 'RaspiElf'...
pi@cosmac:~/elf $

Build (compile) from the sources:

pi@cosmac:~/elf $ cd RaspiElf
pi@cosmac:~/elf/RaspiElf $ cd eeprom
pi@cosmac:~/elf/RaspiElf/eeprom $ make
cc -g -c eeprom2bin.c
cc -g -o eeprom2bin -lwiringPi eeprom2bin.o
cc -g -c bin2eeprom.c
cc -g -o bin2eeprom -lwiringPi bin2eeprom.o
pi@cosmac:~/elf/RaspiElf/eeprom $ 

Install the binaries into /usr/local/bin

pi@cosmac:~/elf/RaspiElf/eeprom $ sudo make install
install -m 557 eeprom2bin bin2eeprom /usr/local/bin

Install wiringPi (GPIO Interface library for the Raspberry Pi), details see http://wiringpi.com/download-and-install/

Enable the SPI interface

@cosmac:~/elf/RaspiElf/eeprom $ sudo raspi-config
  • 5 Interfacing Options Configure connections to peripherals
  • P4 SPI Enable/Disable automatic loading of SPI kernel module

Mini Boot Loader

Mini Boot Loader

For RAM only Membership Cards you need some kind of bootstrap loader. To toggle in a monitor or even BASIC or FORTH is nearly impossible. Serial EEPROMs in DIP8 packages are cheap and easy to get. But you still have to use the front panel to toggle in the boot loader itself (this is the first-stage boot loader).

The "patched on MC PCB" boot loader takes about 25 s for 32 KiB, the DB25 variant takes about 29 s. For the sources see https://github.com/spyren/RaspiElf/tree/master/eeprom .

                        ;       TITL    "EEPROM Boot Loader for Elf Memebership Card"
                        ;               EJCT    60
                        
                                        CPU     1802
                        
      NUMBER            EQU     0
                        
                        ;
                        ; Register Definitions:
                        ;
      R0                EQU     0
      R1                EQU     1
      R2                EQU     2
      R3                EQU     3
      R4                EQU     4
      R5                EQU     5
      R6                EQU     6
      R7                EQU     7
      R8                EQU     8
      R9                EQU     9
      R10               EQU     10
      R11               EQU     11
      R12               EQU     12
      R13               EQU     13
      R14               EQU     14
      R15               EQU     15
                        
                        ;
                        ; I/O Port Definitions:
                        ;
      P1                EQU     1
      P2                EQU     2
      P3                EQU     3
      P4                EQU     4
      P5                EQU     5
      P6                EQU     6
      P7                EQU     7       
                        
                ORG     0H
                                        
                        ; R0   program counter
                        ; R1   subroutine pc
                        ; R2   stack pointer
                        ; R5.0 byte (read/write)
                        ; R6.0 bit counter
                        ; R4.0 carry bits
                        ; R7   destination address 
                        ; R8   length
                        ; R9   start address
                        
      START
   0000   c0:11000000           LBR     BOOTLOADER
   0001   00:00000000
   0002   03:00000011
      BOOTLOADER
   0003   f8:11111000           LDI     080H            ; set destination address
   0004   80:10000000
   0005   b7:10110111           PHI     R7
   0006   b9:10111001           PHI     R9              ; start address
   0007   f8:11111000           LDI     080H            ; set length
   0008   80:10000000
   0009   b8:10111000           PHI     R8
   000a   90:10010000           GHI     R0              ; D = 00H
   000b   a7:10100111           PLO     R7              ; destination address page boundry
   000c   a8:10101000           PLO     R8              ; length in pages
   000d   a9:10101001           PLO     R9
   000e   b1:10110001           PHI     R1              ; high byte subroutine
   000f   a2:10100010           PLO     R2              ; stack pointer = 0100H 
   0010   f8:11111000           LDI     01H
   0011   01:00000001
   0012   b2:10110010           PHI     R2
   0013   a4:10100100           PLO     R4              ; for the carry          
   0014   f8:11111000           LDI     LOW WRITEBYTE   ; low byte subroutine
   0015   4f:01001111
   0016   a1:10100001           PLO     R1
   0017   61:01100001           OUT     P1              ; deactivate CS to start operation
   0018   00:00000000           BYTE    00H
                                        
   0019   e1:11100001           SEX     R1              ; for immediate OUT in subroutine 
   001a   f8:11111000           LDI     03H             ; EEPROM read command
   001b   03:00000011
   001c   d1:11010001           SEP     R1              ; CALL WRITEBYTE
   001d   90:10010000           GHI     R0              ; address bit 16 to 23 = 0
   001e   d1:11010001           SEP     R1              ; CALL WRITEBYTE, replace by NOP for
                                                                ; 1 to 512 Kibit EEPROMs
   001f   90:10010000           GHI     R0              ; address bit 8 to 15 = 0
   0020   d1:11010001           SEP     R1              ; CALL WRITEBYTE, replace by NOP for
                                                                ; 1 to 4 Kibit EEPROMs
   0021   90:10010000           GHI     R0              ; address bit 0 to 7 = 0
   0022   d1:11010001           SEP     R1              ; CALL WRITEBYTE
   0023   e6:11100110           SEX     R6              ; Rx for OUT
   0024   90:10010000 BLOCKLOOP GHI     R0              ; D = 0
   0025   a5:10100101           PLO     R5              ; reset all bits
   0026   f8:11111000           LDI     0 - 8           ; counting up 8 times
   0027   f8:11111000
   0028   a6:10100110           PLO     R6              ; bit counter
   0029   84:10000100 RDBITLOOP GLO     R4              ; set CARRY
   002a   f6:11110110           SHR
   002b   85:10000101           GLO     R5              ; get bits
   002c   3d:00111101           BN2     SETBIT          ; branch if bit set (EF2 == 0)
   002d   30:00110000
   002e   fe:11111110           SHL                     ; bit not set
   002f   38:00111000           SKP                     ; BR SAVEBIT
   0030   7e:01111110 SETBIT    SHLC                    ; set bit
   0031   62:01100010 SAVEBIT   OUT     P2              ; CLK for SPI, INC Rx   
   0032   a5:10100101           PLO     R5              ; save bits
   0033   86:10000110           GLO     R6
   0034   3a:00111010           BNZ     RDBITLOOP
   0035   29:00101001
   0036   85:10000101           GLO     R5              ; get byte
   0037   57:01010111           STR     R7              ; save byte
   0038   17:00010111           INC     R7              
   0039   28:00101000           DEC     R8
   003a   88:10001000           GLO     R8
   003b   3a:00111010           BNZ     BLOCKLOOP
   003c   24:00100100
   003d   e2:11100010           SEX     R2              ; one page finished
   003e   98:10011000           GHI     R8
   003f   52:01010010           STR     R2
   0040   64:01100100           OUT     P4              ; show left pages on LEDs
   0041   22:00100010           DEC     R2
   0042   3a:00111010           BNZ     BLOCKLOOP-1
   0043   23:00100011
   0044   61:01100001           OUT     P1              ; deactivate CS to stop operation
   0045   e0:11100000           SEX     R0
   0046   18:00011000           INC     R8              ; set R8 to 0001h
   0047   99:10011001           GHI     R9              ; store start address
   0048   58:01011000           STR     R8
   0049   18:00011000           INC     R8
   004a   89:10001001           GLO     R9
   004b   58:01011000           STR     R8
   004c   30:00110000           BR      START
   004d   00:00000000
                        
   004e   d0:11010000           SEP     R0
   004f   a5:10100101 WRITEBYTE PLO     R5              ; save transmit byte
   0050   f8:11111000           LDI     8               ; counter 8 bits
   0051   08:00001000
   0052   a6:10100110           PLO     R6
   0053   85:10000101 WRBITLOOP GLO     R5              ; get the next bit
   0054   fe:11111110           SHL                     ; next bit is in the carry
   0055   a5:10100101           PLO     R5
   0056   c7:11000111           LSNF                    ; skip if bit is 0
   0057   62:01100010           OUT     P2
   0058   01:00000001           BYTE    00000001B       ; CLK for SPI with data bit cleared
   0059   cf:11001111           LSDF                    ; skip if bit is 1
   005a   62:01100010           OUT     P2
   005b   00:00000000           BYTE    00000000B       ; CLK for SPI with data bit set 
   005c   26:00100110           DEC     R6
   005d   86:10000110           GLO     R6
   005e   3a:00111010           BNZ     WRBITLOOP
   005f   53:01010011
   0060   30:00110000           BR      WRITEBYTE-1
   0061   4e:01001110
                        
                        
                END

Kermit/ZModem

Kermit/ZModem

What about using KERMIT or ZMODEM protocol for the file transfer and use the file system on the host? No need to add additional hardware (SD-card is anyway to modern wink You could use an old CP/M or even a PDP11 as host. The C-Kermit Local Server mode, e.g. MC can read/write the blocks as files block.0, block.2, block.255.

The serial communication is really slow, not only because of the 9600 baud, but you have to wait after each character to give CDP1802 some computation time.

https://github.com/utoh/pygmy-forth/blob/master/extras/kermit/pfkerm.doc

-- Peter Schmid - 2019-01-24

Comments

Topic attachments
I Attachment History Action Size Date Who Comment
JPEGjpg db25-eeprom-bottom.jpg r1 manage 251.2 K 2019-02-09 - 20:45 PeterSchmid  
JPEGjpg db25-eeprom-top.jpg r1 manage 333.9 K 2019-02-09 - 20:45 PeterSchmid  
JPEGjpg eeprom-bottom-s.jpg r1 manage 57.3 K 2019-02-06 - 19:59 PeterSchmid  
JPEGjpg eeprom-mc.jpg r1 manage 268.2 K 2019-02-06 - 20:02 PeterSchmid  
PNGpng mc-eeprom-u1.png r1 manage 17.9 K 2019-02-06 - 20:15 PeterSchmid  
PNGpng raspi-eeprom.png r3 r2 r1 manage 25.8 K 2019-01-27 - 21:22 PeterSchmid  
JPEGjpg raspi-zero-eeprom.jpg r1 manage 201.6 K 2019-01-27 - 21:40 PeterSchmid  
Edit | Attach | Watch | Print version | History: r23 < r22 < r21 < r20 < r19 | Backlinks | Raw View | Raw edit | More topic actions
Topic revision: r23 - 2019-05-30 - PeterSchmid
 
This site is powered by the TWiki collaboration platform Powered by PerlCopyright © 2008-2019 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback