Y
Published on

Memory Interface Connections A Complete Deep Dive

Authors
  • avatar
    Name
    Yinhuan Yuan
    Twitter

Memory Interface Connections: A Complete Deep Dive

Let me explain how memory connects to the Hack computer in exhaustive detail - this is where program instructions and data actually live!


The Big Picture: Two Separate Memory Systems

The Harvard Architecture Advantage

The Hack computer uses Harvard architecture - separate instruction and data memory:

Traditional Von Neumann (one memory):
────────────────────────────────────
         ┌──────────┐
Memory           (BothInst+Data│
         └────┬─────┘
         Single Bus
      ┌───────┴────────┐
      │                │
      ▼                ▼
   [Fetch         [Load/Store
    Instruct]      Data]
      │                │
      └────┬───────────┘
    CONFLICT! Can't do both at once!


Harvard (separate memories):
────────────────────────────
    ┌──────────┐      ┌──────────┐
ROM    │      │   RAM    (Instruct)  (Data)    └────┬─────┘      └────┬─────┘
         │                 │
    Inst Bus          Data Bus
         │                 │
         ▼                 ▼
    [Fetch           [Load/Store
     Instruct]        Data]
         │                 │
         └────┬────────────┘
    NO CONFLICT! Simultaneous access!

Advantages:
───────────
Fetch instruction AND access data in same cycle
Simpler bus arbitration
Faster execution (no waiting)
Separate optimization for each memory type
Security (ROM can't be modified)

Disadvantages:
──────────────
Two separate address buses needed
Two separate data buses
More complex board layout
Can't execute code from RAM (in pure Harvard)

Memory Map Overview

Hack Computer Memory Organization:

INSTRUCTION MEMORY (ROM):
┌────────────────────────────┐
0x0000 - 0x7FFF (32K)│                            │
Program Code (Read-Only)│                            │
Connected to PC└────────────────────────────┘


DATA MEMORY (RAM):
┌────────────────────────────┐
0x0000 - 0x3FFF (16K)General Purpose RAM├────────────────────────────┤
0x4000 - 0x5FFF (8K)Screen Memory (optional)├────────────────────────────┤
0x6000 - 0x6000 (1 word)Keyboard Register (opt)├────────────────────────────┤
0x6001 - 0x7FFFReserved / Unused└────────────────────────────┘

Connected to A Register

Connection 1: ROM (Instruction Memory)

Purpose and Role

What ROM Does:
──────────────
├─ Stores program instructions
├─ Read-only (written once during programming)
├─ Addressed by Program Counter (PC)
├─ Outputs instruction to Control Unit
└─ Fastest access (no write circuitry)

Why EEPROM for "ROM":
─────────────────────
├─ Electrically Erasable (can reprogram)
├─ Non-volatile (keeps data when power off)
├─ Fast read access (~70ns)
├─ Easy to program with USB programmer
└─ Perfect for development!

Hardware Choice: AT28C256 EEPROM

AT28C256 Specifications:
────────────────────────
Capacity:     32K × 8 bits (32,768 bytes)
Organization: 32K addresses × 8-bit data
Package:      28-pin DIP
Technology:   CMOS EEPROM
Access Time:  70ns (typical), 150ns (max)
Power:        5V ±10%
Endurance:    10,000 write cycles
Retention:    10 years data retention

Why AT28C256?
─────────────
Perfect size (32K matches Hack requirement)
Available in DIP package (easy prototyping)
✓ 5V operation (matches 74HC logic)
Fast enough for MHz operation
Cheap (~$3-5 per chip)
Easy to program (standard EEPROM programmer)
Two chips give us 16-bit instruction width

AT28C256 Pinout (Detailed)

         AT28C256 (28-pin DIP)
         ┌──────┴──────┐
    A141          28VCC (+5V)
    A122          27/WE (Write Enable)
     A73          26A13
     A64          25A8
     A55          24A9
     A46          23A11
     A37          22/OE (Output Enable)
     A28          21A10
     A19          20/CE (Chip Enable)
     A010         19D7
     D011         18D6
     D112         17D5
     D213         16D4
    GND14         15D3
         └─────────────┘

PIN FUNCTIONS:
──────────────

ADDRESS PINS (A0-A14):
├─ 15 address lines = 2^15 = 32K addresses
├─ Connected directly to Program Counter
├─ Must be stable before /OE asserted
└─ Valid throughout read cycle

DATA PINS (D0-D7):
├─ 8-bit data output (half of 16-bit instruction)
├─ Connected to instruction bus
├─ Tri-state (hi-Z when /OE high)
└─ Drive valid data when /OE low

CONTROL PINS:

/CE (Chip Enable, pin 20):
├─ Active LOW
├─ Must be LOW to access chip
├─ For Hack: tied to GND (always enabled)
└─ Power saving if disabled (hi-Z outputs)

/OE (Output Enable, pin 22):
├─ Active LOW
├─ Controls data output drivers
├─ For Hack: tied to GND (always outputting)
└─ When HIGH: data pins are hi-Z

/WE (Write Enable, pin 27):
├─ Active LOW
├─ For Hack: tied to VCC (never write during operation)
├─ Only used during programming with EEPROM programmer
└─ READ ONLY during normal operation

POWER PINS:
VCC (pin 28): +5V
GND (pin 14): Ground

Complete ROM Connection Schematic

We need TWO AT28C256 chips for 16-bit instructions:

ROM Schematic - Full 16-bit Instruction Memory:

ADDRESSING (shared by both chips):
──────────────────────────────────

Program Counter:
    PC[0] ───────┬────────────────┐
    PC[1] ───────┼────────────────┤
    PC[2] ───────┼────────────────┤
    PC[3] ───────┼────────────────┤
    PC[4] ───────┼────────────────┤
    PC[5] ───────┼────────────────┤
    PC[6] ───────┼────────────────┤
    PC[7] ───────┼────────────────┤
    PC[8] ───────┼────────────────┤
    PC[9] ───────┼────────────────┤
    PC[10] ──────┼────────────────┤
    PC[11] ──────┼────────────────┤
    PC[12] ──────┼────────────────┤
    PC[13] ──────┼────────────────┤
    PC[14] ──────┴────────────────┤
                 │                │
                 ▼                ▼
         ┌───────────────┐  ┌───────────────┐
ROM_LO (U1)   │  │ ROM_HI (U2)AT28C256      │  │ AT28C256         │               │  │               │
    A0 ◄─┤10             │  │             10├─► A0
    A1 ◄─┤9              │  │              9├─► A1
    A2 ◄─┤8              │  │              8├─► A2
    A3 ◄─┤7              │  │              7├─► A3
    A4 ◄─┤6              │  │              6├─► A4
    A5 ◄─┤5              │  │              5├─► A5
    A6 ◄─┤4              │  │              4├─► A6
    A7 ◄─┤3              │  │              3├─► A7
    A8 ◄─┤25             │  │             25├─► A8
    A9 ◄─┤24             │  │             24├─► A9
   A10 ◄─┤21             │  │             21├─► A10
   A11 ◄─┤23             │  │             23├─► A11
   A12 ◄─┤2              │  │              2├─► A12
   A13 ◄─┤26             │  │             26├─► A13
   A14 ◄─┤1              │  │              1├─► A14
         │               │  │               │
         │               │  │               │
DATA OUT      │  │ DATA OUT         │               │  │               │
    D0 ──┤11             │  │             11├── D8
    D1 ──┤12             │  │             12├── D9
    D2 ──┤13             │  │             13├── D10
    D3 ──┤15             │  │             15├── D11
    D4 ──┤16             │  │             16├── D12
    D5 ──┤17             │  │             17├── D13
    D6 ──┤18             │  │             18├── D14
    D7 ──┤19             │  │             19├── D15
         │               │  │               │
         └───────────────┘  └───────────────┘
              │                     │
              └──────────┬──────────┘
                   INST[15:0]
                   (16-bit instruction bus)
                 To Control Unit


CONTROL SIGNALS (fixed for read-only):
──────────────────────────────────────

Both chips:
    GND ──► /CE (pin 20)  [Always enabled]
    GND ──► /OE (pin 22)  [Always outputting]
    VCC ──► /WE (pin 27)  [Never writing]

POWER (both chips):
───────────────────
    VCC ──► pin 28 (with 0.1µF bypass cap)
    GND ──► pin 14


INSTRUCTION BUS OUTPUT:
───────────────────────
ROM_LO (U1) provides: INST[7:0]   (low byte)
ROM_HI (U2) provides: INST[15:8]  (high byte)

Combined: INST[15:0]Control Unit

Timing Characteristics

AT28C256 Read Cycle Timing:

Parameters (from datasheet):
────────────────────────────
Symbol  Parameter                   Min    Typ    Max    Unit
──────────────────────────────────────────────────────────────
tACC    Address to Output Valid     -      70     150    ns
tCE     Chip Enable to Output       -      70     150    ns
tOE     Output Enable to Output     -      -      70     ns
tOH     Output Hold from Address    10     -      -      ns
tDF     Output Disable Time         -      -      100    ns

Critical Timing (worst case):
──────────────────────────────
Address setup → Data valid:  150ns maximum
Output enable → Data valid:   70ns maximum
Data hold after address:      10ns minimum


Timing Diagram:
───────────────

Time:    0     50    100   150   200   250   300   (ns)
         │     │     │     │     │     │     │
Address: ──┴─────────────────────────────────────  (stable)
         ──►│◄─────────────────────────────────
           New address applied
         
         
/CE:     ════════════════════════════════════════  (LOW, enabled)


/OE:     ════════════════════════════════════════  (LOW, enabled)


Data:    ═══════╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╲══════════════
                 └─► Invalid ──►│◄─ Valid Data
                               150ns
                          (worst case)

Output   ═══════════════════════════╱╲╱╲╱════════
Enable                              └─►│◄─ Valid
to                                    70ns
Data:


For Hack Computer at 8 MHz:
───────────────────────────
Clock period:     125ns
Available time:   125ns - setup time (12ns) = 113ns
ROM access time:  150ns maximum
Margin:           NEGATIVE! Too slow!

Solution:
─────────
Option 1: Use faster EEPROM (70ns versions exist)
Option 2: Wait states (add extra cycle for memory)
Option 3: Lower clock speed to 6 MHz (167ns period)
Option 4: Pipeline (fetch ahead)

Practical choice: 70ns EEPROM at 8 MHz

Read Operation Step-by-Step

ROM Read Cycle (detailed):

Scenario: Fetch instruction at address 0x0100

Step 1: PC outputs address (t=0ns)
───────────────────────────────────
Program Counter:
    PC_Out[14:0] = 0x0100 (256 decimal)

Electrical:
    ┌──────────┐
PCU6-U9    │ 74HC161  │
    └────┬─────┘
    PC[14:0] = 0000000100000000
         
    (propagates to ROM address pins)


Step 2: Address propagation (t=0-25ns)
───────────────────────────────────────
Signal travels through:
├─ PC output buffer: 5ns
├─ PCB trace: 1ns (speed of light in FR4)
├─ ROM input buffer: 5ns
└─ Internal address decode: 14ns

Total: 25ns to address decoder stable


Step 3: Memory cell selection (t=25-50ns)
──────────────────────────────────────────
Inside EEPROM:
├─ Row decoder activates
├─ Column decoder activates
├─ Selected memory cell found
├─ Floating gate transistor read
└─ Sense amplifier detects charge

Time: ~25ns for cell selection


Step 4: Data appears at outputs (t=50-70ns)
────────────────────────────────────────────
From memory cell to output pins:
├─ Sense amplifier: 10ns
├─ Output buffer: 10ns
└─ Pin driver: 5ns

At t=70ns:
    ROM_LO.D[7:0] = valid data (low byte)
    ROM_HI.D[7:0] = valid data (high byte)
    
    INST[15:0] = complete instruction ✓


Step 5: Data stable (t=70-125ns)
─────────────────────────────────
Data remains valid:
├─ Output drivers maintain voltage
├─ No change until address changes
├─ Plenty of time for control unit
└─ Setup time met (12ns before clock)


Complete timing:
────────────────

t=0ns:   PC changes to 0x0100
t=25ns:  Address decoded in ROM
t=70ns:  Data valid at ROM outputs
t=113ns: Setup time margin (13ns)
t=125ns: Clock edge (next cycle starts)

Timing margin: 55ns ✓
Safe operation at 8 MHz!


Electrical View:
────────────────

    PC Output          ROM Input
         │                │
    ════════════════════════  Address lines
         │    25ns        │
         └───────────────►│
                     [Decode]
                          │ 45ns
                    ════════════  Data lines
                    │          │
              ROM Output   (to Control Unit)

Programming the ROM

Before Operation: Loading Program
──────────────────────────────────

The EEPROM must be programmed with Hack machine code:

Step 1: Write Hack Assembly
────────────────────────────
Example program:
    @17         // Load 17 into A
    D=A         // Copy A to D
    @100        // Load 100 into A
    M=D         // Store D to RAM[100]


Step 2: Assemble to Machine Code
─────────────────────────────────
Assembler output (binary):
    0000000000010001  // @17
    1110110000010000  // D=A
    0000000001100100  // @100
    1110001100001000  // M=D


Step 3: Create ROM Image Files
───────────────────────────────
Split into two 8-bit files:

ROM_LO.bin (low byte):
Address  Data (binary)   Data (hex)
0x0000   00010001        0x11
0x0001   00010000        0x10
0x0002   01100100        0x64
0x0003   00001000        0x08

ROM_HI.bin (high byte):
Address  Data (binary)   Data (hex)
0x0000   00000000        0x00
0x0001   11101100        0xEC
0x0002   00000000        0x00
0x0003   11100011        0xE3


Step 4: Program EEPROM Chips
─────────────────────────────
Using TL866 or similar programmer:

1. Insert ROM_LO chip into programmer
2. Load ROM_LO.bin file
3. Click "Program"
4. Verify successful
5. Remove chip, label "ROM LO"

6. Insert ROM_HI chip
7. Load ROM_HI.bin file
8. Click "Program"
9. Verify successful
10. Remove chip, label "ROM HI"


Step 5: Install in Computer
────────────────────────────
1. Insert ROM_LO into U1 socket
   - Pin 1 (A14) toward indicator
   - Check orientation!

2. Insert ROM_HI into U2 socket
   - Match orientation with ROM_LO
   - Double-check pin 1 location

3. Power on
4. Program runs from address 0x0000

Optional: ZIF Sockets
─────────────────────
Use Zero Insertion Force sockets:
├─ Easy chip removal
├─ No bent pins
├─ Rapid development
└─ ~$5 each, worth it!

Connection 2: RAM (Data Memory)

Purpose and Role

What RAM Does:
──────────────
├─ Stores program data (variables)
├─ Read AND write operations
├─ Addressed by A Register
├─ Accessed via M (memory[A])
└─ Volatile (loses data when power off)

Why SRAM:
─────────
Fast access (55ns typical)
No refresh needed (unlike DRAM)
Simple interface (like ROM + write)
TTL compatible (5V)
Random access (any address any time)

Hardware Choice: AS6C4008 SRAM

AS6C4008 Specifications:
────────────────────────
Capacity:     512K × 8 bits (524,288 bytes)
Organization: 512K addresses × 8-bit data
Package:      32-pin DIP
Technology:   CMOS SRAM
Access Time:  55ns (typical), 70ns (max)
Power:        5V ±10%
Standby:      <1µA (when /CE high)
Active:       50mA typical

Why AS6C4008?
─────────────
Large capacity (we use only 32K of 512K)
Fast enough (55ns < 70ns ROM)
DIP package available
✓ 5V operation
Standard SRAM interface
No refresh needed
Available and affordable (~$3-5)
Overkill capacity (but that's okay!)

Alternative: AS6C62256 (32K × 8)
────────────────────────────────
Exact size match (32K)
28-pin DIP (smaller)
Same interface
Also works perfectly!

AS6C4008 Pinout (Detailed)

         AS6C4008 (32-pin DIP)
         ┌──────┴──────┐
    A181          32VCC (+5V)
    A162          31A17
    A153          30A14
    A124          29A13
     A75          28A8
     A66          27A9
     A57          26A11
     A48          25/OE (Output Enable)
     A39          24A10
     A210         23/CE (Chip Enable)
     A111         22D7
     A012         21D6
    I/O013         20D5
    I/O114         19D4
    I/O215         18D3
    GND16         17/WE (Write Enable)
         └─────────────┘

PIN FUNCTIONS:
──────────────

ADDRESS PINS (A0-A18):
├─ 19 address lines = 2^19 = 512K addresses
├─ For Hack: only use A0-A14 (32K)
├─ A15-A18: tied to GND (unused)
├─ Connected to A Register output
└─ Must be stable before access

DATA PINS (I/O0-I/O7, or D0-D7):
├─ BIDIRECTIONAL! (Read or Write)
├─ Three-state outputs
├─ During READ: RAM drives bus
├─ During WRITE: CPU drives bus
└─ Critical: only one driver at a time!

CONTROL PINS:

/CE (Chip Enable, pin 23):
├─ Active LOW
├─ For Hack: tied to GND (always enabled)
├─ Selecting which RAM chip (if multiple)
└─ Low power when HIGH (standby mode)

/OE (Output Enable, pin 25):
├─ Active LOW
├─ Controls READ operation
├─ When LOW: RAM drives data bus
├─ When HIGH: data pins are hi-Z
└─ Connected to READ_M signal

/WE (Write Enable, pin 17):
├─ Active LOW
├─ Controls WRITE operation
├─ When LOW: RAM captures data from bus
├─ When HIGH: no write occurs
└─ Connected to WRITE_M signal

TRUTH TABLE:
────────────
/CE  /OE  /WEOperationData Pins
─────────────────────────────────────────
 H    X    XStandbyHi-Z (disconnected)
 L    L    HREADOutput (RAMCPU)
 L    H    LWRITEInput (CPURAM)
 L    L    LFORBIDDEN!BUS FIGHT!
 L    H    HDeselectedHi-Z

POWER PINS:
VCC (pin 32): +5V
GND (pin 16): Ground

Complete RAM Connection Schematic

Two AS6C4008 chips for 16-bit data:

RAM Schematic - Full 16-bit Data Memory:

ADDRESSING (from A Register):
──────────────────────────────

A Register:
    A[0] ────────┬────────────────┐
    A[1] ────────┼────────────────┤
    A[2] ────────┼────────────────┤
    A[3] ────────┼────────────────┤
    A[4] ────────┼────────────────┤
    A[5] ────────┼────────────────┤
    A[6] ────────┼────────────────┤
    A[7] ────────┼────────────────┤
    A[8] ────────┼────────────────┤
    A[9] ────────┼────────────────┤
    A[10] ───────┼────────────────┤
    A[11] ───────┼────────────────┤
    A[12] ───────┼────────────────┤
    A[13] ───────┼────────────────┤
    A[14] ───────┴────────────────┤
                 │                │
                 ▼                ▼
         ┌───────────────┐  ┌───────────────┐
RAM_LO (U3)   │  │ RAM_HI (U4)AS6C4008      │  │ AS6C4008         │               │  │               │
    A0 ◄─┤12             │  │             12├─► A0
    A1 ◄─┤11             │  │             11├─► A1
    A2 ◄─┤10             │  │             10├─► A2
    A3 ◄─┤9              │  │              9├─► A3
    A4 ◄─┤8              │  │              8├─► A4
    A5 ◄─┤7              │  │              7├─► A5
    A6 ◄─┤6              │  │              6├─► A6
    A7 ◄─┤5              │  │              5├─► A7
    A8 ◄─┤28             │  │             28├─► A8
    A9 ◄─┤27             │  │             27├─► A9
   A10 ◄─┤24             │  │             24├─► A10
   A11 ◄─┤26             │  │             26├─► A11
   A12 ◄─┤4              │  │              4├─► A12
   A13 ◄─┤29             │  │             29├─► A13
   A14 ◄─┤30             │  │             30├─► A14
   GND ─►┤3,2,1          │  │          1,2,3├─ GND
           (A15-A18)    (A15-A18)         │               │  │               │
DATA I/O      │  │ DATA I/O          (bidirection) (bidirection)         │               │  │               │
   I/O0 ◄┤13             │  │             13├─► I/O0
   I/O1 ◄┤14             │  │             14├─► I/O1
   I/O2 ◄┤15             │  │             15├─► I/O2
   I/O3 ◄┤18             │  │             18├─► I/O3
   I/O4 ◄┤19             │  │             19├─► I/O4
   I/O5 ◄┤20             │  │             20├─► I/O5
   I/O6 ◄┤21             │  │             21├─► I/O6
   I/O7 ◄┤22             │  │             22├─► I/O7
         │               │  │               │
         └───────┬───────┘  └───────┬───────┘
                 │                  │
To BusTransceiver                 ▼                  ▼


CONTROL SIGNALS:
────────────────

Both chips:
    GND ────────►  /CE (pin 23)  [Always enabled]
    
    READ_M  ────►  /OE (pin 25)  [Output when reading]
    
    WRITE_M ────►  /WE (pin 17)  [Write when pulsed]

POWER (both chips):
───────────────────
    VCC ──► pin 32 (with 0.1µF bypass cap)
    GND ──► pin 16


BUS INTERFACE (via 74HC245 transceivers):
──────────────────────────────────────────
RAM data pins don't connect directly to CPU bus!
Need bidirectional buffers (see next section)

The Bidirectional Problem

Challenge: RAM Data Pins are Bidirectional
───────────────────────────────────────────

RAM during READ:
    RAM I/O pins → OUTPUT mode → drive bus

RAM during WRITE:
    RAM I/O pins → INPUT mode → receive from bus

But CPU also uses same bus:
    CPU → drives bus during WRITE
    CPU ← receives bus during READ


Direct Connection (BAD):
────────────────────────

    CPU Bus ──────┬─────── RAM I/O pins
                 CONFLICT!
                  
If both try to drive:
├─ CPU says HIGH (5V)
├─ RAM says LOW (0V)
├─ SHORT CIRCUIT through drivers!
├─ Excessive current
└─ CHIP DAMAGE!


Solution: Bus Transceiver (74HC245)
────────────────────────────────────

    CPU Bus ◄──┬──► [74HC245] ◄──┬──► RAM I/O
              DIR              /OE

The 74HC245 acts as a "traffic cop":
├─ Controls direction of data flow
├─ Isolates CPU from RAM when needed
├─ Prevents bus conflicts
└─ Provides buffering

74HC245 Bus Transceiver

74HC245 Pinout and Function:
────────────────────────────

         74HC245 (20-pin DIP)
         ┌──────┴──────┐
    DIR1          20VCC
    A12          19B1
    A23          18B2
    A34          17B3
    A45          16B4
    A56          15B5
    A67          14B6
    A78          13B7
    A89          12B8
    GND10         11/OE
         └─────────────┘

PIN FUNCTIONS:
──────────────

A1-A8:  "A" side data pins (typically CPU side)
B1-B8:  "B" side data pins (typically RAM side)

DIR (pin 1): Direction control
├─ DIR = 0: BA (data flows from RAM to CPU) = READ
├─ DIR = 1: AB (data flows from CPU to RAM) = WRITE
└─ Sets direction of ALL 8 bits together

/OE (pin 11): Output Enable
├─ /OE = 0: Transceiver active (data flows)
├─ /OE = 1: All outputs hi-Z (isolated)
└─ Master enable for whole chip

TRUTH TABLE:
────────────
/OE  DIROperationData Flow
──────────────────────────────────────
 L    LBARAM to CPU (READ)
 L    HABCPU to RAM (WRITE)
 H    XHi-ZIsolated


Internal Operation:
───────────────────

When DIR = 0 (READ):
    B pins → Input buffers → A pins
    A pins ← Output drivers  (from B)
    B drivers: disabled (hi-Z)
    A drivers: enabled

When DIR = 1 (WRITE):
    A pins → Input buffers → B pins
    B pins ← Output drivers  (from A)
    A drivers: disabled (hi-Z)
    B drivers: enabled

This prevents bus conflicts!

Complete RAM Bus Interface

Full 16-bit RAM Interface with Transceivers:

CPU DATA BUS (16 bits):
───────────────────────
    DB[0-7]  ◄────────┐
    DB[8-15] ◄────────┼───┐
                      │   │
                      ▼   ▼
            ┌──────────────────┐
U5         U6            │ 74HC245  74HC245 │
[7:0]    [15:8]            │                  │
    DIR ───►│1              1  │◄─── DIR (shared)
            │                  │
   /OE ────►│11             11 │◄─── /OE (shared)
            │                  │
A side (CPU)B side (RAM)            └──────┬──────┬────┘
                   │      │
                   ▼      ▼
    RAM_D[0-7]   RAM_D[8-15]
                   │      │
                   ▼      ▼
         ┌─────────────────────┐
RAM_LO    RAM_HIAS6C4008  AS6C4008            (U3)      (U4)         └─────────────────────┘


CONTROL SIGNAL GENERATION:
──────────────────────────

From Control Unit:
    READ_M  signal (active HIGH when reading)
    WRITE_M signal (active HIGH when writing)

Bus Transceiver Control:
    DIR = WRITE_M
        ├─ When WRITE_M=1: DIR=1CPU to RAM
        └─ When WRITE_M=0: DIR=0RAM to CPU

    /OE = 0 (always enabled for Hack)
        └─ Could gate with (READ_M OR WRITE_M)

RAM Control:
    /OE = NOT READ_M
        ├─ When READ_M=1: /OE=0RAM outputs enabled
        └─ When READ_M=0: /OE=1RAM outputs hi-Z

    /WE = NOT WRITE_M
        ├─ When WRITE_M=1: /WE=0Write to RAM
        └─ When WRITE_M=0: /WE=1No write


Control Logic Circuit (using 74HC04 inverters):
────────────────────────────────────────────────

         ┌────────┐
WRITE_M ─┤   NOT  ├──► /WE (to RAM)
         └────────┘
         74HC04

         ┌────────┐
READ_M ──┤   NOT  ├──► /OE (to RAM)
         └────────┘
         74HC04

WRITE_M ─────────────► DIR (to 74HC245)
                       (direct connection!)


Complete Connection Summary:
────────────────────────────

    A[14:0] ──────────────────────►  RAM Address

    READ_M ──┬──► NOT ──► RAM /OE
             └──► (used for transceiver)

    WRITE_M ─┬──► NOT ──► RAM /WE
             └──────────► Transceiver DIR

    DB[15:0] ◄──► 74HC245 ◄──► RAM I/O[15:0]

Memory Timing Diagrams

RAM Read Cycle

Operation: D = M (read from memory)

Preconditions:
──────────────
A = 0x0100 (address already in A register)
RAM[0x0100] = 0xABCD (data stored there)

Control Signals:
────────────────
READ_M = 1 (active)
WRITE_M = 0 (inactive)


Detailed Timing:
────────────────

t=0ns: A Register stable
───────────────────────
    A_Out[14:0] = 0x0100

           A Register
           ┌────┐
U1U2           └─┬──┘
        A_Out = 0x0100


t=10ns: Address reaches RAM
────────────────────────────
    Propagation through traces

           RAM Chips
           ┌────┐
        ┌─►│ U3        │  │ U4        │  └─┬──┘
     Addr    0x0100

t=15ns: Control signals asserted
─────────────────────────────────
    READ_M = 1/OE = 0 (inverter delay)
    
           ┌────────┐
   READ_M ─┤   NOT  ├─► /OE = 0
           └────────┘
              5ns


t=20ns: RAM address decode starts
──────────────────────────────────
    Internal row/column decode
    Memory cell selection begins

    Inside RAM:
    [Address Decoder]
    [Row Select]
    [Col Select]
    [Memory Cell at 0x0100]


t=65ns: RAM data valid at chip outputs
───────────────────────────────────────
    AS6C4008 access time: 55ns typical
    
    RAM I/O pins:
    RAM_D[15:0] = 0xABCD

t=70ns: Through transceiver to CPU bus
───────────────────────────────────────
    74HC245 propagation: 5ns
    
           ┌─────────┐
    RAM_D ─┤74HC245  ├─► DB[15:0]
DIR=0     (BA)
           └─────────┘
              5ns
    
    DB[15:0] = 0xABCD

t=80ns: To X input MUX
──────────────────────
    Data available for ALU
    
    DB[15:0] ──┐
               ├─ MUX ──► X_IN
    A_Out ─────┘
               (A_OR_M=1)


t=100ns: Through ALU
────────────────────
    If operation is just "D = M":
    ALU might just pass through
    
    ALU_Out = 0xABCD


t=125ns: Clock edge, save to D
──────────────────────────────
    LOAD_D pulses
    D0xABCD

Complete Timing Diagram:
─────────────────────────

Time:    0    25   50   75   100  125  (ns)
         │    │    │    │    │    │

CLK:     ───╗     ╔════╗     ╔════
            ╚═════╝    ╚═════╝

A_Out:   ──── 0x0100 ─────────────────  (stable)

READ_M:  ════════════════════════════  (HIGH)

/OE:     ───╗    ╔════════════════════  (LOW - output enable)
            ╚════╝
             15ns

RAM_D:   ═══════════╱╲╱╲╲═════════════
                   └──►│◄─ Valid
                      65ns

DB[15:0]: ═══════════════╱╲╱╲╲═════════
                        └──►│◄─ Valid
                           70ns

ALU_Out:  ════════════════════╱╲╱╲╲═══
                              └─►│ Valid
                                100ns

LOAD_D:   ─────────────────────╗  ╔═══
                               ╚══╝
                              125ns

D_Out:    ──── old ─────────────┴─ 0xABCD

RAM Write Cycle

Operation: M = D (write to memory)

Preconditions:
──────────────
A = 0x0100 (address in A register)
D = 0x1234 (data to write)

Control Signals:
────────────────
READ_M = 0 (inactive)
WRITE_M = 1 (active)


Detailed Timing:
────────────────

t=0ns: A and D registers stable
────────────────────────────────
    A_Out = 0x0100 (address)
    D_Out = 0x1234 (data)

           ┌────┐      ┌────┐
A  │      │ D           │Reg │      │Reg │
           └─┬──┘      └─┬──┘
             │           │
        0x0100       0x1234


t=10ns: Address reaches RAM
────────────────────────────
    A_OutRAM address pins

           RAM
           ┌────┐
    0x0100►│Addr│
           └────┘


t=15ns: Data on CPU bus
────────────────────────
    D_Out drives data bus
    (no transceiver in the way yet)

    D_Out ──────────► DB[15:0] = 0x1234


t=20ns: Control signals active
───────────────────────────────
    WRITE_M = 1/WE = 0 (inverter)
    WRITE_M = 1DIR = 1 (direct)

           ┌────────┐
   WRITE_MNOT  ├─► /WE = 0
           └────────┘

   WRITE_M─────────────► DIR = 1


t=25ns: Transceiver direction set
──────────────────────────────────
    DIR = 1A to B (CPU to RAM)
    
    Data flows through 74HC245:

           ┌─────────┐
    DB ───►│74HC245  │──► RAM_D
     (A)DIR=1      (B)
           └─────────┘
              5ns

    RAM_D[15:0] = 0x1234


t=30ns: RAM receives data
─────────────────────────
    Data present at RAM I/O pins
    /WE is LOW (write enabled)
    Address is stable

    RAM sees:
    ├─ Address: 0x0100
    ├─ Data: 0x1234
    └─ /WE: LOWWRITE!


t=50ns: RAM write begins
────────────────────────
    Internal write circuitry activates
    
    Inside RAM:
    [Addr] ──► [Row/Col Decode]
              [Memory Cell 0x0100]
    [Data] ──► [Write Driver] ──► CELL
                   WRITING!


t=80ns: RAM write complete
───────────────────────────
    Data stored in memory cell
    RAM[0x0100] = 0x1234
    Note: /WE must be LOW for minimum
    write pulse width (typically 50ns)


t=125ns: /WE goes HIGH
───────────────────────
    Write cycle ends
    RAM latches data
    
    WRITE_M = 0/WE = 1
    
    Write is COMPLETE

Complete Timing Diagram:
─────────────────────────

Time:    0    25   50   75   100  125  (ns)
         │    │    │    │    │    │

CLK:     ───╗     ╔════╗     ╔════
            ╚═════╝    ╚═════╝

A_Out:   ──── 0x0100 ─────────────────  (stable)

D_Out:   ──── 0x1234 ─────────────────  (stable)

WRITE_M: ════════════════════════╗    ╔═
                                ╚════╝
                               (pulse)

DIR:     ════════════════════════╗    ╔═ (1 = write)
                                ╚════╝

/WE:     ───╗                   ╔══════
            ╚═══════════════════╝
             ◄──────80ns────────►
               (write pulse)

DB[15:0]: ──── 0x1234 ──────────────── (data from D)

RAM_D:    ════════╱╲╱╲╲════════════════ (through transceiver)
                  └──►│◄─ Valid
                     25ns

RAM Cell: ──── old ────────────┴─ 0x1234
                          Write complete


Critical Timing Parameters:
───────────────────────────
Address setup before /WE:   10ns (met: 30ns)Data setup before /WE:      0ns  (met: 5ns)/WE pulse width:            50ns (met: 80ns)Data hold after /WE:        0ns  (met: many)
Write is SAFE at 8 MHz!

Memory Access Through Instructions

Example 1: Load from Memory (D = M)

Complete Instruction Execution:

Assembly: @100
          D=M

Machine Code:
@100:     0000000001100100  (A-instruction)
D=M:      1111110000010000  (C-instruction)
           This is 'a' bit = 1 (use M not A)


Cycle 1: @100 (Load Address)
─────────────────────────────

t=0ns:   PC = address of @100 instruction
         ROM[PC] = 0000000001100100

t=70ns:  Instruction fetched
         Control unit decodes:
         ├─ A-instruction (bit 15 = 0)
         └─ Value = 0x0064 (100 decimal)

t=110ns: Clock edge
         LOAD_A pulses
         A0x0064

Result: A = 0x0064 (100)

Cycle 2: D=M (Read Memory)
───────────────────────────

Phase 1: Fetch instruction (0-70ns)
─────────────────────────────────────
t=0ns:   PC = address of D=M instruction
         ROM[PC] = 1111110000010000

t=70ns:  Instruction fetched
         Control decodes:
         ├─ C-instruction (bit 15 = 1)
         ├─ a-bit = 1 (use M, not A)
         ├─ ALU control = 110000 (pass Y)
         ├─ dest = 010 (save to D)
         └─ jump = 000 (no jump)


Phase 2: Memory read (0-80ns, parallel!)
──────────────────────────────────────────
t=0ns:   A = 0x0064
         A_OutRAM address

t=15ns:  READ_M = 1 (from control unit)
         /OE = 0 (to RAM)

t=65ns:  RAM[0x0064] data valid
         Assume RAM[100] = 0x0042

t=70ns:  Data through transceiver
         DB = 0x0042


Phase 3: Through ALU (70-110ns)
────────────────────────────────
t=70ns:  Data reaches X input mux
         A_OR_M = 1 → select M
         X_IN = 0x0042

t=85ns:  Through ALU preprocessing
         ALU control = 110000
         ├─ zx=1, nx=1: X becomes !0 = 0xFFFF
         ├─ zy=0, ny=0: Y unchanged
         ├─ f=0: AND operation
         └─ Result meaningless, but...
         
         Wait, let me recheck the control:
         For D=M, we want to pass M through
         
         Actually, "pass Y through" operation:
         zx=1, nx=1, zy=0, ny=0, f=0, no=0
         └─ This gives: (!X) AND Y
            When X=0 (from zx): !0 = 0xFFFF
            0xFFFF AND Y = Y
            Result = Y = M value ✓

t=100ns: ALU_Out = 0x0042


Phase 4: Save to D (110-125ns)
───────────────────────────────
t=110ns: Clock edge
         LOAD_D = 1 (from dest bits)
         D0x0042

Result: D = 0x0042 (value from RAM[100])

Complete Data Flow:
───────────────────

    A Register
0x0064 (address)
    RAM Chip
RAM[100] = 0x0042
    RAM I/O pins
         (through transceiver)
    Data Bus
DB = 0x0042
    X Input MUX
         (A_OR_M=1 selects M)
    ALU
         (pass through)
    D Register
    D = 0x0042

Example 2: Store to Memory (M = D)

Complete Instruction Execution:

Assembly: @100
          M=D

Preconditions:
──────────────
A = 0x0064 (100) [from previous @100]
D = 0x0042 (66)  [value to store]


Cycle 1: M=D (Write Memory)
────────────────────────────

Phase 1: Fetch instruction (0-70ns)
────────────────────────────────────
t=0ns:   ROM[PC] → instruction
         C-instruction: 1110001100001000
         
t=70ns:  Control unit decodes:
         ├─ a-bit = 1 (use M)
         ├─ ALU control = 001100 (pass X, which is A when a=0... wait)
         Let me recalculate for M=D:
         └─ Actually, dest=001 (M), source is irrelevant
            ALU just outputs D value


Phase 2: ALU processes (70-100ns)
──────────────────────────────────
We need ALU to output D value:

The ALU receives:
├─ Y input: D = 0x0042
└─ Control to pass Y through

t=100ns: ALU_Out = 0x0042


Phase 3: Write to memory (100-125ns)
─────────────────────────────────────
t=100ns: WRITE_M = 1 (from dest bits)
         A_Out = 0x0064 (address)
         ALU_Out = 0x0042 (data)

t=105ns: DIR = 1 (CPU to RAM)
         /WE = 0 (write enable)
         
t=110ns: Data reaches RAM
         RAM receives:
         ├─ Address: 0x0064
         ├─ Data: 0x0042
         └─ /WE: LOW

t=125ns: Write completes
         RAM[100]0x0042

Complete Data Flow:
───────────────────

    D Register
D = 0x0042 (data)
    ALU
         (passes through)
    Data Bus
DB = 0x0042
    Transceiver (DIR=1)
         (CPURAM direction)
    RAM I/O pins
Address from A = 0x0064
    RAM[100] = 0x0042

Example 3: Memory Arithmetic (M = M + 1)

Instruction: M = M + 1

Preconditions:
──────────────
A = 0x0064 (address 100)
RAM[100] = 0x0005

This requires:
1. Read RAM[100]0x0005
2. Add 10x0006
3. Write back to RAM[100]

All in ONE instruction cycle!


Phase 1: Read Memory (0-80ns)
──────────────────────────────
t=0ns:   A = 0x0064
         A_OutRAM address

t=15ns:  READ_M might be asserted
         (even though we're also writing!)
         
t=65ns:  RAM[100] = 0x0005
         Data valid at RAM outputs

t=70ns:  M = 0x0005 through transceiver
         To X input MUX


Phase 2: ALU Computation (70-100ns)
────────────────────────────────────
ALU control for "+1":
zx=0, nx=1, zy=1, ny=1, f=1, no=1

Computation:
├─ X = M = 0x0005
├─ Process: !X + !0 = NOT(!X + !0)
├─ Result: 0x0006
└─ (This is the magic of Hack ALU encoding!)

t=100ns: ALU_Out = 0x0006


Phase 3: Write Back (100-125ns)
────────────────────────────────
t=100ns: WRITE_M = 1
         A still = 0x0064 (same address!)
         
t=105ns: ALU_Out drives data bus
         DB = 0x0006
         
t=110ns: Through transceiver to RAM
         DIR = 1 (write direction)
         /WE = 0 (write enable)

t=125ns: Write completes
         RAM[100]0x0006

Critical Insight:
─────────────────
RAM can READ and WRITE in same cycle!

Timeline:
├─ Early: Read old value (0-70ns)
├─ Middle: Compute new value (70-100ns)
└─ Late: Write new value (100-125ns)

No conflict because timing is carefully separated!

Complete Data Flow (circular!):
────────────────────────────────

    A Register
0x0064 (address)
        ├──────────────────┐
        │                  │
        ▼                  │
    RAM [READ]        │                  │
0x0005        ▼                  │
    X Input MUX        │                  │
        ▼                  │
    ALU        │                  │
+1 operation     │
        ▼                  │
    ALU_Out = 0x0006        │                  │
        ▼                  │
    Data Bus        │                  │
        ▼                  │
    Transceiver        │                  │
        ▼                  │
    RAM [WRITE] ◄──────────┘
0x0064 (same address!)
    RAM[100] = 0x0006
Beautiful feedback loop!

PCB Layout for Memory

Physical Placement Strategy

Board Layout (10cm × 15cm):

┌─────────────────────────────────────────┐
Y = 0mm                                │
[Power connectors, switches]├─────────────────────────────────────────┤
Y = 20mm                               │
[Program Counter - 74HC161 × 4]│         │                               │
│         │ PC[14:0]│         ▼                               │
Y = 40mm                               │
│  ┌──────────────────────────────────┐  │
│  │ INSTRUCTION ROM (U1, U2)         │  │
│  │ 2× AT28C256 (ZIF sockets)        │  │
│  │                                  │  │
│  │ Address ◄─ PC                    │  │
│  │ Data ──► Instruction Bus         │  │
│  └──────────────────────────────────┘  │
│         │                               │
│         │ INST[15:0]│         ▼                               │
Y = 80mm                               │
[Control Unit decode logic]│                                         │
├─────────────────────────────────────────┤
Y = 100mm                              │
[A Register - 74HC574 × 2]│         │                               │
│         │ A[14:0]│         ▼                               │
Y = 120mm                              │
│  ┌──────────────────────────────────┐  │
│  │ DATA RAM (U3, U4)                │  │
│  │ 2× AS6C4008                      │  │
│  │                                  │  │
│  │ Address ◄─ A                     │  │
│  │ Data ◄──► Transceiver            │  │
│  └───────┬──────────────────────────┘  │
│          │                              │
│          │ RAM_D[15:0]│          ▼                              │
Y = 150mm                              │
│  ┌──────────────────────────────────┐  │
│  │ BUS TRANSCEIVERS (U5, U6)        │  │
│  │ 2× 74HC245                       │  │
│  │                                  │  │
│  │ A side ◄──► CPU Data Bus         │  │
│  │ B side ◄──► RAM Data             │  │
│  └──────────────────────────────────┘  │
│                                         │
└─────────────────────────────────────────┘

Critical Trace Routing

GROUP 1: PC to ROM Address (15 traces)
───────────────────────────────────────
From: PC (U6-U9 outputs)
To:   ROM (U1, U2 address pins)

Routing:
├─ Length: 40-60mm
├─ Width: 0.4mm (15 mil)
├─ Spacing: 0.3mm
├─ Layer: Top (F.Cu)
├─ Keep parallel
├─ Bus route as group
└─ No vias

Why critical:
├─ These traces determine instruction fetch speed
├─ Must be fast (setup time for ROM)
└─ Keep lengths matched within 5mm


GROUP 2: A Register to RAM Address (15 traces)
───────────────────────────────────────────────
From: A Register (U1, U2 outputs)
To:   RAM (U3, U4 address pins)

Routing:
├─ Length: 50-70mm
├─ Width: 0.4mm
├─ Spacing: 0.3mm
├─ Layer: Top (F.Cu)
├─ Parallel routing
└─ Keep away from data buses

Why critical:
├─ RAM access speed depends on address stability
├─ These traces active every data access
└─ Route carefully to avoid crosstalk


GROUP 3: ROM to Instruction Bus (16 traces)
────────────────────────────────────────────
From: ROM (U1, U2 data pins)
To:   Control Unit

Routing:
├─ Length: 60-80mm
├─ Width: 0.4mm
├─ Layer: Top for most, Bottom for crossings
├─ May need vias for complex routing
└─ Critical timing path!

Why critical:
├─ Instruction must be stable before decode
├─ Sets up all other control signals
└─ Bottleneck for instruction fetch


GROUP 4: RAM to Transceiver (16 traces)
────────────────────────────────────────
From: RAM (U3, U4 I/O pins)
To:   Transceiver (U5, U6 B-side)

Routing:
├─ Length: 30-50mm (keep SHORT!)
├─ Width: 0.4mm
├─ Spacing: 0.4mm (wider for isolation)
├─ Layer: Top preferred
└─ Bidirectional - route carefully!

Why critical:
├─ Bidirectional signals (tricky!)
├─ Fast switching required
├─ Short traces reduce reflections
└─ Minimize stub lengths


GROUP 5: Transceiver to CPU Bus (16 traces)
────────────────────────────────────────────
From: Transceiver (U5, U6 A-side)
To:   Main CPU data bus

Routing:
├─ Length: variable (bus connection)
├─ Width: 0.4mm
├─ Fan-out to multiple destinations
├─ Star topology if possible
└─ Use bottom layer for longer runs

Why critical:
├─ Main data highway
├─ Connects to multiple destinations
├─ Bus loading considerations
└─ Must handle bidirectional flow

Decoupling and Power Distribution

Memory ICs Need Stable Power:

AT28C256 (ROM):
───────────────
Current draw:
├─ Active: 30mA (during access)
├─ Standby: 100µA (when /CE high)
└─ Switching spikes: 50mA peak

Decoupling:
├─ 0.1µF ceramic (close to VCC pin)
├─ Place within 5mm of pin 28
└─ Connect directly to power plane

AS6C4008 (RAM):
───────────────
Current draw:
├─ Active: 50mA (during access)
├─ Standby: 1µA (when /CE high)
├─ Write: 70mA peak
└─ Read: 40mA typical

Decoupling:
├─ 0.1µF ceramic (close to VCC pin)
├─ 10µF tantalum (for write spikes)
├─ Place ceramic within 5mm
└─ Tantalum within 20mm

74HC245 (Transceiver):
──────────────────────
Current draw:
├─ Standby: <1mA
├─ Active: 5mA per gate
└─ Peak: 40mA (all 8 bits switching)

Decoupling:
├─ 0.1µF ceramic
└─ Standard 74HC practice


Power Plane Strategy:
──────────────────────

Bottom Layer:
┌────────────────────────────────┐
VCC Plane (partial fill)│  ┌──────────────────────────┐  │
│  │ ███████████████████████  │  │
│  │ ██ Under RAM chips ████  │  │
│  │ ███████████████████████  │  │
│  └──────────────────────────┘  │
│                                │
GND Plane (large fill)│  ████████████████████████████  │
│  ████████████████████████████  │
│  ████████████████████████████  │
└────────────────────────────────┘

Via stitching:
├─ Every 10mm around perimeter
├─ Connect top ground to plane
├─ Multiple vias per IC
└─ Low inductance path

Summary: Memory Interface Philosophy

Key Design Principles

1. Separation of Concerns

Instruction Memory (ROM):
└─ Read-only, addressed by PC
└─ No bidirectional complexity

Data Memory (RAM):
└─ Read/write, addressed by A
└─ Bidirectional handled by transceiver

2. Timing is Everything

All operations must fit in clock cycle:
├─ ROM access: 70ns
├─ RAM read: 70ns
├─ RAM write: 80ns (includes setup)
└─ Total cycle: 125ns @ 8MHz ✓

3. Bus Management

Prevent conflicts with:
├─ Transceivers (74HC245)
├─ Direction control
├─ Careful timing
└─ Tri-state outputs

4. Simplicity Through Harvard

Separate memories mean:
├─ No bus arbitration needed
├─ Simultaneous fetch + data access
├─ Simpler control logic
└─ Faster execution

The Beautiful Integration

The memory system connects everything:

Program Counter ──► ROM ──► Instructions ──► Control Unit
                                          Control Signals
                      ┌────────────────────────┼─────────┐
                      │                        │         │
                      ▼                        ▼         ▼
A Register ──► RAM ◄─── Transceiver ◄──── Data Bus ◄─── ALU

Complete computational feedback loop!

The memory interface is the foundation - it's where programs live and where data resides. Everything else in the computer exists to move data between memory and the ALU!


Would you like me to explain:

  1. How to optimize memory access for speed?
  2. Adding memory-mapped I/O (screen, keyboard)?
  3. The complete instruction fetch-execute cycle with all connections?
  4. How to expand memory beyond 32K?