RAM Expansion hat!

If you’re like me, one of the things you find endearing about old 80’s and 90’s computers is that had a lot of expansion options, much like the hat idea of the Pokitto. One of these expansions that was popular throughout a range of these computers was a RAM expansion of some sort.

So I present you…
image

The RAM HAT!

The chip I opted for was the 23LC1024-I/SN SRAM Memory Chip, which is 1024kbit, which is 128kB. It is accessed randomly without speed loss, so is not restricted to reading full ‘pages’ like flash memory is. Speed-wise its best to read/write large amounts of data sequentially so that you don’t send the command and address over and over. In tests using TAS mode, I was able to stream a full screen 8bit (256 colour) image at about 54fps.

Looking online, I could only fine libraries to access these chips in normal SPI mode, which, although would work fine, would not use the chip to its full potential. So I had to start from scratch writing code to use the QSPI (Quad Serial Peripheral Interface) which can be likened to a 4bit parallel interface, sending data along 4 lines for every clock tick, rather than just 1.
So with help from some of the more experienced Pokitto coders, who enlightened me on the methods of reading the PEX pins lightning fast, I eventually got this device working and probably as fast as the Pokitto can manage it.

I wired up the chip as follows, keeping the SPI pins correct to hardware just in case the whole idea failed and I was forced to use SPI only. The rest of the pins are connected next to each other so that fast port reading can be used. Also, like most serial devices, it uses a CS line to know if you’re talking to it or not, meaning we could easily add multiple RAM chips with very little change to the software.

   __ __
1-|  U  |-8
2-|     |-7
3-|     |-6
4-|_____|-5

1 - CS ------------------------ P1_5
2 - SIO1 - Slave Out (MISO) --- P1_21
3 - SIO2 - Slave Out 2 -------- P1_22
4 - VSS ----------------------- GND
5 - SIO0 - Slave In (MOSI) ---- P1_20
6 - SCK ----------------------- P1_6
7 - SIO3 - Slave Out 3 / Hold - P1_23
8 - VCC ----------------------- 3v3

My working example project is https://github.com/spinalcode/Pokitto-QSPI-RAM
Here it is in action.

[update] Added support for 1024 (128k) RAM chip, just change the define at the top of ram.i to 1024 for full 128k, or something else for 64k.

[update]
image

After one tiny mistake was corrected, here are the gerber files needed to got your own boards made!
gerbers.zip (34.2 KB)

9 Likes

This is a great HW project!

Well, after looking at the ASM output for this project, trying to see if there was a difference between TOG_CLOCK and TOG_CLOCK_NOP that would account for the occasional failure to read the data correctly, I have no idea what any of it means…

_Z8readQuadPhm():
f:\pokitto\femtoide\projects\__ram_test__new/ram.i:195
    for(int t = number; t; --t){
    905c:	9e01      	ldr	r6, [sp, #4]
    905e:	2e00      	cmp	r6, #0
    9060:	d017      	beq.n	9092 <_Z19readFromAddressQuadmPhm+0xd6>
f:\pokitto\femtoide\projects\__ram_test__new/ram.i:196
        TOG_CLOCK;
    9062:	4a19      	ldr	r2, [pc, #100]	; (90c8 <_Z19readFromAddressQuadmPhm+0x10c>)
    9064:	2140      	movs	r1, #64	; 0x40
    9066:	6011      	str	r1, [r2, #0]
f:\pokitto\femtoide\projects\__ram_test__new/ram.i:197
        temp = ((volatile uint32_t *) 0xA0002184)[0] >> 16;
    9068:	4818      	ldr	r0, [pc, #96]	; (90cc <_Z19readFromAddressQuadmPhm+0x110>)
    906a:	6803      	ldr	r3, [r0, #0]
    906c:	0c1b      	lsrs	r3, r3, #16
f:\pokitto\femtoide\projects\__ram_test__new/ram.i:198
        TOG_CLOCK_NOP;
    906e:	6011      	str	r1, [r2, #0]
    9070:	46c0      	nop			; (mov r8, r8)
f:\pokitto\femtoide\projects\__ram_test__new/ram.i:199
        TOG_CLOCK_NOP;
    9072:	6011      	str	r1, [r2, #0]
    9074:	46c0      	nop			; (mov r8, r8)
f:\pokitto\femtoide\projects\__ram_test__new/ram.i:200
        temp |= ((volatile uint32_t *) 0xA0002184)[0] >> 20;
    9076:	6800      	ldr	r0, [r0, #0]
    9078:	0d00      	lsrs	r0, r0, #20
    907a:	4303      	orrs	r3, r0
f:\pokitto\femtoide\projects\__ram_test__new/ram.i:201
        TOG_CLOCK;
    907c:	6011      	str	r1, [r2, #0]
f:\pokitto\femtoide\projects\__ram_test__new/ram.i:202
        *buffer++ = temp;
    907e:	703b      	strb	r3, [r7, #0]
f:\pokitto\femtoide\projects\__ram_test__new/ram.i:195
    for(int t = number; t; --t){
    9080:	3e01      	subs	r6, #1
f:\pokitto\femtoide\projects\__ram_test__new/ram.i:202
        *buffer++ = temp;
    9082:	3701      	adds	r7, #1
    9084:	e7eb      	b.n	905e <_Z19readFromAddressQuadmPhm+0xa2>
gpio_write():
f:\pokitto\femtoide\pokittolib\pokitto\mbed-pokitto\targets\hal\target_nxp\target_lpc11u6x/gpio_object.h:36
    MBED_ASSERT(obj->pin != (PinName)NC);
    9086:	2224      	movs	r2, #36	; 0x24
    9088:	4911      	ldr	r1, [pc, #68]	; (90d0 <_Z19readFromAddressQuadmPhm+0x114>)
    908a:	4812      	ldr	r0, [pc, #72]	; (90d4 <_Z19readFromAddressQuadmPhm+0x118>)
    908c:	f7fc ffca 	bl	6024 <mbed_assert_internal>
    9090:	e7a4      	b.n	8fdc <_Z19readFromAddressQuadmPhm+0x20>

[edit] If anyone can clue me in? I see that TOG_CLOCK seems to do different things in different places?

1 Like

It does different things because TOG_CLOCK comes first, so it has the extra work of loading r1 and r2. TOG_CLOCK_NOP already has them loaded so all it needs to do is the str and the nop. Try replacing TOG_CLOCK_NOP with TOG_CLOCK and compare the two disassemblies against each other to find out what is the actual difference of having a nop.

1 Like

Without nop

_Z8readQuadPhm():
f:\pokitto\femtoide\projects\__ram_test__new/ram.i:195
    for(int t = number; t; --t){
    905c:	9a01      	ldr	r2, [sp, #4]
    905e:	2a00      	cmp	r2, #0
    9060:	d015      	beq.n	908e <_Z19readFromAddressQuadmPhm+0xd2>
f:\pokitto\femtoide\projects\__ram_test__new/ram.i:196
        TOG_CLOCK;
    9062:	4918      	ldr	r1, [pc, #96]	; (90c4 <_Z19readFromAddressQuadmPhm+0x108>)
    9064:	2040      	movs	r0, #64	; 0x40
    9066:	6008      	str	r0, [r1, #0]
f:\pokitto\femtoide\projects\__ram_test__new/ram.i:197
        temp = ((volatile uint32_t *) 0xA0002184)[0] >> 16;
    9068:	4c17      	ldr	r4, [pc, #92]	; (90c8 <_Z19readFromAddressQuadmPhm+0x10c>)
    906a:	6823      	ldr	r3, [r4, #0]
    906c:	0c1b      	lsrs	r3, r3, #16
f:\pokitto\femtoide\projects\__ram_test__new/ram.i:198
        TOG_CLOCK;
    906e:	6008      	str	r0, [r1, #0]
f:\pokitto\femtoide\projects\__ram_test__new/ram.i:199
        TOG_CLOCK;
    9070:	6008      	str	r0, [r1, #0]
f:\pokitto\femtoide\projects\__ram_test__new/ram.i:200
        temp |= ((volatile uint32_t *) 0xA0002184)[0] >> 20;
    9072:	6824      	ldr	r4, [r4, #0]
    9074:	0d24      	lsrs	r4, r4, #20
    9076:	4323      	orrs	r3, r4
f:\pokitto\femtoide\projects\__ram_test__new/ram.i:201
        TOG_CLOCK;
    9078:	6008      	str	r0, [r1, #0]
f:\pokitto\femtoide\projects\__ram_test__new/ram.i:202
        *buffer++ = temp;
    907a:	702b      	strb	r3, [r5, #0]
f:\pokitto\femtoide\projects\__ram_test__new/ram.i:195
    for(int t = number; t; --t){
    907c:	3a01      	subs	r2, #1
f:\pokitto\femtoide\projects\__ram_test__new/ram.i:202
        *buffer++ = temp;
    907e:	3501      	adds	r5, #1
    9080:	e7ed      	b.n	905e <_Z19readFromAddressQuadmPhm+0xa2>

With nop

_Z8readQuadPhm():
f:\pokitto\femtoide\projects\__ram_test__new/ram.i:195
    for(int t = number; t; --t){
    905c:	9e01      	ldr	r6, [sp, #4]
    905e:	2e00      	cmp	r6, #0
    9060:	d019      	beq.n	9096 <_Z19readFromAddressQuadmPhm+0xda>
f:\pokitto\femtoide\projects\__ram_test__new/ram.i:196
        TOG_CLOCK_NOP;
    9062:	4a1a      	ldr	r2, [pc, #104]	; (90cc <_Z19readFromAddressQuadmPhm+0x110>)
    9064:	2140      	movs	r1, #64	; 0x40
    9066:	6011      	str	r1, [r2, #0]
    9068:	46c0      	nop			; (mov r8, r8)
f:\pokitto\femtoide\projects\__ram_test__new/ram.i:197
        temp = ((volatile uint32_t *) 0xA0002184)[0] >> 16;
    906a:	4819      	ldr	r0, [pc, #100]	; (90d0 <_Z19readFromAddressQuadmPhm+0x114>)
    906c:	6803      	ldr	r3, [r0, #0]
    906e:	0c1b      	lsrs	r3, r3, #16
f:\pokitto\femtoide\projects\__ram_test__new/ram.i:198
        TOG_CLOCK_NOP;
    9070:	6011      	str	r1, [r2, #0]
    9072:	46c0      	nop			; (mov r8, r8)
f:\pokitto\femtoide\projects\__ram_test__new/ram.i:199
        TOG_CLOCK_NOP;
    9074:	6011      	str	r1, [r2, #0]
    9076:	46c0      	nop			; (mov r8, r8)
f:\pokitto\femtoide\projects\__ram_test__new/ram.i:200
        temp |= ((volatile uint32_t *) 0xA0002184)[0] >> 20;
    9078:	6800      	ldr	r0, [r0, #0]
    907a:	0d00      	lsrs	r0, r0, #20
    907c:	4303      	orrs	r3, r0
f:\pokitto\femtoide\projects\__ram_test__new/ram.i:201
        TOG_CLOCK_NOP;
    907e:	6011      	str	r1, [r2, #0]
    9080:	46c0      	nop			; (mov r8, r8)
f:\pokitto\femtoide\projects\__ram_test__new/ram.i:202
        *buffer++ = temp;
    9082:	703b      	strb	r3, [r7, #0]
f:\pokitto\femtoide\projects\__ram_test__new/ram.i:195
    for(int t = number; t; --t){
    9084:	3e01      	subs	r6, #1
f:\pokitto\femtoide\projects\__ram_test__new/ram.i:202
        *buffer++ = temp;
    9086:	3701      	adds	r7, #1
    9088:	e7e9      	b.n	905e <_Z19readFromAddressQuadmPhm+0xa2>

And… here it is in use.

10 Likes