您是否曾经遇到过“处理器如何工作?”的问题。 是的,是的,正是您的PC /笔记本电脑/智能手机中的那一台。 在本文中,我想给出一个使用Verilog设计的自发明处理器的示例。 Verilog并不完全像它的编程语言。 这是硬件描述语言。 编写的代码不会被任何东西执行(当然,除非您在模拟器中运行它),而是变成物理电路的设计,或者变成FPGA感知的形式(现场可编程门阵列)。
免责声明:本文是某大学项目工作的结果,因此工作时间有限,该项目的许多部分仍处于开发的初始阶段。
请注意,本文中创建的处理器与现代广泛使用的处理器几乎没有共同点,但是我尝试通过其创建来实现略有不同的目标。
为了真正理解编程过程,您需要想象使用的每种工具是如何工作的:语言的编译器/解释器,虚拟机(如果有),中间代码,当然还有处理器本身。 很多时候,学习编程的人都处于很长一段时间的第一阶段-他们只考虑语言及其编译器的工作方式。 这通常会导致错误,其解决方案对于新手程序员来说是未知的,因为他不知道这些问题的根源在哪里。 我本人看到了一些现场示例,其中的情况类似于上面的描述,因此我决定尝试解决这种情况,并创建一套可以帮助初学者理解所有步骤的东西。
该套件包括:
- 实际发明的语言
- VS Code的高亮插件
- 编译到它
- 指令集
- 一个能够执行此指令集的简单处理器(用Verilog编写)
我再次提醒您,本文并未描述任何与现代真实处理器类似的内容,而是描述了一个易于理解而又无需详细介绍的模型。
如果您想自己做,则需要做的事情:
要运行CPU仿真,您需要ModelSim,您可以从Intel网站下载。
要运行OurLang编译器,您需要Java版本> = 8。
链接到项目:
https://github.com/IamMaxim/OurCPU
https://github.com/IamMaxim/OurLang
扩展名:
https://github.com/IamMaxim/ourlang-vscode
要构建Verilog部分,我通常使用bash脚本:
#/bin/bash vlib work vlog *.v vsim -c testbench_1 -do "run; exit"
但这可以通过GUI重复进行。
使用Intellij IDEA与编译器一起使用很方便。 最主要的是要跟踪依赖项中所需的模块包含哪些模块。 我没有发布现成的.jar来打开访问权限,因为我希望读者可以阅读编译器的源代码。
启动的模块是编译器和解释器。 编译器一切都清楚了,Interpreter只是Java中OurCPU的仿真器,但在本文中我们将不予考虑。
指令集
我认为最好从指令集开始。
有几种指令集架构:
- 本文介绍了基于堆栈的内容。 一个独特的功能是所有操作数都被压入堆栈并从堆栈中弹出,这立即排除了并行执行的可能性,但这是处理数据的最简单方法之一。
- 基于累加器-最重要的是,只有一个寄存器可存储由指令修改的值。
- 基于寄存器的是现代处理器中使用的,因为它允许您通过使用各种优化(包括执行并行化,流水线化等)来实现最高性能。
我们的处理器指令集包含30条指令
接下来,我建议看一下处理器的实现:
该代码包含几个模块:
RAM是直接包含内存本身以及访问其中数据的模块。
CPU-直接控制程序进度的模块:读取指令,将控制权转移到所需的指令,存储必要的寄存器(指向当前指令的指针,等等)。
几乎所有指令仅适用于堆栈,因此只需遵循它们即可。 有些(例如putw,putb,jmp和jif)在指令本身中有一个附加参数。 他们需要传递整个指令,以便可以读取必要的数据。
以下是处理器工作原理的概述:

指令级设备设计的一般原则
我认为是时候直接了解程序本身的设备了。 从上图可以看到,每条指令之后,地址将移至下一条。 这为程序提供了线性过程。 当有必要打破这种线性关系(条件,循环等)时,将使用分支指令(在我们的指令集中,这些指令为jmp和jif)。
调用函数时,我们需要保存所有内容的当前状态,为此,存在激活记录-存储此信息的记录。 它们不以任何方式与处理器或指令绑定;这只是编译器在生成代码时使用的概念。 OurLang中的激活记录具有以下结构:

从该图可以看出,局部变量也存储在激活记录中,这使您可以在编译时(而不是在运行时)计算内存中变量的地址,从而加快程序执行速度。
对于函数调用,我们的指令集提供了使用CPU模块中包含的两个寄存器(操作指针和激活地址指针)的方法-putopa / popopa,putara / popara。
编译器
现在,让我们看一下最接近最终程序员的那部分-编译器。 通常,作为程序的编译器包括三个部分:
词法分析器负责将程序的源代码转换为解析器可以理解的词法单元。
解析器从这些词法单元构建一个抽象语法树。
编译器遍历此树并生成某种由低级指令组成的代码。 它可以是准备由处理器执行的字节码或二进制代码。
在OurLang编译器中,这些部分分别由类表示
语言能力
OurLang尚处于起步阶段,也就是说,它可以正常工作,但是到目前为止,它并不多,甚至语言的核心部分都尚未完成。 但是要了解编译器的本质,当前状态就足够了。
作为理解语法的程序示例,提出了以下代码片段(也用于测试功能):
// single-line comments /* * Multi-line comments */ function print(int arg) { instr(putara, 0); instr(putw, 4); instr(add, 0); instr(lw, 0); instr(printword, 0); } function func1(int arg1, int arg2): int { print(arg1); print(arg2); if (arg1 == 0) { return arg2; } else { return func1(arg1 - 1, arg2); }; } function main() { var i: int; i = func1(1, 10); if (i == 0) { i = 1; } else { i = 2; }; print(i); }
我不会专注于语言,而是将其留给您学习。 通过编译器代码,当然;)。
在编写它时,我试图使自解释代码清晰可见,没有注释,因此理解编译器代码应该没有问题。
好吧,当然,最有趣的是编写代码,然后观察它变成了什么。 幸运的是,OurLang编译器会生成带有注释的类似于汇编的代码,
这有助于避免混淆内部发生的事情。
我还建议安装Visual Studio Code扩展,这将有助于使用该语言。
祝您学习项目顺利!