为此编写一个简单的处理器和环境

你好 在本文中,我将告诉您需要执行哪些步骤来为其创建一个简单的处理器和环境。


命令集架构(ISA)


首先,您需要确定处理器是什么。 以下参数很重要:


  • 机器字寄存器的大小(处理器的位/“位”)
  • 机器指令(指令)及其大小

根据指令的大小,处理器体系结构可以分为两种类型(实际上,它们的数量更多,但是其他选项不那么流行):



它们的主要区别在于RISC处理器具有相同的指令大小。 它们的指令很简单并且运行相对较快,而CISC处理器可以具有不同的指令大小,其中一些可能会花费一些时间。


我决定制作一个像MIPS一样的RISC处理器。


我这样做的原因有很多:


  • 创建这种处理器的原型非常简单。
  • 这种处理器的所有复杂性都转移到了汇编器和/或编译器之类的程序上。

以下是我的处理器的主要功能:


  • 机器字和寄存器大小-32位
  • 64个寄存器(包括命令计数器
  • 2种指示

寄存器类型 (以下称为寄存器类型)如下所示:


rtype


这种指令的独特之处在于它们使用三个寄存器进行操作。


即时类型


itype


这种类型的指令有两个寄存器和一个数字。


OP是要执行的指令的编号(或指示该寄存器类型的指令)。


R0R1R2是用作指令的操作数的寄存器号。


Func是一个附加字段,用于指示寄存器类型指令的类型


Imm是写入值的字段,我们希望将其明确提供指令作为操作数。


  • 仅28条指令

完整的指令列表可以在github仓库中找到。


这里只是其中的几个:


nor r0, r1, r2 

NOR是一种寄存器类型的指令,它对寄存器r1和r2进行逻辑或运算 ,然后将结果写入寄存器r0。


为了使用此指令,您需要在二进制数系统中将OP字段更改为0000 ,将Func字段更改为0000000111


 lw r0, n(r1) 

LW立即数类型的指令,它将r1 + n处的存储值加载到r0寄存器中。


为了依次使用此指令,您需要将OP字段更改为0111 ,并将数字n写入IMM字段。


编写处理器代码


创建ISA之后,您可以开始编写处理器。


为此,我们需要某种设备描述语言的知识。 以下是其中一些:


  • Verilog
  • VHDL(不要与前一个混淆!)

我选择了Verilog,因为 编程是我大学课程的一部分。


要编写处理器,您需要了解其操作逻辑:


  1. 命令计数器(PC)上获取说明
  2. 解码说明
  3. 指令执行
  4. 将已执行指令的命令大小添加到计数器中

等等广告无限。


事实证明,您需要创建几个模块:



我们将分别分析每个模块。


注册文件


寄存器文件提供对寄存器的访问。 使用它,您需要获取某些寄存器的值,或对其进行更改。


就我而言,我有64个寄存器。 在其中一个寄存器中写入对其他两个寄存器的操作结果,因此我需要提供仅更改一个寄存器并从其他两个寄存器中获取值的机会。


解码器


解码器是负责解码指令的单元。 它指示ALU和其他单元需要执行哪些操作。


例如, addi语句应将寄存器$ 0的值 (始终存储0 )和20相加,并将结果放入寄存器$ t0中。


 addi $t0, $zero, 20 

此时,解码器确定以下指令:


  • 立即型
  • 必须将结果写入寄存器

并将此信息传输到以下块。


ALU


控制权传递给ALU之后。 它通常执行所有数学,逻辑运算以及比较数字的运算。


也就是说,如果我们考虑相同的addi指令,则在此阶段会发生020的加法运算


其他


除了上述块之外,处理器还应该能够:


  • 获取和更改内存中的值
  • 执行条件跳转

您可以在这里那里看到代码中的外观。


组装工


编写处理器后,我们需要一个程序,该程序将文本命令转换为机器代码,以免手动执行。 因此,您需要编写汇编程序。


我决定用C编程语言实现它。


由于我的处理器具有RISC架构,为了简化我的生活,我决定设计汇编程序,以便可以轻松添加自己的伪指令(几个基本指令或其他伪指令的组合)。


您可以使用存储指令类型,其格式,指向返回机器指令代码的函数的指针及其名称的数据结构来实现此目的。


常规程序以段声明开始。


对于我们来说,两段.text就足够了-其中将存储程序的源代码-和.data-中将存储我们的数据和常量。


一条指令可能看起来像这样:


 .text jie $zero, $zero, $zero #  addi $t1, $zero, 2 # $t1 = $zero + 2 lw $t1, 5($t2) # $t1 = *($t2 + 5) syscall 0, $zero, $zero # syscall(0, 0, 0) la $t1, label# $t1 = label 

首先,指示指令的名称,然后指示操作数。


.data中 ,指示数据声明。


 .data .byte 23 #   1  .half 1337 #   2  .word 69000, 25000 #   4  .asciiz "Hello World!" #     ( ) .ascii "12312009" #   ( ) .space 45 #  45  

广告必须以句点和数据类型名称开头,后跟常量或参数。


如下分析(扫描)汇编文件很方便:


  1. 首先,扫描细分
  2. 如果它是.data段,则我们解析不同的数据类型或.text
  3. 如果它是.text段,则我们解析命令或.data段

要工作,汇编器需要遍历源文件两次。 他第一次考虑链接的偏移量(为它们服务)时,它们通常如下所示:


  la $s4, loop #   loop  s4 loop: # ! mul $s2, $s2, $s1 # s2 = s2 * s1 addi $s1, $s1, -1 # s1 = s1 - 1 jil $s3, $s1, $s4 #  s3 < s1     

在第二遍中,您已经可以生成文件。


总结


将来,您可以在我们的处理器上运行汇编程序的输出文件并评估结果。


同样,现成的汇编程序可以在C编译器中使用。 但这是以后。


参考文献:


Source: https://habr.com/ru/post/zh-CN430680/


All Articles