设计一个 RISC-V CPU,第 1 部分:软件工程师如何学习硬件设计

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我在数字逻辑设计方面并没有经验。也就是说,直到最近我才决定尝试设计自己的 CPU,并在 FPGA 上运行!如果你也是一名软件工程师,并对硬件设计有着模糊的兴趣,那么我希望这一系列关于我所学到的知识的文章能够对你有所帮助,并让你感到有趣。本系列文章的第一部分中,将回答以下问题:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"什么是数字逻辑设计?"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如何开始,我应该使用什么工具?"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我将在以后的系列文章中详细讨论我的 CPU 设计和 RISC-V 架构,并将回答以下问题:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"数字逻辑设计与软件设计有什么本质区别?"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"数字逻辑设计和软件设计有什么相似之处?"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你可以在"},{"type":"link","attrs":{"href":"https:\/\/github.com\/lochsh\/riscy-boi\/tree\/47e94dc6e9665f73c871add002c34d1516fd5106","title":null,"type":null},"content":[{"type":"text","text":"这里"}]},{"type":"text","text":"看到我写这篇文章时的 CPU 的代码,或者在"},{"type":"link","attrs":{"href":"https:\/\/github.com\/lochsh\/riscy-boi","title":null,"type":null},"content":[{"type":"text","text":"这里"}]},{"type":"text","text":"查看最新的版本。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"什么是数字逻辑设计?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"数字逻辑设计就是设计一个逻辑电路,对二进制数值进行运算。基本元件是逻辑门:例如,与门一样,有两个输入和一个输出。它的输出为 1 或 iff,两个输入均为 1。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我们所设计的同步电路,一般都是利用触发器来储存状态,使电路运行与共时钟同步。触发器由逻辑门组成。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"模拟电路设计包括构成逻辑门的电子元件,例如晶体管和二极管。这种抽象通常是用于直接处理来自模拟传感器的信号的应用,例如无线电接收器。在设计 CPU 时,这种抽象水平是行不通的:现代的 CPU 有几十亿个晶体管!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"相反,我们使用的工具可以将数字逻辑设计转化为不同的有用格式:FPGA 的配置(见下文);模拟;晶片布局。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"FPGA 是什么,为什么要用 FPGA?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上文中我们指出,不管我们是创建自定义 ASIC 芯片还是配置 FPGA,都可以使用相同的数字逻辑设计工具。现场可编程门阵列(Field-Programmable Gate Array,FPGA)是一种集成集成电路,其中包含了可编程逻辑块阵列。你可以把它想象成一个大型的逻辑门阵列,可以通过多种方式连接起来。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"定制一款芯片动辄需要几百万美元,当然,一旦芯片被生产出来,就无法对它进行更改。所以 FPGA 通常用于下列情况:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由于缺乏资金,无法负担制作定制 ASIC 的费用(例如,如果你只是像我这样的黑客,而不是 ARM 或英特尔)。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"无法负担制作定制 ASIC 的费用,因为产量太低,不值得一次性支付高昂的费用 (例如,如果你正在使用定制的数据采集硬件生产少量的 MRI 机器)。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"需要灵活性。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"缺点是什么?那就是 FPGA 的单芯片成本要高得多,并且由于它能够以非常灵活的方式将逻辑块连接在一起,因此速度通常要慢得多。与此相反,定制的设计可以减少晶体管的数量,而无需考虑灵活性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在我看来,比较 ASIC 的定制设计过程和 FPGA 的设计过程是很有帮助的:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"逻辑设计:就像做 FPGA 一样,ASIC 的逻辑设计也是用硬件描述语言来完成的。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"验证:FPGA 设计可能会被验证,但是可以期待 ASIC!设计的过程更严格了。毕竟,设计一旦制造出来就不能更改!验证通常包括设计部分的正式验证。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"合成:这将创建一个网表,一个逻辑块及其连接的列表。连接被称为网,而块被称为单元。对于 FPGA 和 ASIC 来说,单元是特定于厂商的。"}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"布局布线(Placement and routing,P&R):对于 FPGA 来说,它涉及到将网表中描述的逻辑块映射到 FPGA 中的实际块。由此产生的二进制通常称为比特流。对于 ASIC 来说,这涉及到决定在晶片上何处放置单元,以及如何将它们连接起来。这两种应用通常都要使用自动优化工具。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"我需要什么工具?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"硬件描述语言:我使用的是 "},{"type":"link","attrs":{"href":"https:\/\/github.com\/nmigen\/nmigen","title":null,"type":null},"content":[{"type":"text","text":"nMigen"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你可能听说过 Verilog 或 VHDL:这两种流行的硬件描述语言(hardware description language,HDL)。这里我所说的“流行”,是指广泛使用,而非广受欢迎。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我不会假装对这些工具很了解。我只知道那些比我更聪明的人,有着丰富的逻辑设计经验,却对这些工具恨之入骨。由于 Verilog 和其他类似工具存在的问题,人们尝试着开发出更有用、更友好的替代方法。nMigen 就是在 Python 中创建一 门领域专用语言的项目。用它自己的话就是:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"虽然用 Verilog 和 VHDL 进行硬件设计比输入原理图的速度要快,但是由于一些原因,硬件设计还是很枯燥,而且效率也不高。对目前逻辑设计中占有重要地位的同步电路而言,事件驱动模型引入了不必要的问题,并引入了人工编码。逆直觉的算术规则导致了更陡峭的学习曲线,并为设计上的微小缺陷提供了温床。最后,通过“generate”语句来支持逻辑过程生成(元编程)非常有限,并且限制了代码的通用、重用和组织方式。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":"br"}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"针对这些问题,我们开发了 nMigen FHDL,该库取代了事件驱动范例,它采用了组合语句和同步语句的概念,并采用了算术规则,使整型始终像数学整型一样,最重要的是允许 Python 程序构建所设计的逻辑。这一点使硬件设计人员能够充分利用 Python 语言的丰富内容:面向对象编程、函数参数、生成器、操作符重载、库等,构建组织良好、可重用的优雅设计。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"假如你和我一样,从未使用过 Verilog,那么这些对你来说不仅仅是抽象的含义。但是听起来确实很有前景,而且我可以证明,在没有 Verilog 障碍的情况下,从逻辑设计开始就非常简单。如果你对 Python 非常熟悉,我将推荐它!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我能想到的唯一缺点是,nMigen 仍然处于开发阶段,特别是文档还不完整。但你可以通过 chat.freenode.net 的 #nmigen 频道找到有用的社区。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"用于检查模拟的波形显示器:我使用的是 "},{"type":"link","attrs":{"href":"http:\/\/gtkwave.sourceforge.net\/","title":null,"type":null},"content":[{"type":"text","text":"GTKWave"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"nMigen 提供了模拟工具。我将它用于用 "},{"type":"codeinline","content":[{"type":"text","text":"pytest"}]},{"type":"text","text":" 编写的测试。为了帮助调试,我记录了这些测试中的信号,并在波形显示器中观察它们。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/9b\/9bf35b44830ec86b8f35c1d9fe106be2.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"FPGA 开发板:我使用的是 myStorm BlackIce II"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你不必使用 FPGA 开发板来创建自己的 CPU。在模拟中,你可以做任何事情。对于我来说,工作中使用板子的乐趣就是能闪烁 LED,看着自己的设计运行。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"当然,如果你要创建的东西比我的最基本的 CPU 更有用,那么你可能需要一些硬件来运行它,而这并非“可选”选项!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"开始使用 nMigen"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 nMigen 系统中,我并没有立刻尝试设计一个 CPU,而是首先制作一个算术逻辑单元(Arithmetic Logic Unit ,ALU)。 在我见过的所有 CPU 设计中, ALU 是一个关键部件:它执行算术运算。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"为什么要从这里开始呢?我知道我的 CPU 需要一个 ALU;我知道我能做一个简单的 ALU;我知道当开始一个新的项目时,做事情的感觉是一种重要的动力!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我的设计看起来像这样:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"\"\"\"Arithmetic Logic Unit\"\"\"import enum\n\nimport nmigen as nm\nclass ALUOp(enum.IntEnum):\n\n\"\"\"Operations for the ALU\"\"\"\n ADD = 0\n SUB = 1\n \n \nclass ALU(nm.Elaboratable):\n\"\"\"\n Arithmetic Logic Unit\n\n * op (in): the opcode\n * a (in): the first operand\n * b (in): the second operand\n\n * o (out): the output\n \"\"\"\n\ndef __init__(self, width):\n\"\"\"\n Initialiser\n\n Args:\n width (int): data width\n \"\"\"\n self.op = nm.Signal()\n self.a = nm.Signal(width)\n self.b = nm.Signal(width)\n self.o = nm.Signal(width)\n\ndef elaborate(self, _):\n m = nm.Module()\n\nwith m.Switch(self.op):\nwith m.Case(ALUOp.ADD):\n m.d.comb += self.o.eq(self.a + self.b)\nwith m.Case(ALUOp.SUB):\n m.d.comb += self.o.eq(self.a - self.b)\nreturn m"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"正如你所看到的,我们已经创建了大量的 nMigen "},{"type":"codeinline","content":[{"type":"text","text":"Signal"}]},{"type":"text","text":" 实例,以很好地表示定义 ALU 接口的信号!但这个复杂的方法是什么呢?这个 "},{"type":"codeinline","content":[{"type":"text","text":"elaborate"}]},{"type":"text","text":" 方法又是什么呢?我的理解是,“elaboration”是合成网表的第一步的名称(见上文)。在上面的 nMigen 代码中,我们的想法是,已经创建了一些可阐述的结构(通过继承 "},{"type":"codeinline","content":[{"type":"text","text":"nm.Elaboratable"}]},{"type":"text","text":"),也就是用来描述想要合成的数字逻辑的东西。这个 "},{"type":"codeinline","content":[{"type":"text","text":"elaborate"}]},{"type":"text","text":" 方法描述了数字逻辑。它必须返回一个 "},{"type":"codeinline","content":[{"type":"text","text":"nMigen"}]},{"type":"text","text":" 模块。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面让我们进一步了解一下 "},{"type":"codeinline","content":[{"type":"text","text":"elaborate"}]},{"type":"text","text":" 的方法的内容。"},{"type":"codeinline","content":[{"type":"text","text":"Switch"}]},{"type":"text","text":" 将创造某种形式的合成设计决策逻辑。但什么是 "},{"type":"codeinline","content":[{"type":"text","text":"m.d.comb"}]},{"type":"text","text":" 呢? nMigen 提出了同步("},{"type":"codeinline","content":[{"type":"text","text":"m.d.sync"}]},{"type":"text","text":")和组合("},{"type":"codeinline","content":[{"type":"text","text":"m.d.comb"}]},{"type":"text","text":")控制域的概念。来自 nMigen "},{"type":"link","attrs":{"href":"https:\/\/nmigen.info\/nmigen\/latest\/lang.html#lang-domains","title":null,"type":null},"content":[{"type":"text","text":"文档"}]},{"type":"text","text":":"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"控制域是指在相同条件下改变其值的一组命名信号。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":"br"}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所有的设计都有一个预定义的组合域,其中包含所有的信号,当用来计算这些信号的任何值发生变化时,这些信号也随之发生变化。名称 comb 是为组合域保留的。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":"br"}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一种设计还可以有任意数量的用户定义的同步域,也称为时钟域,其中包含的信号在域的时钟信号出现特定边缘时会发生变化,或者,对于具有异步复位功能的域,域的复位信号会发生变化。大多数模块只使用一个同步域。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":"br"}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在组合域和同步域中,信号的赋值的行为各不相同。总的来说,同步域中的信号包含了设计的状态,而组合域中的信号并不能形成反馈回路或维持状态。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面以移位寄存器为例,说明要设计的逻辑。假定移位寄存器有 8 位,每个时钟周期,该位值都会有一个移位(最左边的值来自输入信号)。这必然是同步的:不能通过简单地将位连接在一起来创建这个功能,而在 nMigen 中,将位分配到组合域中将代表此功能。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我将在这个系列博客的下一部分详细讨论我的 CPU 设计。现在的情况是,我试图在每个周期中只停用一个指令,而不使用流水线——这很不寻常,但是我希望这样做可以简化 CPU 的各个方面。其结果是,大多数逻辑是组合的,而非同步的,因为我几乎没有在时钟周期之间维持这种状态。现在,我的寄存器文件设计有问题,为了解决这个问题,我可能需要重新考虑我的“无流水线”想法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"编写测试"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"对于 Python 测试,我喜欢使用 "},{"type":"codeinline","content":[{"type":"text","text":"pytest"}]},{"type":"text","text":",当然你也可以使用任何能吸引你的框架。以下是我在上面测试的 ALU 代码:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"\"\"\"ALU tests\"\"\"\nimport nmigen.sim\nimport pytest\n\nfrom riscy_boi import alu\n\[email protected]( \n\"op, a, b, o\", [ \n(alu.ALUOp.ADD, 1, 1, 2), \n(alu.ALUOp.ADD, 1, 2, 3), \n(alu.ALUOp.ADD, 2, 1, 3),\n(alu.ALUOp.ADD, 258, 203, 461), \n(alu.ALUOp.ADD, 5, 0, 5), \n(alu.ALUOp.ADD, 0, 5, 5), \n(alu.ALUOp.ADD, 2**32 - 1, 1, 0), \n(alu.ALUOp.SUB, 1, 1, 0), \n(alu.ALUOp.SUB, 4942, 0, 4942), \n(alu.ALUOp.SUB, 1, 2, 2**32 - 1)])\ndef test_alu(comb_sim, op, a, b, o): \nalu_inst = alu.ALU(32)\n\ndef testbench():\nyield alu_inst.op.eq(op)\nyield alu_inst.a.eq(a)\nyield alu_inst.b.eq(b)\nyield nmigen.sim.Settle()\nassert (yield alu_inst.o) == o\n\n comb_sim(alu_inst, testbench)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以及我的 "},{"type":"codeinline","content":[{"type":"text","text":"conftest.py"}]},{"type":"text","text":":"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"python"},"content":[{"type":"text","text":"\"\"\"Test configuration\"\"\"\nimport os\nimport shutil\n\nimport nmigen.sim\nimport pytest\n\nVCD_TOP_DIR = os.path.join(\n os.path.dirname(os.path.realpath(__file__)),\n\"tests\",\n\"vcd\")\n\ndef vcd_path(node):\n directory = os.path.join(VCD_TOP_DIR, node.fspath.basename.split(\".\")[0])\n os.makedirs(directory, exist_ok=True)\nreturn os.path.join(directory, node.name + \".vcd\")\n\[email protected](scope=\"session\", autouse=True)\ndef clear_vcd_directory():\n shutil.rmtree(VCD_TOP_DIR, ignore_errors=True)\n\[email protected]\ndef comb_sim(request):\n\ndef run(fragment, process):\n sim = nmigen.sim.Simulator(fragment)\n sim.add_process(process)\nwith sim.write_vcd(vcd_path(request.node)):\n sim.run_until(100e-6)\n\nreturn run\n\[email protected]\ndef sync_sim(request):\n\ndef run(fragment, process):\n sim = nmigen.sim.Simulator(fragment)\n sim.add_sync_process(process)\n sim.add_clock(1 \/ 10e6)\nwith sim.write_vcd(vcd_path(request.node)):\n sim.run()\n\nreturn run"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"每次测试都会生成一个 "},{"type":"codeinline","content":[{"type":"text","text":"vcd"}]},{"type":"text","text":" 文件,我可以通过 GTKWave 等波形显示器来查看,以便调试。你会注意到,组合模拟固定运行的时间段是任意小的,而同步模拟功能运行的时间段是确定的时钟周期数。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一个信号产生于一个测试函数,它将从模拟器请求它的当前值。对于组合逻辑,我们生成 n"},{"type":"codeinline","content":[{"type":"text","text":"nmigen.sim.Settle()"}]},{"type":"text","text":" ,要求完成模拟。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"对于同步逻辑,还可以开始新的时钟周期,而不需要参数。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"设计一个 CPU"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在熟悉了 nMigen 之后,我开始尝试绘制一个框图来显示我的 CPU。在本系列博客的下一部分中,我将对这个问题进行更详细的讨论,但我将简单地说,我先绘制出一个指令所需要的逻辑,然后绘制出另一个指令的逻辑,然后找到如何将它们结合起来的方法。这里有第一个混乱的草图:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/0c\/0c4a2c23132e9fc4b72f0eaa45da33b5.jpeg","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在弄清楚不同元件的接口要求是什么时,这个框图步骤非常有价值,但是在开始使用 nMigen 和在这个过程中学习数字逻辑设计之前,我不想这么做。修改后的框图如下所示:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https:\/\/static001.geekbang.org\/infoq\/bc\/bcc615fc6210155159b2413ba9ad9dcf.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"请关注本系列博客的下一部分,我将深入研究 RISC-V 和 CPU 设计。我想用第三部分来重新设计我的设计,使其适用于我要实现的全部指令集(RV32I)上工作😃"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"作者介绍:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"lochsh,住在英国牛津的软件工程师,供职于 Perspectum Diagnostics,为医学图像诊断工具编写 C++。曾在 CMR Surgical 供职,在哪里为下一代手术机器人编写裸机嵌入式 C。对 Rust 很感兴趣,已经写过很多 Python,愿意尝试更多的函数式编程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"原文链接:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"https:\/\/mcla.ug\/blog\/risc-v-cpu-part-1.html"}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章