跳转至内容

05. Computer Architecture

术语

冯诺依曼体系结构的基础是一个 CPU,它与记忆设备(memory device)即内存进行交互,负责从输入设备(input device)接收输入,向输出设备(output device)发送数据。内存不仅要存储要进行操作的数据,还要存储指示计算机运行的指令。

算术逻辑单元(ALU)执行所有底层的算术和逻辑运算

寄存器(Registers)存储 CPU 内部的临时数据

控制单元(Control Unit)解码指令,还负责决定下一步要取出和执行哪条指令

数据寄存器(Data Registers)为 CPU 提供短期记忆。

寻址寄存器(Address Registers)存储内存地址。

程序计数寄存器(Program Counter Registers)存储下一条要执行的指令的地址。

I/O 映像(Memory Mapped I/O)为输入输出设备分配内存地址。通过访问这些地址,CPU 可以与输入输出设备进行交互。

Chip API

chip
芯片名:  CPU
输入:    inM[16],            // 输入值 M(M=RAM(A))
         instruction[16],    // 用于执行的指令
         reset               // 决定是重启当前程序(reset=1)
                             // 还是继续执行当前程序(reset=0)的 rest 信号
输出:    outM[16],           // 输出值 M
         writeM,             // 写入 M ?
         addressM[15],       // M 在数据内存中的地址
         pc[15]              // 下一条指令的地址
功能:    根据 Hack 机器语言规范执行指令,该语言规范指定 D 和 A 为 CPU 中的寄存器,
         M 代表内存单元,其地址由寄存器 A 中的内容确定(该内存单元的值从 inM 端输出)。
         如果指令希望将一个数值写入 M 中,该数值会从 outM 端输出,同时 M 的地址会被
         置于 addressM 端,而且 writeM 位被置位(当 writeM=0,任何值都可能出现
         在 outM)。如果 reset=1,那么 CPU 跳转到地址 0(也就是,在下一个时间单元
         设置 pc=0),而不是执行当前指令所确定的下一条指令。
chip
芯片名:  ROM32K               // 容量为 32K 的 16-位只读内存
输入:    address[15]          // ROM 中的地址
输出:    out[16]              // ROM[address] 中存储的数值
功能:    out=ROM[address]     // 16-bit 数值的赋值
说明:    指令内存,用机器语言编写的程序被预先加载到 ROM 中。硬件实现可以将 ROM 当做一个
         内置芯片,而软件模拟器必须提供一种将程序载入 ROM 的机制。
chip
芯片名:  Screen                // 物理屏幕的内存映像
输入:    in[16],               // 要写入的内容
         load,                 // 写使能位
         address[13]           // 写入的目的内存单元
输出:    out[16]               // 给定地址的屏幕值
功能:    功能与一个 8K 的 16-位 RAM 相似:
         1. out(t)=Screen[address(t)](t)
         2. If load(t-1) then Screen[address(t-1)](t)=in(t-1)
chip
芯片名:  Keyboard              // 物理键盘的内存映像,输出当前按下键的代码
输出:    out[16]               // 输出按键的 ASCII 码
                               // 或者之前提到的特殊码
                               // 或者 0(没有任何按键按下)

Project 05

CPU 还是有难度的,写了很多注释,更详细的内容可以参考 第四章笔记

hdl
/**
 * The complete address space of the Hack computer's memory,
 * 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 
 * 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 
 * memory map. The behavior in these addresses is described in the Screen
 * and Keyboard chip specifications given in the lectures and the book.
 */
CHIP Memory {
    IN in[16], load, address[15];
    OUT out[16];

    PARTS:
        // address[14..13]
        // 00/01 -> RAM16K (0x0000-0x3FFF)
        // 10    -> Screen (0x4000-0x5FFF)
        // 11    -> Keyboard (0x6000)
        Not(in=address[14], out=not14);
        Not(in=address[13], out=not13);
        And(a=load, b=not14, out=loadram);
        And(a=address[14], b=not13, out=screen);
        And(a=load, b=screen, out=loadscreen);

        RAM16K(in=in, load=loadram, address=address[0..13], out=ramout);
        Screen(in=in, load=loadscreen, address=address[0..12], out=screenout);
        Keyboard(out=keyboardout);

        Mux4Way16(a=ramout, b=ramout, c=screenout, d=keyboardout, 
                  sel[0]=address[13], sel[1]=address[14], out=out);
}
hdl
/**
 * The Hack Central Processing unit (CPU).
 * Parses the binary code in the instruction input and executes it according to the
 * Hack machine language specification. In the case of a C-instruction, computes the
 * function specified by the instruction. If the instruction specifies to read a memory
 * value, the inM input is expected to contain this value. If the instruction specifies
 * to write a value to the memory, sets the outM output to this value, sets the addressM
 * output to the target address, and asserts the writeM output (when writeM = 0, any
 * value may appear in outM).
 * If the reset input is 0, computes the address of the next instruction and sets the
 * pc output to that value. If the reset input is 1, sets pc to 0.
 * Note: The outM and writeM outputs are combinational: they are affected by the
 * instruction's execution during the current cycle. The addressM and pc outputs are
 * clocked: although they are affected by the instruction's execution, they commit to
 * their new values only in the next cycle.
 */
CHIP CPU {
    IN  inM[16],         // M value input (M = contents of RAM[A])
        instruction[16], // Instruction for execution
        reset;           // Signals whether to re-start the current
                         // program (reset==1) or continue executing
                         // the current program (reset==0).

    OUT outM[16],        // M value output
        writeM,          // Write to M?
        addressM[15],    // Address in data memory (of M)
        pc[15];          // address of next instruction

    PARTS:
        // A 指令,直接写入
        // C 指令且 dest 包含 A,也就是 d1 == 1 时写入
        // addressM 永远来自 A,M = contents of RAM[A]
        Not(in=instruction[15], out=isA);
        Mux16(a=instruction, b=aluout, sel=instruction[15], out=Ain);
        Or(a=isA, b=instruction[5], out=loadA);
        ARegister(in=Ain, load=loadA, out=Aout, out[0..14]=addressM);

        // C 指令且 dest 包含 D,也就是 d2 == 1 时写入
        And(a=instruction[15], b=instruction[4], out=loadD);
        DRegister(in=aluout, load=loadD, out=Dout);

        // ALU 左输入永远是 D 寄存器的输出
        // 右输入看 C 指令的 a 位,a == 0 时是 A 寄存器的输出,a == 1 时是 inM
        Mux16(a=Aout, b=inM, sel=instruction[12], out=aluy);
        ALU(x=Dout, y=aluy, 
            zx=instruction[11], nx=instruction[10],
            zy=instruction[9], ny=instruction[8],
            f=instruction[7], no=instruction[6],
            out=aluout, zr=zr, ng=ng);
        
        // outM 就是 ALU 的输出
        // C 指令且 dest 包含 M,也就是 d3 == 1 时,writeM 才为 1
        Or16(a=aluout, b=false, out=outM);
        And(a=instruction[15], b=instruction[3], out=writeM);

        // ALU 结果 为 0 时 zr=1,结果为负数时 ng=1
        // 任何 jump 操作都是跳转到 A 寄存器中存储的地址
        And(a=instruction[2], b=ng, out=jlt);
        And(a=instruction[1], b=zr, out=jeq);

        Not(in=ng, out=notng);
        And(a=instruction[0], b=notng, out=ge);
        Not(in=zr, out=notzr);
        And(a=notzr, b=ge, out=jgt);

        Or(a=jlt, b=jeq, out=jlteq);
        Or(a=jlteq, b=jgt, out=jump);
        And(a=instruction[15], b=jump, out=loadPC);

        PC(in=Aout, load=loadPC, inc=true, reset=reset, out[0..14]=pc);
}
hdl
/**
 * The Hack computer, consisting of CPU, ROM and RAM.
 * When reset = 0, the program stored in the ROM executes.
 * When reset = 1, the program's execution restarts. 
 * Thus, to start running the currently loaded program,
 * set reset to 1, and then set it to 0. 
 * From this point onwards, the user is at the mercy of the software.
 * Depending on the program's code, and whether the code is correct,
 * the screen may show some output, the user may be expected to enter
 * some input using the keyboard, or the program may do some procerssing. 
 */
CHIP Computer {
    IN reset;

    PARTS:
        ROM32K(address=pc, out=pcout);
        CPU(inM=memout, instruction=pcout, reset=reset,
            outM=outM, writeM=writeM, addressM=addressM, pc=pc);
        Memory(in=outM, load=writeM, address=addressM, out=memout);
}