So many memories Nov 16, 2016
An EP4CE6 FPGA has 30 KB of built-in “Block RAM” memory. Fast, but limited in size. With SDRAM, you can have megabytes more, but it’s not quite as fast and harder to interface to, due to the periodic refreshes.
Another option is normal static RAM. Low-cost chips are available in various sizes and speed grades, e.g. this 512 KB chip on eBay.
SRAM still needs some glue logic to get the details right, but the result is predictable timing. This (basic!) SRAM controller needs 3 clock cycles per access, but it supports a 100 MHz clock, i.e. 30 ns per read or write.
I decided to create a little breakout board for these 44-pin TSSOP chips:
To stay within low-cost PCB manufacturing limits, the board is less than 50x50 mm. It won’t accommodate a full 2x20 pin header, but there are enough I/O pins to simply leave the outer four disconnected:
I don’t know why, but Quartus refused to let me define a connection on pin 101, so I had to use a spare pin on the header (pin 110).
Note that connecting the chip is only half the story. Once you have such a controller implemented, it needs to be tested - open or shorted wiring, but also driving the chip for extended periods to make sure it’s reliable.
For now, I’m re-using my SpiPeek trick, i.e. running a full test in Forth from a HyTiny:
The test code is simple. First we define two words to send and receive one SRAM byte:
31 bit constant SR.REQ 8 bit constant SR.WRn \ will be lshifted 22 more : sr-cycle ( data addr -- u ) swap 22 lshift or dup >fpga> drop dup SR.REQ or >fpga> drop >fpga> ; : >sr ( data addr -- ) sr-cycle drop ; : sr> ( addr -- data ) SR.WRn swap sr-cycle 22 rshift $FF and ;
With these, we can define additional words:
zero-ramclears all address locations in the SRAM chip
zero-checkreads and verifies that all values are zero
fill-ramstores a small pseudo-random sequence into all addresses
fill-checkverifies that the values read back are identical
Verification errors are reported on Forth’s serial console. Here is the code:
19 bit constant TEST-SIZE \ 14 = 16 KB, 19 = 512 KB, 21 = 2048 KB : test-range ( leds -- hi lo ) 3 and 20 lshift dup TEST-SIZE or swap ; : test-value ( n -- u ) 211 * 8 rshift $FF and ; : zero-ram ( leds -- ) test-range do 0 i >sr loop ; : fill-ram ( leds -- ) test-range do i test-value i >sr loop ; : zero-check ( leds -- ) test-range do i sr> if cr i hex. i sr> h.2 ." ?" then loop ; : fill-check ( leds -- ) test-range do i sr> i test-value xor if cr i hex. i sr> h.2 ." ?" then loop ;
And finally, a continuous test:
: test 0 begin dup fill-ram 1+ dup fill-check 1+ dup zero-ram 1+ dup zero-check 1+ key? until drop ;
There is a counter (on the stack), which gets incremented after each phase - this counter is sent to the FPGA board, and is connected to its LEDs. That way, successful tests are easy to spot: a binary counting pattern on the LEDs and no output on the serial port.
All 524,288 bytes work flawlessly, with the FPGA code running at 100 MHz. Note that this test is not a full speed back-to-back test: SpiPeek takes 10..20 µs per command, during which time the SRAM sits idle.
But still, it’s a good start: half a megabyte of RAM, who could possibly need more, eh?
Update - Puzzle solved: pin 101 is “nCEO”, Quartus must be told to allow normal I/O.