Posts 从零开始构建计算机chapter5_计算机体系结构
Post
Cancel

从零开始构建计算机chapter5_计算机体系结构

背景

一些基础性东西

冯诺依曼结构

p1 经典的冯诺依曼结果 CPU + 内存 + 输入输出

内存

  • 数据内存
  • 指令内存

中央处理器

ALU + 寄存器 + 控制单元 CPU操作现在可以被描述成一个重复的循环:从内存中取一条指令(字);将其解码;执行该指令,取下一条指令;如此反复循环。指令的执行过程可能包含下面的一些子任务:让 ALU计算一些值,控制内部寄存器,从存储设备中读取-一-个字,或向存储设备中写入一个字。在执行这些任务的过程中,CPU也会计算出下一步该读取并执行哪一条指令。

寄存器

怎么说呢 , cpu从内存中取内容,时间消耗较大。 直接访问寄存器速度会快得多。

数据寄存器 数据寄存器(Data registers)这些寄存器为CPU提供短期记忆( memory)服务。比如,当计算(a-b)·c时,必须首先计算(a-b)的值并记住它。虽然这个结果可以暂时地被存储到存储单元中,但更好的办法是存储在CPU内部即数据寄存器中。

寻址寄存器 据。这样我们必须确定被访问的内存字( word)所在的内存地址。在某些情况下这个地址作为当前指令的一个部分给出,而其他某些情况下它依赖于前面一条指令的执行结果。对于后者,这个地址应该被存储到某个寄存器中,使得该寄存器的内容在今后的操作中能够被当作存储单元的地址一这就需要用到寻址寄存器。

程序计数寄存器 程序计数寄存器(Program counter register)执行程序时,CPU必须总是知道下一条指令在指令内存中的地址。这个地址保存在一个特殊的寄存器即程序计数器中(或称PC,Program Counter)。PC的内容就被当作从指令内存中取指令的地址。因此,在执行当前指令的过程中,CPU通过两种方式之一来更新PC的内容:1)如果当前指令不包括goto 命令,PC 增1以便使指针指向程序中的下一条指令;2)如果当前指令中包含需要执行的goton命令,则CPU将PC置为n。

输入输出设备

见上章。

硬件设计

Hack 平台是16-位冯·诺依曼机,包括一个CPU、两个独立的内存模块(指令内存和数据内存)和两个内存映像IO设备(屏幕和键盘)。

在hack计算机中,指令内存为只读设备,所以指令内存可以通过rom芯片实现 下文中,我们统一:

  • RAM 数据内存
  • ROM 指令内存

CPU的设计

p2

架构图 p3

三个输入:

  • D A M[A]

三个输出:

  • outM 输出值
  • writeM 是否存入
  • addressM 存入的M地址

pc计数器: p4

  • nojump : pc ++
  • goto : pc = A
  • conditional goto : if conditional true pc = A else pc ++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
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:
    // 指令的最大地址位 如果为1则是C指令 为0则是A指令
    Not(in = instruction[15], out = isA);
    // 对isC再次取返 判断是否C指令 读者可以自行用1或0代入想像一下 isC实际上就是instruction[15]的值
    Not(in = isA, out = isC);

    // 如果是C指令并且指令指定ALU输出存到AR 则将ALU的输出 输入到AR 否则将指令输入到AR
    And(a = isC, b = instruction[5], out = isLoadAluOut);
    Mux16(a = instruction, b = outALU, sel = isLoadAluOut, out = inAR);

    // 如果为A指令或指令指定输出到A处理器 则将AR的load位置1
    Or(a = isA, b = instruction[5], out = isLoadAR);
    ARegister(in = inAR, load = isLoadAR, out = outAR, out[0..14] = addressM);

    // 根据指令中的a位域判断将AR的输出或者inM输入到ALU
    Mux16(a = outAR, b = inM, sel = instruction[12], out = outAM);

    // 如果是C指令并且规定写入到M
    And(a = isC, b = instruction[3], out = writeM);

    // 如果是C指令并且规定写入到DR
    And(a = instruction[4], b = isC, out = isLoadDR); 
    DRegister(in = outALU, load = isLoadDR, out = outDR);


    And(a = isC, b = instruction[6], out = no);
    And(a = isC, b = instruction[7], out = f);
    And(a = isC, b = instruction[8], out = ny);
    And(a = isC, b = instruction[9], out = zy);
    And(a = isC, b = instruction[10], out = nx);
    And(a = isC, b = instruction[11], out = zx);

    ALU(x = outDR, y = outAM, zx = zx, nx = nx, zy = zy, ny = ny, f = f, no = no, out = outALU, out = outM, zr=zr, ng=ng);

    // 根据j位域和ALU的zr、ng位来判断跳转
    // zr out = 0 , zr = 1
    // ng out < 0 , ng = 1
    And(a = isC, b = instruction[0], out = isGT);
    And(a = isC, b = instruction[1], out = isEQ);
    And(a = isC, b = instruction[2], out = isLT);

    // 输出小于0跳转
    And(a = ng, b = isLT, out = isLtJump);
    // 输出等于0 跳转
    And(a = zr, b = isEQ, out = isEqJump);

    // 输出是否大于0  isOutGt = 1 , 输出大于0 
    Not(in = ng, out = notNg);
    Not(in = zr, out = notZr);
    And(a = notNg, b = notZr, out = isOutGt);
    
    // 输出大于0跳转。  isGtJump
    And(a = isOutGt, b = isGT, out = isGtJump);

    Or(a = isLtJump, b = isEqJump, out = isJump);
    Or(a = isJump, b = isGtJump, out = jump);

    // 计数器芯片有两个附加的控制位reset和inc。当inc=1时,计数器在每个时钟周期自加,
    //输出值out(t)=out(t-1)+1。如果想要将计数器重置为0,就将reset位置为1;
    //如果想要将其初始化为某个计数值d,就将d置于in输入管脚然后将1oad位置1。
    PC(in = outAR, load = jump, inc = true, reset = reset, out[0..14] = pc);

}

内存设计

p5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
CHIP Memory {
    IN in[16], load, address[15];
    OUT out[16];

    PARTS:

    // 0011 1111 1111 1111  16383
    // 0100 0000 0000 0000  16384
    // 0101 1111 1111 1111  24575
    // 0110 0000 0000 0000  24576

    // 根据最大的两个地址选出对应的操作 00 01都是RAM 10是屏幕 11是键盘

    DMux4Way(in = load, sel = address[13..14], a = loadA, b = loadB, c = loadS, d = loadK);

    // 00|01 
    Or(a = loadA, b = loadB, out = loadRam);
    
    RAM16K(in = in, load = loadRam, address = address[0..13], out = outRam);
    Keyboard(out = outK);
    Screen(in = in, load = loadS, address = address[0..12], out = outS);

    // 根据最大的两个地址选出对应的操作 00 01都是RAM 10是屏幕 11是键盘
    Mux4Way16(a = outRam, b = outRam, c = outS, d = outK, sel = address[13..14], out = out);

}

Computer 设计

p6

1
2
3
4
5
6
7
8
9
10
CHIP Computer {

    IN reset;

    PARTS:
    // Put your code here:
    CPU(reset = reset, inM = outMemory, instruction = outROM, outM = outMvalue, writeM = isLoadValue, addressM = outAddressM, pc = outPC);
    Memory(in = outMvalue, load = isLoadValue, address = outAddressM, out = outMemory);
    ROM32K(address = outPC, out = outROM);
}

测试

略。

This post is licensed under CC BY 4.0 by the author.

Contents

Trending Tags