From 516d1878cbd01f43ea3dd08f78284cde02a881de Mon Sep 17 00:00:00 2001 From: Sean O'Connor Date: Mon, 22 Sep 2025 15:17:34 -0400 Subject: [PATCH] project05 - complete --- 05/CPU.hdl | 71 ++++++++++++++++++++++++++++++++++++++++++++++--- 05/Computer.hdl | 10 ++++++- 05/Memory.hdl | 52 +++++++++++++++++++++++++++++------- 3 files changed, 119 insertions(+), 14 deletions(-) diff --git a/05/CPU.hdl b/05/CPU.hdl index bc30e47..ea3e7b2 100644 --- a/05/CPU.hdl +++ b/05/CPU.hdl @@ -27,10 +27,75 @@ CHIP CPU { // the current program (reset==0). OUT outM[16], // M value output - writeM, // Write to M? + writeM, // Write to M? addressM[15], // Address in data memory (of M) pc[15]; // address of next instruction PARTS: - //// Replace this comment with your code. -} \ No newline at end of file + // CPU implements Hack machine language from book c4/c5 + // step 1: decode instruction type (addr instruction "A" vs compute instruction "C") + // step 2: handle A reg load and ALU input sel + // step 3: handle D reg and ALU comp + // step 4: handle mem write and jump logic + // step 5: update PC + // + // Instruction formats (for reference, duh): + // A-instruction: 0vvvvvvvvvvvvvvv (load 15-bit value into A) + // C-instruction: 111ACCCCCCDDDJJJ + // A = ALU input sel (0=A, 1=M) + // CCCCCC = ALU control bits + // DDD = destination (A=bit5, D=bit4, M=bit3) + // JJJ = jump condition (bit2=j(ump)l(ess)t(han), bit1=j(ump)eq(ual), bit0=j(ump)g(reater)t(han)) + + // STEP 1 + // decode instruction type + Not(in=instruction[15], out=aInstr); // aInstr = 1 when instruction[15] = 0 + Not(in=aInstr, out=cInstr); // cInstr = 1 when instruction[15] = 1 + + // STEP 2 + // pick A reg input (instruction or ALU out) + Mux16(a=instruction, b=aluOut, sel=cInstr, out=aRegIn); // A-instr uses instruction, C-instr uses ALU + + // load A reg if A-instruction or dest A + Or(a=aInstr, b=instruction[5], out=loadA); // load A if A-instr or C-instr with A dest + ARegister(in=aRegIn, load=loadA, out=aRegOut); // A reg stores addr/value + + // pick ALU y input (A reg or M) + Mux16(a=aRegOut, b=inM, sel=instruction[12], out=aluY); // instruction[12] picks A vs M for ALU + + // STEP 3 + // load D reg if C-instruction with dest D + And(a=cInstr, b=instruction[4], out=loadD); // loadD = 1 when C-instr and dest D + DRegister(in=aluOut, load=loadD, out=dRegOut); // D reg stores data + + // compute ALU operation + ALU(x=dRegOut, y=aluY, zx=instruction[11], nx=instruction[10], // ALU control from instruction[11..6] + zy=instruction[9], ny=instruction[8], f=instruction[7], + no=instruction[6], out=aluOut, zr=zr, ng=ng); + + // STEP 4 + // writeM set if C-instruction with dest M + And(a=cInstr, b=instruction[3], out=writeM); // writeM = 1 when C-instr and dest M + + // compute jump conditions + Not(in=zr, out=notZr); // notZr = 1 when ALU out is not zero + Not(in=ng, out=notNg); // notNg = 1 when ALU out is not negative + And(a=notZr, b=notNg, out=pos); // pos = 1 when ALU out is positive + + And(a=instruction[2], b=ng, out=jlt); // jlt = 1 when jump if less than and ALU < 0 + And(a=instruction[1], b=zr, out=jeq); // jeq = 1 when jump if equal and ALU = 0 + And(a=instruction[0], b=pos, out=jgt); // jgt = 1 when jump if greater and ALU > 0 + + Or(a=jlt, b=jeq, out=jle); // combine jlt and jeq + Or(a=jle, b=jgt, out=jump); // combine all jump conditions + And(a=cInstr, b=jump, out=pcLoad); // only jump on C-instructions + + // STEP 5 + // program counter + PC(in=aRegOut, load=pcLoad, inc=true, reset=reset, out[0..14]=pc); // PC jumps to A reg or increments + + // OUT + // connect outputs + Or16(a=false, b=aluOut, out=outM); // outM gets ALU output + Or16(a=false, b=aRegOut, out[0..14]=addressM); // addressM gets A register (15 bits) +} diff --git a/05/Computer.hdl b/05/Computer.hdl index e5cb1e3..55a5a33 100644 --- a/05/Computer.hdl +++ b/05/Computer.hdl @@ -18,5 +18,13 @@ CHIP Computer { IN reset; PARTS: - //// Replace this comment with your code. + // ROM32K provides instructions to CPU + // find where PC points to, output instruction + ROM32K(address=pc, out=instruction); + + // CPU executes instructions and manages data flow + CPU(inM=memOut, instruction=instruction, reset=reset, outM=outM, writeM=writeM, addressM=addressM, pc=pc); + + // memory does storage and IO + Memory(in=outM, load=writeM, address=addressM, out=memOut); } diff --git a/05/Memory.hdl b/05/Memory.hdl index 79df0e2..c49b41b 100644 --- a/05/Memory.hdl +++ b/05/Memory.hdl @@ -4,19 +4,19 @@ // File name: projects/5/Memory.hdl /** * The complete address space of the Hack computer's memory, - * including RAM and memory-mapped I/O. + * including RAM and memory-mapped I/O. * The chip facilitates read and write operations, as follows: * Read: out(t) = Memory[address(t)](t) * Write: if load(t-1) then Memory[address(t-1)](t) = in(t-1) - * In words: the chip always outputs the value stored at the memory - * location specified by address. If load=1, the in value is loaded - * into the memory location specified by address. This value becomes + * In words: the chip always outputs the value stored at the memory + * location specified by address. If load=1, the in value is loaded + * into the memory location specified by address. This value becomes * available through the out output from the next time step onward. * Address space rules: - * Only the upper 16K+8K+1 words of the Memory chip are used. - * Access to address>0x6000 is invalid. Access to any address in - * the range 0x4000-0x5FFF results in accessing the screen memory - * map. Access to address 0x6000 results in accessing the keyboard + * Only the upper 16K+8K+1 words of the Memory chip are used. + * Access to address>0x6000 is invalid. Access to any address in + * the range 0x4000-0x5FFF results in accessing the screen memory + * map. Access to address 0x6000 results in accessing the keyboard * memory map. The behavior in these addresses is described in the Screen * and Keyboard chip specifications given in the lectures and the book. */ @@ -25,5 +25,37 @@ CHIP Memory { OUT out[16]; PARTS: - //// Replace this comment with your code. -} \ No newline at end of file + // Memory = RAM16K + Screen + Keyboard (using address decoding) + // step 1: decode high address bits to pick component + // step 2: route load signal to correct component + // step 3: select output from correct component + // + // addr map from c5 reading (for reference, duh): + // 0x0000-0x3FFF: RAM16K (address[14]=0) + // 0x4000-0x5FFF: Screen (address[14]=1, address[13]=0) + // 0x6000: Keyboard (address[14]=1, address[13]=1) + // address[14] choose RAM vs I/O + // address[13] choose screen vs kb when I/O + + // STEP 1 + // split address to selectors + Not(in=address[14], out=ramSel); // ramSel = 1 when address[14] = 0 (RAM range) + And(a=address[14], b=address[13], out=kbSel); // kbSel = 1 when both bits = 1 (kb address) + Not(in=address[13], out=notkb); // invert address[13] for screen selection + And(a=address[14], b=notkb, out=screenSel); // screenSel = 1 when address[14]=1, address[13]=0 + + // STEP 2 + // send load to correct component + And(a=load, b=ramSel, out=ramLoad); // ramLoad = load when accessing RAM + And(a=load, b=screenSel, out=screenLoad); // screenLoad = load when accessing screen + + // mem components + RAM16K(in=in, load=ramLoad, address=address[0..13], out=ramOut); // 16K RAM uses 14 addr bits + Screen(in=in, load=screenLoad, address=address[0..12], out=screenOut); // screen uses 13 addr bits + Keyboard(out=kbOut); // kb is ro, no load/addr + + // STEP 3 + // pick correct out + Mux16(a=screenOut, b=kbOut, sel=kbSel, out=ioOut); // pick screen or kb out + Mux16(a=ramOut, b=ioOut, sel=address[14], out=out); // pick RAM or I/O out +}