Lesson 5. Two-phase clock: the write never meets a new address

In pictures

After 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 DFF whose nQ goes back to its D input flips on every CPUCLK edge — this gives the slow clock CLK2;
  • all the registers of the processor (PC, accumulator, flags, stack, display latches — 28 of them) step on CLK2: phase ① — the address and the instruction change and settle;
  • the write window weRAM = AND3(oST, nCLK2, CPUCLK) — phase ②: the second half of CLK2, and only while CPUCLK = 1. It closes half a CPUCLK period before CLK2 moves PC;
  • an AND with nRST holds the divider at 0 during reset: otherwise at power-on it sits at "nothing", like any latch.
DFFdivide by 2DCLKQnQCPUCLKCLK2→ every registernCLK2RSTnRSTdCLK2oSTAND3weRAM
The two-phase clock. A DFF whose nQ comes back to D through an AND divides CPUCLK by 2 — that is CLK2, every register steps on it. The AND with nRST holds the divider at 0 during reset. The write window is AND3(oST, nCLK2, CPUCLK): the second half of CLK2 and only while CPUCLK = 1.

NOT CLK2 comes from the flip-flop itself (pin nQ) — no separate NOT is needed.

5.2. Why it doesn't break now

CPUCLKCLK2ir0-7611413oSTweRAM① address② windownext addressthe window closed 2 slices before the address changes
The oscilloscope, lesson 5. The processor steps on CLK2 — half as often as CPUCLK. ① on the CLK2 edge the address changes and the instruction has time to settle; ② a slice later the weRAM window opens and closes long before the next address change. No slice holds "new address" and "open window" at once.

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

ElementAction
NOT, whose output = nCLKdelete
AND, whose output = weRAMdelete

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.

ElementPinDirectionLabel
NOTI0inRST
NOTO0outnRST
ANDI0innCLK2
ANDI1innRST
ANDO0outdCLK2
DFFDindCLK2
DFFCLKinCPUCLK
DFFQoutCLK2
DFFnQoutnCLK2
AND3I0inoST
AND3I1innCLK2
AND3I2inCPUCLK
AND3O0outweRAM

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.

ElementPinWasNow
Memory (RAM) ACCCLKCPUCLKCLK2
Memory (RAM) FZCLKCPUCLKCLK2
Memory (RAM) FCCLKCPUCLKCLK2
Memory (RAM) PCCLKCPUCLKCLK2
Memory (RAM) PHCLKCPUCLKCLK2
Memory (RAM) PTCLKCPUCLKCLK2
Memory (RAM) PCLKCPUCLKCLK2
Memory (RAM) HTCLKCPUCLKCLK2
Memory (RAM) HCLKCPUCLKCLK2
Memory (RAM) MTCLKCPUCLKCLK2
Memory (RAM) MCLKCPUCLKCLK2
Memory (RAM) STKCLKCPUCLKCLK2
Memory (RAM) STATECLKCPUCLKCLK2
Memory (RAM) T0CLKCPUCLKCLK2
Memory (RAM) T0HICLKCPUCLKCLK2
Memory (RAM) T1CLKCPUCLKCLK2
Memory (RAM) T1HICLKCPUCLKCLK2
Memory (RAM) T2CLKCPUCLKCLK2
Memory (RAM) T2HICLKCPUCLKCLK2
Memory (RAM) T3CLKCPUCLKCLK2
Memory (RAM) T3HICLKCPUCLKCLK2
Memory (RAM) ARMSCLKCPUCLKCLK2
Memory (RAM) BODYCLKCPUCLKCLK2
Memory (RAM) MISSCLKCPUCLKCLK2
Memory (RAM) OVERCLKCPUCLKCLK2
Memory (RAM) FALLCLKCPUCLKCLK2
Memory (RAM) COMETCLKCPUCLKCLK2
Memory (RAM) LEVELCLKCPUCLKCLK2

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 doWhat you see
power onthe clock shows 12:00
hold Q / A / P / Lhand: upper left / lower left / upper right /
lower right
STARTcrystals 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.CLK2before the next one

If not:

You seeWhere to look
the clock stands stilldivider: DFF CLK = CPUCLK, D = dCLK2,
AND(nCLK2, nRST) → dCLK2; nRST = NOT(RST)
the processor runs twice as fast as it shouldCLK2 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)

Stuck? Something doesn't work →