CS ATLAS A map of computing

Project 3 · RAM

RAM: a bank of registers plus address decoding

RAM introduces no new part. It just puts many registers side by side and uses the address to decide which one is written and which one is read. To grow, stack the previous level again.

01One address, two paths

A write uses DMux to send load to one register; a read uses Mux to select that register’s contents. The address drives both selectors, so what goes in and what comes out are always the same slot.

in[16]DMux8Waypick one registerR0R1R2R3… 8 of themMux8Way16read one backout[16]address[3]address[3]load

Note: with load=0 all eight DMux outputs are 0 and no register is written — that is what “read without writing” means.

02How five levels of capacity stack up

Each level does one thing: place eight copies of the level below side by side, and split the address into “high bits pick the block, low bits pick the word”.

ChipBuilt fromAddress bitsCapacity
RAM88 × Register3eight 16-bit words
RAM648 × RAM8664 words
RAM5128 × RAM649512 words
RAM4K8 × RAM512124096 words
RAM16K4 × RAM4K1416384 words

The one exception is RAM16K: 16K ÷ 4K = 4, so it is four RAM4K blocks, using DMux4Way and Mux4Way16.

03RAM8: eight registers and two 8-way selectors

The simplest RAM: 3 address bits, exactly enough to pick one of eight registers.

in00000000000000000x0000
address 0 · writes: 0

out 0x0000 (contents of address 0)

Click an address to pick a register, change in, then press “Write once” — the value lands only when load=1.

CHIP RAM8 {
  IN in[16], load, address[3];
  OUT out[16];
  PARTS:
  DMux8Way(in=load, sel=address, a=l0, b=l1, c=l2, d=l3, e=l4, f=l5, g=l6, h=l7);
  Register(in=in, load=l0, out=r0);
  Register(in=in, load=l1, out=r1);
  Register(in=in, load=l2, out=r2);
  Register(in=in, load=l3, out=r3);
  Register(in=in, load=l4, out=r4);
  Register(in=in, load=l5, out=r5);
  Register(in=in, load=l6, out=r6);
  Register(in=in, load=l7, out=r7);
  Mux8Way16(a=r0, b=r1, c=r2, d=r3, e=r4, f=r5, g=r6, h=r7, sel=address, out=out);
}

Eight Register chips with one DMux8Way (write) and one Mux8Way16 (read). The same address decides which one is written and which one is read.

04RAM64: eight RAM8 blocks

The 6-bit address splits: address[3..5] picks the RAM8 block and address[0..2] picks the register inside it.

CHIP RAM64 {
  IN in[16], load, address[6];
  OUT out[16];
  PARTS:
  DMux8Way(in=load, sel=address[3..5], a=l0, b=l1, c=l2, d=l3, e=l4, f=l5, g=l6, h=l7);
  RAM8(in=in, load=l0, address=address[0..2], out=r0);
  RAM8(in=in, load=l1, address=address[0..2], out=r1);
  RAM8(in=in, load=l2, address=address[0..2], out=r2);
  RAM8(in=in, load=l3, address=address[0..2], out=r3);
  RAM8(in=in, load=l4, address=address[0..2], out=r4);
  RAM8(in=in, load=l5, address=address[0..2], out=r5);
  RAM8(in=in, load=l6, address=address[0..2], out=r6);
  RAM8(in=in, load=l7, address=address[0..2], out=r7);
  Mux8Way16(a=r0, b=r1, c=r2, d=r3, e=r4, f=r5, g=r6, h=r7, sel=address[3..5], out=out);
}

Eight RAM8 blocks: address[3..5] picks the block, address[0..2] picks the register inside it.

05RAM512: eight RAM64 blocks

The same trick once more: the top 3 bits pick the block, the low 6 pass straight through to RAM64.

CHIP RAM512 {
  IN in[16], load, address[9];
  OUT out[16];
  PARTS:
  DMux8Way(in=load, sel=address[6..8], a=l0, b=l1, c=l2, d=l3, e=l4, f=l5, g=l6, h=l7);
  RAM64(in=in, load=l0, address=address[0..5], out=r0);
  RAM64(in=in, load=l1, address=address[0..5], out=r1);
  RAM64(in=in, load=l2, address=address[0..5], out=r2);
  RAM64(in=in, load=l3, address=address[0..5], out=r3);
  RAM64(in=in, load=l4, address=address[0..5], out=r4);
  RAM64(in=in, load=l5, address=address[0..5], out=r5);
  RAM64(in=in, load=l6, address=address[0..5], out=r6);
  RAM64(in=in, load=l7, address=address[0..5], out=r7);
  Mux8Way16(a=r0, b=r1, c=r2, d=r3, e=r4, f=r5, g=r6, h=r7, sel=address[6..8], out=out);
}

Eight RAM64 blocks: the top 3 bits pick the block, the low 6 are the address inside it.

06RAM4K: eight RAM512 blocks

A 12-bit address: the top 3 bits pick the block, the low 9 address inside it.

CHIP RAM4K {
  IN in[16], load, address[12];
  OUT out[16];
  PARTS:
  DMux8Way(in=load, sel=address[9..11], a=l0, b=l1, c=l2, d=l3, e=l4, f=l5, g=l6, h=l7);
  RAM512(in=in, load=l0, address=address[0..8], out=r0);
  RAM512(in=in, load=l1, address=address[0..8], out=r1);
  RAM512(in=in, load=l2, address=address[0..8], out=r2);
  RAM512(in=in, load=l3, address=address[0..8], out=r3);
  RAM512(in=in, load=l4, address=address[0..8], out=r4);
  RAM512(in=in, load=l5, address=address[0..8], out=r5);
  RAM512(in=in, load=l6, address=address[0..8], out=r6);
  RAM512(in=in, load=l7, address=address[0..8], out=r7);
  Mux8Way16(a=r0, b=r1, c=r2, d=r3, e=r4, f=r5, g=r6, h=r7, sel=address[9..11], out=out);
}

Eight RAM512 blocks: the top 3 bits pick the block, the low 9 go inside.

07RAM16K: four RAM4K blocks

The last level differs: 16K is only four 4K blocks, so it uses 4-way selectors. Of the 14 address bits, the top 2 pick the block.

CHIP RAM16K {
  IN in[16], load, address[14];
  OUT out[16];
  PARTS:
  DMux4Way(in=load, sel=address[12..13], a=l0, b=l1, c=l2, d=l3);
  RAM4K(in=in, load=l0, address=address[0..11], out=r0);
  RAM4K(in=in, load=l1, address=address[0..11], out=r1);
  RAM4K(in=in, load=l2, address=address[0..11], out=r2);
  RAM4K(in=in, load=l3, address=address[0..11], out=r3);
  Mux4Way16(a=r0, b=r1, c=r2, d=r3, sel=address[12..13], out=out);
}

Four RAM4K blocks: 16K ÷ 4K = 4, so this level uses DMux4Way and Mux4Way16.

08How the address is sliced

Line up the slicing at every level and the rule is clear: high bits select, low bits pass through.

ChipHigh bits (block)Low bits (inside)Meaning
RAM64address[3..5]address[0..2]top 3 bits pick the RAM8 block
RAM512address[6..8]address[0..5]top 3 bits pick the RAM64 block
RAM4Kaddress[9..11]address[0..8]top 3 bits pick the RAM512 block
RAM16Kaddress[12..13]address[0..11]top 2 bits pick the RAM4K block

That is also why RAM16K takes a 14-bit address: 214 = 16384.