From Nand to Tetris 里的 Project 3

背景

让我们按照 From Nand to Tetris 里 Project 3 的要求,来完成下列的设计。

  1. 实现 Bit
  2. 实现 Register
  3. 实现 RAM8
  4. 实现 RAM64 (todo)
  5. 实现 RAM512 (todo)
  6. 实现 RAM4K (todo)
  7. 实现 RAM16K (todo)
  8. 实现 PC (Program Counter, 程序计数器) (todo)

正文

1. 实现 Bit

前往 Nand to Tetris Online IDE,选择 Project 3 里的 Bit,效果如下图所示 ⬇️

我们的目标是用 DFF(Data Flip Flop, 数据触发器) 以及 Project 1/Project 2 里已经实现的各种 chip 实现一个 Bit(即,一位寄存器)。

注释中的相关描述如下 ⬇️

text 复制代码
/**
 * 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)
 */
  • 输入是
    • i n in in
    • l o a d load load
  • 输出是
    • o u t out out

书中有一些关于一位 Bit寄存器设计的描述 ⬇️

结合书中的描述,我们可以这样来实现

hdl 复制代码
CHIP Bit {
    IN in, load;
    OUT out;

    PARTS:
    Mux(a= previousOut, b= in, sel= load, out= nextOut);
    DFF(in= nextOut, out=out, out= previousOut);
}

这样的代码可以通过仿真测试,效果如下图所示 ⬇️

2. 实现 Register

前往 Nand to Tetris Online IDE,选择 Project 3 里的 Register,效果如下图所示 ⬇️

我们的目标是用 Bit(一位寄存器) 以及 Project 1/Project 2 里已经实现的各种 chip 实现一个 Register(即, 16 16 16 位寄存器)。

注释中的相关描述如下 ⬇️

text 复制代码
/**
 * 16-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) = int(t), else out(t+1) = out(t)
 */
  • 输入是
    • i n 16 in16 in16
    • l o a d load load
  • 输出是
    • o u t 16 out16 out16

我们在上一步里已经实现了 Bit(一位寄存器),既然 Register 是 16 16 16 位寄存器,那么可以把 Register 当作 16 16 16 个一位寄存器的组合。

代码的大意如下 ⬇️

hdl 复制代码
CHIP Register {
    IN in[16], load;
    OUT out[16];

    PARTS:
    Bit(in= in[0], load= load, out= out[0]);
    Bit(in= in[1], load= load, out= out[1]);
    ...
    Bit(in= in[15], load= load, out= out[15]);
}

由于这里有很多行类似的代码,自己复制粘贴加修改感觉有点浪费时间,不如用 java 代码来生成它吧。 请将以下代码保存为 RegisterHdlCodeGenerator.java。

java 复制代码
import java.util.StringJoiner;

public class RegisterHdlCodeGenerator {

    public static void main(String[] args) {
        String template = """
                CHIP Register {
                    IN in[16], load;
                    OUT out[16];
                
                    PARTS:
                %s
                }
                """;

        StringJoiner joiner = new StringJoiner(System.lineSeparator());
        for (int i = 0; i < 16; i++) {
            String line = String.format("    Bit(in= in[%s], load= load, out= out[%s]);", i, i);
            joiner.add(line);
        }
        System.out.printf(template, joiner);
    }
}

使用如下的命令可以编译 RegisterHdlCodeGenerator.java 并运行其中的 main 方法。

bash 复制代码
javac RegisterHdlCodeGenerator.java
java RegisterHdlCodeGenerator

运行结果如下

text 复制代码
CHIP Register {
    IN in[16], load;
    OUT out[16];

    PARTS:
    Bit(in= in[0], load= load, out= out[0]);
    Bit(in= in[1], load= load, out= out[1]);
    Bit(in= in[2], load= load, out= out[2]);
    Bit(in= in[3], load= load, out= out[3]);
    Bit(in= in[4], load= load, out= out[4]);
    Bit(in= in[5], load= load, out= out[5]);
    Bit(in= in[6], load= load, out= out[6]);
    Bit(in= in[7], load= load, out= out[7]);
    Bit(in= in[8], load= load, out= out[8]);
    Bit(in= in[9], load= load, out= out[9]);
    Bit(in= in[10], load= load, out= out[10]);
    Bit(in= in[11], load= load, out= out[11]);
    Bit(in= in[12], load= load, out= out[12]);
    Bit(in= in[13], load= load, out= out[13]);
    Bit(in= in[14], load= load, out= out[14]);
    Bit(in= in[15], load= load, out= out[15]);
}

这样的代码可以通过仿真测试,效果如下图所示 ⬇️

3. 实现 RAM8

前往 Nand to Tetris Online IDE,选择 Project 3 里的 RAM8,效果如下图所示 ⬇️

我们的目标是用 Register( 8 8 8 位寄存器) 以及 Project 1/Project 2 里已经实现的各种 chip 实现一个 RAM8(即, 8 8 8 个 16 16 16 位寄存器的存储器)。

注释中的相关描述如下 ⬇️

text 复制代码
/**
 * 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.
 */
  • 输入是
    • i n 16 in16 in16
    • l o a d load load
    • a d d r e s s 3 address3 address3
  • 输出是
    • o u t 16 out16 out16

我们在上一步里已经实现了 Register( 16 16 16 位寄存器)。现在需要选择将 i n 16 in16 in16 输入 8 8 8 个 Register 中的哪一个,可以使用 Project 1 里的 DMux8Way,对 o u t 16 out16 out16 而言,我们可以借助 Project 1 里的 Mux8Way16 从 8 8 8 个 R e g i s t e r Register Register 的输出中进行选择。

代码的大意如下 ⬇️

hdl 复制代码
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= h, out= out7);

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

由于这里有很多行类似的代码,自己复制粘贴加修改感觉有点浪费时间,不如用 java 代码来生成它吧。 请将以下代码保存为 RAM8HdlCodeGenerator.java。

bash 复制代码
javac RAM8HdlCodeGenerator.java
java RAM8HdlCodeGenerator

运行结果如下

hdl 复制代码
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);
}

这样的代码可以通过仿真测试,效果如下图所示 ⬇️

4. 实现 RAM64

前往 Nand to Tetris Online IDE,选择 Project 3 里的 RAM64,效果如下图所示 ⬇️

我们的目标是用 RAM8(即, 8 8 8 个 16 16 16 位寄存器的存储器) 以及 Project 1/Project 2 里已经实现的各种 chip 实现一个 RAM64(即, 64 64 64 个 16 16 16 位寄存器的存储器)。

注释中的相关描述如下 ⬇️

text 复制代码
/**
 * Memory of sixty four 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.
 */
  • 输入是
    • i n 16 in16 in16
    • l o a d load load
    • a d d r e s s 6 address6 address6
  • 输出是
    • o u t 16 out16 out16

我们在上一步里已经实现了 RAM8,可以用类似的思路来实现 RAM64。

参考资料

相关推荐
sarasuki6 天前
工具设计的坏味道:什么样的定义会让模型乱调
人工智能·agent·设计
ndsc_d8 天前
VS Code 接入 Pixso MCP 实战:配置、安装 AI Skill 与设计稿转代码
vscode·ai·设计·pixso·skill·mcp·设计稿转代码
咖啡八杯10 天前
Controller 基类封装:BaseController 的模板方法设计
java·架构·设计
小哈里12 天前
【知识】从学科到实践:自然・工程・社科・人文|算法・研发・产品・品牌设计
程序人生·算法·产品·设计·工程
leeyi13 天前
Token 账单减下来:从路由到裁剪的五道闸门(第109篇)
aigc·agent·设计
水巷石子18 天前
备考系统架构设计师第一天
学习·架构·软考·设计·考试
星期一研究室18 天前
我们开发了一个「杂志封面.skill」
agent·ai编程·设计
海兰19 天前
3.4 如何配色
设计·插画·插画软件·procreate
海兰19 天前
7.0Procreate 插画进阶
设计·插画·插画软件·procreate
海兰19 天前
4.0完成一幅完整插画
设计·插画·插画软件·procreate