基于RISC-V的单周期CPU开发 | Verilog
文章目录
实验要求
- 完成 64 位单周期 CPU 数据通路设计
- 实现以下指令:
- 实现 R Type 的基本数据通路(累计可获得实验部分 20% 的分数)
- add, sub, sll, slt, sltu, xor, srl, sra, or, and
- 实现 I Type 的基本数据通路(累计可获得实验部分 40% 的分数)
- addi, slti, sltiu, xori, ori, andi, slli, srli, srai
- ld
- 实现 S Type 的基本数据通路(累计可获得实验部分 50% 的分数)
- sd
- 实现 B Type 的完整数据通路(累计可获得实验部分 70% 的分数)
- beq, bne, blt, bge, bltu, bgeu
- 实现 U Type 的完整数据通路(累计可获得实验部分 80% 的分数)
- lui, auipc
- 实现 J Type 的完整数据通路(累计可获得实验部分 90% 的分数)
- jal
- 实现剩余指令的完整数据通路(累计可获得实验部分 100% 的分数)
- jalr, lb, lh, lw, lbu, lhu, lwu
- sb, sh, sw
- addiw, slliw, srliw, sraiw
- addw, subw, sllw, srlw, sraw
理解数据通路
首先,我需要理解这个数据通路究竟是干嘛的,但是对于像我这样智商平平的人,很难直接看懂实验指导。刚开始写的时候,我是尝试写一些比较独立的module,但到后面涉及到数据截断、生成掩码、SCPU集成等环节还是感到力不从心。因此,我也是沉下心来,想要用简单易懂的语言总结一下CPU的数据通路整个流程。
首先让我们从PC开始,PC是什么,是个人电脑吗(bushi)?PC在CPU中是程序计数器(Program Counter)的缩写,也称为指令寄存器(Instruction Register)或指令计数器(Instruction Counter),反正不管怎样,它的本质就是一个非常重要的寄存器。
PC用于存储当前正在执行的指令的地址,它的值随着指令的执行而不断增加,以便指向下一条指令的地址。当CPU执行完一条指令后,它就会从程序计数器中读取下一条指令的地址,并将其存储到指令寄存器中,以便进行下一条指令的执行。
那么问题来了,指令的地址在哪里呢,或者说,指令应该被存储在哪里呢?在冯诺依曼架构的CPU中,指令和数据都存储在同一片存储器中,称为存储器(Memory)。这种存储器被称为“一体化存储器”(Von Neumann Memory),它与CPU通过共享总线进行通信,CPU可以从存储器中读取指令和数据,并将结果写回到存储器中。那这个存储器究竟是啥呢,在本次实验里,它是 RAM
模块。
在前面PC的操作中,我们发现CPU还涉及到一个具有存储功能的单元,叫寄存器。寄存器又是什么东东呢?寄存器是计算机中的一种重要硬件组件,用于暂时存储、处理和传输数据。它通常被实现为一组存储单元,每个存储单元都可以存储一个固定长度的数据。在CPU中,寄存器是一种非常快速的存储器,读写速度比RAM快得多,因此非常适合用于存储临时数据和处理器状态等。
在CPU中,寄存器通常被用于以下几个方面:
-
存储中间结果:在执行计算机指令的过程中,CPU需要对数据进行加、减、乘、除等各种运算,这些运算的结果需要暂时存储到寄存器中,以便后续指令继续使用或处理。
-
存储处理器状态:CPU的状态信息通常存储在一组特殊的寄存器中,例如程序计数器、堆栈指针、标志寄存器等,这些寄存器用于存储CPU的状态信息,以便CPU在执行指令时进行控制和管理。
-
存储函数参数和局部变量:在程序执行期间,函数参数和局部变量通常被存储在CPU的寄存器中,以便CPU能够快速访问和处理这些数据。
-
存储指令操作数:CPU在执行指令时,需要将指令的操作数从内存中读取到寄存器中,以便CPU能够对它们进行处理和操作。
在本次实验中,我们设计了32个64位寄存器,放在Registers
模块中。
搞清楚了这些之后,我们可以进入下一个阶段,就是所谓指令译码(Instruction Decode)阶段,这个阶段用于从指令中提取操作码和操作数,并将其转换为CPU内部的控制信号和操作数。RISC-V指令集有6种指令类型,每种指令类型都有自己独特的结构,因此,对于不同种类的指令,我们要采取不同的处理方式,但这一切的前提就是在指令中提取一些有效信息,主要涉及到立即数、寄存器地址、操作码、操作数等等。
在指令译码阶段,CPU从指令中提取有效信息,包括立即数、寄存器地址、操作码和操作数等。这些信息被用于生成CPU内部的控制信号和操作数。
根据RISC-V指令集的不同类型,指令译码阶段会采取不同的处理方式。常见的指令类型包括:
-
R型指令(Register Type):这类指令进行寄存器之间的操作,如加法、减法等。指令译码阶段会提取源寄存器的地址以及目标寄存器的地址,并生成相应的控制信号。
-
I型指令(Immediate Type):这类指令中包含了一个立即数,用于进行寄存器和立即数之间的操作,如加载、存储等。指令译码阶段会提取源寄存器的地址、目标寄存器的地址以及立即数,并生成相应的控制信号。
-
S型指令(Store Type):这类指令用于将数据存储到内存中。指令译码阶段会提取源寄存器的地址、目标寄存器的地址以及立即数,并生成相应的控制信号。
-
B型指令(Branch Type):这类指令用于条件分支操作,根据条件是否满足跳转到指定地址。指令译码阶段会提取源寄存器的地址、目标寄存器的地址以及立即数,并生成相应的控制信号。
-
U型指令(Upper Immediate Type):这类指令用于加载一个20位的立即数到目标寄存器中。指令译码阶段会提取目标寄存器的地址以及立即数,并生成相应的控制信号。
-
J型指令(Jump Type):这类指令用于无条件跳转到指定地址。指令译码阶段会提取目标寄存器的地址以及立即数,并生成相应的控制信号。
继续
在指令译码阶段之后,进入执行阶段(Execution)。在执行阶段,根据指令的操作类型和译码阶段的结果,CPU执行相应的操作。
对于不同类型的指令,执行阶段的处理方式也不同:
- R型指令:执行阶段会对两个源寄存器中的数据进行操作,比如加法、减法等,并将结果存储到目标寄存器中。
- I型指令:执行阶段会对一个源寄存器和一个立即数(或来自于扩展的立即数)进行操作,比如加载到寄存器、加法、逻辑运算等,并将结果存储到目标寄存器中。
- S型指令:执行阶段会将源寄存器中的数据存储到内存的指定地址中。
- B型指令:执行阶段会对两个源寄存器中的数据进行比较,如果条件满足,则跳转到指定的地址。
- U型指令:执行阶段会将立即数加载到目标寄存器中。
- J型指令:执行阶段会跳转到指定的地址。
在执行阶段,CPU会根据指令和操作数执行相应的操作,并根据计算的结果更新寄存器等状态信息。
之后是访存阶段(Memory Access),在这个阶段,CPU根据执行阶段的结果,进行内存的读取或写入操作。
对于需要访问内存的指令,比如加载或存储指令,访存阶段会根据执行阶段的计算结果,将数据从内存中读取到寄存器中,或者将寄存器中的数据存储到内存中的指定地址。
最后是写回阶段(Write Back),在这个阶段,CPU将执行结果写回到寄存器中。根据实际情况,可能会将执行阶段的计算结果、从内存读取到的数据写回到目标寄存器中。
总的来说,CPU的数据通路从指令译码阶段开始,依次经过执行阶段、访存阶段和写回阶段,完成指令的执行和数据的读写。指令的执行涉及寄存器和内存的读写,以及各种运算和控制操作。整个数据通路的流程是按照指令的类型和顺序进行处理,并在不同阶段生成对应的控制信号和操作数,以实现指令的功能。
RV64指令讲解
在写之前发现全忘光了,先浅浅地复习一下吧,反正考试也要记的
-
LUI
lui(Load Upper Immediate)指令是RV64指令集中的一条指令,用于将一个立即数的上位20位加载到目标寄存器的高位,并将低位补零。
lui指令的格式为:lui rd, imm。
- "lui"是指令名称,表示加载上半部分立即数(Load Upper Immediate)。
- "rd"是目标寄存器,用于存储加载的结果。
- "imm"是立即数,包含要加载的上半部分值。
lui指令的作用是加载一个20位的立即数,并将其左移12位,然后填充为一个64位值存储到目标寄存器中。低位32位设置为零。
例如,如果执行以下lui指令:
lui x1, 0x12345这将把0x12345的上半部分(最高20位)左移12位,得到0x12345000,并将其存储到寄存器x1中。寄存器x1的低位32位将保持为零。
总结:lui指令用于将立即数的上半部分加载到目标寄存器的高位,低位补零。它常用于加载常量或者构建64位的地址值。
-
AUIPC
AUIPC(Add Upper Immediate to PC)指令是RV64指令集中的一条指令,用于将一个立即数的上半部分与当前程序计数器(PC)的高位相加,并将结果存储到目标寄存器中。
AUIPC指令的格式为:AUIPC rd, imm。
- "AUIPC"是指令名称,表示将上半部分立即数与PC相加(Add Upper Immediate to PC)。
- "rd"是目标寄存器,用于存储相加的结果。
- "imm"是立即数,包含要加载的上半部分值。
AUIPC指令用于构建一个绝对地址,通常用于全局变量或者跳转目标的计算。它将立即数的上半部分(最高20位)左移12位,然后与PC的高位相加。结果存储在目标寄存器中。
例如,如果执行以下AUIPC指令:
AUIPC x1, 0x12345这将把0x12345的上半部分(最高20位)左移12位,得到0x12345000。然后将PC的高位与0x12345000相加,并将结果存储到寄存器x1中。
总结:AUIPC指令用于将立即数的上半部分与PC的高位相加构建一个绝对地址,并将结果存储到目标寄存器中。它常用于计算全局变量或者跳转目标的地址。
-
JAL
JAL(Jump and Link)指令是RV64指令集中的一条指令,用于实现无条件跳转并将返回地址存储在目标寄存器中。
JAL指令的格式为:JAL rd, imm。
- "JAL"是指令名称,表示跳转并链接(Jump and Link)。
- "rd"是目标寄存器,用于存储跳转前的返回地址。
- "imm"是立即数,包含了跳转目标相对于当前指令的偏移量。
JAL指令的执行流程如下:
- 将当前指令所在的地址(PC值)存储到目标寄存器rd中作为返回地址。
- 计算跳转目标地址:将当前指令的PC值与立即数imm相加并加上4(由于RV64指令集中的指令都是4字节对齐的)。
- 将计算得到的跳转目标地址作为新的PC值。
通过JAL指令,在跳转到目标地址执行指令的同时,可以将返回地址保存到目标寄存器中。通常情况下,JAL指令用于实现函数调用和子程序的跳转。
例如,执行以下JAL指令:
JAL x1, label这将将当前指令的地址(PC值)存储到寄存器x1,并跳转到标签为"label"的指令处执行。
总结:JAL指令用于实现无条件跳转并将返回地址存储在目标寄存器中。它通常用于函数调用和子程序的跳转。
-
JALR
JALR(Jump and Link Register)指令是RV64指令集中的一条指令,用于实现通过寄存器间接跳转并将返回地址存储在目标寄存器中。
JALR指令的格式为:JALR rd, rs1, imm。
- "JALR"是指令名称,表示通过寄存器间接跳转并链接(Jump and Link Register)。
- "rd"是目标寄存器,用于存储跳转前的返回地址。
- "rs1"是源寄存器,用于存储要计算跳转目标地址的基址。
- "imm"是立即数,表示要与rs1中的值相加得到跳转目标的偏移量。
JALR指令的执行流程如下:
- 将当前指令的地址(PC值)加上4(由于RV64指令集中的指令都是4字节对齐的),存储到目标寄存器rd中作为返回地址。
- 将源寄存器rs1中的值与立即数imm相加,得到跳转目标地址。
- 将跳转目标地址的最低位设置为0(因为RV64指令集要求指令地址是4字节对齐的)。
- 将计算得到的跳转目标地址作为新的PC值。
通过JALR指令,在跳转到间接寄存器指定的地址执行指令的同时,可以将返回地址保存到目标寄存器中。通常情况下,JALR指令用于实现函数调用和子程序的间接跳转。
例如,执行以下JALR指令:
JALR x1, x2, 0这将将当前指令的地址(PC值加4)存储到寄存器x1,并跳转到寄存器x2中存储的地址处执行。
总结:JALR指令用于通过寄存器间接跳转并将返回地址存储在目标寄存器中。它通常用于实现函数调用和子程序的间接跳转。
-
BEQ
BEQ(Branch if Equal)指令是RV64指令集中的一条指令,用于在两个寄存器的值相等时执行有条件的相对跳转。
BEQ指令的格式为:BEQ rs1, rs2, imm。
- "BEQ"是指令名称,表示相等时分支(Branch if Equal)。
- "rs1"和"rs2"是比较操作数的源寄存器。
- "imm"是有符号的立即数偏移量,表示跳转目标相对于当前指令的距离。
BEQ指令的执行流程如下:
- 比较寄存器rs1和rs2的值,如果相等,则执行跳转,否则执行下一条指令。
- 计算跳转目标地址:将当前指令的地址(PC值)加上偏移量imm,作为跳转目标地址。
- 将计算得到的跳转目标地址作为新的PC值。
例如,执行以下BEQ指令:
BEQ x1, x2, label如果寄存器x1和x2的值相等,则执行跳转到标签为"label"的指令处执行。否则,将继续执行下一条指令。
BEQ指令常用于条件判断和循环控制,根据比较结果决定程序的执行路径。
总结:BEQ指令用于在两个寄存器的值相等时执行有条件的相对跳转。它常用于条件判断和循环控制。如果比较结果为真,则执行跳转到目标地址;否则,继续执行下一条指令。
-
BNE
BNE(Branch if Not Equal)指令是RV64指令集中的一条指令,用于在两个寄存器的值不相等时执行有条件的相对跳转。
BNE指令的格式为:BNE rs1, rs2, imm。
- "BNE"是指令名称,表示不等时分支(Branch if Not Equal)。
- "rs1"和"rs2"是比较操作数的源寄存器。
- "imm"是有符号的立即数偏移量,表示跳转目标相对于当前指令的距离。
BNE指令的执行流程如下:
- 比较寄存器rs1和rs2的值,如果不相等,则执行跳转,否则执行下一条指令。
- 计算跳转目标地址:将当前指令的地址(PC值)加上偏移量imm,作为跳转目标地址。
- 将计算得到的跳转目标地址作为新的PC值。
例如,执行以下BNE指令:
BNE x1, x2, label如果寄存器x1和x2的值不相等,则执行跳转到标签为"label"的指令处执行。否则,将继续执行下一条指令。
BNE指令常用于条件判断和循环控制,根据比较结果决定程序的执行路径。
总结:BNE指令用于在两个寄存器的值不相等时执行有条件的相对跳转。它常用于条件判断和循环控制。如果比较结果为真,则执行跳转到目标地址;否则,继续执行下一条指令。
-
BLT
BLT(Branch if Less Than)指令是RV64指令集中的一条指令,用于在有符号整数比较中,当一个寄存器的值小于另一个寄存器的值时执行有条件的相对跳转。
BLT指令的格式为:BLT rs1, rs2, imm。
- "BLT"是指令名称,表示小于时分支(Branch if Less Than)。
- "rs1"和"rs2"是比较操作数的源寄存器。
- "imm"是有符号的立即数偏移量,表示跳转目标相对于当前指令的距离。
BLT指令的执行流程如下:
- 对于带符号整数,比较寄存器rs1和rs2的值,如果rs1的值小于rs2的值,则执行跳转,否则执行下一条指令。
- 计算跳转目标地址:将当前指令的地址(PC值)加上偏移量imm,作为跳转目标地址。
- 将计算得到的跳转目标地址作为新的PC值。
例如,执行以下BLT指令:
BLT x1, x2, label如果寄存器x1的值小于寄存器x2的值,则执行跳转到标签为"label"的指令处执行。否则,将继续执行下一条指令。
BLT指令常用于条件判断和循环控制,特别适用于处理带符号整数的大小比较。
总结:BLT指令用于在有符号整数比较中,当一个寄存器的值小于另一个寄存器的值时执行有条件的相对跳转。它常用于条件判断和循环控制。如果比较结果为真,则执行跳转到目标地址;否则,继续执行下一条指令。
-
BGE
BGE(Branch if Greater Than or Equal)指令是RV64指令集中的一条指令,用于在有符号整数比较中,当一个寄存器的值大于或等于另一个寄存器的值时执行有条件的相对跳转。
BGE指令的格式为:BGE rs1, rs2, imm。
- "BGE"是指令名称,表示大于或等于时分支(Branch if Greater Than or Equal)。
- "rs1"和"rs2"是比较操作数的源寄存器。
- "imm"是有符号的立即数偏移量,表示跳转目标相对于当前指令的距离。
BGE指令的执行流程如下:
- 对于带符号整数,比较寄存器rs1和rs2的值,如果rs1的值大于或等于rs2的值,则执行跳转,否则执行下一条指令。
- 计算跳转目标地址:将当前指令的地址(PC值)加上偏移量imm,作为跳转目标地址。
- 将计算得到的跳转目标地址作为新的PC值。
例如,执行以下BGE指令:
BGE x1, x2, label如果寄存器x1的值大于或等于寄存器x2的值,则执行跳转到标签为"label"的指令处执行。否则,将继续执行下一条指令。
BGE指令常用于条件判断和循环控制,特别适用于处理带符号整数的大小比较。
总结:BGE指令用于在有符号整数比较中,当一个寄存器的值大于或等于另一个寄存器的值时执行有条件的相对跳转。它常用于条件判断和循环控制。如果比较结果为真,则执行跳转到目标地址;否则,继续执行下一条指令。
-
BLTU
BLTU(Branch if Less Than, Unsigned)指令是RV64指令集中的一条指令,用于在无符号整数比较中,当一个寄存器的值小于另一个寄存器的值时执行有条件的相对跳转。
BLTU指令的格式为:BLTU rs1, rs2, imm。
- "BLTU"是指令名称,表示无符号小于时分支(Branch if Less Than, Unsigned)。
- "rs1"和"rs2"是比较操作数的源寄存器。
- "imm"是有符号的立即数偏移量,表示跳转目标相对于当前指令的距离。
BLTU指令的执行流程如下:
- 对于无符号整数,比较寄存器rs1和rs2的值,如果rs1的值小于rs2的值,则执行跳转,否则执行下一条指令。
- 计算跳转目标地址:将当前指令的地址(PC值)加上偏移量imm,作为跳转目标地址。
- 将计算得到的跳转目标地址作为新的PC值。
例如,执行以下BLTU指令:
BLTU x1, x2, label如果寄存器x1的值小于寄存器x2的值(无符号比较),则执行跳转到标签为"label"的指令处执行。否则,将继续执行下一条指令。
BLTU指令常用于处理无符号整数的大小比较和循环控制。
总结:BLTU指令用于在无符号整数比较中,当一个寄存器的值小于另一个寄存器的值时执行有条件的相对跳转。它常用于处理无符号整数的大小比较和循环控制。如果比较结果为真,则执行跳转到目标地址;否则,继续执行下一条指令。
-
BGEU
BGEU(Branch if Greater Than or Equal, Unsigned)指令是RV64指令集中的一条指令,用于在无符号整数比较中,当一个寄存器的值大于或等于另一个寄存器的值时执行有条件的相对跳转。
BGEU指令的格式为:BGEU rs1, rs2, imm。
- "BGEU"是指令名称,表示无符号大于或等于时分支(Branch if Greater Than or Equal, Unsigned)。
- "rs1"和"rs2"是比较操作数的源寄存器。
- "imm"是有符号的立即数偏移量,表示跳转目标相对于当前指令的距离。
BGEU指令的执行流程如下:
- 对于无符号整数,比较寄存器rs1和rs2的值,如果rs1的值大于或等于rs2的值,则执行跳转,否则执行下一条指令。
- 计算跳转目标地址:将当前指令的地址(PC值)加上偏移量imm,作为跳转目标地址。
- 将计算得到的跳转目标地址作为新的PC值。
例如,执行以下BGEU指令:
BGEU x1, x2, label如果寄存器x1的值大于或等于寄存器x2的值(无符号比较),则执行跳转到标签为"label"的指令处执行。否则,将继续执行下一条指令。
BGEU指令常用于处理无符号整数的大小比较和循环控制。
总结:BGEU指令用于在无符号整数比较中,当一个寄存器的值大于或等于另一个寄存器的值时执行有条件的相对跳转。它常用于处理无符号整数的大小比较和循环控制。如果比较结果为真,则执行跳转到目标地址;否则,继续执行下一条指令。
-
LB
LB(Load Byte)指令是RV64指令集中的一条指令,用于从内存中读取一个字节(8位数据)到指定的目标寄存器。
LB指令的格式为:LB rd, imm(rs1)。
- "LB"是指令名称,表示加载字节(Load Byte)。
- "rd"是目标寄存器,用于存储读取的字节数据。
- "imm"是有符号的立即数偏移量,表示相对于寄存器rs1的偏移量。
- "rs1"是基址寄存器,存储了存储器地址的基地址。
LB指令的执行流程如下:
- 计算内存地址:将基址寄存器rs1的值加上偏移量imm,得到要读取的字节的内存地址。
- 从内存中读取一个字节(8位数据)。
- 将读取的字节数据符号扩展为64位,并将其存储到目标寄存器rd中。
例如,执行以下LB指令:
LB x1, 4(x2)该指令将从寄存器x2的值加上4作为内存地址,读取该地址处的一个字节,并将符号扩展后的值存储到寄存器x1中。
LB指令常用于加载字节数据,例如从存储器中读取字符、处理图像、文件输入等场景。
总结:LB指令用于从内存中读取一个字节(8位数据),将其符号扩展为64位,并存储到指定的目标寄存器中。它常用于加载字节数据的场景。
-
LH
LH(Load Halfword)指令是RV64指令集中的一条指令,用于从内存中读取一个半字(16位数据)到指定的目标寄存器。
LH指令的格式为:LH rd, imm(rs1)。
- "LH"是指令名称,表示加载半字(Load Halfword)。
- "rd"是目标寄存器,用于存储读取的半字数据。
- "imm"是有符号的立即数偏移量,表示相对于寄存器rs1的偏移量。
- "rs1"是基址寄存器,存储了存储器地址的基地址。
LH指令的执行流程如下:
- 计算内存地址:将基址寄存器rs1的值加上偏移量imm,得到要读取的半字的内存地址。
- 从内存中读取一个半字(16位数据)。
- 将读取的半字数据符号扩展为64位,并将其存储到目标寄存器rd中。
例如,执行以下LH指令:
LH x1, 8(x2)该指令将从寄存器x2的值加上8作为内存地址,读取该地址处的一个半字,并将符号扩展后的值存储到寄存器x1中。
LH指令常用于加载半字数据,例如处理图像、音频等场景。
总结:LH指令用于从内存中读取一个半字(16位数据),将其符号扩展为64位,并存储到指定的目标寄存器中。它常用于加载半字数据的场景。
-
LW
LW(Load Word)指令是RV64指令集中的一条指令,用于从内存中读取一个字(32位数据)到指定的目标寄存器。
LW指令的格式为:LW rd, imm(rs1)。
- "LW"是指令名称,表示加载字(Load Word)。
- "rd"是目标寄存器,用于存储读取的字数据。
- "imm"是有符号的立即数偏移量,表示相对于寄存器rs1的偏移量。
- "rs1"是基址寄存器,存储了存储器地址的基地址。
LW指令的执行流程如下:
- 计算内存地址:将基址寄存器rs1的值加上偏移量imm,得到要读取的字的内存地址。
- 从内存中读取一个字(32位数据)。
- 将读取的字数据存储到目标寄存器rd中。
例如,执行以下LW指令:
LW x1, 12(x2)该指令将从寄存器x2的值加上12作为内存地址,读取该地址处的一个字,并将值存储到寄存器x1中。
LW指令常用于加载字数据,通常用于读取变量、数组、数据结构等。
总结:LW指令用于从内存中读取一个字(32位数据),并将其存储到指定的目标寄存器中。它常用于加载字数据的场景。
-
LBU
LBU(Load Byte, Unsigned)指令是RV64指令集中的一条指令,用于从内存中读取一个字节(8位数据)的无符号值,并将其存储到目标寄存器中。
LBU指令的格式为:LBU rd, imm(rs1)。
- "LBU"是指令名称,表示加载无符号字节(Load Byte, Unsigned)。
- "rd"是目标寄存器,用于存储读取的无符号字节数据。
- "imm"是有符号的立即数偏移量,表示相对于寄存器rs1的偏移量。
- "rs1"是基址寄存器,存储了存储器地址的基地址。
LBU指令的执行流程如下:
- 计算内存地址:将基址寄存器rs1的值加上偏移量imm,得到要读取的字节的内存地址。
- 从内存中读取一个字节(8位数据)。
- 将读取的字节数据的高位补零,并将结果存储到目标寄存器rd中。
例如,执行以下LBU指令:
LBU x1, 4(x2)该指令将从寄存器x2的值加上4作为内存地址,读取该地址处的一个字节,并将高位补零后的无符号值存储到寄存器x1中。
LBU指令常用于加载无符号的字节数据,例如处理图像、传感器数据等场景。
总结:LBU指令用于从内存中读取一个字节(8位数据)的无符号值,并将高位补零后的结果存储到指定的目标寄存器中。它常用于加载无符号字节数据的场景。
-
LHU
LHU(Load Halfword, Unsigned)指令是RV64指令集中的一条指令,用于从内存中读取一个半字(16位数据)的无符号值,并将其存储到目标寄存器中。
LHU指令的格式为:LHU rd, imm(rs1)。
- "LHU"是指令名称,表示加载无符号半字(Load Halfword, Unsigned)。
- "rd"是目标寄存器,用于存储读取的无符号半字数据。
- "imm"是有符号的立即数偏移量,表示相对于寄存器rs1的偏移量。
- "rs1"是基址寄存器,存储了存储器地址的基地址。
LHU指令的执行流程如下:
- 计算内存地址:将基址寄存器rs1的值加上偏移量imm,得到要读取的半字的内存地址。
- 从内存中读取一个半字(16位数据)。
- 将读取的半字数据的高位补零,并将结果存储到目标寄存器rd中。
例如,执行以下LHU指令:
LHU x1, 8(x2)该指令将从寄存器x2的值加上8作为内存地址,读取该地址处的一个半字,并将高位补零后的无符号值存储到寄存器x1中。
LHU指令常用于加载无符号的半字数据,例如处理图像、传感器数据等场景。
总结:LHU指令用于从内存中读取一个半字(16位数据)的无符号值,并将高位补零后的结果存储到指定的目标寄存器中。它常用于加载无符号半字数据的场景。
-
SB
SB(Store Byte)指令是RV64指令集中的一条指令,用于将一个字节(8位数据)存储到内存中的指定位置。
SB指令的格式为:SB rs2, imm(rs1)。
- "SB"是指令名称,表示存储字节(Store Byte)。
- "rs2"是源寄存器,存储要存储的字节数据。
- "imm"是有符号的立即数偏移量,表示相对于寄存器rs1的偏移量。
- "rs1"是基址寄存器,存储了存储器地址的基地址。
SB指令的执行流程如下:
- 计算内存地址:将基址寄存器rs1的值加上偏移量imm,得到要存储的字节的内存地址。
- 将源寄存器rs2中的字节数据存储到内存地址中。
例如,执行以下SB指令:
SB x1, 4(x2)该指令将寄存器x1中的字节数据存储到寄存器x2的值加上4作为内存地址的位置。
SB指令常用于存储字节数据到内存中,例如写入字符、操作图像、文件输出等场景。
总结:SB指令用于将一个字节(8位数据)存储到内存中的指定位置。它提供了存储字节数据的功能,常用于存储字节数据到内存的场景。
-
SH
SH(Store Halfword)指令是RV64指令集中的一条指令,用于将一个半字(16位数据)存储到内存中的指定位置。
SH指令的格式为:SH rs2, imm(rs1)。
- "SH"是指令名称,表示存储半字(Store Halfword)。
- "rs2"是源寄存器,存储要存储的半字数据。
- "imm"是有符号的立即数偏移量,表示相对于寄存器rs1的偏移量。
- "rs1"是基址寄存器,存储了存储器地址的基地址。
SH指令的执行流程如下:
- 计算内存地址:将基址寄存器rs1的值加上偏移量imm,得到要存储的半字的内存地址。
- 将源寄存器rs2中的半字数据存储到内存地址中。
例如,执行以下SH指令:
SH x1, 8(x2)该指令将寄存器x1中的半字数据存储到寄存器x2的值加上8作为内存地址的位置。
SH指令常用于存储半字数据到内存中,例如存储图像像素数据、音频采样数据等场景。
总结:SH指令用于将一个半字(16位数据)存储到内存中的指定位置。它提供了存储半字数据的功能,常用于存储半字数据到内存的场景。
-
SW
SW(Store Word)指令是RV64指令集中的一条指令,用于将一个字(32位数据)存储到内存中的指定位置。
SW指令的格式为:SW rs2, imm(rs1)。
- "SW"是指令名称,表示存储字(Store Word)。
- "rs2"是源寄存器,存储要存储的字数据。
- "imm"是有符号的立即数偏移量,表示相对于寄存器rs1的偏移量。
- "rs1"是基址寄存器,存储了存储器地址的基地址。
SW指令的执行流程如下:
- 计算内存地址:将基址寄存器rs1的值加上偏移量imm,得到要存储的字的内存地址。
- 将源寄存器rs2中的字数据存储到内存地址中。
例如,执行以下SW指令:
SW x1, 12(x2)该指令将寄存器x1中的字数据存储到寄存器x2的值加上12作为内存地址的位置。
SW指令常用于存储字数据到内存中,例如保存变量、数组、数据结构等场景。
总结:SW指令用于将一个字(32位数据)存储到内存中的指定位置。它提供了存储字数据的功能,常用于将字数据存储到内存的场景。
-
ADDI
ADDI(Add Immediate)指令是RV64指令集中的一条指令,用于将立即数(immediate)和一个源寄存器的值相加,并将结果存储到目标寄存器中。
ADDI指令的格式为:ADDI rd, rs1, imm。
- "ADDI"是指令名称,表示加法立即数(Add Immediate)。
- "rd"是目标寄存器,用于存储相加后的结果。
- "rs1"是源寄存器,存储了参与相加的值。
- "imm"是有符号的立即数,表示要与rs1相加的常数值。
ADDI指令的执行流程如下:
- 将源寄存器rs1的值和立即数imm进行有符号整数相加。
- 将相加后的结果存储到目标寄存器rd中。
例如,执行以下ADDI指令:
ADDI x1, x2, 10该指令将寄存器x2的值与立即数10相加,并将结果存储到寄存器x1中。
ADDI指令常用于进行常数的加法运算,例如添加偏移量、增加计数器等。它是一条常见的指令,用于在计算中加上一个常数值。
总结:ADDI指令用于将立即数和源寄存器的值相加,并将结果存储到目标寄存器中。它用于进行常数的加法运算,常用于添加偏移量、增加计数器等场景。
-
SLTI
SLTI(Set Less Than Immediate)指令是RV64指令集中的一条指令,用于比较一个源寄存器的值是否小于一个有符号的立即数,并将比较结果(0或1)存储到目标寄存器中。
SLTI指令的格式为:SLTI rd, rs1, imm。
- "SLTI"是指令名称,表示设置小于立即数(Set Less Than Immediate)。
- "rd"是目标寄存器,用于存储比较的结果。如果rs1的值小于立即数imm,则将1存储到rd中;否则将0存储到rd中。
- "rs1"是源寄存器,存储了要进行比较的值。
- "imm"是有符号的立即数,用作比较的参考值。
SLTI指令的执行流程如下:
- 将源寄存器rs1的值与有符号立即数imm进行比较。如果rs1的值小于imm,则设置目标寄存器rd为1;否则设置为0。
例如,执行以下SLTI指令:
SLTI x1, x2, 10该指令将比较寄存器x2的值和立即数10。如果x2的值小于10,则将1存储到寄存器x1中;否则将0存储到寄存器x1中。
SLTI指令常用于在条件判断和分支操作中,用于检查一个值是否小于一个常数。
总结:SLTI指令用于比较源寄存器的值是否小于有符号的立即数,并将比较结果(0或1)存储到目标寄存器中。它常用于条件判断和分支操作,用于检查一个值是否小于一个常数。
-
SLTIU
SLTIU(Set Less Than Immediate Unsigned)指令是RV64指令集中的一条指令,用于比较一个源寄存器的无符号值是否小于一个无符号的立即数,并将比较结果(0或1)存储到目标寄存器中。
SLTIU指令的格式为:SLTIU rd, rs1, imm。
- "SLTIU"是指令名称,表示无符号比较立即数(Set Less Than Immediate Unsigned)。
- "rd"是目标寄存器,用于存储比较的结果。如果rs1的无符号值小于无符号立即数imm,则将1存储到rd中;否则将0存储到rd中。
- "rs1"是源寄存器,存储了要进行比较的值。
- "imm"是无符号的立即数,用作比较的参考值。
SLTIU指令的执行流程如下:
- 将源寄存器rs1的无符号值与无符号立即数imm进行比较。如果rs1的无符号值小于imm,则设置目标寄存器rd为1;否则设置为0。
例如,执行以下SLTIU指令:
SLTIU x1, x2, 10该指令将比较寄存器x2的无符号值和立即数10。如果x2的无符号值小于10,则将1存储到寄存器x1中;否则将0存储到寄存器x1中。
SLTIU指令常用于在条件判断和分支操作中,用于检查一个无符号值是否小于一个常数。
总结:SLTIU指令用于比较源寄存器的无符号值是否小于无符号的立即数,并将比较结果(0或1)存储到目标寄存器中。它常用于条件判断和分支操作,用于检查一个无符号值是否小于一个常数。
-
XORI
XORI(XOR Immediate)指令是RV64指令集中的一条指令,用于对一个源寄存器的值执行与一个立即数的位异或操作,并将结果存储到目标寄存器中。
XORI指令的格式为:XORI rd, rs1, imm。
- "XORI"是指令名称,表示位异或立即数(XOR Immediate)。
- "rd"是目标寄存器,用于存储位异或操作的结果。
- "rs1"是源寄存器,存储了要进行位异或操作的值。
- "imm"是无符号的立即数,作为位异或操作的参考值。
XORI指令的执行流程如下:
- 将源寄存器rs1的值与立即数imm进行位异或操作。
- 将位异或操作的结果存储到目标寄存器rd中。
例如,执行以下XORI指令:
XORI x1, x2, 10该指令将寄存器x2的值与立即数10进行位异或操作,并将结果存储到寄存器x1中。
XORI指令常用于执行逻辑运算,特别是对寄存器中的某些位进行反转或切换的场景。
总结:XORI指令用于对源寄存器的值执行与立即数的位异或操作,并将结果存储到目标寄存器中。它常用于执行逻辑运算,特别是对寄存器中的某些位进行反转或切换的场景。
-
ORI
ORI(OR Immediate)指令是RV64指令集中的一条指令,用于对一个源寄存器的值执行与一个立即数的按位或操作,并将结果存储到目标寄存器中。
ORI指令的格式为:ORI rd, rs1, imm。
- "ORI"是指令名称,表示按位或立即数(OR Immediate)。
- "rd"是目标寄存器,用于存储按位或操作的结果。
- "rs1"是源寄存器,存储了要进行按位或操作的值。
- "imm"是无符号的立即数,作为按位或操作的参考值。
ORI指令的执行流程如下:
- 将源寄存器rs1的值与立即数imm进行按位或操作。
- 将按位或操作的结果存储到目标寄存器rd中。
例如,执行以下ORI指令:
ORI x1, x2, 10该指令将寄存器x2的值与立即数10进行按位或操作,并将结果存储到寄存器x1中。
ORI指令常用于执行逻辑运算,特别是对寄存器中的特定位进行控制或合并的场景。通过与立即数进行按位或操作,可以将特定位设置为1,忽略或保留其他位的值。
总结:ORI指令用于对源寄存器的值执行与立即数的按位或操作,并将结果存储到目标寄存器中。它常用于执行逻辑运算,特别是对寄存器中的特定位进行控制或合并的场景。
-
ANDI
ANDI(AND Immediate)指令是RV64指令集中的一条指令,用于对一个源寄存器的值执行与一个立即数的按位与操作,并将结果存储到目标寄存器中。
ANDI指令的格式为:ANDI rd, rs1, imm。
- "ANDI"是指令名称,表示按位与立即数(AND Immediate)。
- "rd"是目标寄存器,用于存储按位与操作的结果。
- "rs1"是源寄存器,存储了要进行按位与操作的值。
- "imm"是无符号的立即数,作为按位与操作的参考值。
ANDI指令的执行流程如下:
- 将源寄存器rs1的值与立即数imm进行按位与操作。
- 将按位与操作的结果存储到目标寄存器rd中。
例如,执行以下ANDI指令:
ANDI x1, x2, 10该指令将寄存器x2的值与立即数10进行按位与操作,并将结果存储到寄存器x1中。
ANDI指令常用于掩码操作、位清除或位保留等场景。通过与立即数进行按位与操作,可以将特定位保留为1或清除为0,将其他位的值保持不变。
总结:ANDI指令用于对源寄存器的值执行与立即数的按位与操作,并将结果存储到目标寄存器中。它常用于掩码操作、位清除或位保留等场景。
-
SLLI
SLLI(Shift Left Logical Immediate)指令是RV64指令集中的一条指令,用于将一个源寄存器的值左移一个指定的立即数位数,并将结果存储到目标寄存器中。左移操作通过在右侧插入零来移动位。
SLLI指令的格式为:SLLI rd, rs1, shamt。
- "SLLI"是指令名称,表示左移逻辑立即数(Shift Left Logical Immediate)。
- "rd"是目标寄存器,用于存储左移操作的结果。
- "rs1"是源寄存器,存储了要进行左移操作的值。
- "shamt"是无符号的立即数,表示左移的位数。
SLLI指令的执行流程如下:
- 将源寄存器rs1的值左移shamt个位数。
- 将左移操作的结果存储到目标寄存器rd中。
例如,执行以下SLLI指令:
SLLI x1, x2, 3该指令将寄存器x2的值左移3位,并将结果存储到寄存器x1中。
SLLI指令常用于位级操作和乘法运算的优化,例如在位级编码、位掩码和乘以2的幂等运算中。
总结:SLLI指令用于将源寄存器的值左移一个指定的立即数位数,并将结果存储到目标寄存器中。它常用于位级操作和乘法运算的优化。
-
SRLI
SRLI(Shift Right Logical Immediate)指令是RV64指令集中的一条指令,用于将一个源寄存器的值右移一个指定的立即数位数,并将结果存储到目标寄存器中。右移操作通过在左侧插入零来移动位。
SRLI指令的格式为:SRLI rd, rs1, shamt。
- "SRLI"是指令名称,表示右移逻辑立即数(Shift Right Logical Immediate)。
- "rd"是目标寄存器,用于存储右移操作的结果。
- "rs1"是源寄存器,存储了要进行右移操作的值。
- "shamt"是无符号的立即数,表示右移的位数。
SRLI指令的执行流程如下:
- 将源寄存器rs1的值右移shamt个位数。
- 将右移操作的结果存储到目标寄存器rd中。
例如,执行以下SRLI指令:
SRLI x1, x2, 3该指令将寄存器x2的值右移3位,并将结果存储到寄存器x1中。
SRLI指令常用于位级操作和除法运算的优化,例如在位级编码、位清除和除以2的幂等运算中。
总结:SRLI指令用于将源寄存器的值右移一个指定的立即数位数,并将结果存储到目标寄存器中。它常用于位级操作和除法运算的优化。
-
SRAI
SRAI(Shift Right Arithmetic Immediate)指令是RV64指令集中的一条指令,用于将一个源寄存器的值右移一个指定的立即数位数,并将结果存储到目标寄存器中。右移操作通过在左侧插入原始值的最高位来保持有符号数的符号不变。
SRAI指令的格式为:SRAI rd, rs1, shamt。
- "SRAI"是指令名称,表示右移算术立即数(Shift Right Arithmetic Immediate)。
- "rd"是目标寄存器,用于存储右移操作的结果。
- "rs1"是源寄存器,存储了要进行右移操作的值。
- "shamt"是无符号的立即数,表示右移的位数。
SRAI指令的执行流程如下:
- 将源寄存器rs1的值右移shamt个位数。
- 在右移操作过程中,将原始值的最高位保持不变。
- 将右移操作的结果存储到目标寄存器rd中。
例如,执行以下SRAI指令:
SRAI x1, x2, 3该指令将寄存器x2的值右移3位,并将结果存储到寄存器x1中,保持有符号数的符号位不变。
SRAI指令常用于有符号数的算术右移操作,例如对带有符号位的整型数据进行除法或降低精度操作时。
总结:SRAI指令用于将源寄存器的值右移一个指定的立即数位数,并将结果存储到目标寄存器中。它常用于有符号数的算术右移操作。
-
ADD
ADD指令是RV64指令集中的一条指令,用于将两个源寄存器的值相加,并将结果存储到目标寄存器中。
ADD指令的格式为:ADD rd, rs1, rs2。
- "ADD"是指令名称,表示相加(Add)。
- "rd"是目标寄存器,用于存储相加操作的结果。
- "rs1"和"rs2"分别是源寄存器1和源寄存器2,存储了要相加的两个值。
ADD指令的执行流程如下:
- 将源寄存器rs1和rs2的值相加。
- 将相加的结果存储到目标寄存器rd中。
例如,执行以下ADD指令:
ADD x1, x2, x3该指令将寄存器x2和x3的值相加,并将结果存储到寄存器x1中。
ADD指令常用于算术和数据操作,用于对两个寄存器中的值进行相加操作。
总结:ADD指令用于将两个源寄存器的值相加,并将结果存储到目标寄存器中。它常用于算术和数据操作,用于对寄存器中的值进行相加操作。
-
SUB
SUB指令是RV64指令集中的一条指令,用于将一个源寄存器的值减去另一个源寄存器的值,并将结果存储到目标寄存器中。
SUB指令的格式为:SUB rd, rs1, rs2。
- "SUB"是指令名称,表示减法(Subtract)。
- "rd"是目标寄存器,用于存储减法操作的结果。
- "rs1"是被减数的源寄存器,存储了要进行减法操作的值。
- "rs2"是减数的源寄存器,存储了要减去的值。
SUB指令的执行流程如下:
- 将源寄存器rs1的值减去源寄存器rs2的值。
- 将减法操作的结果存储到目标寄存器rd中。
例如,执行以下SUB指令:
SUB x1, x2, x3该指令将寄存器x2的值减去寄存器x3的值,并将结果存储到寄存器x1中。
SUB指令常用于算术和数据操作,用于对两个寄存器中的值进行减法操作。
总结:SUB指令用于将一个源寄存器的值减去另一个源寄存器的值,并将结果存储到目标寄存器中。它常用于算术和数据操作,用于对寄存器中的值进行减法操作。
-
SLL
在RV64指令集中,SLL指令(Shift Left Logical)用于将一个源寄存器中的值左移一个指定的位数,并将结果存储到目标寄存器中。左移操作通过在右侧插入零来移动位。
SLL指令的格式为:SLL rd, rs1, rs2。
- "SLL"是指令名称,表示左移逻辑(Shift Left Logical)。
- "rd"是目标寄存器,用于存储左移操作的结果。
- "rs1"是源寄存器1,存储要进行左移操作的值。
- "rs2"是源寄存器2,存储了左移的位数。
SLL指令的执行流程如下:
- 将源寄存器rs1的值左移si2寄存器中指定的位数。
- 将左移操作的结果存储到目标寄存器rd中。
例如,执行以下SLL指令:
SLL x1, x2, x3该指令将寄存器x2的值左移寄存器x3中指定的位数,并将结果存储到寄存器x1中。
SLL指令常用于位级操作和乘法运算的优化,例如在位级编码、位掩码和乘以2的幂等运算中。
总结:SLL指令用于将源寄存器的值左移一个指定的位数,并将结果存储到目标寄存器中。它常用于位级操作和乘法运算的优化。
-
SLT
在RV64指令集中,SLT指令(Set Less Than)用于比较两个寄存器中的有符号整数值,如果第一个寄存器的值小于第二个寄存器的值,则将目标寄存器设置为1,否则设置为0。
SLT指令的格式为:SLT rd, rs1, rs2。
- "SLT"是指令名称,表示设置小于(Set Less Than)。
- "rd"是目标寄存器,用于存储比较结果。
- "rs1"是第一个源寄存器,存储要进行比较的值。
- "rs2"是第二个源寄存器,存储要将第一个值与之进行比较的值。
SLT指令的执行流程如下:
- 比较源寄存器rs1和rs2中的有符号整数值。
- 如果rs1中的值小于rs2中的值,则将目标寄存器rd设置为1;否则设置为0。
例如,执行以下SLT指令:
SLT x1, x2, x3该指令将比较寄存器x2和x3中的有符号整数值。如果x2的值小于x3的值,则将寄存器x1设置为1,否则设置为0。
SLT指令常用于条件判断和控制流操作,例如在分支语句和循环中根据两个值的大小关系进行条件分支。
总结:SLT指令用于比较两个寄存器中的有符号整数值,如果第一个寄存器的值小于第二个寄存器的值,则设置目标寄存器为1,否则为0。它常用于条件判断和控制流操作中。
-
SLTU
在RV64指令集中,SLTU指令(Set Less Than Unsigned)用于比较两个寄存器中的无符号整数值,若第一个寄存器的值小于第二个寄存器的值,则将目标寄存器设置为1,否则设置为0。
SLTU指令的格式为:SLTU rd, rs1, rs2。
- "SLTU"是指令名称,表示设置无符号小于(Set Less Than Unsigned)。
- "rd"是目标寄存器,用于存储比较结果。
- "rs1"是第一个源寄存器,存储要进行比较的值。
- "rs2"是第二个源寄存器,存储要将第一个值与之进行比较的值。
SLTU指令的执行流程如下:
- 比较源寄存器rs1和rs2中的无符号整数值。
- 如果rs1中的值小于rs2中的值(无符号比较),则将目标寄存器rd设置为1;否则设置为0。
例如,执行以下SLTU指令:
SLTU x1, x2, x3该指令将比较寄存器x2和x3中的无符号整数值。如果x2的值小于x3的值(无符号比较),则将寄存器x1设置为1,否则设置为0。
SLTU指令常用于无符号整数的条件判断和控制流操作,例如在分支语句和循环中根据两个无符号整数的大小关系进行条件分支。
总结:SLTU指令用于比较两个寄存器中的无符号整数值,如果第一个寄存器的值小于第二个寄存器的值(无符号比较),则将目标寄存器设置为1,否则为0。它常用于无符号整数的条件判断和控制流操作中。
-
XOR
在RV64指令集中,XOR指令(Exclusive OR)用于对两个源寄存器中的值执行异或(XOR)操作,并将结果存储到目标寄存器中。
XOR指令的格式为:XOR rd, rs1, rs2。
- "XOR"是指令名称,表示异或操作。
- "rd"是目标寄存器,用于存储异或操作的结果。
- "rs1"和"rs2"是源寄存器,存储了要执行异或操作的两个值。
XOR指令的执行流程如下:
- 将源寄存器rs1和rs2中的值执行异或操作。
- 将异或操作的结果存储到目标寄存器rd中。
例如,执行以下XOR指令:
XOR x1, x2, x3该指令将寄存器x2和x3中的值执行异或操作,并将结果存储到寄存器x1中。
XOR指令常用于逻辑运算和数据加密等领域,用于对二进制数据进行异或操作。
总结:XOR指令用于对两个源寄存器中的值执行异或操作,并将结果存储到目标寄存器中。它常用于逻辑运算和数据加密等领域。
-
SRL
在RV64指令集中,SRL指令(Shift Right Logical)用于将一个源寄存器中的值右移一个指定的位数,并将结果存储到目标寄存器中。右移操作通过在左侧插入零来移动位。
SRL指令的格式为:SRL rd, rs1, rs2。
- "SRL"是指令名称,表示右移逻辑(Shift Right Logical)。
- "rd"是目标寄存器,用于存储右移操作的结果。
- "rs1"是源寄存器1,存储要进行右移操作的值。
- "rs2"是源寄存器2,存储了右移的位数。
SRL指令的执行流程如下:
- 将源寄存器rs1的值右移rs2寄存器中指定的位数。
- 将右移操作的结果存储到目标寄存器rd中。
例如,执行以下SRL指令:
SRL x1, x2, x3该指令将寄存器x2的值右移寄存器x3中指定的位数,并将结果存储到寄存器x1中。
SRL指令常用于位级操作和除法运算的优化,例如在位级编码、位截断和除以2的幂等运算中。
总结:SRL指令用于将源寄存器的值右移一个指定的位数,并将结果存储到目标寄存器中。它常用于位级操作和除法运算的优化。
-
SRA
在RV64指令集中,SRA指令(Shift Right Arithmetic)用于将一个源寄存器中的值右移一个指定的位数,并将结果存储到目标寄存器中。右移操作通过在左侧插入原始值的最高位来保持符号。
SRA指令的格式为:SRA rd, rs1, rs2。
- "SRA"是指令名称,表示右移算术(Shift Right Arithmetic)。
- "rd"是目标寄存器,用于存储右移操作的结果。
- "rs1"是源寄存器1,存储要进行右移操作的值。
- "rs2"是源寄存器2,存储了右移的位数。
SRA指令的执行流程如下:
- 将源寄存器rs1的值右移rs2寄存器中指定的位数。
- 保持符号位不变,通过在左侧插入原始值的最高位来保持值的符号。
- 将右移操作的结果存储到目标寄存器rd中。
例如,执行以下SRA指令:
SRA x1, x2, x3该指令将寄存器x2的值右移寄存器x3中指定的位数,并将结果存储到寄存器x1中。
SRA指令常用于带符号整数的算术操作,例如除法运算中的带符号右移以保持正确的符号。
总结:SRA指令用于将源寄存器的值右移一个指定的位数,并保持符号位不变,将结果存储到目标寄存器中。它常用于带符号整数的算术操作。
-
OR
在RV64指令集中,OR指令用于执行按位逻辑或(OR)操作,将两个源寄存器中的值进行逻辑或运算,并将结果存储到目标寄存器中。
OR指令的格式为:OR rd, rs1, rs2。
- "OR"是指令名称,表示按位逻辑或(Bitwise OR)操作。
- "rd"是目标寄存器,用于存储逻辑或操作的结果。
- "rs1"和"rs2"是源寄存器,存储了要进行逻辑或操作的两个值。
OR指令的执行流程如下:
- 执行源寄存器rs1和rs2中的值的逻辑或操作:对于每一个位,如果任一源寄存器中的对应位为1,结果寄存器的对应位就为1,否则为0。
- 将逻辑或操作的结果存储到目标寄存器rd中。
例如,执行以下OR指令:
OR x1, x2, x3该指令将寄存器x2和x3中的值执行逻辑或操作,并将结果存储到寄存器x1中。
OR指令常用于位级操作,例如在掩码和标志设置中用来设置特定位。
总结:OR指令用于将两个源寄存器中的值进行按位逻辑或操作,并将结果存储到目标寄存器中。它常用于位级操作和标志设置中。
-
AND
在RV64指令集中,AND指令用于执行按位逻辑与(AND)操作,将两个源寄存器中的值进行逻辑与运算,并将结果存储到目标寄存器中。
AND指令的格式为:AND rd, rs1, rs2。
- "AND"是指令名称,表示按位逻辑与(Bitwise AND)操作。
- "rd"是目标寄存器,用于存储逻辑与操作的结果。
- "rs1"和"rs2"是源寄存器,存储了要进行逻辑与操作的两个值。
AND指令的执行流程如下:
- 执行源寄存器rs1和rs2中的值的逻辑与操作:对于每一个位,只有当两个源寄存器中对应位都为1时,结果寄存器的对应位才为1,否则为0。
- 将逻辑与操作的结果存储到目标寄存器rd中。
例如,执行以下AND指令:
AND x1, x2, x3该指令将寄存器x2和x3中的值执行逻辑与操作,并将结果存储到寄存器x1中。
AND指令常用于位级操作,例如在位掩码、清除特定位和获取特定位的值等场景中。
总结:AND指令用于将两个源寄存器中的值进行按位逻辑与操作,并将结果存储到目标寄存器中。它常用于位级操作和位掩码计算中。
-
LWU
在RV64指令集中,LWU指令(Load Word Unsigned)用于从内存中加载一个32位的无符号整数,并将其存储到目标寄存器中。
LWU指令的格式为:LWU rd, offset(rs1)。
- "LWU"是指令名称,表示加载无符号字(Load Word Unsigned)。
- "rd"是目标寄存器,用于存储从内存中加载的无符号整数。
- "offset"是一个立即数,表示相对于基地址寄存器rs1的偏移量。
- "rs1"是基地址寄存器,用于计算内存访问的实际地址。
执行LWU指令的步骤如下:
- 计算实际的内存地址为基地址寄存器rs1的值加上偏移量offset。
- 从计算得到的内存地址中读取4个字节(32位)的数据。
- 将读取的无符号整数存储到目标寄存器rd中。
例如,执行以下LWU指令:
LWU x1, 100(x2)该指令将从内存地址x2 + 100处加载一个无符号整数,并将其存储到寄存器x1中。
LWU指令适用于需要加载无符号整数的场景,例如处理图像数据、网络数据包和其他无符号整数数据的处理。
总结:LWU指令用于从内存中加载一个32位的无符号整数,并存储到目标寄存器中。它适用于处理无符号整数数据的场景。
-
LD
在RV64指令集中,LD指令(Load Doubleword)用于从内存中加载一个64位的数据,并将其存储到目标寄存器中。
LD指令的格式为:LD rd, offset(rs1)。
- "LD"是指令名称,表示加载双字(Load Doubleword)。
- "rd"是目标寄存器,用于存储从内存中加载的64位数据。
- "offset"是一个立即数,表示相对于基地址寄存器rs1的偏移量。
- "rs1"是基地址寄存器,用于计算内存访问的实际地址。
执行LD指令的步骤如下:
- 计算实际的内存地址为基地址寄存器rs1的值加上偏移量offset。
- 从计算得到的内存地址中读取8个字节(64位)的数据。
- 将读取的数据存储到目标寄存器rd中。
例如,执行以下LD指令:
LD x1, 100(x2)该指令将从内存地址x2 + 100处加载一个64位的数据,并将其存储到寄存器x1中。
LD指令通常用于加载64位的整数、地址或者双精度浮点数等数据,适用于处理需要较大数据类型的场景,比如数组操作、指针计算和浮点数运算等。
总结:LD指令用于从内存加载一个64位的数据,并将其存储到目标寄存器中。它适用于处理较大的数据类型,如整数、地址或双精度浮点数等。
-
SD
在RV64指令集中,SD指令(Store Doubleword)用于将一个64位的数据存储到内存中。
SD指令的格式为:SD rs2, offset(rs1)。
- "SD"是指令名称,表示存储双字(Store Doubleword)。
- "rs2"是源寄存器,包含要存储到内存中的64位数据。
- "offset"是一个立即数,表示相对于基地址寄存器rs1的偏移量。
- "rs1"是基地址寄存器,用于计算内存访问的实际地址。
执行SD指令的步骤如下:
- 计算实际的内存地址为基地址寄存器rs1的值加上偏移量offset。
- 将源寄存器rs2中的64位数据存储到计算得到的内存地址中。
例如,执行以下SD指令:
SD x1, 100(x2)该指令将寄存器x1中的64位数据存储到内存地址x2 + 100处。
SD指令常用于将64位的整数、地址或者双精度浮点数等数据存储到内存中的操作。在数组操作、指针计算和浮点数运算等场景中经常使用。
总结:SD指令用于将一个64位的数据存储到内存中。它适用于存储较大的数据类型,如整数、地址或双精度浮点数等。
-
SLLI
在RV64指令集中,SLLI指令(Shift Left Logical Immediate)用于对一个源寄存器中的数值进行逻辑左移操作,并将结果存储到目标寄存器中。
SLLI指令的格式为:SLLI rd, rs1, shamt。
- "SLLI"是指令名称,表示逻辑左移(Shift Left Logical Immediate)。
- "rd"是目标寄存器,用于存储逻辑左移操作的结果。
- "rs1"是源寄存器,存储了要进行左移操作的数值。
- "shamt"是立即数,表示左移的位数。
执行SLLI指令的步骤如下:
- 将源寄存器rs1中的数值左移shamt位,空位用零填充。
- 将左移后的结果存储到目标寄存器rd中。
例如,执行以下SLLI指令:
SLLI x1, x2, 4该指令将寄存器x2中的数值进行逻辑左移4位,然后将结果存储到寄存器x1中。
SLLI指令常用于位操作,用于对数值进行位级的移位操作。逻辑左移可以实现对数值的乘以2的幂的操作,例如将一个数左移1位相当于将其乘以2。该指令在位操作和算术运算中都有广泛的应用。
总结:SLLI指令用于对一个源寄存器中的数值进行逻辑左移操作,并将结果存储到目标寄存器中。它常用于位操作和算术运算,实现数值的乘以2的幂操作。
-
SRLI
在RV64指令集中,SRLI指令(Shift Right Logical Immediate)用于对一个源寄存器中的数值进行逻辑右移操作,并将结果存储到目标寄存器中。
SRLI指令的格式为:SRLI rd, rs1, shamt。
- "SRLI"是指令名称,表示逻辑右移(Shift Right Logical Immediate)。
- "rd"是目标寄存器,用于存储逻辑右移操作的结果。
- "rs1"是源寄存器,存储了要进行右移操作的数值。
- "shamt"是立即数,表示右移的位数。
执行SRLI指令的步骤如下:
- 将源寄存器rs1中的数值右移shamt位,空位用零填充。
- 将右移后的结果存储到目标寄存器rd中。
例如,执行以下SRLI指令:
SRLI x1, x2, 3该指令将寄存器x2中的数值进行逻辑右移3位,然后将结果存储到寄存器x1中。
SRLI指令常用于位操作,用于对数值进行位级的移位操作。逻辑右移可以实现对数值的除以2的幂的操作,例如将一个数右移1位相当于将其除以2。该指令在位操作和算术运算中都有广泛的应用。
总结:SRLI指令用于对一个源寄存器中的数值进行逻辑右移操作,并将结果存储到目标寄存器中。它常用于位操作和算术运算,实现数值的除以2的幂操作。
-
SRAI
在RV64指令集中,SRAI指令(Shift Right Arithmetic Immediate)用于对一个源寄存器中的带符号数进行算术右移操作,并将结果存储到目标寄存器中。
SRAI指令的格式为:SRAI rd, rs1, shamt。
- "SRAI"是指令名称,表示算术右移(Shift Right Arithmetic Immediate)。
- "rd"是目标寄存器,用于存储算术右移操作的结果。
- "rs1"是源寄存器,存储了要进行右移操作的带符号数。
- "shamt"是立即数,表示右移的位数。
执行SRAI指令的步骤如下:
- 将源寄存器rs1中的带符号数右移shamt位,空位用最高位的值填充。
- 将右移后的结果存储到目标寄存器rd中。
例如,执行以下SRAI指令:
SRAI x1, x2, 2该指令将寄存器x2中的带符号数进行算术右移2位,然后将结果存储到寄存器x1中。
SRAI指令用于对带符号数进行算术右移操作,保持数值的符号位不变。算术右移用于对有符号整数进行除以2的幂的操作。例如,将一个负数右移1位相当于将其除以2并向下取整。该指令在位操作和算术运算中都有广泛的应用。
总结:SRAI指令用于对一个源寄存器中的带符号数进行算术右移操作,并将结果存储到目标寄存器中。它常用于位操作和算术运算,保持数值的符号位不变,实现带符号数的除以2的幂操作。
-
ADDIW
在RV64指令集中,ADDIW指令(Add Immediate Word)用于对一个源寄存器中的值与一个立即数进行符号扩展的加法操作,并将结果存储到目标寄存器中。
ADDIW指令的格式为:ADDIW rd, rs1, imm。
- "ADDIW"是指令名称,表示加法立即数(Add Immediate Word)。
- "rd"是目标寄存器,用于存储加法操作的结果。
- "rs1"是源寄存器,存储了要进行加法操作的值。
- "imm"是一个立即数,表示需要与源寄存器值相加的常数。
执行ADDIW指令的步骤如下:
- 将源寄存器rs1中的值进行符号扩展,将32位的值扩展为64位。
- 将符号扩展后的值与立即数imm进行加法操作。
- 将加法操作的结果存储到目标寄存器rd中,结果进行截断,只保留低32位。
例如,执行以下ADDIW指令:
ADDIW x1, x2, 100该指令将寄存器x2中的值与立即数100相加,结果进行截断,最后将低32位的结果存储到寄存器x1中。
ADDIW指令用于对带符号的32位整数立即数进行加法操作,并将结果截断为32位。该指令主要用于处理需要进行符号扩展的立即数的场景,例如处理符号常数或者进行整数运算。
总结:ADDIW指令用于对一个源寄存器中的值与一个立即数进行符号扩展的加法操作,并将结果存储到目标寄存器中。它适用于处理符号扩展的立即数和进行整数运算的场景。
-
SLLIW
在RV64指令集中,SLLIW指令(Shift Left Logical Immediate Word)用于对一个源寄存器中的数值进行32位逻辑左移操作,并将结果存储到目标寄存器中。
SLLIW指令的格式为:SLLIW rd, rs1, shamt。
- "SLLIW"是指令名称,表示逻辑左移立即数(Shift Left Logical Immediate Word)。
- "rd"是目标寄存器,用于存储逻辑左移操作的结果。
- "rs1"是源寄存器,存储了要进行左移操作的数值。
- "shamt"是一个立即数,表示左移的位数,范围在0到31之间。
执行SLLIW指令的步骤如下:
- 将源寄存器rs1中的数值进行32位逻辑左移操作,空位用零填充。
- 将左移后的结果存储到目标寄存器rd中。
例如,执行以下SLLIW指令:
SLLIW x1, x2, 4该指令将寄存器x2中的数值进行32位逻辑左移4位,然后将结果存储到寄存器x1中。
SLLIW指令常用于对32位整数进行位级的移位操作。逻辑左移可以实现对数值的乘以2的幂的操作,例如将一个数左移1位相当于将其乘以2。该指令在位操作和算术运算中都有广泛的应用。
总结:SLLIW指令用于对一个源寄存器中的数值进行32位逻辑左移操作,并将结果存储到目标寄存器中。该指令常用于位操作和算术运算,实现数值的乘以2的幂操作。
-
SRLIW
在RV64指令集中,SRLIW指令(Shift Right Logical Immediate Word)用于对一个源寄存器中的数值进行32位逻辑右移操作,并将结果存储到目标寄存器中。
SRLIW指令的格式为:SRLIW rd, rs1, shamt。
- "SRLIW"是指令名称,表示逻辑右移立即数(Shift Right Logical Immediate Word)。
- "rd"是目标寄存器,用于存储逻辑右移操作的结果。
- "rs1"是源寄存器,存储了要进行右移操作的数值。
- "shamt"是一个立即数,表示右移的位数,范围在0到31之间。
执行SRLIW指令的步骤如下:
- 将源寄存器rs1中的数值进行32位逻辑右移操作,空位用零填充。
- 将右移后的结果存储到目标寄存器rd中。
例如,执行以下SRLIW指令:
SRLIW x1, x2, 3该指令将寄存器x2中的数值进行32位逻辑右移3位,然后将结果存储到寄存器x1中。
SRLIW指令常用于对32位整数进行位级的移位操作。逻辑右移可以实现对数值的除以2的幂的操作,例如将一个数右移1位相当于将其除以2。该指令在位操作和算术运算中都有广泛的应用。
总结:SRLIW指令用于对一个源寄存器中的数值进行32位逻辑右移操作,并将结果存储到目标寄存器中。该指令常用于位操作和算术运算,实现数值的除以2的幂操作。
-
SRAIW
在RV64指令集中,SRAIW指令(Shift Right Arithmetic Immediate Word)用于对一个源寄存器中的带符号数进行32位算术右移操作,并将结果存储到目标寄存器中。
SRAIW指令的格式为:SRAIW rd, rs1, shamt。
- "SRAIW"是指令名称,表示算术右移立即数(Shift Right Arithmetic Immediate Word)。
- "rd"是目标寄存器,用于存储算术右移操作的结果。
- "rs1"是源寄存器,存储了要进行右移操作的带符号数。
- "shamt"是一个立即数,表示右移的位数,范围在0到31之间。
执行SRAIW指令的步骤如下:
- 将源寄存器rs1中的带符号数进行32位算术右移操作,空位用最高位的值填充。
- 将右移后的结果存储到目标寄存器rd中。
例如,执行以下SRAIW指令:
SRAIW x1, x2, 2该指令将寄存器x2中的带符号数进行32位算术右移2位,然后将结果存储到寄存器x1中。
SRAIW指令用于对带符号数进行32位算术右移操作,保持数值的符号位不变。算术右移用于对有符号整数进行除以2的幂的操作。例如,将一个负数右移1位相当于将其除以2并向下取整。该指令在位操作和算术运算中都有广泛的应用。
总结:SRAIW指令用于对一个源寄存器中的带符号数进行32位算术右移操作,并将结果存储到目标寄存器中。它常用于位操作和算术运算,保持数值的符号位不变,实现带符号数的除以2的幂操作。
-
ADDW
在RV64指令集中,ADDW指令(Add Word)用于对两个源寄存器中的带符号数进行加法操作,并将结果存储到目标寄存器中。
ADDW指令的格式为:ADDW rd, rs1, rs2。
- "ADDW"是指令名称,表示加法(Add Word)。
- "rd"是目标寄存器,用于存储加法操作的结果。
- "rs1"和"rs2"是源寄存器,存储了要进行加法操作的带符号数。
执行ADDW指令的步骤如下:
- 将源寄存器rs1和rs2中的带符号数进行相加操作。
- 将加法操作的结果存储到目标寄存器rd中,只保留低32位。
例如,执行以下ADDW指令:
ADDW x1, x2, x3该指令将寄存器x2和x3中的带符号数进行加法操作,结果进行截断,最后将低32位的结果存储到寄存器x1中。
ADDW指令适用于对带符号的32位整数进行相加操作,并将结果截断为32位。该指令特别用于处理带符号数的加法操作,对于需要进行带符号数加法的场景非常有用。
总结:ADDW指令用于对两个源寄存器中的带符号数进行加法操作,并将结果存储到目标寄存器中。它适用于处理带符号数的加法操作,结果截断为32位。
-
XSUBW
在RV64指令集中,XSUBW指令(Extended Subtract Word)用于对两个源寄存器中的带符号数进行减法操作,并将结果存储到目标寄存器中。
XSUBW指令的格式为:XSUBW rd, rs1, rs2。
- "XSUBW"是指令名称,表示扩展减法(Extended Subtract Word)。
- "rd"是目标寄存器,用于存储减法操作的结果。
- "rs1"和"rs2"是源寄存器,存储了要进行减法操作的带符号数。
执行XSUBW指令的步骤如下:
- 将源寄存器rs1和rs2中的带符号数进行减法操作。
- 将减法操作的结果存储到目标寄存器rd中,只保留低32位。
例如,执行以下XSUBW指令:
XSUBW x1, x2, x3该指令将寄存器x2中的带符号数减去寄存器x3中的带符号数,结果进行截断,最后将低32位的结果存储到寄存器x1中。
XSUBW指令适用于对带符号的32位整数进行减法操作,并将结果截断为32位。该指令主要用于处理带符号数的减法操作,对于需要进行带符号数减法的场景非常有用。
总结:XSUBW指令用于对两个源寄存器中的带符号数进行减法操作,并将结果存储到目标寄存器中。它适用于处理带符号数的减法操作,结果截断为32位。
-
SLLW
在RV64指令集中,SLLW指令(Shift Left Logical Word)用于对一个源寄存器中的带符号数进行32位逻辑左移操作,并将结果存储到目标寄存器中。
SLLW指令的格式为:SLLW rd, rs1, rs2。
- "SLLW"是指令名称,表示逻辑左移(Shift Left Logical Word)。
- "rd"是目标寄存器,用于存储逻辑左移操作的结果。
- "rs1"和"rs2"是源寄存器,其中rs1存储了要进行左移操作的带符号数,而rs2存储了左移的位数。
执行SLLW指令的步骤如下:
- 从源寄存器rs1中获取带符号数。
- 从源寄存器rs2中获取左移的位数。
- 将带符号数进行32位逻辑左移操作,空位用零填充。
- 将左移后的结果存储到目标寄存器rd中。
例如,执行以下SLLW指令:
SLLW x1, x2, x3该指令将寄存器x2中的带符号数进行32位逻辑左移,移动的位数由寄存器x3提供,然后将结果存储到寄存器x1中。
SLLW指令在处理带符号数的位级移位操作时非常有用。逻辑左移可以实现对数值的乘以2的幂操作,例如将一个数左移1位相当于将其乘以2。该指令在位操作和算术运算中都有广泛的应用。
总结:SLLW指令用于对一个源寄存器中的带符号数进行32位逻辑左移操作,并将结果存储到目标寄存器中。它常用于位操作和算术运算,实现带符号数的乘以2的幂操作。
-
SRLW
在RV64指令集中,SRLW指令(Shift Right Logical Word)用于对一个源寄存器中的带符号数进行32位逻辑右移操作,并将结果存储到目标寄存器中。
SRLW指令的格式为:SRLW rd, rs1, rs2。
- "SRLW"是指令名称,表示逻辑右移(Shift Right Logical Word)。
- "rd"是目标寄存器,用于存储逻辑右移操作的结果。
- "rs1"和"rs2"是源寄存器,其中rs1存储了要进行右移操作的带符号数,而rs2存储了右移的位数。
执行SRLW指令的步骤如下:
- 从源寄存器rs1中获取带符号数。
- 从源寄存器rs2中获取右移的位数。
- 将带符号数进行32位逻辑右移操作,空位用零填充。
- 将右移后的结果存储到目标寄存器rd中。
例如,执行以下SRLW指令:
SRLW x1, x2, x3该指令将寄存器x2中的带符号数进行32位逻辑右移,右移的位数由寄存器x3提供,然后将结果存储到寄存器x1中。
SRLW指令用于处理带符号数的位级移位操作。逻辑右移可以实现对数值的除以2的幂的操作,例如将一个数右移1位相当于将其除以2。该指令在位操作和算术运算中都有广泛的应用。
总结:SRLW指令用于对一个源寄存器中的带符号数进行32位逻辑右移操作,并将结果存储到目标寄存器中。它常用于位操作和算术运算,实现带符号数的除以2的幂操作。
-
SRAW
在RV64指令集中,SRAW指令(Shift Right Arithmetic Word)用于对一个源寄存器中的带符号数进行32位算术右移操作,并将结果存储到目标寄存器中。
SRAW指令的格式为:SRAW rd, rs1, rs2。
- "SRAW"是指令名称,表示算术右移(Shift Right Arithmetic Word)。
- "rd"是目标寄存器,用于存储算术右移操作的结果。
- "rs1"和"rs2"是源寄存器,其中rs1存储了要进行右移操作的带符号数,而rs2存储了右移的位数。
执行SRAW指令的步骤如下:
- 从源寄存器rs1中获取带符号数。
- 从源寄存器rs2中获取右移的位数。
- 将带符号数进行32位算术右移操作,空位用最高位的值填充。
- 将右移后的结果存储到目标寄存器rd中。
例如,执行以下SRAW指令:
SRAW x1, x2, x3该指令将寄存器x2中的带符号数进行32位算术右移,右移的位数由寄存器x3提供,然后将结果存储到寄存器x1中。
SRAW指令用于对带符号数进行32位算术右移操作,保持数值的符号位不变。算术右移用于对有符号整数进行除以2的幂的操作。例如,将一个负数右移1位相当于将其除以2并向下取整。该指令在位操作和算术运算中都有广泛的应用。
总结:SRAW指令用于对一个源寄存器中的带符号数进行32位算术右移操作,并将结果存储到目标寄存器中。它常用于位操作和算术运算,保持数值的符号位不变,实现带符号数的除以2的幂操作。
数据通路设计
ALUop.vh
ALUop.vh
`ifndef ALUOP_H
`define ALUOP_H
parameter
ADD = 4'b0000,
SUB = 4'b0001,
AND = 4'b0010,
OR = 4'b0011,
XOR = 4'b0100,
SLT = 4'b0101,
SLTU = 4'b0110,
SLL = 4'b0111,
SRL = 4'b1000,
SRA = 4'b1001,
ADDW = 4'b1010,
SUBW = 4'b1011,
SLLW = 4'b1100,
SRLW = 4'b1101,
SRAW = 4'b1110;
`endif
Opcodes.vh
Opcodes.vh
`ifndef OPCODES_H
`define OPCODES_H
// R-type Instructions
parameter ADD = 7'b0110011; // Add instruction, R-type instruction
parameter SUB = 7'b0110011; // Subtract instruction, R-type instruction
parameter SLL = 7'b0110011; // Shift Left Logical instruction, R-type instruction
parameter SLT = 7'b0110011; // Set Less Than instruction, R-type instruction
parameter SLTU = 7'b0110011; // Set Less Than Unsigned instruction, R-type instruction
parameter XOR = 7'b0110011; // XOR instruction, R-type instruction
parameter SRL = 7'b0110011; // Shift Right Logical instruction, R-type instruction
parameter SRA = 7'b0110011; // Shift Right Arithmetic instruction, R-type instruction
parameter AND = 7'b0110011; // AND instruction, R-type instruction
parameter OR = 7'b0110011; // OR instruction, R-type instruction
// I-type Instructions
parameter ADDI = 7'b0010011; // Add Immediate instruction, I-type instruction
parameter SLTI = 7'b0010011; // Set Less Than Immediate instruction, I-type instruction
parameter SLTIU = 7'b0010011; // Set Less Than Immediate Unsigned instruction, I-type instruction
parameter XORI = 7'b0010011; // XOR Immediate instruction, I-type instruction
parameter ORI = 7'b0010011; // OR Immediate instruction, I-type instruction
parameter ANDI = 7'b0010011; // AND Immediate instruction, I-type instruction
parameter SLLI = 7'b0010011; // Shift Left Logical Immediate instruction, I-type instruction
parameter SRLI = 7'b0010011; // Shift Right Logical Immediate instruction, I-type instruction
parameter LD = 7'b0000011; // Load Word instruction, I-type instruction
parameter SRAI = 7'b0000011; // Shift Right Arithmetic Immediate instruction, I-type instruction
// S-type Instructions
parameter SD = 7'b0100011; // Store Doubleword instruction, S-type instruction
// B-type Instructions
parameter BEQ = 7'b1100011; // Branch Equal instruction, B-type instruction
parameter BNE = 7'b1100011; // Branch Not Equal instruction, B-type instruction
parameter BLT = 7'b1100011; // Branch Less Than instruction, B-type instruction
parameter BGE = 7'b1100011; // Branch Greater Than Equal instruction, B-type instruction
parameter BLTU = 7'b1100011; // Branch Less Than Unsigned instruction, B-type instruction
parameter BGEU = 7'b1100011; // Branch Greater Than Equal Unsigned instruction, B-type instruction
// U-type Instructions
parameter LUI = 7'b0110111; // Load Upper Immediate instruction, U-type instruction
parameter AUIPC = 7'b0010111; // Add Upper Immediate to PC instruction, U-type instruction
// J-type Instructions
parameter JAL = 7'b1101111; // Jump and Link instruction, J-type instruction
// Additional Instructions and their types
parameter JALR = 7'b1100111; // Jump and Link Register instruction, I-type instruction
parameter LB = 7'b0000011; // Load Byte instruction, I-type instruction
parameter LH = 7'b0000011; // Load Half instruction, I-type instruction
parameter LW = 7'b0000011; // Load Word instruction, I-type instruction
parameter LBU = 7'b0000011; // Load Byte Unsigned instruction, I-type instruction
parameter LHU = 7'b0000011; // Load Half Unsigned instruction, I-type instruction
parameter LWU = 7'b0000011; // Load Word Unsigned instruction, I-type instruction
parameter ADDIW = 7'b0011011; // Add Immediate Word instruction, I-type instruction
parameter SLLIW = 7'b0011011; // Shift Left Logical Immediate Word instruction, I-type instruction
parameter SRLIW = 7'b0011011; // Shift Right Logical Immediate Word instruction, I-type instruction
parameter SRAIW = 7'b0011011; // Shift Right Arithmetic Immediate Word instruction, I-type instruction
parameter SB = 7'b0100011; // Store Byte instruction, S-type instruction
parameter SH = 7'b0100011; // Store Half instruction, S-type instruction
parameter SW = 7'b0100011; // Store Word instruction, S-type instruction
parameter ADDW = 7'b0111011; // Add Word instruction, R-type instruction
parameter SUBW = 7'b0111011; // Subtract Word instruction, R-type instruction
parameter SLLW = 7'b0111011; // Shift Left Logical Word instruction, R-type instruction
parameter SRLW = 7'b0111011; // Shift Right Logical Word instruction, R-type instruction
parameter SRAW = 7'b0111011; // Shift Right Arithmetic Word instruction, R-type instruction
// Instruction Types
parameter R = 3'b000; // R-type
parameter I = 3'b001; // I-type
parameter S = 3'b010; // S-type
parameter B = 3'b011; // B-type
parameter U = 3'b100; // U-type
parameter J = 3'b101; // J-type
`endif
Registers.v
`timescale 1ns / 1ps
module Registers (
input clk,
input rst,
input we,
input [4:0] read_addr_1,
input [4:0] read_addr_2,
input [4:0] write_addr,
input [63:0] write_data,
input [4:0] debug_reg_addr,
output reg [63:0] read_data_1,
output reg [63:0] read_data_2
);
reg [63:0] register [1:31]; // x1 - x31, x0 keeps zero
always @(posedge clk or posedge rst) begin
if (rst == 1)
for (integer i = 1; i < 64; i = i + 1)
register[i] <= 0; // reset
else if (we == 1 && write_addr != 0)
register[write_addr] <= write_data;
end
assign read_data_1 = (read_addr_1 == 0) ? 0 : register[read_addr_1]; // read
assign read_data_2 = (read_addr_2 == 0) ? 0 : register[read_addr_2]; // read
endmodule
RAM.v
`timescale 1ns / 1ps
// Copyright 2023 Sycuricon Group
// Author: Jinyan Xu (phantom@zju.edu.cn)
module RAM (
input clk,
input rw_wmode,
input [8:0] rw_addr,
input [63:0] rw_wdata,
input [7:0] rw_wmask,
output [63:0] rw_rdata,
input [8:0] ro_addr,
output [63:0] ro_rdata
);
localparam FILE_PATH = "testcase.hex";
integer i;
reg [63:0] mem [0:511];
initial begin
$readmemh(FILE_PATH, mem);
end
always @(posedge clk) begin
if (rw_wmode) begin
for(i = 0; i < 8; i = i+1) begin
if(rw_wmask[i]) begin
mem[rw_addr][i*8 +: 8] <= rw_wdata[i*8 +: 8];
end
end
end
end
assign rw_rdata = mem[rw_addr];
assign ro_rdata = mem[ro_addr];
endmodule
ImmGen.v
ImmGen模块的作用是产生立即数,根据控制单元中immgen_op
的命令,对不同类型的指令生成不同的立即数,输出一个64位的立即数。
ImmGen.v
// Immediate Generation (ImmGen) module
// This module is responsible for generating immediate values for instructions.
`timescale 1ns / 1ps
module ImmGen(
input [31:0] inst, // Input instruction
input [ 2:0] immgen_op, // Immediate type opcode
output [63:0] imm // Generated immediate value
);
`include "Opcodes.vh" // Include opcode definitions
reg [63:0] out; // Intermediate variable to store the concatenated immediate value
always @(*) begin // Combinational logic process
case (immgen_op)
R: out = 64'b0; // Immediate value is 0 for R-type instructions
I: begin // Immediate value concatenation for I-type instructions
if(inst[14:12] == 3'b101 && (inst[6:0] == 7'b0010011 || inst[6:0] == 7'b0011011))
out = {{54{inst[31]}}, inst[29:20]}; // Concatenate SHAMT
else if(inst[6:0] == 7'b0110111)
out = {44'b0, inst[31:12]}; // Concatenate UIMM20
else
out = {{52{inst[31]}}, inst[31:20]}; // Concatenate IMM12
end
S: out = {{52{inst[31]}}, inst[31:25], inst[11:7]}; // Concatenate IMM12 and IMM5
B: out = {{52{inst[31]}}, inst[31], inst[7], inst[30:25], inst[11:8], 1'b0}; // Concatenate IMM13 and IMM5, with 0 in between
U: out = {{32{inst[31]}}, inst[31:12], 12'b0}; // Concatenate UIMM20, with 0 in between
J: out = {{44{inst[31]}}, inst[19:12], inst[20], inst[30:21], 1'b0}; // Concatenate IMM21, with 0 in between
default: out = 64'b0; // Default immediate value is 0
endcase
end
assign imm = out; // Assign the concatenated immediate value to the output signal
endmodule
BranchCmp.v
BranchComp模块的作用是实现分支跳转功能,根据控制单元中bralu_op[2:0]
的命令来实现不同类型跳转指令的逻辑判断
bralu_op[2:0]
取值 | 含义 |
---|---|
3’b000 | 不是 Branch 操作 |
3’b001 | EQ |
3’b010 | NE |
3’b011 | LT |
3’b100 | GE |
3’b101 | LTU |
3’b110 | GEU |
BranchCmp.v
`timescale 1ns / 1ps
module BranchCmp (
input [2:0] bralu_op, // Branch comparison opcode
input [63:0] dataR1, // First comparison data
input [63:0] dataR2, // Second comparison data
output br_taken // Output signal indicating whether the branch should be taken
);
reg out; // Intermediate variable to store the branch comparison result
always @(*) begin // Combinational logic process
case (bralu_op) // Perform branch comparison based on the branch opcode
3'b000: out = 1; // none, no branch
3'b001: out = (dataR1 == dataR2) ? 1 : 0; // EQ, equal branch
3'b010: out = (dataR1 == dataR2) ? 0 : 1; // NE, not equal branch
3'b011: begin // LT, less than branch
if (dataR1[63] == 0 && dataR2[63] == 1)
out = 0; // Different sign bits, not satisfying less than relationship
else if (dataR1[63] == 1 && dataR2[63] == 0)
out = 1; // Different sign bits, satisfying less than relationship
else if (dataR1[62:0] < dataR2[62:0])
out = 1; // Same sign bits, satisfying less than relationship
else
out = 0; // Same sign bits, not satisfying less than relationship
end
3'b100: begin // GE, greater than or equal branch
if (dataR1[63] == 0 && dataR2[63] == 1)
out = 1; // Different sign bits, satisfying greater than or equal relationship
else if (dataR1[63] == 1 && dataR2[63] == 0)
out = 0; // Different sign bits, not satisfying greater than or equal relationship
else if (dataR1[62:0] < dataR2[62:0])
out = 0; // Same sign bits, not satisfying greater than or equal relationship
else
out = 1; // Same sign bits, satisfying greater than or equal relationship
end
3'b101: out = (dataR1 < dataR2) ? 1 : 0; // LTU, unsigned less than branch
3'b110: out = (dataR1 >= dataR2) ? 1 : 0; // GEU, unsigned greater than or equal branch
default: out = 0; // Default, no branch
endcase
end
assign br_taken = out; // Assign the branch comparison result to the output signal
endmodule
InputALU.v
为了使数据通路整体结构更加清晰,在这里我们定义了InputALU这个模块,它将根据控制单元中alu_asel[1:0]
和alu_bsel[1:0]
的命令来实现对输入到ALU端数据的选择。
alu_asel[1:0]
取值 | 含义 |
---|---|
2’b00 | 选择 0 输入 |
2’b01 | 选择 RS1 |
2’b10 | 选择 PC |
alu_bsel[1:0]
取值 | 含义 |
---|---|
2’b00 | 选择 0 输入 |
2’b01 | 选择 RS2 |
2’b10 | 选择 imm |
InputALU.v
module InputALU(
input [1:0] asel, // A selection signal
input [1:0] bsel, // B selection signal
input [63:0] pc_addr, // PC address signal
input [63:0] rs1, // Source register 1 value signal
input [63:0] rs2, // Source register 2 value signal
input [63:0] imm, // Immediate value signal
output reg [63:0] a, // A input port signal
output reg [63:0] b // B input port signal
);
// A selection signal
always @* begin
case (asel)
2'b00: a = 64'h0; // Select 0 as A input
2'b01: a = rs1; // Select RS1 as A input
2'b10: a = pc_addr; // Select PC as A input
default: a = 0; // A input is 0 in case of an error
endcase
end
// B selection signal
always @* begin
case (bsel)
2'b00: b = 64'h0; // Select 0 as B input
2'b01: b = rs2; // Select RS2 as B input
2'b10: b = imm; // Select imm as B input
default: b = 0; // B input is 0 in case of an error
endcase
end
endmodule
//alu_asel = 2'b00:选择 0 输入
//alu_asel = 2'b01:选择 RS1
//alu_asel = 2'b10:选择 PC
//alu_bsel = 2'b00:选择 0 输入
//alu_bsel = 2'b01:选择 RS2
//alu_bsel = 2'b10:选择 imm
ALU.v
ALU是数据通路中核心的计算模块,它能根据控制单元中alu_op
的命令来实现不同类型的运算。
alu_op[3:0]
取值 | 含义 | 取值 | 含义 |
---|---|---|---|
4’b0000 | ADD | 4’b1000 | SRL |
4’b0001 | SUB | 4’b1001 | SRA |
4’b0010 | AND | 4’b1010 | ADDW |
4’b0011 | OR | 4’b1011 | SUBW |
4’b0100 | XOR | 4’b1100 | SLLW |
4’b0101 | SLT | 4’b1101 | SRLW |
4’b0110 | SLTU | 4’b1110 | SRAW |
4’b0111 | SLL |
ALU.v(错误版)
//ALU基本功能:
//算术运算:加法、减法、乘法、除法
//逻辑运算:与、或、非、异或、取反
//比较运算:是否相等、大于、小于
`timescale 1ns / 1ps
module ALU (
input [63:0] a,
input [63:0] b,
input [3:0] alu_op,
output reg [63:0] res,
output zero
);
always @(*) begin
case (alu_op)
4'b0000: res <= a + b; //ADD
4'b0001: res <= a - b; //SUB
4'b0111: res <= a << b; //SLL
4'b0101: begin //SLT
if (a[63] == 0 && b[63] == 1) res <= 0;
else if (a[63] == 1 && b[63] == 0) res <= 1;
else if (a[63] == b[63]) begin
if (a[62:0] < b[62:0]) res <= 1;
else res <= 0;
end
end
4'b0110: begin //SLTU
if (a < b) res <= 1;
else res <= 0;
end
4'b0100: res <= a ^ b; //XOR
4'b1000: res <= a >> b; //SRL
4'b1001: res <= a >>> b; //SRA
4'b0011: res <= a | b; //OR
4'b0010: res <= a & b; //AND
4'b1010: res <= a[31:0] + b[31:0]; //ADDW
4'b1011: res <= a[31:0] - b[31:0]; //SUBW
4'b1100: res <= a[31:0] << b[31:0]; //SLLW
4'b1101: res <= a[31:0] >> b[31:0]; //SRLW
4'b1110: res <= a[31:0] >>> b[31:0]; //SRAW
default: res = 0;
endcase
end
assign zero = (a-b) ? 1'b0 : 1'b1;
endmodule
//alu_op[3:0]
//取值 含义 取值 含义
//4'b0000 ADD 4'b1000 SRL
//4'b0001 SUB 4'b1001 SRA
//4'b0010 AND 4'b1010 ADDW
//4'b0011 OR 4'b1011 SUBW
//4'b0100 XOR 4'b1100 SLLW
//4'b0101 SLT 4'b1101 SRLW
//4'b0110 SLTU 4'b1110 SRAW
//4'b0111 SLL
但是这样子写好丑欸,而且在后面带W的指令中要考虑到符号问题
上网查了一下,我们可以使用$signed()
函数将结果转换为有符号数。
ALU.v
//alu_op[3:0]
//取值 含义 取值 含义
//4'b0000 ADD 4'b1000 SRL
//4'b0001 SUB 4'b1001 SRA
//4'b0010 AND 4'b1010 ADDW
//4'b0011 OR 4'b1011 SUBW
//4'b0100 XOR 4'b1100 SLLW
//4'b0101 SLT 4'b1101 SRLW
//4'b0110 SLTU 4'b1110 SRAW
//4'b0111 SLL
`timescale 1ns / 1ps
module ALU (
input [63:0] a, // Input port a with a width of 64 bits
input [63:0] b, // Input port b with a width of 64 bits
input [3:0] alu_op, // Input port alu_op with a width of 4 bits for selecting ALU operation
output [63:0] res // Output register res with a width of 64 bits to store the ALU operation result
);
reg [63:0] reg_result;
`include "ALUop.vh" // Include header file defining ALU operation codes
always @(*) begin
case (alu_op)
ADD: reg_result = a + b; // Addition operation
SUB: reg_result = a - b; // Subtraction operation
AND: reg_result = a & b; // Bitwise AND operation
OR: reg_result = a | b; // Bitwise OR operation
XOR: reg_result = a ^ b; // Bitwise XOR operation
SLT: begin
if(a[63] == 0 && b[63] == 1) reg_result = 0;
else if(a[63] == 1 && b[63] == 0) reg_result = 1;
else if(a[62:0] < b[62:0]) reg_result = 1;
else reg_result = 0;
end // Signed comparison to check if a is less than b
SLTU: reg_result = (a < b) ? 1 : 0; // Unsigned comparison to check if a is less than b
SLL: begin // Logical left shift operation
if(b[63])
reg_result = a << (63-~b);
else
reg_result = a << b;
end
SRL: begin // Logical right shift operation
if(b[63])
reg_result = a >> (63-~b);
else
reg_result = a >> b;
end
SRA: begin // Arithmetic right shift operation
if(b[63])
reg_result = $signed(a) >>> (63-~b);
else
reg_result = $signed(a) >>> b;
end
ADDW: reg_result = $signed(a[31:0] + b[31:0]); // Signed addition operation with result truncated to 32 bits
SUBW: reg_result = $signed(a[31:0] - b[31:0]); // Signed subtraction operation with result truncated to 32 bits
SLLW: begin
if(b[31])
reg_result = $signed(a[31:0] << (31-~b[31:0]));
else
reg_result = $signed(a[31:0] << b[31:0]); // Logical left shift operation with result truncated to 32 bits
end
SRLW: begin
if(b[31])
reg_result = $signed(a[31:0] >> (31-~b[31:0]));
else
reg_result = $signed(a[31:0] >> b[31:0]); // Logical right shift operation with result truncated to 32 bits
end
SRAW: begin
if(b[31])
reg_result = $signed(a[31:0]) >>> (31-~b[31:0]);
else
reg_result = $signed(a[31:0]) >>> b[31:0]; // Signed arithmetic right shift operation with result truncated to 32 bits
end
default: reg_result = 0; // Default case, set res to zero
endcase
end
assign res = reg_result;
endmodule
MaskGen.v
MaskGen是干嘛的呢?这是最困扰我的问题之一。实际上,顾名思义,翻译一下就是“生成掩码”,它根据控制单元中memdata_width
的命令生成不同位宽的掩码
memdata_width[2:0]
取值 | 含义 |
---|---|
3’b000 | 不访存 |
3’b001 | Double Word |
3’b010 | Word |
3’b011 | Half Word |
3’b100 | Byte |
3’b101 | Unsigned Word |
3’b110 | Unsigned Half Word |
3’b111 | Unsigned Byte |
MaskGen.v
`timescale 1ns / 1ps
module MaskGen (
input [2:0] width, // Input: Width used to determine the type of mask
input [2:0] remain, // Input: Remaining number of bits to determine mask shift
output [7:0] mask // Output: Generated mask
);
reg [7:0] mask_reg; // Internal register: Stores the generated mask value
always @(*) begin // Combinatorial logic block always runs
case (width) // Select based on different width cases
3'b000: mask_reg = 8'b11111111; // No access: Set all bits to 1, no shift
3'b001: mask_reg = 8'b11111111; // Double word: Set all bits to 1, no shift
3'b010: mask_reg = 8'b00001111; // Word: Set lower 4 bits as 1, remaining bits as 0, no shift
3'b011: mask_reg = 8'b00000011; // Half word: Set lower 2 bits as 1, remaining bits as 0, no shift
3'b100: mask_reg = 8'b00000001; // Byte: Set lowest bit as 1, remaining bits as 0, no shift
3'b101: mask_reg = 8'b00001111; // Unsigned word: Set lower 4 bits as 1, remaining bits as 0, no shift
3'b110: mask_reg = 8'b00000011; // Unsigned half word: Set lower 2 bits as 1, remaining bits as 0, no shift
3'b111: mask_reg = 8'b00000001; // Unsigned byte: Set lowest bit as 1, remaining bits as 0, no shift
default: mask_reg = 8'b11111111; // Set all bits to 1 by default
endcase
end
assign mask = mask_reg << remain; // Left shift the value of mask_reg by remain bits and assign to mask output port
endmodule
Data_Trunc.v
Data_Trunc.v
`timescale 1ns / 1ps
module Data_Trunc (
input [ 2:0] width,
input [63:0] rdata,
output [63:0] trdata
);
reg [63:0] data_reg;
always @(*) begin
case (width)
3'b000: data_reg = rdata; // 不访存
3'b001: data_reg = rdata; // double word
3'b010: data_reg = {{32{rdata[31]}}, rdata[31:0]}; // word
3'b011: data_reg = {{48{rdata[15]}}, rdata[15:0]}; // Half word
3'b100: data_reg = {{56{rdata[7]}}, rdata[7:0]}; // Byte
3'b101: data_reg = {32'b0, rdata[31:0]}; // Unsigned word
3'b110: data_reg = {48'b0, rdata[15:0]}; // Unsigned Half word
3'b111: data_reg = {56'b0, rdata[7:0]}; // Unsigned Byte
default:data_reg = rdata;
endcase
end
assign trdata = data_reg;
endmodule
这段代码定义了一个模块 Data_Trunc
,它有三个输入端口和一个输出端口。
输入端口:
width
是一个 3 位宽度的信号,表示数据宽度选择。rdata
是一个 64 位的输入数据。
输出端口:
trdata
是一个 64 位的输出数据,经过截断处理后的结果。
在 always
块中,根据 width
信号的不同,使用 case
语句对 rdata
进行截断处理,并将结果存储到 data_reg
寄存器中。
根据不同的 width
值,具体的截断处理如下:
3'b000
:不对数据进行截断,直接将rdata
的值赋给data_reg
。3'b001
:将rdata
的值赋给data_reg
。3'b010
:将rdata
的低 32 位复制为高 32 位,然后将结果存储到data_reg
。3'b011
:将rdata
的低 16 位复制为高 48 位,然后将结果存储到data_reg
。3'b100
:将rdata
的低 8 位复制为高 56 位,然后将结果存储到data_reg
。3'b101
:将rdata
作为无符号数的低 32 位,高 32 位补零,然后将结果存储到data_reg
。3'b110
:将rdata
作为无符号数的低 16 位,高 48 位补零,然后将结果存储到data_reg
。3'b111
:将rdata
作为无符号数的低 8 位,高 56 位补零,然后将结果存储到data_reg
。
最后,将 data_reg
的值赋给输出端口 trdata
,完成截断处理后的数据输出。
Mux_PC.v
Mux_PC.v
module Mux_PC (
input [63:0] in0, // Input port: Input signal in0, width of 64 bits
input [63:0] in1, // Input port: Input signal in1, width of 64 bits
input npc_sel, // Input port: Control signal npc_sel, single-bit input
input br_taken, // Input port: Control signal br_taken, single-bit input
output [63:0] out // Output port: Output signal out, width of 64 bits
);
assign out = (npc_sel == 1 && br_taken == 1) ? in1 : in0; // Select input signal based on control signals for output
endmodule
这段代码定义了一个模块 Mux_PC
,它有以下端口:
in0
和in1
是 64 位宽度的输入信号端口。npc_sel
是单比特输入的控制信号端口。br_taken
也是单比特输入的控制信号端口。out
是一个 64 位宽度的输出信号端口。
这段代码使用条件运算符(三元运算符)根据控制信号选择输入信号并将其赋值给输出端口 out
。如果 npc_sel
和 br_taken
均为 1,那么输出信号 out
将等于输入信号 in1
;否则,输出信号 out
将等于输入信号 in0
。
Mux_rb.v
Mux_rb.v
`timescale 1ns / 1ps
module Mux_rb (
input [63:0] in0, // Input port: Input signal in0, width of 64 bits
input [63:0] in1, // Input port: Input signal in1, width of 64 bits
input [63:0] in2, // Input port: Input signal in2, width of 64 bits
input [63:0] in3, // Input port: Input signal in3, width of 64 bits
input [1:0] s, // Input port: Selection signal s, width of 2 bits
output [63:0] o // Output port: Output signal o, width of 64 bits
);
reg [63:0] out; // Internal register: Used to store the selected result
always @(*) begin // Combinational logic block always runs
case (s) // Select based on different cases of selection signal s
2'b00: out = in0; // When s is 2'b00, select in0 as the output
2'b01: out = in1; // When s is 2'b01, select in1 as the output
2'b10: out = in2; // When s is 2'b10, select in2 as the output
2'b11: out = in3; // When s is 2'b11, select in3 as the output
endcase
end
assign o = out; // Assign the value of out to the output port o
endmodule
这段代码定义了一个模块 Mux_rb
,它有以下端口:
in0
、in1
、in2
、in3
是 64 位宽度的输入信号端口。s
是一个 2 位宽度的选择信号端口。o
是一个 64 位宽度的输出信号端口。
在这个模块中,我们使用一个内部寄存器 out
来存储选择后的结果。
这段代码使用了一个 always @(*)
块,表示组合逻辑块始终运行。根据选择信号 s
的不同情况,通过 case
语句将相应的输入信号选中,并将结果存储在 out
中。当 s
为 2'b00
时,选择 in0
作为输出;当 s
为 2'b01
时,选择 in1
作为输出;当 s
为 2'b10
时,选择 in2
作为输出;当 s
为 2'b11
时,选择 in3
作为输出。
SCPU.v
在SCPU中,我们应该集成上面的全部module,就和搭积木一样把数据通路搭起来
SCPU.v
// Timescale declaration
`timescale 1ns / 1ps
// Module definition
module SCPU (
input wire clk, // Clock signal
input wire rstn, // Reset signal
output wire [63:0] cosim_pc, // Current PC
output wire [31:0] cosim_inst, // Current instruction
output wire [ 7:0] cosim_rs1_id, // rs1 ID
output wire [63:0] cosim_rs1_data, // rs1 data
output wire [ 7:0] cosim_rs2_id, // rs2 ID
output wire [63:0] cosim_rs2_data, // rs2 data
output wire [63:0] cosim_alu, // ALU output
output wire [63:0] cosim_mem_addr, // Memory address
output wire [ 3:0] cosim_mem_we, // Memory write enable
output wire [63:0] cosim_mem_wdata, // Memory write data
output wire [63:0] cosim_mem_rdata, // Memory read data
output wire [ 3:0] cosim_rd_we, // Register write enable
output wire [ 7:0] cosim_rd_id, // Register ID
output wire [63:0] cosim_rd_data, // Register data
output wire [ 3:0] cosim_br_taken, // Branch taken?
output wire [63:0] cosim_npc // Next PC
);
// Declare internal wires and registers
wire [31:0] inst;
wire [21:0] decode;
wire [63:0] ro_out;
wire [63:0] rw_rdata;
wire [63:0] dataW;
wire [63:0] data_Origin;
reg [63:0] pc;
wire [63:0] write_data;
wire [63:0] read_data_1;
wire [63:0] read_data_2;
wire [63:0] imm;
wire [63:0] A_data;
wire [63:0] B_data;
wire [63:0] alu_out;
wire br_taken;
wire [7:0] rw_wmask;
wire [63:0] trdata;
wire [63:0] pc_next;
// Instantiate Control module
Control control(
.inst(inst),
.comb_decode(decode)
);
// PC
always @(posedge clk) begin
if (~rstn)
pc <= 32'b0; // Reset PC
else
pc <= pc_next; // Set new value for PC
end
// Instantiate RAM module
RAM MEM(
.clk(clk),
.rw_wmode(decode[20]),
.rw_addr(alu_out[11:3]),
.rw_wdata(dataW),
.rw_wmask(rw_wmask),
.rw_rdata(data_Origin),
.ro_addr(pc[11:3]),
.ro_rdata(ro_out)
);
// Generate rw_rdata from data_Origin based on alu_out
always @* begin
integer i;
integer k;
k = 0;
for (i = 0; i < 64; i = i + 1) begin
if (i >= {3'b0, alu_out[2:0]} << 3) begin
rw_rdata[k] = data_Origin[i];
k = k + 1;
end
end
end
// inst_selec
assign inst = pc[2] ? ro_out[63:32] : ro_out[31:0];
// Instantiate Registers module
Registers Regs(
.clk(clk),
.rst(~rstn), // Inverted reset signal
.we(decode[21]),
.read_addr_1(inst[19:15]),
.read_addr_2(inst[24:20]),
.write_addr(inst[11:7]),
.write_data(write_data),
.read_data_1(read_data_1),
.read_data_2(read_data_2)
);
// Instantiate ImmGen module
ImmGen immgen (
.inst(inst),
.immgen_op(decode[18:16]),
.imm(imm)
);
// Instantiate InputALU module
InputALU inputalu(
.asel(decode[8:7]), // A selection signal
.bsel(decode[6:5]), // B selection signal
.pc_addr(pc), // PC address signal
.rs1(read_data_1), // Source register 1 value signal
.rs2(read_data_2), // Source register 2 value signal
.imm(imm), // Immediate value signal
.a(A_data), // A input port signal
.b(B_data) // B input port signal
);
// Instantiate ALU module
ALU alu (
.a(A_data),
.b(B_data),
.alu_op(decode[15:12]),
.res(alu_out)
);
// Instantiate BranchCmp module
BranchCmp BCp (
.bralu_op(decode[11:9]),
.dataR1(read_data_1),
.dataR2(read_data_2),
.br_taken(br_taken)
);
// Instantiate MaskGen module
MaskGen genmask(
.width(decode[2:0]),
.remain(alu_out[2:0]),
.mask(rw_wmask)
);
// Instantiate Data_Trunc module
Data_Trunc datatrunc(
.width(decode[2:0]),
.rdata(rw_rdata),
.trdata(trdata)
);
// Instantiate Mux_rb module
Mux_rb readback (
.in0(0),
.in1(alu_out),
.in2(trdata),
.in3(pc+4),
.s(decode[4:3]),
.o(write_data)
);
// Instantiate Mux_PC module
Mux_PC muxpc(
.in0(pc+4),
.in1(alu_out),
.npc_sel(decode[19]),
.br_taken(br_taken),
.out(pc_next)
);
// Assign signals to corresponding outputs
assign cosim_pc = pc; // Current PC
assign cosim_inst = inst; // Current instruction
assign cosim_rs1_id = inst[19:15]; // rs1 ID
assign cosim_rs1_data = read_data_1; // rs1 data
assign cosim_rs2_id = inst[24:20]; // rs2 ID
assign cosim_rs2_data = read_data_2; // rs2 data
assign cosim_alu = alu_out; // ALU output
assign cosim_mem_addr = alu_out; // Memory address
assign cosim_mem_we = decode[20]; // Memory write enable
assign cosim_mem_wdata = dataW; // Memory write data
assign cosim_mem_rdata = rw_rdata; // Memory read data
assign cosim_rd_we = decode[21]; // Register write enable
assign cosim_rd_id = inst[11:7]; // Register ID
assign cosim_rd_data = write_data; // Register data
assign cosim_br_taken = br_taken; // Branch taken?
assign cosim_npc = pc_next; // Next PC
endmodule
控制模块设计(Controller.v)
// Time scale for simulation accuracy
`timescale 1ns/1ps
// Definition of the controller module
module controller(
input [31:0] comb_decode_inst, // Input port: combination decode instruction (32 bits)
output wire [21:0] comb_decode // Output port: combination decode result (22 bits)
);
reg we_reg, npc_sel; // Register write enable, PC writeback select
reg [1:0] alu_asel, alu_bsel; // ALU A and B port select
reg [3:0] alu_op; // Arithmetic operation
reg we_mem; // Memory write enable
reg [2:0] immgen_op; // Immediate value select
reg [2:0] bralu_op; // Branch calculation operation
reg [1:0] wb_sel; // Writeback data select
reg [2:0] memdata_width; // Access width
wire [6:0] op_code; // Operation code
wire [2:0] func3; // func3
// Assign operation code by extracting bits from the combination decode instruction
assign op_code = comb_decode_inst[6:0];
// Assign func3 by extracting bits from the combination decode instruction
assign func3 = comb_decode_inst[14:12];
always @(*) begin
// Initialize all control signals to 0
we_reg = 0;
alu_asel = 0;
alu_bsel = 0;
alu_op = 0;
we_mem = 0;
npc_sel = 0;
immgen_op = 0;
bralu_op = 0;
wb_sel = 0;
memdata_width = 0;
// Assign control signals based on the operation code and func3
case (op_code)
// li instruction
7'b0001101:
begin
we_reg = 1'b1;
alu_bsel = 2'b10;
immgen_op = 3'b100;
wb_sel = 2'b01;
end
// U lui instruction
7'b0110111:
begin
we_reg = 1'b1;
alu_bsel = 2'b10;
immgen_op = 3'b100;
wb_sel = 2'b01;
end
// U auipc instruction
7'b0010111:
begin
we_reg = 1'b1;
alu_asel = 2'b10;
alu_bsel = 2'b10;
immgen_op = 3'b100;
wb_sel = 2'b01;
end
// I-type instruction
7'b0010011:
begin
case(func3)
3'b000: alu_op = 4'b0000; // addi
3'b001: alu_op = 4'b0111; // slli
3'b010: alu_op = 4'b0101; // slti
3'b011: alu_op = 4'b0110; // sltiu
3'b100: alu_op = 4'b0100; // xori
3'b101:
if(comb_decode_inst[31:25] == 0)
alu_op = 4'b1000; // srli
else
alu_op = 4'b1001; // srai
3'b110: alu_op = 4'b0011; // ori
3'b111: alu_op = 4'b0010; // andi
default: alu_op = 4'b0000;
endcase
alu_asel = 2'b01;
alu_bsel = 2'b10;
we_reg = 1;
immgen_op = 3'b001;
wb_sel = 2'b01;
end
// I-type extension instruction
7'b0011011:
begin
case(func3)
3'b000: alu_op = 4'b1010; // addiw
3'b001: alu_op = 4'b1100; // slliw
3'b101:
if(comb_decode_inst[31:25] == 0)
alu_op = 4'b1101; // srliw
else
alu_op = 4'b1110; // sraiw
default: alu_op = 0;
endcase
alu_asel = 2'b01;
alu_bsel = 2'b10;
we_reg = 1;
immgen_op = 3'b001;
wb_sel = 2'b01;
end
// Load instruction
7'b0000011:
begin
case(func3)
3'b000: memdata_width = 3'b100; // lb
3'b001: memdata_width = 3'b011; // lh
3'b010: memdata_width = 3'b010; // lw
3'b011: memdata_width = 3'b001; // double word
3'b100: memdata_width = 3'b111; // lbu, unsigned byte
3'b101: memdata_width = 3'b110; // lhu, unsigned half word
3'b110: memdata_width = 3'b101; // unsigned word
default: memdata_width = 3'b000;
endcase
we_reg = 1;
alu_asel = 2'b01;
alu_bsel = 2'b10;
immgen_op = 3'b001;
wb_sel = 2'b10;
end
// R-type instruction
7'b0110011:
begin
case(func3)
3'b000:
if(comb_decode_inst[31:25] == 0)
alu_op = 4'b0000; // add
else
alu_op = 4'b0001; // sub
3'b001: alu_op = 4'b0111; // sll
3'b010: alu_op = 4'b0101; // slt
3'b011: alu_op = 4'b0110; // sltu
3'b100: alu_op = 4'b0100; // xor
3'b101:
if(comb_decode_inst[31:25] == 0)
alu_op = 4'b1000; // srl
else
alu_op = 4'b1001; // sra
3'b110: alu_op = 4'b0011; // or
3'b111: alu_op = 4'b0010; // and
default: alu_op = 4'b0;
endcase
we_reg = 1'b1;
alu_asel = 2'b01;
alu_bsel = 2'b01;
wb_sel = 1'b1;
end
// R型拓展指令
7'b0111011: // R-type instructions
begin
case(func3)
3'b000:
if(comb_decode_inst[31:25] == 0)
alu_op = 4'b1010; // addw
else
alu_op = 4'b1011; // subw
3'b001: alu_op = 4'b1100; // sllw
3'b101:
if(comb_decode_inst[31:25] == 0)
alu_op = 4'b1101; // srlw
else
alu_op = 4'b1110; // sraw
default: alu_op = 0;
endcase
we_reg = 1'b1;
alu_asel = 2'b01;
alu_bsel = 2'b01;
wb_sel = 1'b1;
end
7'b0100011: // S-type instructions
begin
case(func3)
3'b000: memdata_width = 3'b100; // sb
3'b001: memdata_width = 3'b011; // sh
3'b010: memdata_width = 3'b010; // sw
3'b011: memdata_width = 3'b001; // sd
default: memdata_width = 3'b000;
endcase
alu_asel = 2'b01;
alu_bsel = 2'b10;
we_mem = 1;
immgen_op = 3'b010;
end
7'b1100011: // B-type instructions (branches)
begin
case(func3)
3'b000: bralu_op = 3'b001; // beq
3'b001: bralu_op = 3'b010; // bne
3'b100: bralu_op = 3'b011; // blt
3'b101: bralu_op = 3'b100; // bge
3'b110: bralu_op = 3'b101; // bltu
3'b111: bralu_op = 3'b110; // bgeu
default: bralu_op = 3'b000;
endcase
alu_asel = 2'b10;
alu_bsel = 2'b10;
npc_sel = 1;
immgen_op = 3'b011;
wb_sel = 2'b01;
end
7'b1101111: // jal instruction
begin
we_reg = 1'b1;
alu_asel = 2'b10;
alu_bsel = 2'b10;
npc_sel = 1;
immgen_op = 3'b101;
wb_sel = 2'b11;
end
7'b1100111: // jalr instruction
begin
we_reg = 1;
npc_sel = 1;
immgen_op = 3'b001;
alu_asel = 2'b01;
alu_bsel = 2'b10;
wb_sel = 2'b11;
end
default: alu_op = 0;
endcase
assign comb_decode = {we_reg, we_mem, npc_sel, immgen_op, alu_op, bralu_op, alu_asel, alu_bsel, wb_sel, memdata_width};
end
endmodule
控制器模块用于为各类指令设计控制信号。控制信号根据输入的组合解码指令(32位)进行逻辑运算和赋值得出。以下是详细解释:
-
在
always @(*) begin
块中,首先将所有的控制信号初始化为0。 -
然后使用
case
语句根据操作码(op_code
)和功能码(func3
)设置控制信号。 -
根据不同的操作码和功能码,代码中设置了多个控制信号,下面将详细解释其作用:
-
we_reg
(寄存器写使能):用于控制是否进行寄存器写操作。 -
alu_asel
和alu_bsel
(ALU A 和 B 端口选择):用于选择 ALU 的 A 和 B 输入端口。 -
alu_op
(算术操作):用于指定 ALU 进行的算术操作,如加法、减法、位移等。 -
we_mem
(内存写使能):用于控制是否进行内存写操作。 -
npc_sel
(PC 写回选择):用于控制是否将 PC 的值写回寄存器。 -
immmgen_op
(立即数生成操作):用于指定立即数生成器执行的操作,如零扩展、符号扩展等。 -
bralu_op
(分支计算操作):用于指定分支计算器的操作,如等于、不等于、小于等。 -
wb_sel
(写回数据选择):用于选择写回的数据来源,如 ALU 计算结果或内存读取的数据。 -
memdata_width
(访问宽度):用于指定内存访问的宽度,如字节、半字、字、双字等。
-
根据操作码和功能码的不同,以上控制信号会被分别设置为不同的值,以适应不同指令的需求。最后,使用assign
语句,将所有的控制信号组合成一个输出信号(comb_decode
)。