TD4 4位DIY CPU:从组装到编程,带你探索计算机架构原理!

TD4 4位DIY CPU指南

2024年2月12日,有人从速卖通买了一个名为TD4的可爱4位CPU套件。它有2个寄存器、一些LED灯,以及16字节的程序ROM。功能虽有限,但非常酷,还能让人学到很多计算机架构的原理。

这个CPU的文档、原理图和图片都放在指定位置,不过内容有点少,可能会让学生们不知所措,所以写一些更详细的笔记会很有帮助。

组装

组装时,分两次才把它焊接好。有一张图片和原理图,这些说明足够让组装过程不会太难。

贴片二极管的方向让人犯难,但用万用表的二极管测试功能解决了这个问题。二极管“正面”的小绿线朝向电路板底部,“背面”的线朝向顶部,但现在已焊好无法再确认。

最让人头疼的是焊接USB接口,建议在安装IC插座之前先焊这个,因为它会挡住烙铁。USB接口的中间引脚是不连接的,必要时可以直接焊接,USB仅用于供电。

通过查看原理图上的部件编号和IC编号,就能知道每个芯片该放在哪里,芯片的缺口要和PCB板上印的缺口对齐。基本上第一次就成功了,只是USB接口的电源连接偶尔会出问题。焊接那些二极管很折磨人。

工作原理

从高层次来看,它的工作原理如下。程序ROM是由16个拨码开关组成的,这就是全部的程序空间,实现复杂功能很棘手。每个拨码开关的5 - 8引脚是操作码,可在`ADD`、`MOV`、`IN`、`OUT`、`JNC`、`JMP`这些指令中进行选择,这些位会通过一些组合电路来控制信号进入加法单元和寄存器锁存加法单元的输出。拨码开关的1 - 4位是指令的立即数,总是会被输入到加法器中。

数据选择器还可以选择一个硬连线的零信号(接地),例如`MOV A,Im`指令就会用到这个。立即数和命令位的顺序有点让人困惑,拨码开关上标着低位的是最低有效位。

深入探究
地址解码器

地址解码器芯片IC11是一个多路分配器,它接收一个4位信号,并将其16个输出信号中对应这个数字的那个信号拉低。当信号被拉低时,如果拨码开关处于连接状态,电流就可以通过该拨码组的二极管,否则,上拉电阻R21 - R26会使信号保持高电平。IC12本质上是一个用于ROM结果的缓冲反相电路。

命令解码器

命令位由IC8和IC10中的离散组合逻辑芯片转换为`not_LOAD0,1,2,3`和`SEL_A`/`SEL_B`信号,实际上,`not_LOAD0,1,2,3`可以称为`not_LOADA,B,Out,PC`,因为它们就是连接到这些地方的。这里还给出了指令与相关位的对应表格。

数据选择器

`SEL_A`和`SEL_B`连接到数据选择器芯片IC6和IC7上,它们可以从4种可能的选项中进行选择,零信号是硬连线接地的。

加法器

被选中的4位输出到加法器芯片IC8,同时它还会接收所选ROM组的立即数1 - 4位。立即数被读取的情况比从指令助记符上看起来要多,例如`OUT B`实际上仍然会加上立即数。如果不需要立即数,默认将其清零。

寄存器

寄存器A、B、Out、PC都是计数器芯片,只有PC启用了计数功能。在时钟信号的边沿,如果`not_LOADx`信号为低电平,数据就会从引脚`A`、`B`、`C`、`D`锁存到输出引脚`QA`、`QB`、`QC`、`QD`中。`JMP`指令是通过将值移动到`PC`寄存器中来实现的,`PC`寄存器的值会反馈到ROM地址解码器中。

进位触发器

进位电路有一个D触发器,用于存储上一次操作的溢出进位位,这对于实现`JNC`(无进位则跳转)功能是必要的。它的工作原理是将进位信号输入到命令解码器电路中,如果有进位,就禁用来自指令的`LOADPC`信号。注意,这个版本的命令解码器原理图似乎有一个错误,能找到它吗?

时钟

右下角是时钟电路,开关可以在手动时钟和自动时钟之间切换,还能改变时钟速度。时钟是一个由RC控制的多谐振荡器,还没有认真分析过它。

简单程序

运行了几个实验性的程序,首先尝试一些非常简单的程序是个好主意。这里再次给出了指令集表格。

输出到LED

尝试让程序`OUT 0101`运行起来,在表格中查找`OUT Im`的操作码是`1011`,这意味着第一个拨码开关要设置为`1010 1101`,第一部分是常量立即数,第二部分是操作码的反转。

简单闪烁

尝试一个带有跳转的简单闪烁程序,输出两个不同的常量,然后跳回到指令0,可转换为相应的代码。

递增计数

尝试计数的程序代码为`ADD B 1`、`OUT B`、`JMP 0`。

递减计数

当意识到加15在模16运算中等同于减1时,就可以实现递减计数,程序代码为`ADD B 15`、`OUT B`、`JMP 0`。

先递增后递减计数

这个程序要复杂得多,在运行过程中犯了很多错误,如过早吃冰淇淋(可能是玩笑话)、把进入`JNC`和`JMP`的标签弄反等。要记住更新跳转地址,把指令按正确的顺序排列,手动组装等。实际上,`ADD B 1`和`OUT B`可以压缩为`OUT B 1`,因为`OUT`指令实际上可以接受立即数。程序计数器是从0开始索引的,这里给出了相应的程序代码。还有一个可能有问题的替代版本,利用`JC ~ JNC;JMP`的思路,能否不使用A作为临时寄存器,直接测试B的溢出情况呢?不过这样会不会漏掉一些数字呢?循环的边界处理很棘手。

Python汇编器和模拟器

有人和Ben匆忙编写了一个简单的Python模拟器和汇编器,主要是Ben写的。也许用正则表达式来做这个有点傻,但也挺不错的,`match`语句很实用。应该再回来把它完善一下,但这是个好的开始。TD4仓库里也有一个汇编器,但还没试过。这里给出了Python代码。

杂谈

如果觉得这个很有趣,可能会想了解两个相关的项目。几年前参加了Nand2Tetris课程,在这个课程中,将学习如何从门电路开始设计计算机,一直到设计一种小型编程语言,这是了解TD4背后原理的很好参考。另一个选择是Ben Eater计算机,它的套件要贵得多,CPU更复杂、功能更强大,相关资料也更多。

好像有一本关于这个东西的日语书,但找不到翻译版本。查看原理图,似乎没有什么可用的RAM,所以这是一台功能非常有限的机器。A + B + OUT + PC意味着只有16位的状态,可以很容易地对任何想检查的内容进行穷举检查。那合成程序呢?也许这样也不错,它的限制这么多,感觉更像一个有限状态机。试试对它进行模型检查?

能在Python HDL、Verilog或者那个图形界面工具中实现它吗?还有一些可以尝试的其他事情,如减法、相等性检查、超优化等。Conor Mcbride是不是有关于4位CPU的视频?真的有像这样的4位CPU吗?

能写出什么有趣的程序呢?这可有点难。对每个芯片进行建模,这里给出了相关代码。74HC154是地址解码器,四个输入信号会被转换为16个互斥的输出信号,例如输入引脚为LLHH时,会将输出的第3引脚拉低。两个使能引脚接地,这样输出始终是启用的。这些线进入这个解码器,选择要拉低的触发器。74HC540是一个反相缓冲器。这里还给出了一些图片。

这些二极管都是采用查理复用技术吗?不,算了,不是的。这里还给出了一些相关项目的信息。这里还有一段尝试在电路层面进行仿真但失败的Python代码。使用Lark?这里给出了相关代码。

嘿,朋友!

这里提供了联系方式,包括邮箱和一些社交账号。这是一个关于生活、编程、数学、逻辑和物理的博客。