Lesson 4. Your own memory in the processor — and the first error

In pictures

In the processor kit's machine all memory is a single ram primitive of 256 words. In this lesson your RAM256x4 takes its place. The game will start… and begin to make mistakes: some buttons will act wrongly, and crystals will go out before they roll all the way down. This is not a build error — it's meant to happen. We'll find the cause with the built-in oscilloscope and fix it in lesson 5.


4.1. What is in the machine now and how our memory differs

Inside GW-CPU the memory is connected like this: the address is the low 8 bits of the instruction ir0–ir7, the data to write is the accumulator acc0–acc3, what is read, mq0–mq3, goes to the ALU, "write" WE = oST (the ST instruction), the clock is CPUCLK.

The ram primitive writes at the moment of the clock edge, like a flip-flop. Our memory has no clock: it writes while WE = 1 — that's a window, not an instant. So WE must get a window when the address and the data have already settled. Right after the CPUCLK 0→1 edge the processor moves to the next instruction — the address changes. By the second half of the clock cycle everything has settled. The window:

  • weRAM = oST · NOT CPUCLK — "this is an ST instruction, and it's the second half of the clock cycle".
instructionir0–ir7accumulatoracc0–acc3RAM256x4our memoryaddressdatamq0–mq3ALULD, ADD…oST · NOT CPUCLKweRAM"an ST instruction,second half of the clock"
The swap inside the processor. Instead of the ram primitive — our RAM256x4 on the same labels: the address is the low 8 bits of the instruction, the data is the accumulator, what is read goes to the ALU. The only new thing is the write window: weRAM = oST · NOT CPUCLK — "an ST instruction, the second half of the clock".

4.2. Memory at power-on — "whatever"

The ram primitive contains zeros at power-on. Latches don't: each one comes up however it happens to (in REAL — "nothing", orange; in Fast — ones). Real memory behaves the same way: at startup a computer itself puts in order whatever it is going to read.

The processor kit's game program reads two cells before it writes them: LASTB and LASTK — which buttons were pressed last time. All the others it writes first. So two instructions are added at the start of the program:

reset:        LD #0        ST LASTB        ; own memory: cells read before being written — clear them        ST LASTK

The whole program is the file game-myram.rom.json (311 instructions).

4.3. Building

Step 1. Move the chip into the project with the machine

You built lessons 1–3 in a separate project. The chip moves over as a file:

  1. In the lessons 1–3 project: in the Library, in the RAM256x4 row — the ⬇ icon (save the chip to a file). All its parts go into the file too: RAM16x4, WORD4, DLatch and the gates.
  2. Open the project with the processor kit's machine.
  3. In the Library header — the ⬆ icon (load a chip) → choose the file.
  4. If a "built differently" window appears, you already have these gates, just built differently. Keep the suggested Use mine → Import.

Step 2. Remove the memory primitive

Inside GW-CPU ("Edit"):

ElementAction
Memory (RAM) RAM, whose output = mq0delete

The labels on its pins (ir0–ir7, acc0–acc3, mq0–mq3) don't need to be deleted — the new memory will sit on them.

Step 3. Your own memory and the write window

What we build. A clock inverter, the weRAM window and RAM256x4 on the same labels.

ElementPinDirectionLabel
NOTI0inCPUCLK
NOTO0outnCLK
ANDI0inoST
ANDI1innCLK
ANDO0outweRAM
RAM256x4A0inir0
RAM256x4A1inir1
RAM256x4A2inir2
RAM256x4A3inir3
RAM256x4A4inir4
RAM256x4A5inir5
RAM256x4A6inir6
RAM256x4A7inir7
RAM256x4D0inacc0
RAM256x4D1inacc1
RAM256x4D2inacc2
RAM256x4D3inacc3
RAM256x4WEinweRAM
RAM256x4Q0outmq0
RAM256x4Q1outmq1
RAM256x4Q2outmq2
RAM256x4Q3outmq3

Leave chip editing, saving the changes.

Step 4. The program

ROM (outside GW-CPU): ⚙ → ⬆ → file game-myram.rom.json.

4.4. Running it — and the error

Engine Fast, clock CPUCLK — MAX.

What you doWhat you see
power onthe clock 12:00 runs — the processor works on its own memory
hold A, then Plower left hand, upper right — correct
hold Q✗ lower left hand instead of upper left
hold L✗ upper right instead of lower right
START✗ a crystal appears at the start of a pipe and goes out

That's how it should be in this lesson. The memory is built correctly, the write window is correct in its idea — and yet a write sometimes lands in the wrong cell. Q spoils the pose cell, the crystals spoil the pipe cells.

4.5. Looking for the cause with the oscilloscope

The oscilloscope shows signals over time. To open it — the w key.

  1. In the add field type each of these and press Enter after each one: CPUCLK, ir0-7 (the address bus in one row), GW-CPU.oST, GW-CPU.weRAM.
  2. In the trig field — GW-CPU.weRAM=1, Enter. As soon as the write window opens, the picture freezes (FROZEN), with the event in the middle.
  3. To catch again — LIVE.
CPUCLKir0-70f1413020doSTweRAMone slice: new address AND the windownext slice: address 13 → 02, and oST does not drop at once
The oscilloscope, lesson 4. A slice is one simulator computation on a clock edge. In the highlighted slice the address has just changed (13) and the weRAM window opened in the same slice. Whatever lands in one slice happens at once for the simulator — and on the next edge, while the decoder drops oST but the address is already new, the write goes to the wrong place.

How to read it. One column is one slice: one simulator computation on a clock edge. It's the smallest unit of time the simulator has: everything that falls into one slice happens at the same time for it, with no "before" and "after".

In the highlighted slice the address has just changed (13) — and in the same slice the weRAM window opened. In real hardware there would be an instant between these events: first the new instruction settles, then the window opens. In the simulator they coincided. And on the next edge the address already jumps to the next instruction (02), and while the decoder is turning oST off, the window is not closed yet — the value manages to get written there too.

Conclusion: the write window must not open or close on the same edge where the address changes. How to achieve that without slowing the processor down endlessly — lesson 5.

Lesson 4 paste (Cmd+I)

In GW-CPU edit mode, after steps 1–2.

NOT(CPUCLK, nCLK)AND(oST, nCLK, weRAM) RAM256x4(A0=ir0, A1=ir1, A2=ir2, A3=ir3, A4=ir4, A5=ir5, A6=ir6, A7=ir7, D0=acc0, D1=acc1, D2=acc2, D3=acc3, WE=weRAM, Q0=mq0, Q1=mq1, Q2=mq2, Q3=mq3)

¿No te sale? Algo no funciona →