Lesson 5. Two-phase clock: the write never meets a new address
In picturesAfter this lesson the game runs on your memory without errors — in Fast and in REAL. For this the processor will do every instruction in two phases: in the first, the address and the instruction are set up; in the second, the write happens. That's how real processors work: the Intel 8080 and the MOS 6502 have two clocks, φ1 and φ2.
5.1. The idea: put "new address" and "write" into different slices
From lesson 4: the trouble is that the write window opens and closes on the same edges where the address changes. The solution is to give each instruction two periods of the fast clock:
- divide by 2: a
DFFwhosenQgoes back to itsDinput flips on everyCPUCLKedge — this gives the slow clockCLK2; - all the registers of the processor (
PC, accumulator, flags, stack, display latches — 28 of them) step onCLK2: phase ① — the address and the instruction change and settle; - the write window
weRAM = AND3(oST, nCLK2, CPUCLK)— phase ②: the second half ofCLK2, and only whileCPUCLK = 1. It closes half aCPUCLKperiod beforeCLK2movesPC; - an
ANDwithnRSTholds the divider at 0 during reset: otherwise at power-on it sits at "nothing", like any latch.
NOT CLK2 comes from the flip-flop itself (pin nQ) — no separate NOT is needed.
5.2. Why it doesn't break now
The oscilloscope shows it: the address changes in one slice, the window opens one slice later and closes two slices before the next address change. There is not a single slice where a "new address" and an "open window" happen at the same time.
The cost. An instruction takes two CPUCLK periods — the processor has become twice as slow. On the last levels of the game a step may not keep up; it's more comfortable to play in Fast.
5.3. Building
Everything is inside GW-CPU ("Edit"). The chip's pins and everything outside stay unchanged.
Step 1. Delete lesson 4's window
| Element | Action |
|---|---|
| NOT, whose output = nCLK | delete |
| AND, whose output = weRAM | delete |
Step 2. The divider and the new window
What we build. A reset NOT, the divider's AND, a DFF and the window's AND3. The AND3 output is the weRAM label again, so the memory's WE doesn't need to be reconnected.
| Element | Pin | Direction | Label |
|---|---|---|---|
| NOT | I0 | in | RST |
| NOT | O0 | out | nRST |
| AND | I0 | in | nCLK2 |
| AND | I1 | in | nRST |
| AND | O0 | out | dCLK2 |
| DFF | D | in | dCLK2 |
| DFF | CLK | in | CPUCLK |
| DFF | Q | out | CLK2 |
| DFF | nQ | out | nCLK2 |
| AND3 | I0 | in | oST |
| AND3 | I1 | in | nCLK2 |
| AND3 | I2 | in | CPUCLK |
| AND3 | O0 | out | weRAM |
Step 3. Registers on CLK2
Each of the 28 registers has its own CPUCLK label on its CLK pin — double-click it and rename it to CLK2.
⚠ One of these labels is also connected by a wire to the chip's CPUCLK port pin. Don't rename that one: on that register cut the wire from it and put a new CLK2 label on CLK. Otherwise the chip's input will move to CLK2, and the divider will stop dividing.
| Element | Pin | Was | Now |
|---|---|---|---|
| Memory (RAM) ACC | CLK | CPUCLK | CLK2 |
| Memory (RAM) FZ | CLK | CPUCLK | CLK2 |
| Memory (RAM) FC | CLK | CPUCLK | CLK2 |
| Memory (RAM) PC | CLK | CPUCLK | CLK2 |
| Memory (RAM) PH | CLK | CPUCLK | CLK2 |
| Memory (RAM) PT | CLK | CPUCLK | CLK2 |
| Memory (RAM) P | CLK | CPUCLK | CLK2 |
| Memory (RAM) HT | CLK | CPUCLK | CLK2 |
| Memory (RAM) H | CLK | CPUCLK | CLK2 |
| Memory (RAM) MT | CLK | CPUCLK | CLK2 |
| Memory (RAM) M | CLK | CPUCLK | CLK2 |
| Memory (RAM) STK | CLK | CPUCLK | CLK2 |
| Memory (RAM) STATE | CLK | CPUCLK | CLK2 |
| Memory (RAM) T0 | CLK | CPUCLK | CLK2 |
| Memory (RAM) T0HI | CLK | CPUCLK | CLK2 |
| Memory (RAM) T1 | CLK | CPUCLK | CLK2 |
| Memory (RAM) T1HI | CLK | CPUCLK | CLK2 |
| Memory (RAM) T2 | CLK | CPUCLK | CLK2 |
| Memory (RAM) T2HI | CLK | CPUCLK | CLK2 |
| Memory (RAM) T3 | CLK | CPUCLK | CLK2 |
| Memory (RAM) T3HI | CLK | CPUCLK | CLK2 |
| Memory (RAM) ARMS | CLK | CPUCLK | CLK2 |
| Memory (RAM) BODY | CLK | CPUCLK | CLK2 |
| Memory (RAM) MISS | CLK | CPUCLK | CLK2 |
| Memory (RAM) OVER | CLK | CPUCLK | CLK2 |
| Memory (RAM) FALL | CLK | CPUCLK | CLK2 |
| Memory (RAM) COMET | CLK | CPUCLK | CLK2 |
| Memory (RAM) LEVEL | CLK | CPUCLK | CLK2 |
The CPUCLK port and the CPUCLK labels on the DFF and the AND3 stay on CPUCLK.
5.4. Check
Engine Fast, clock MAX, the program is the same game-myram.rom.json.
| What you do | What you see |
|---|---|
| power on | the clock shows 12:00 |
| hold Q / A / P / L | hand: upper left / lower left / upper right / lower right |
| START | crystals roll down the pipes to the edge, the hand catches them |
| oscilloscope: the same probes + | the weRAM window — one slice after the address change, closes |
| GW-CPU.CLK2 | before the next one |
If not:
| You see | Where to look |
|---|---|
| the clock stands still | divider: DFF CLK = CPUCLK, D = dCLK2, AND(nCLK2, nRST) → dCLK2; nRST = NOT(RST) |
| the processor runs twice as fast as it should | CLK2 got onto the CPUCLK port (see ⚠ in step 3) |
| some registers "lag behind" | some register stayed on CPUCLK — step 3 table |
Lesson 5 paste (Cmd+I)
In GW-CPU edit mode, after step 1. Step 3 (28 labels) — by hand.
NOT(RST, nRST)AND(nCLK2, nRST, dCLK2)DFF(D=dCLK2, CLK=CPUCLK, Q=CLK2, nQ=nCLK2) AND3(oST, nCLK2, CPUCLK, weRAM)¿No te sale? Algo no funciona →