Featured image of post Nand2Tetris Project(3)

Nand2Tetris Project(3)

给电脑以记忆

Project3: Memory

在本次项目中我们将从寄存器出发,逐步实现不同大小的RAM。

Bit

register 这个问题的主要难点在于如何去设置每一轮中DFF的输出作为Mux的输入。

而为了解决这一个问题,我们需要澄清一个潜在的误区,即HDL与平时我们使用的编程语言不同,它是一种声明式语言,所有的语句是并行执行的。

所以我们可以直接在Mux的接口就使用一个feedback作为传输的中间值,而在DFF中再对它进行定义即可。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/**
 * 1-bit register:
 * If load is asserted, the register's value is set to in;
 * Otherwise, the register maintains its current value:
 * if (load(t)) out(t+1) = in(t), else out(t+1) = out(t)
 */
CHIP Bit {
    IN in, load;
    OUT out;

    PARTS:
    //// Replace this comment with your code.
    Mux(a=feedback,b=in,sel=load,out=input);
    DFF(in=input,out=out,out=feedback);
}

Register

使用Bit进行逐位置运算即可。

RAM

核心步骤一共有三步:

  • 根据地址选择到底应该在哪个Register上面进行操作。这里我们使用DMux8Way。
  • 根据第一步拆解出的8个分量,分配给各个Register。
  • 对于8个分量的输出,同样根据地址选择应该输出哪个output作为RAM的最终输出。这里使用Mux8Way16。

在硬件层面编程,实现分支判断是一场灾难。所以我们不得不把所有分支的结果全部进行计算,然后使用已经提前完成的分支选择器选择一个作为输出。这和我们常见的编程语言的基础思想是不一样的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
 * Memory of eight 16-bit registers.
 * If load is asserted, the value of the register selected by
 * address is set to in; Otherwise, the value does not change.
 * The value of the selected register is emitted by out.
 */
CHIP RAM8 {
    IN in[16], load, address[3];
    OUT out[16];

    PARTS:
    DMux8Way(in=load,sel=address,a=a,b=b,c=c,d=d,e=e,f=f,g=g,h=h);
    Register(in=in ,load=a ,out=out0 );
    Register(in=in ,load=b ,out=out1 );
    Register(in=in ,load=c ,out=out2 );
    Register(in=in ,load=d ,out=out3 );
    Register(in=in ,load=e ,out=out4 );
    Register(in=in ,load=f ,out=out5 );
    Register(in=in ,load=g ,out=out6 );
    Register(in=in ,load=h ,out=out7 );
    Mux8Way16(a=out0,b=out1,c=out2,d=out3,e=out4,f=out5,g=out6,h=out7,sel=address,out=out);
}

完成RAM8之后,更大的RAM使用相同的方式也可完成,不再赘述。

PC

插播一个HDL里面的坑:变量名里面不要加_ !!!!!!!!!!否则会报错。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
/**
 * A 16-bit counter.
 * if      reset(t): out(t+1) = 0
 * else if load(t):  out(t+1) = in(t)
 * else if inc(t):   out(t+1) = out(t) + 1
 * else              out(t+1) = out(t)
 */
CHIP PC {
    IN in[16],inc, load, reset;
    OUT out[16];
    
    PARTS:
    Inc16(in=state,out=outinc);
    Mux16(a=feedback,b=outinc,sel=inc,out=temp1);
    Mux16(a=temp1,b=in,sel=load,out=temp2);
    Mux16(a=temp2,b=false,sel=reset,out=seloutput);
    Register(in=seloutput,load=true,out=state,out=out);
}

依然是经典使用HDL完成分支判断。本题里面的分支判断蕴含有较强的优先性大小,所以需要先使用Register获取state,然后从优先级较低的inc变量开始逐级判断,完成优先级高的变量对优先级低的变量的覆盖。这与传统的纯粹并行分支判断不同。

以上就是本次Project的全部解答了。可以说本章引入的时序逻辑问题还是有一点绕的,但是如果理解了HDL的声明式编程原理,我们在编程的时候就会更少的受到曾经学过的编程语言的思想的束缚,写出更符合电路设计的代码。

comments powered by Disqus