Nand2Tetris(计算机系统要素) Unit 1 学习笔记 - 从与非门到基础芯片设计

起初是在CS自学指南里找到的这门课(依据基本原理构建现代计算机:从与非门到俄罗斯方块),感觉还不错而且零基础,是一个比较好的入门课程。同时全英教学可以逆向让我捉襟见肘的英语水平稍微强一点,多理解一个单词就是胜利(

课程链接:https://www.coursera.org/learn/build-a-computer

Unit 1 应该都是关于电路的,爽了,正好学到电路这门课(

Unit 1.1 Boolean Logic(布尔逻辑)

这节课主要讲述逻辑

二进制听过无数遍了,但是一直没有对这个进行思考。为什么是二进制?或者说计算机是怎么通过01来实现计算然后一点一点设计电路组成芯片从而组合成一台真正的计算机的。

一直以来对计算机的理解就是存在即合理,从来没有细想过里面的逻辑

image-20251016205405027

这就是一个比较简单的AND操作从而可以实现逻辑 只有两个输入值都为1时 AND操作的输出值才为1

可以很详细的看出01的不同可能性(穷举操作),把01组合起来并回显结果的表格叫做真值表

image-20251016205808283

这个是OR操作从而可以实现逻辑 只要有一个值为真 结果也就为真

image-20251016210351031

最后的是NOT)运算,仅有一个运算操作数,所以是一元逻辑运算。上方三种运算是最基本的逻辑运算。

image-20251016211113450

上面的基本都是运算方法,以下为计算例子,就算是布尔计算也要遵守括号内先去计算,其余的和数学基本一致。

image-20251016211342885

定义一个函数 函数内有三个变量x y z可以用以下公式来描写函数

这里应该是国外的写法,我学数电的时候似乎是用的数学运算符来表达

image-20251016211708440

我们穷举所有的变量,得到一个真值表如下

image-20251016211828606

然后将值代入函数进行运算,得到完整的真值表

image-20251016211900972

普通数学有交换律,布尔计算也有交换律 如下

image-20251016215659187

同样也适用于结合律

image-20251016215758165

当然,分配律也是一样的

image-20251016215853766

除去数学之外,有一个叫做德摩根定理的东西,公式如下

这个还没有学过,但是可以用真值表进行证明,稍后应该会有详细的理解?

image-20251016220233964

给了一个化简的实例,使用定理进行化简

学到这里的时候其实电路学到化简了可惜没学会(至少没有特别融会贯通

image-20251021203527325

Unit 1.2 Boolean Function Synthesis(布尔函数合成)

1
2
构造流程:真值表 → 析取范式 → 逻辑简化 → 门电路实现
理论递进:AND/OR/NOT → AND/NOT → 仅NAND

课程开始回顾了上节课内容 从布尔表达式生成真值表的方法:

穷举x,y,z的可能性 得到如下表

image-20251029201947078

但工程实践中更常见的是反向需求:已知电路功能(真值表),如何用基本门电路来实现?

实际情况中我们知道想让部件做什么,但不知道如何用门电路来表达,这种转换能力就显得尤为重要

image-20251029204337297

将所有真值表输出值为1的标记一下 输入值为1取对应变量写入,输入值为0取变量的非(NOT)将变量之间使用AND(*)进行连接,将真值表输出值为1的每行表达式使用OR(+)连接可得到函数表达式

输入1→保留变量,输入0→取非

image-20251030103722606

任何布尔函数都可用AND、OR、NOT三种基本运算实现

上一节课讲的摩根定理在这里可以直接运用,已知 ANDORNOT 可构造任意布尔函数,使用使用定理就可以仅用 AND 和 NOT 即可实现所有函数

image-20251030110704337

还有一个NAND门运算 是由与非门(AND 和 NOT)组成的

image-20251030105740086

从而延伸出仅用 NAND 门可实现所有布尔函数,计算机硬件设计仅需一种基础门(NAND)即可构建所有复杂逻辑电路。

如下图可以用NAND表示NOT)和AND

image-20251030111916062

Unit 1.3 Logic Gates(逻辑门)

逻辑门就是实现布尔运算的组件,是一种使用硬件实现布尔函数的技术。它是一种独立的、简单的芯片,旨在提供明确定义的基本功能。

基本逻辑门

逻辑门就是实现布尔运算的组件 例如Nand**(与非)**门

image-20251102211628937

这个是Nand门的描述

如果输入都是1时输出0,其它情况均为1

image-20251102211725099

真值表如下

a b out(输出)
0 0 1
0 1 1
1 0 1
1 1 0

当然还有三个经典的门电路:

  • And(与)门:当它的两个输入都是1时输出1,在其他情况下输出0。

image-20251102212119580

  • Or(或)门:当其中一个输入为1时输出1,在其他情况下输出0。

image-20251102212138009

  • Not(非)门:它可以用作转换器,输出与输入相反。

image-20251102212157554

复合逻辑门与接口

复合逻辑门是由基本逻辑门和其他复合逻辑门组成的门

这里以三输入And门为例:

  • 接口:这是门的抽象描述,定义了用户能看到和使用的部分——三个输入和一个输出。它回答了门“做什么”的问题。

image-20251102212419731

  • 实现:这是门“怎么做”的内部构造。我们可以通过组合两个双输入And门来实现一个三输入And门的功能。

image-20251102212445663

接口与实现的概念

  • 一个门的接口是唯一的,只有一种方式描述其功能。
  • 但一个门的实现可以有多种(可能更节能、更便宜等),即“一个抽象规范,多种不同实现”。

硬件实现的视角

物理电路实现(电气工程视角)

举个例子 这是And(门)电路(物理实现)的样式

只有当两个开关都闭合(输入为1)时,灯泡才会亮起(输出为1)。

image-20251102212855448

这个电路就是一个复合的And(与)门电路(物理实现)

image-20251102213144463

逻辑门组合实现(计算机视角)

但是实际上我们不用考虑物理实现(那个应该归电气管),只需要学会对应的计算机实现即可。我们关心的是如何从最基本的Nand门开始,通过逻辑组合来构建更复杂的功能,例如下面的三输入And门。

image-20251102213308033

Unit 1.4 Hardware Description Language(硬件描述语言)

这期主要是如何使用HDL,来构建逻辑门进行模拟以及测试。

HDL是一种声明性语言,没有程序执行。只是对门电路的一种描述方式。

假如说给定一个异或芯片

image-20251110210942925

使用这些信息就可以写一个HDL文件 如下效果

使用CHIP关键字定义芯片名称 然后自己设置IN(INPUT 输入)名称以及OUT(OUTPUT 输出)名称即可

image-20251110211012460

可以看一下Xor门的逻辑 只有两种情况下输出相等而为1 这一种逻辑不同于真值表

就是A和B非以及B和A非的时刻相同

image-20251111210405119

对于普通用户来讲 只需要知道这是有两个输入 一个输出的接口就可以(不需要理解虚线中的运算逻辑),只需要知道这个接口可以用,就像我现在可以轻松的使用电脑利用Markdown编辑器直接编写文字但具体不知道它是如何和操作系统进行交互以及如何打印的屏幕上的一样,只知道这个接口提供了Xor的功能。

在HDL设计中,芯片接口是唯一的,由芯片规范确定,而实现方式可以多样化。

image-20251111210839585

引出可以深入研究内部架构 逻辑图刚学可能比较难懂 没办法一眼看出来内部线是怎么走的 这时就需要给每个步骤起名 如下的效果 将所有逻辑运算组合起来就是Xor的表达式辣

HDL具有”无限扇出”特性,任何信号都可以根据需要分发到任意数量的目的地,所有连接都是同时进行的

image-20251111211750037

现在就可以用HDL进行实现啦 可以通过这样的写法来描述IO接口的调用 定义为一个两入一出的芯片

image-20251111212124800

刚刚只是定义了接口 但是没有具体定义接口中是如何进行逻辑运算的

首先的输入是a 输出的结果是a非

然后又描述了两个And(与)门 定义了a与b的输入(Input)以及输出(Output)

说实话有点像一种变量的映射,有点像一层一层的变量调用对应的函数(这里应该是芯片)方法

image-20251111213021468

最后描述一个 Or(或)门,把之前的输出作为输出再进行下一步逻辑运算 HDL就算写完辣

这个HDL文件就是对芯片的一个结构描述方法和真值表和表达式以及逻辑图应该是一样的

其中应该最重要的就是命名了,有点像设置变量名的感觉(也有可能是变量名像这个

image-20251111213134104

HDL上方的为定义接口(Interface) 下方的为具体实现(Implementation)

一遍情况下,一种相同的芯片,例如Xor。

接口都是相同的,但是具体的实现(逻辑)是可以不同的,可以用别的门电路组合成相同的效果。

HDL语句顺序无关紧要,但习惯从左到右描述以提高可读性。

image-20251111213432880

不同的实现方案在成本、功耗、门数量等方面存在差异,这是硬件优化的重要考虑因素

Unit 1.5 Hardware Simulation(硬件模拟)

在上一单元中,我们学习了如何使用 HDL 实现门逻辑。但是,我们的所作所为并不能保证我们编写的 HDL 代码确实是正确的。我们不知道我们组装的这种架构能否实现我们想要设计的芯片的预期结果。

因此,在本单元中,如何使用 HDL程序,并尽我们所能验证该程序或 HDL文件是否提供了底层芯片的预期功能这件事就成为了主要学习的方向。

假如说有一个特定的HDL文件需要进行测试

image-20251112234409545

可以把HDL加载到硬件模拟器的程序中 来进行测试

硬件模拟器是验证HDL代码正确性的关键工具

image-20251112234540523

硬件模拟器提供了两种主要的测试模式:交互式仿真和基于脚本的仿真

可以通过加载HDL到程序实现调试测试等,可以在这个芯片中交互式的进行各种操作,这种交互方式称为交互式模拟

除了交互式测试,还可以编写测试脚本文件,使用专门设计的测试语言来定义一组预定的、可重复的测试。这种方式称为基于脚本的模拟

测试脚本实现了测试的自动化和可重复性

在模拟过程中,可以将仿真结果记录到输出文件中,甚至可以将仿真结果与存储在比较文件中的期望输出进行对比验证。

比较文件机制实现了自动化验证,大大提高了测试效率

硬件模拟器界面详解

课程中使用的是老版本的模拟器,这里演示的是最新的官方文档提供的Web界面模拟器(效果是一样的

最左侧显示加载的HDL代码

中间为允许交互式设置输入引脚的值(0或1)

最右侧是脚本区域(课程中没有详细说新版本的 我猜测有比对功能来判断是否编写正确

image-20251113111935945

编写了一个Xor的HDL文件

那个Web端确实好用 居然还有自动补全

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// This file is part of www.nand2tetris.org
// and the book "The Elements of Computing Systems"
// by Nisan and Schocken, MIT Press.
// File name: projects/1/Xor.hdl
/**
* Exclusive-or gate:
* if ((a and Not(b)) or (Not(a) and b)) out = 1, else out = 0
*/
CHIP Xor {
IN a, b;
OUT out;

PARTS:
Not(in= a, out= nota);
Not(in= b, out= notb);
And(a= a, b= notb, out= w1);
And(a= nota, b= b, out= w2);
Or(a= w1, b= w2, out= out);
}

使用交互式模拟可以很轻松的 看到输入输出结果

image-20251113113357413

看起来功能完全正常

image-20251113113426077

但是交互式模拟也有缺点 虽然非常的直观而且方便调试 对于Xor芯片来讲 真值表无非就四行 尝试四次就能出结果了

a b out(输出)
0 0 0
0 1 1
1 0 1
1 1 0

但假如说真值表有几十行甚至几百行,手动测试自然效率低下了,这时候就引出测试脚本的概念了。这是Xor的测试脚本示例

image-20251113201940935

点击Run即可跑这个脚本

image-20251113201755981

Output中就能看到输出的真值表啦 能看出来回显的内容和我们的需求一致 HDL写的没问题~

image-20251113201821543

测试脚本的核心价值在于可重复性和自动化。当芯片设计需要反复修改时,只需重新运行测试脚本即可快速验证所有功能,无需手动重复测试

在实际项目中,测试脚本通常与比较文件(.cmp)配合使用,自动化验证输出结果是否符合预期,大大提升开发效率和代码可靠性

比较文件的作用

比较文件包含了芯片的预期输出结果,当测试脚本运行时,硬件模拟器会自动将实际输出与比较文件中的预期值进行对比,如果发现不一致就会标记错误,从而实现自动化的功能验证。

image-20251113211702369

Unit 1.6 Multi-bit Buses(多位总线)

本节课主要研究如何处理一堆数据,或者说如何同时操作多个位。通过引入“总线”的概念,我们可以在更高的抽象层次上设计和思考数字电路。

什么是总线?

总线(Bus) 是一组被共同操作的硬件信号线的集合,在数字电路设计中,它让多个位(bits)能够作为一个统一的实体来传输和处理。

可以把总线想象成一条”数据高速公路”,多个位像车辆一样在这条公路上并行传输,而不是每个位都走自己独立的小路

物理层面:总线实际上还是一组独立的电线
逻辑层面:我们把这些电线看作一个完整的数据单元

例如:

  • 4位总线:4根电线,可表示0-15的数值
  • 16位总线:16根电线,可表示0-65535的数值
  • 32位总线:32根电线,可表示0-4294967295的数值

为什么要用总线?

硬件设计时经常需要同时操作一组相关的位。不是单独处理每个位,而是把一组位当作一个完整实体来操作。

例如:设置IP地址

没有总线时

  • 就像要手动设置 192.168.1.1 的每个二进制位
  • 11000000.10101000.00000001.00000001
  • 需要逐个设置32个开关,极其繁琐

使用总线时

  • 直接说:IP = 192.168.1.1
  • 一次性完成所有32个位的设置
  • 把32个独立操作变成1个整体操作

就像把32个位看作一个IP地址,而不是32根独立的电线。

总线的基本概念

总线在HDL中用数组语法表示:

image-20251116215603661

1
2
3
4
CHIP Add16 {
IN a[16], b[16]; // 两条16位输入总线
OUT out[16]; // 16位输出总线
}

操作总线:整体与部分

1. 将总线作为整体操作

三数相加

1
2
3
4
5
6
7
8
CHIP Add3Numbers {
IN first[16], second[16], third[16];
OUT out[16];

PARTS:
Add16(a=first, b=second, out=temp);
Add16(a=temp, b=third, out=out);
}

2. 访问总线中的单个位

有时需要访问和操作总线中的特定位。

4位与门(And4Way)

1
2
3
4
5
6
7
8
9
CHIP And4Way {
IN in[4];
OUT out;

PARTS:
And(a=in[0], b=in[1], out=t1);
And(a=t1, b=in[2], out=t2);
And(a=t2, b=in[3], out=out);
}

索引约定:在HDL中,总线的索引从0开始:

  • 4位总线:in[0], in[1], in[2], in[3]

3. 并行位操作(按位操作)

4位按位与门

1
2
3
4
5
6
7
8
9
10
CHIP BitwiseAnd4 {
IN a[4], b[4];
OUT out[4];

PARTS:
And(a=a[0], b=b[0], out=out[0]);
And(a=a[1], b=b[1], out=out[1]);
And(a=a[2], b=b[2], out=out[2]);
And(a=a[3], b=b[3], out=out[3]);
}

Unit 1.7 Project 1 Overview(项目 1 概述)

一开始给了一个这样的图(其实就是想说只需要Nand门就能手搓计算机

image-20251118213844612

仅仅用Nand 这节课就要实现以下这么多的门

很多都不认识(

image-20251118214002644

为什么要选这十五个门?

这些门(如 Not、And、Or、Mux、DMux 等)是数字逻辑设计中的基本构建块,被广泛应用于几乎所有数字设备中。它们是经过验证的、最常用且最基础的功能单元。

补充要点: 选择这15个门有两个核心原因:1. 实用性:它们被广泛用于几乎所有数字设备中。2. 基础性:它们包含了构建本课程后续计算机(Hack计算机)所需的所有基本逻辑门,为后续项目奠定基础。

十五个门电路的分类

image-20251119213010699

多路复用器(Multiplexor)

比一般的输入输出电路 多一个sel(可以理解为数字选择器叭) 来选择信号

按照我上的数电课理解就是 假如有一个四选一电路 有两个选择信号 也可以称为四加二入一出电路

而且我猜测 这个多路复用器 也可以应用在片选信号上 但是只是数电课学了然后感觉可能会用到

补充要点: 多路复用器(Mux)的功能是根据选择位(sel)的值,从两个输入(a和b)中选择一个进行输出。其理想行为是:if sel=0 then out=a else out=b。这是一种在数字设计和通信网络中非常基础且重要的数据选择操作。

image-20251120231255107

AndMuxOr

这是一个AndMuxOr门 一个比较简单的使用Mux的电路

效果就是假如sel信号为0 则为与门 如果不是 则是或门

image-20251120232109152

这个是门电路的真值表 和刚刚描述的一样

补充要点: 这个例子展示了多路复用器如何用于构建“可编程门”。通过一个选择位,可以动态地改变门的功能(如此处在And和Or之间切换)。实现方法是将输入同时馈送给And和Or门,然后用Mux根据选择位决定输出哪一个门的结果,这个过程也涉及了“扇出”(Fanout)概念。

image-20251120232227641

这个是逻辑图

我数电好像还没有学到这个Mux门(或许见过但是没有深入学

image-20251120232327251

关于这个门电路的HDL文件是这个样子的

其实主要是调用Mux模块 其他的没什么不同

image-20251120232448553

多路分配器(DeMultiplexor)

多路分配器就感觉像是刚刚的逆运算吧

image-20251120232822875

一个是选择 另一个就相当于分发吧 真的很像逆运算

image-20251120233205462

这里还举了一个多路复用器和多路分配器在网络通信中的例子 规定了只有一条线路的情况下 如何才能同时传输两个信号 可以用这两个元器件 进行有点像编解码的功能 实现数据的传递

补充要点: 在网络通信中,Mux和DMux结合使用可以实现“时分复用”。在发送端,Mux快速交替地选择不同来源的数据位。在接收端,DMux根据同步的选择信号,将数据流分离并分发到正确的目的地。这个方案可以是异步的,非常高效。

image-20251120233629515

新的门电路介绍

And16

And16长这个样子 看起来它的输出就是值同时为1时输出1 其余为0 看起来好像挺简单的 两入一出的电路

补充要点: And16是一个16位变体的门电路。它同时对两个16位输入总线的每一位执行按位与操作。这是一个并行操作,所有16位的计算是同时(在硬件中并行)完成的,而不是顺序执行。实现上可以通过16个独立的And门组合而成。

image-20251120234002430

Mux4Way16

补充要点: Mux4Way16是一个多路、16位变体的复杂例子。它有四个16位输入,需要2位选择信号(因为2^2=4)来选择其中之一输出。这类芯片可以通过组合我们已经构建好的更简单的芯片(如多个Mux16)来构建。

一下子就上强度了 将Mux和带总线的一结合 变成了以下的效果

课程中就是一带而过 更多的就是认识一下

image-20251120234340805

如何制作这些门电路呢?

以 Xor 为例子 会提供这个样子的图

image-20251120234627409

然后还会提供HDL的基本接口文件 以及脚本文件 以及对比文件(其实就相当于真值表啦)

就相当于给了真值表答案 就看自己能不能设计出来这样的电路辣

image-20251120234736417

Unit 1.8 Persectives(视角)

是一个Q&A的课 这节课的翻译不是特别好使 文档AI使用就多一点点(

是否可以从其他逻辑门开始构建计算机?

核心观点: 是的,完全可能从 NAND 以外的逻辑门开始构建计算机。

具体说明:

  • 可以使用 NOR 门(非或门)作为原子构建块来构建整个计算机系统
  • 也可以从 AND、OR、NOT 门组成的套件开始构建系统

现实应用:

  • NAND 门在硬件系统的物理实现中非常流行
  • 在许多集成电路技术中,构建 NAND 门的成本相对较低

如果真的要建一个Nand门该怎么去做?

答: 这涉及到物理层面的实现,本课程通常不深入讨论,但可以一个简单的实现方案为例(基于NMOS技术):

image-20251121001321864

  1. 基本元件:你需要晶体管电阻电压源。设定正电压代表逻辑“1”,负电压(或地)代表逻辑“0”。
  2. 电路连接
    • 将一个电阻连接到正电压源。
    • 将两个NMOS晶体管串联起来(一个接在另一个的后面),然后连接到负电压/地。
    • 将电阻和晶体管串联电路的连接点作为输出端。
    • 两个晶体管的栅极分别作为输入A和输入B。
  3. 工作原理
    • 当 A 和 B 都是 “1” (高压):两个晶体管都导通,形成一条从输出端到负电压(逻辑“0”)的低电阻通路。此时,无论连接到正电压的电阻多么“努力”,输出都会被“拉低”到“0”。这正是NAND门的功能:输入全1,输出为0。
    • 当 A 或 B 是 “0” (低压):至少有一个晶体管会关闭,切断输出端到负电压的通路。此时,输出端通过电阻被“拉高”到正电压,输出为“1”。

核心要点:这个例子揭示了硬件抽象之下的物理现实。但在本课程中,我们更关心的是NAND门做什么(它的逻辑功能),而不是它具体如何实现。我们将其视为一个完美的、无需深究的黑盒,并基于此来构建整个计算机系统。

原来黑盒的概念这里就有了 我一直以为是网安的词汇

课程中使用的 HDL 和工业级的 HDL(如 Verilog/VHDL)有何异同?

  • 相同点:它们都是用于描述和模拟数字硬件的真正的硬件描述语言
  • 不同点:工业级语言(如 Verilog、VHDL)功能更复杂、强大。它们的语法混合了 HDL 和 C 语言特性,包含高级编程结构(如循环),并且能够对时序、时钟等概念进行建模,这对于构建内存等时序逻辑至关重要。掌握它们通常需要数周甚至数月。
  • 课程选择:为了让大家能快速上手并专注于计算机架构的核心思想,我们提供的是一个简化版的 HDL。它包含了构建本课程计算机所需的全部功能,但设计得非常简洁,可以在大约一小时内学会

如何设计包含数百个元件的复杂芯片?

答: 设计复杂芯片没有放之四海而皆准的简单方法,它是一项需要创造力的挑战。工程师会使用各种工具和方法:

  1. 优化技巧:如卡诺图,用于优化门级设计。
  2. 自动化工具:如“硅编译器”,你可以指定所需功能,由工具内部的算法自动进行逻辑优化和布局。

Programming Assignment: Project 1(编程作业: 项目 1)

1
2
3
4
5
6
7
您在本项目中的工作成果将是一组 15 个文本文件,其中包含您要构建的 15 个芯片的 HDL 代码。如果您能成功构建并测试所有芯片,那就太好了。如果您只完成了部分芯片,您仍然可以提交它们以获得部分学分。

为了确保正确的测试和反馈,您提交的 HDL 文件名必须与我们提供的骨架 .hdl 文件名完全一致。请注意,大小写很重要:名为 and.hdl 或 AND.hdl 的文件可能无法通过我们的测试和评分程序(正确的名称必须是 And.hdl)。您只需在我们提供的 *.hdl 文件中编写 HDL 代码,并使用给定的文件名提交这些文件进行评分,即可省去这些麻烦。

准备提交时,将您编写的所有 *.hdl 文件打包成一个名为 project1.zip 的压缩文件(打包文件本身,不要放在任何子文件夹中),然后提交。您提交的次数不限,成绩将是您所有提交成绩的最大值,因此您不会因为再次提交而丢分。

如果您以审核员身份参加课程,您可以使用硬件模拟器中的测试脚本自行检查您的工作。如果您选择证书选项,请在此提交您的项目压缩文件。

要做这 15个芯片的 HDL 代码

由于 Nand 被认为是基本门,无需实现它。

image-20251121183354738

Not

Not门长这个样子

image-20251121224831461

看见真值表为

效果为输入然后取反

image-20251121225442542

我起初就是直接想的直接设计吧!然后发现没办法用自己来设计自己 因为Not本来就是一个芯片 是我要设计出来的芯片

image-20251121230046598

其实自己是陷入逻辑误区了 我以为这个和编程的角度是差不多的 把这个当作函数了 但其实按照课程来讲应该用Nand来进行基础门电路的设计

image-20251121230534070

Nand的真值表

将Nand当作一个基础芯片来看就好啦

a b out(输出)
0 0 1
0 1 1
1 0 1
1 1 0

如果不直接去搜索答案的话 使用这个构建Not的最大问题是 如何将二入一出的芯片 转化为一入一出的 我来观察真值表 发现当输入都为0时 输出为1 输出都为1时 输出为0 这不就恰好符合Not的效果!!!

所以我们将a与b都定义为in out定义为out(可能表述的不准确) 效果如下

1
2
3
4
5
6
7
CHIP Not {
IN in;
OUT out;

PARTS:
Nand(a= in, b= in, out= out);
}

测试出来效果正确

image-20251122104112862

使用脚本来跑真值表也对啦 Not攻克成功!

image-20251122104140894

And

And电路长这个样子

image-20251122111053952

看一下And的真值表

image-20251122111806186

Nand的真值表

将Nand当作一个基础芯片来看就好啦

a b out(输出)
0 0 1
0 1 1
1 0 1
1 1 0

这个其实很明显辣 就是每一个进行取反 一开始先使用一个Nand门 然后再使用一个非门(Nand实现 刚刚实现的那个) 就好啦

这里也可以直接使用Not门 以为刚刚已经设计出来了

1
2
3
4
5
6
7
8
CHIP And {
IN a, b;
OUT out;

PARTS:
Nand(a= a, b= b, out= nandout);
Nand(a= nandout, b= nandout, out= out);
}

测试正常

image-20251122112732912

然后来看一下最后跑出来的真值表 芯片设计完成!

image-20251122112834033

Or

Or芯片的样子如下

image-20251122113131521

Or的真值表如下

image-20251122113313161

Nand的真值表

将Nand当作一个基础芯片来看就好啦

a b out(输出)
0 0 1
0 1 1
1 0 1
1 1 0

这个其实有难度了 因为没办法一眼设计出来了 应该就要用到之前设计的Not以及And芯片啦

这个真的给我卡住了 一瞬间不知道怎么设计这个最基础的芯片了

然后我问AI找了思路 AI让我去看一下摩根定律(没想到这个东西还能在这用

image-20251124151749849

image-20251124151758490

用第二条可以秒出结果啦 一下子就简单起来了

这个我是真的想不出来 我只能想出来用非和与门去构建 想不到具体的角度

1
2
3
4
5
6
7
8
9
10
CHIP Or {
IN a, b;
OUT out;

PARTS:
Not(in= a, out= nota);
Not(in= b, out= notb);
And(a= nota, b= notb, out= notanotb);
Not(in= notanotb, out= out);
}

成功啦

image-20251124154842525

Xor

  • X 代表 “Exclusive” 的缩写。“Exclusive” 的意思是 “排他的”XOR 的全意是 “排他性或”,中文精准地翻译为 “异或”(相异则为真)。简单来说,这个 X 就指明了它与普通 OR 最关键的区别:它排除了两者同时为真的可能性

Xor芯片的样子和真值表如下

image-20251124195542345

我们已经成功设计出来了三个最基本的门电路 然后根据这个可以进行拓展啦 Xor明显就是当a和b不同时为1

1
2
3
4
5
6
7
8
9
10
11
CHIP Xor {
IN a, b;
OUT out;

PARTS:
Not(in= a, out= nota);
Not(in= b, out= notb);
And(a= a, b= notb, out= anotb);
And(a= nota, b= b, out= notab);
Or(a= anotb, b= notab, out= out);
}

非常完美 这几个都比较简单

image-20251124195856261

Mux

Mux芯片长这样 这个也叫多路复用器 有一个控制信号

image-20251124200134490

逻辑如下 如果sel等于0 则输出a 如果sel等于1 则输出b

image-20251124200320110

真值表如下

image-20251124200426040

这实际上是一个”条件选择”的逻辑。想想看:

  • sel = 0 时,我们想要 a “通过”,b “阻断”
  • sel = 1 时,我们想要 b “通过”,a “阻断”

一个与门的特性:当一个输入为0时,输出总是0;当一个输入为1时,输出等于另一个输入

那就可以使用Not门来搞出来sel的方向信号 然后使用与门特性来创造芯片!!

1
Not(in= sel, out= Notsel);

然后题目说 如果sel等于0 则输出a 如果sel等于1 则输出b 将a与Notsel相与 b和sel相与 最后相或就好啦

1
2
3
4
5
6
7
8
9
10
CHIP Mux {
IN a, b, sel;
OUT out;

PARTS:
Not(in= sel, out= Notsel);
And(a= b, b= sel, out= bsel);
And(a= a, b= Notsel, out= notasel);
Or(a= bsel, b= notasel, out= out);
}

测试成功!!!

image-20251125212951806

DMux

DMux 多路分配器(DeMultiplexor)长这个样子

image-20251125213112339

这个是它的描述和真值表 有两个输入端 假如sel=0 输出a=in 反之假如sel=1 输出b=in 这是第一个设计的双输出的门电路

image-20251125213150769

image-20251125213156778

一开始的逻辑就是使用Not来区分sel和Notsel

1
Not(in= sel, out= Notsel);

然后重点sel=0 输出a=in 反之假如sel=1 输出b=in

其中的重点还是与门的特性 当一个输入为0时,输出总是0;当一个输入为1时,输出等于另一个输入

这确实和数电根据真值表直接设计然后化简的想法不太一样 更多是从电路逻辑上设计然后验证的 居然没有通过真值表就能设计出这样的电路还是挺神奇的 看起来挺简洁的

1
2
3
4
5
6
7
8
9
CHIP DMux {
IN in, sel;
OUT a, b;

PARTS:
Not(in= sel, out= Notsel);
And(a= Notsel, b= in, out= a);
And(a= sel, b= in, out= b);
}

验证结果

image-20251125215204523

Not16

Not16芯片的逻辑如下

输入: in[16] // 一个16位引脚
输出: out[16]
功能: 对于 i=0..15,out[i] = Not(in[i])。

image-20251125215354078

效果很不一样了 现在就像是变成了一条数据 而不是单独的bit

image-20251125215704655

初步验证设想是 使用16个Not门 明显是可以的 使用两个Not门进行验证出来了

image-20251125215938022

然后其实失败了 我问AI也没有什么好的方法 HDL并不支持for循环 最后使用十六个非门做出来的、

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
CHIP Not16 {
IN in[16];
OUT out[16];

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

这样的百分百也可以验证通过啦

image-20251125220535588

And16

And16的芯片逻辑如下

image-20251125220720551

这个其实我也没有什么好的方法了(问遍了AI And门只能一个输入一个输出属实想不到了) 十六个芯片直接实现叭

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
CHIP And16 {
IN a[16], b[16];
OUT out[16];

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

测试成功

image-20251125220942365

Or16

Or16芯片的逻辑如下

image-20251125221025860

还是和前面两位一样的实现方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
CHIP Or16 {
IN a[16], b[16];
OUT out[16];

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

测试成功

image-20251125221142275

Mux16

Mux16芯片逻辑如下

image-20251125221213612

除了多一个控制芯片之外 依旧一样的设计思路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
CHIP Mux16 {
IN a[16], b[16], sel;
OUT out[16];

PARTS:
Mux(a=a[0], b=b[0], sel=sel, out=out[0]);
Mux(a=a[1], b=b[1], sel=sel, out=out[1]);
Mux(a=a[2], b=b[2], sel=sel, out=out[2]);
Mux(a=a[3], b=b[3], sel=sel, out=out[3]);
Mux(a=a[4], b=b[4], sel=sel, out=out[4]);
Mux(a=a[5], b=b[5], sel=sel, out=out[5]);
Mux(a=a[6], b=b[6], sel=sel, out=out[6]);
Mux(a=a[7], b=b[7], sel=sel, out=out[7]);
Mux(a=a[8], b=b[8], sel=sel, out=out[8]);
Mux(a=a[9], b=b[9], sel=sel, out=out[9]);
Mux(a=a[10], b=b[10], sel=sel, out=out[10]);
Mux(a=a[11], b=b[11], sel=sel, out=out[11]);
Mux(a=a[12], b=b[12], sel=sel, out=out[12]);
Mux(a=a[13], b=b[13], sel=sel, out=out[13]);
Mux(a=a[14], b=b[14], sel=sel, out=out[14]);
Mux(a=a[15], b=b[15], sel=sel, out=out[15]);
}

测试成功

image-20251125221337369

Or8Way

Or8Way芯片逻辑如下

输入: in[8]
输出: out

功能: out = Or(in[0], in[1], …, in[7])

image-20251125221400959

其实也比较简单 含义就是输出八个门 结果是八个门相互或

也就是四个一组Or门 然后输出四个新的信号 之后再使用两个Or门 最后时候一个Or门即可

让AI生成了一个比较易懂的图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
层级 0 (输入层):
in[0] in[1] in[2] in[3] in[4] in[5] in[6] in[7]
| | | | | | | |
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
层级 1 (第一层Or):
Or Or Or Or
(0,1) (2,3) (4,5) (6,7)
| | | |
o01 o23 o45 o67
| | | |
↓ ↓ ↓ ↓
层级 2 (第二层Or):
Or Or
(o01,o23) (o45,o67)
| |
o0123 o4567
| |
↓ ↓
层级 3 (第三层Or):
Or
(o0123,o4567)
|
out

然后写hdl文件就好啦 就是Or递归吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
CHIP Or8Way {
IN in[8];
OUT out;

PARTS:
Or(a=in[0], b=in[1], out=o01);
Or(a=in[2], b=in[3], out=o23);
Or(a=in[4], b=in[5], out=o45);
Or(a=in[6], b=in[7], out=o67);

Or(a=o01, b=o23, out=o0123);
Or(a=o45, b=o67, out=o4567);

Or(a=o0123, b=o4567, out=out);
}

验证结果 成功!

image-20251125222123022

Mux4Way16

Mux4Way16芯片逻辑如下 这一次上强度了

输入: a[16], b[16], c[16], d[16], sel[2]
输出: out[16]
功能: 如果 sel=00 则 out=a,否则如果 sel=01 则 out=b,否则如果 sel=10 则 out=c,否则如果 sel=11 则 out=d
注释: 上述赋值操作都是16位的。例如,”out=a” 表示 “对于 i=0..15,out[i]=a[i]”。

image-20251125222228524

其实看到这个描述 感觉就和数电里面的那个74LS153特别像

Mux4Way16芯片长这个样子

image-20251125222728132

起初我的想法是一个一个实现 就如同下面的样子

1
2
3
4
5
6
7
8
9
10
11
12
13
CHIP Mux4Way16 {
IN a[16], b[16], c[16], d[16], sel[2];
OUT out[16];

PARTS:
Not(in= sel[0], out= notsel0);
Not(in= sel[1], out= notsel1);

And(a= notsel0, b= notsel1, out= a0);
And(a= sel[0], b= notsel1, out= b0);
And(a= sel[1], b= notsel0, out= c0);
And(a= sel[1], b= sel[0], out= d0);
}

然后我突然发现我设计过Mux16 就不用这样再造轮子了 可以用现有的工具直接写啦 但是这里有错误的点

1
2
3
4
5
6
7
8
9
10
CHIP Mux4Way16 {
IN a[16], b[16], c[16], d[16], sel[2];
OUT out[16];

PARTS:
Mux16(a= a, b= b, sel= sel[0], out= ab);
Mux16(a= c, b= d, sel= sel[1], out= cd);

Mux16(a= ab, b= cd, sel= sel[0], out= out);
}

真正正确的思路表是这个样子的

AI帮忙生成的

1
2
3
4
5
6
7
8
9
            SEL[1]=0 (走上面)    SEL[1]=1 (走下面)
↓ ↓
SEL[0]=0 (走左路) │ SEL[0]=1 (走右路) │ SEL[0]=0 (走左路) │ SEL[0]=1 (走右路)
↓ ↓ ↓ ↓ ↓
🛣️ a路 🛣️ b路 🛣️ c路 🛣️ d路
│ │ │ │
└─Mux16─ab─┘ └─Mux16─cd─┘
│ │
└───Mux16──out────────┘

正确的hdl文件

1
2
3
4
5
6
7
8
9
10
CHIP Mux4Way16 {
IN a[16], b[16], c[16], d[16], sel[2];
OUT out[16];

PARTS:
Mux16(a= a, b= b, sel= sel[0], out= ab);
Mux16(a= c, b= d, sel= sel[0], out= cd);

Mux16(a= ab, b= cd, sel= sel[1], out= out);
}

测试成功啦

image-20251126200212262

Mux8Way16

Mux8Way16芯片的逻辑如下

芯片名称: Mux8Way16
输入: a[16], b[16], c[16], d[16], e[16], f[16], g[16], h[16],
sel[3]
输出: out[16]
功能: 若 sel=000 则 out=a,否则若 sel=001 则 out=b,
若 sel=010 则 out=c … 否则若 sel=111 则 out=h
注释: 上述赋值操作均为 16 位。例如,”out=a” 表示
“对于 i=0..15,out[i]=a[i]”

image-20251126200927906

这个就不用讲啦 和刚刚一模一样 就是多了一层嵌套而已

第一层(处理 sel[0] 最低位):

  • Mux16(a,b,sel[0]) → 输出 ab:选择 a 或 b
  • Mux16(c,d,sel[0]) → 输出 cd:选择 c 或 d
  • Mux16(e,f,sel[0]) → 输出 ef:选择 e 或 f
  • Mux16(g,h,sel[0]) → 输出 gh:选择 g 或 h

第二层(处理 sel[1] 中间位):

  • Mux16(ab,cd,sel[1]) → 输出 abcd:选择 ab 或 cd(即前四个输入)
  • Mux16(ef,gh,sel[1]) → 输出 efgh:选择 ef 或 gh(即后四个输入)

第三层(处理 sel[2] 最高位):

  • Mux16(abcd,efgh,sel[2]) → 输出 out:选择 abcd 或 efgh(最终输出)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
CHIP Mux8Way16 {
IN a[16], b[16], c[16], d[16],
e[16], f[16], g[16], h[16],
sel[3];
OUT out[16];

PARTS:
Mux16(a= a, b= b, sel= sel[0], out= ab);
Mux16(a= c, b= d, sel= sel[0], out= cd);
Mux16(a= e, b= f, sel= sel[0], out= ef);
Mux16(a= g, b= h, sel= sel[0], out= gh);

Mux16(a= ab, b= cd, sel= sel[1], out= abcd);
Mux16(a= ef, b= gh, sel= sel[1], out= efgh);

Mux16(a= abcd, b= efgh, sel= sel[2], out= out);
}

测试成功!

image-20251126205525804

DMux4Way

DMux4Way长这个样子

image-20251126205808008

DMux4Way逻辑如下

芯片名称: DMux4Way
输入: in, sel[2]
输出: a, b, c, d

功能: 如果 sel=00 则 {a=in, b=c=d=0}
否则如果 sel=01 则 {b=in, a=c=d=0}
否则如果 sel=10 则 {c=in, a=b=d=0}
否则如果 sel=11 则 {d=in, a=b=c=0}。

image-20251126205834845

这里主要是复用Dmux门电路 实现这样的功能

sel[1]=0 → 左组(a,b) → sel[0]=0→a, sel[0]=1→b
sel[1]=1 → 右组(c,d) → sel[0]=0→c, sel[0]=1→d

1
2
3
4
5
6
7
8
9
10
CHIP DMux4Way {
IN in, sel[2];
OUT a, b, c, d;

PARTS:
DMux(in=in, sel=sel[1], a=left, b=right);

DMux(in=left, sel=sel[0], a=a, b=b);
DMux(in=right, sel=sel[0], a=c, b=d);
}

来测试一下 测试成功!

image-20251126212643231

DMux8Way

这个芯片的逻辑如下

芯片名称: DMux8Way
输入: in, sel[3]
输出: a, b, c, d, e, f, g, h

功能: 如果 sel=000 则 {a=in, b=c=d=e=f=g=h=0}
否则如果 sel=001 则 {b=in, a=c=d=e=f=g=h=0}
否则如果 sel=010 …
否则如果 sel=111 则 {h=in, a=b=c=d=e=f=g=0}。

image-20251126212738726

和刚刚一样 也只是嵌套一下递归就好啦

1
2
3
4
5
6
7
8
9
10
11
12
13
14
CHIP DMux8Way {
IN in, sel[3];
OUT a, b, c, d, e, f, g, h;

PARTS:
DMux(in= in, sel= sel[2], a= left, b= right);
DMux(in= left, sel= sel[1], a= left1, b= left2);
DMux(in= right, sel= sel[1], a= right1, b= right2);

DMux(in= left1, sel= sel[0], a= a, b= b);
DMux(in= left2, sel= sel[0], a= c, b= d);
DMux(in= right1, sel= sel[0], a= e, b= f);
DMux(in= right2, sel= sel[0], a= g, b= h);
}

image-20251126222607207

测试一下

image-20251126222455948

然后打包提交就好啦

image-20251126222443086