盘算机构成原理(盘算机体系3)--实验七:新增指令实验
一、实验目的相识RISC-V mini处理处罚器架构,在其底子之上新增一个指令,完成筹划并观察指令执⾏。
二、实验内容
1) 修改数据通路,新增指令comb rs1,rs2,rd接纳R型指令格式,实现将rs1高16位和rs2低16位拼接成32位整数,而且生存到rd寄存器。
2) 在处理处罚器上实验该指令,观察仿真波形,验证功能是否精确。
3) 自行筹划其他功能指令,并验证筹划是否精确
三、实验情况
硬件:桌面PC
软件:Chisel开辟情况
四、实验步调及阐明
1)实验要求
学习Chisel数据通路的Chisel形貌,特殊是指令译码部门和core核心代码。然后按照下面操纵完成指令译码器的修改,以及数据通路的修改,按照参考文档完成comb指令的实现,自行筹划新指令实现其功能并验证。
2)实验过程
(一)COMB指令
分析:添加新指令 comb ,起首须要根据riscv指令格式,设置该指令各个字段的值,并在相应文件中添加该指令的比特模式。然后设置该指令的译码效果,接着在ALU中实现该指令的功能。末了让该指令在处理处罚器上实验,验证功能是否精确。
1. 在Instrutcions.scala文件中添加 comb 指令比特模式串
comb 为R型指令,riscv的R型指令格式如下:
指令功能:comb 是一种 R 型指令,将 rs1 的高 16 位与 rs2 的低 16 位拼接成 32 位数据,效果存储到 rd。
指令格式:基于 RISC-V 的 R 型指令格式,详细字段如下:
opcode(操纵码):7位,用于指定指令范例。
rs2(源寄存器2):5位,指定第二个源寄存器。
rs1(源寄存器1):5位,指定第一个源寄存器。
funct3(功能字段3):3位,用于区分具有雷同操纵码的差别指令。
rd(目的寄存器):5位,指定目的寄存器,用于存放指令实验效果。
opcode/funct7(功能字段7):7位,用于区分具有雷同操纵码和funct3的差别指令。
为了制止新加指令与riscv-mini已有指令辩论,将 comb 指令的opcode、funct3和funct7部门设置为0110011、111、0000001。然后利用 BitPat() 函数设置 comb 指令的比特模式。
步调 1.1:界说指令比特模式
在Instructions.scala文件中,添加以下的代码来界说comb指令的比特模式:
[*]// Instructions.scala
[*]package min
[*]import chisel3.util.BitPat
[*]object Instructions {
[*]// 省略RISCV-mini已界说的指令
[*]// 新增指令COMB
[*]def COMB = BitPat("b0000001??????????111?????0110011")
[*]}
这里的BitPat()函数用于界说一个位模式,确保与现有指令无辩论,此中?体现不关心的位,可以是0或1。
2. 添加 comb 指令的译码
步调 2.1:界说 ALU 操纵常量
comb 指令须要在ALU中将rs1高16位和rs2低16位拼接成32位整数,因此须要在Alu.scala文件中添加常量 ALU_COMB ,让译码器可以译码出精确的信号。因此,在 Alu.scala 文件中添加新的操纵码:
[*]// Alu.scala
[*]import chisel3._
[*]import chisel3.util._
[*]object Alu {
[*] val ALU_ADD = 0.U(4.W)
[*] val ALU_SUB = 1.U(4.W)
[*] val ALU_AND = 2.U(4.W)
[*] val ALU_OR = 3.U(4.W)
[*] val ALU_XOR = 4.U(4.W)
[*] val ALU_SLT = 5.U(4.W)
[*] val ALU_SLL = 6.U(4.W)
[*] val ALU_SLTU = 7.U(4.W)
[*] val ALU_SRL = 8.U(4.W)
[*] val ALU_SRA = 9.U(4.W)
[*] val ALU_COPY_A = 10.U(4.W)
[*] val ALU_COPY_B = 11.U(4.W)
[*] // 新加常量
[*] val ALU_COMB = 12.U(4.W)
[*] val ALU_XXX = 15.U(4.W)
[*]}
[*]//省略RISCV-mini的ALU实现部门
步调 2.2:设置控制信号映射
接下来为 comb 指令添加对应的译码映射。 comb 指令实验后pc须要加4,并将从寄存器文件中读取的数据rs1和rs2举行拼接操纵,然后将ALU输出的拼接效果写回到寄存器文件中。在 Control.scala 文件中为 comb 指令设置控制信号:
[*]// Control.scala
[*]import chisel3._
[*]import chisel3.util._
[*]object Control {
[*]//省略常量界说部门
[*]// format: off
[*]val default =
[*]List(PC_4 , A_XXX, B_XXX, IMM_X, ALU_XXX , BR_XXX, N, ST_XXX, LD_XXX, WB_ALU, N, CSR.N, Y)
[*]val map = Array(
[*]LUI -> List(PC_4 , A_PC, B_IMM, IMM_U, ALU_COPY_B, BR_XXX, N, ST_XXX, LD_XXX, WB_ALU, Y, CSR.N, N),
[*]AUIPC -> List(PC_4 , A_PC, B_IMM, IMM_U, ALU_ADD , BR_XXX, N, ST_XXX, LD_XXX, WB_ALU, Y, CSR.N, N),
[*]JAL -> List(PC_ALU, A_PC, B_IMM, IMM_J, ALU_ADD , BR_XXX, Y, ST_XXX, LD_XXX, WB_PC4, Y, CSR.N, N),
[*]//省略部门指令译码映射
[*]// 这是COMB指令的译码映射
[*]COMB -> List(PC_4 , A_RS1, B_RS2, IMM_X, ALU_COMB , BR_XXX, N, ST_XXX, LD_XXX, WB_ALU, N, CSR.N, N))
[*]// format: on
[*]}
[*]//省略RISCV-mini的控制实现部门
3. 实现 comb 指令的实验操纵
步调 3.1:实现拼接逻辑
在Alu.scala文件添加将rs1高16位和rs2低16位拼接成32位整数的操纵。因此,在 Alu.scala 文件中为 comb 指令添加详细实验逻辑:利用 MuxLookup 多路选择器,根据 alu_op 确定实验操纵。Cat() 函数将 io.A(31,16) 与 io.B(15,0) 拼接为 32 位数据。
[*]// Alu.scala
[*]import chisel3._
[*]import chisel3.util._
[*]//省略Alu常量界说和端口声明部门
[*]class AluSimple(val width: Int) extends Alu {
[*] val io = IO(new AluIO(width))
[*] val shamt = io.B(4, 0).asUInt
[*] io.out := MuxLookup(
[*] io.alu_op,
[*] io.B,
[*] Seq(
[*] ALU_ADD -> (io.A + io.B),
[*] ALU_SUB -> (io.A - io.B),
[*] ALU_SRA -> (io.A.asSInt >> shamt).asUInt,
[*] ALU_SRL -> (io.A >> shamt),
[*] ALU_SLL -> (io.A << shamt),
[*] ALU_SLT -> (io.A.asSInt < io.B.asSInt),
[*] ALU_SLTU -> (io.A < io.B),
[*] ALU_AND -> (io.A & io.B),
[*] ALU_OR -> (io.A | io.B),
[*] ALU_XOR -> (io.A ^ io.B),
[*] ALU_COPY_A -> io.A,
[*] // COMB指令实验
[*] ALU_COMB -> Cat(io.A(31,16), io.B(15,0))
[*] )
[*] )
[*] io.sum := io.A + Mux(io.alu_op(0), -io.B, io.B)
[*]}
[*]class AluArea(val width: Int) extends Alu {
[*] val io = IO(new AluIO(width))
[*] val sum = io.A + Mux(io.alu_op(0), -io.B, io.B)
[*] val cmp =
[*]Mux(io.A(width - 1) === io.B(width - 1), sum(width - 1), Mux(io.alu_op(1), io.B(width - 1), io.A(width -1)))
[*] val shamt = io.B(4, 0).asUInt
[*] val shin = Mux(io.alu_op(3), io.A, Reverse(io.A))
[*]val shiftr = (Cat(io.alu_op(0) && shin(width - 1), shin).asSInt >> shamt)(width - 1, 0)
[*] val shiftl = Reverse(shiftr)
[*] // 将A(rs1)的高16位与B(rs2)的低16位拼接
[*] val comb = Cat(io.A(31,16), io.B(15,0))
[*] val out =
[*] Mux(
[*] io.alu_op === ALU_ADD || io.alu_op === ALU_SUB,
[*] sum,
[*] Mux(
[*] io.alu_op === ALU_SLT || io.alu_op === ALU_SLTU,
[*] cmp,
[*] Mux(
[*] io.alu_op === ALU_SRA || io.alu_op === ALU_SRL,
[*] shiftr,
[*] Mux(
[*] io.alu_op === ALU_SLL,
[*] shiftl,
[*] Mux(
[*] io.alu_op === ALU_AND,
[*] io.A & io.B,
[*] Mux(
[*] io.alu_op === ALU_OR,
[*] io.A | io.B,
[*] Mux(io.alu_op === ALU_XOR, io.A ^ io.B,
[*] // COMB指令实验
[*]Mux(io.alu_op === ALU_COMB, comb, Mux(io.alu_op===ALU_COPY_A, io.A, io.B)))
[*] )
[*] )
[*] )
[*] )
[*] )
[*] )
[*] io.out := out
[*] io.sum := sum
[*]}
对 comb 指令举行测试
步调 4.1:编写测试步伐
编写一个步伐加载随处理处罚器中,调用 comb 指令并观察效果。comb.s代码如下:
[*] .text # Define beginning of text section
[*] .global _start # Define entry _start
[*]_start:
[*] lui x6, 1 # x6 = 0x00001000
[*] lui x7, 2 # x7 = 0x00002000
[*] # comb x5, x6, x7
[*]exit:
[*] csrw mtohost, 1
[*] j exit
[*] .end # End of file
请注意,由于 comb 为自己参加的指令,不能被汇编器汇编,以是这里将其解释掉,到背面天生的comb.hex文件中再将 comb x5, x6, x7 的二进制添加进去。
步调 4.2:编译测试步伐
编写完步伐后,利用如下下令举行编译:
$ riscv32-unknown-elf-gcc -nostdlib -Ttext=0x200 -o comb comb.s
然后利用 elf2hx 下令将comb二进制文件转换成十六进制:
$ elf2hex 16 4096 comb > comb.hex
在comb.hex文件中,可以找到 lui x6, 1 和 lui x7, 2 的呆板码对应的十六进制情势:
comb x5, x6, x7 转换成呆板码的十六机制情势为 027372b3。因此处指令存储为小端模式,故我们须要将十六进制数插入到第一个红线的前面。修改后如下:
步调 4.3:运行仿真并观察波形
接着须要在主目次下一次实验 make 和 make verilator 下令(若之前已经实验过,则在此次操纵之前须要实验 make clean ),实验后会产生VTile可实验文件。然后实验下面下令,使mini处理处罚器实验新建指令并产生波形文件。
$ ./VTile comb.hex comb.vcd
利用GTKWave打开comb.vcd文件,其波形图如下:
指令对应的十六进制情势见下表:
表 1:指令所对应16进制
指令
十六进制情势
阐明
lui x6,1
00001337
x6=0x00001000
lui x7,2
000023b7
x7=0x00002000
comb x5,x6,x7
027372b3
x5=cat(x6(31:16),x7(15:0))s
从波形图中可以看出, comb 指令将拼接后的效果0x00002000写回到了5号寄存器中,故该指令实验正常。
(二)new_inst指令
添加新指令 new_inst ,new_inst 是一个 R 型指令,功能为:将 rs1 和 rs2 的值举行对 rs1 的值取反后与 rs2 举行按位与操纵,将效果存储到 rd。
1. 在Instrutcions.scala文件中添加 new_inst 指令比特模式串
指令格式:
基于 R 型指令格式,opcode、funct3 和 funct7 的设置为:
[*]opcode: 0110011
[*]funct3: 110
[*]funct7: 0000001
在 Instructions.scala 文件中添加以下内容:
[*]// Instructions.scala
[*]import chisel3.util.BitPat
[*]object Instructions {
[*] // 已有指令省略
[*] def NEW_INST = BitPat("b0000001??????????110?????0110011")
[*]}
2. 添加 comb 指令的译码
步调 2.1:界说 ALU 操纵常量
在 Alu.scala 文件中为 new_inst 指令添加常量:
[*]// Alu.scala
[*]object Alu {
[*] // 已有常量省略
[*] val ALU_NEW_INST = 13.U(4.W) // 新指令对应的 ALU 操纵常量
[*]}
步调 2.2:设置控制信号映射
修改 Control.scala 文件,设置指令控制信号。为 new_inst 指令添加控制信号映射,确保译码后能精确触发对应的 ALU 操纵:
[*]// Control.scala
[*]val map = Array(
[*] // 已有映射省略
[*] NEW_INST -> List(PC_4, A_RS1, B_RS2, IMM_X, ALU_NEW_INST, BR_XXX, N, ST_XXX, LD_XXX, WB_ALU, N, CSR.N, N)
[*])
3. 实现 new_inst 指令的实验操纵
步调 3.1:实现指令逻辑
为 new_inst 添加操纵(~io.A & io.B 体现对 rs1 的值取反后与 rs2 举行按位与操纵):
[*]// Alu.scala
[*]class AluSimple(val width: Int) extends Alu {
[*] val io = IO(new AluIO(width))
[*] io.out := MuxLookup(
[*] io.alu_op,
[*] io.B,
[*] Seq(
[*] // 已有操纵省略
[*] ALU_NEW_INST -> (~io.A & io.B) // 新指令逻辑实现
[*] )
[*] )
[*]}
要在 AluArea 中添加对 new_inst 指令的支持,举行以下步调:
[*]界说指令逻辑:new_inst 的功能是对 rs1 取反后与 rs2 按位与,将效果存储到 rd。对应到硬件逻辑,可以直接通过 ~io.A & io.B 实现。
[*]在ALU的Mux逻辑中插入新指令处理处罚逻辑:在out的天生逻辑中添加new_inst的逻辑。
[*]确保ALU操纵码精确映射:在控制逻辑中界说ALU_NEW_INST并在AluArea中利用。
[*]class AluArea(val width: Int) extends Alu {
[*] val io = IO(new AluIO(width))
[*] val sum = io.A + Mux(io.alu_op(0), -io.B, io.B)
[*] val cmp = Mux(
[*] io.A(width - 1) === io.B(width - 1),
[*] sum(width - 1),
[*] Mux(io.alu_op(1), io.B(width - 1), io.A(width - 1))
[*] )
[*] val shamt = io.B(4, 0).asUInt
[*] val shin = Mux(io.alu_op(3), io.A, Reverse(io.A))
[*] val shiftr = (Cat(io.alu_op(0) && shin(width - 1), shin).asSInt >> shamt)(width - 1, 0)
[*] val shiftl = Reverse(shiftr)
[*]
[*] // 新增指令逻辑: rs1取反后与rs2按位与
[*] val newInst = ~io.A & io.B
[*]
[*] // 其他指令逻辑
[*] val comb = Cat(io.A(31, 16), io.B(15, 0))
[*]
[*] val out =
[*] Mux(
[*] io.alu_op === ALU_ADD || io.alu_op === ALU_SUB,
[*] sum,
[*] Mux(
[*] io.alu_op === ALU_SLT || io.alu_op === ALU_SLTU,
[*] cmp,
[*] Mux(
[*] io.alu_op === ALU_SRA || io.alu_op === ALU_SRL,
[*] shiftr,
[*] Mux(
[*] io.alu_op === ALU_SLL,
[*] shiftl,
[*] Mux(
[*] io.alu_op === ALU_AND,
[*] io.A & io.B,
[*] Mux(
[*] io.alu_op === ALU_OR,
[*] io.A | io.B,
[*] Mux(
[*] io.alu_op === ALU_XOR,
[*] io.A ^ io.B,
[*] Mux(
[*] io.alu_op === ALU_COMB,
[*] comb,
[*] Mux(
[*] io.alu_op === ALU_NEW_INST, // 新增指令的逻辑
[*] newInst,
[*] Mux(io.alu_op === ALU_COPY_A, io.A, io.B)
[*] )
[*] )
[*] )
[*] )
[*] )
[*] )
[*] )
[*] )
[*] )
[*] io.out := out
[*] io.sum := sum
[*]}
4. 对 new_inst 指令举行测试
步调 4.1:编写测试步伐
编写一个步伐加载随处理处罚器中,调用 new_inst 指令并观察效果。new_inst.s代码如下:
[*] .text # Define beginning of text section
[*] .global _start # Define entry _start
[*]_start:
[*] lui x6, 1 # x6 = 0x00001000
[*] lui x7, 2 # x7 = 0x00002000
[*] # new_inst x5, x6, x7
[*]exit:
[*] csrw mtohost, 1
[*] j exit
[*] .end # End of file
由于 new_inst 为自己参加的指令,不能被汇编器汇编,以是这里将其解释掉,到背面天生的new_inst.hex文件中再将 new_inst x5, x6, x7 的二进制添加进去。
步调 4.2:编译测试步伐
编写完步伐后,利用如下下令举行编译:
$ riscv32-unknown-elf-gcc -nostdlib -Ttext=0x200 -o new_inst new_inst.s
然后利用 elf2hx 下令将new_inst二进制文件转换成十六进制:
$ elf2hex 16 4096 new_inst >new_inst.hex
在new_inst.hex文件中,可以找到 lui x6, 1 和 lui x7, 2 的呆板码对应的十六进制情势:
new_inst x5, x6, x7 转换成呆板码的十六机制情势为 01070333。因此处指令存储为小端模式,故我们须要将十六进制数插入到第一个红线的前面。修改后如下:
步调 4.3:运行仿真并观察波形
接着须要在主目次下一次实验 make 和 make verilator 下令(若之前已经实验过,则在此次操纵之前须要实验 make clean ),实验后会产生VTile可实验文件。然后实验下面下令,使mini处理处罚器实验新建指令并产生波形文件。
$ ./VTile new_inst.hex new_inst.vcd
利用GTKWave打开new_inst.vcd文件,其波形图如下:
指令对应的十六进制情势见下表:
表 2:指令所对应16进制
指令
十六进制情势
阐明
lui x6,1
00001337
x6=0x00001000
lui x7,2
000023b7
x7=0x00002000
new_inst x5,x6,x7
01070333
x5=~(x6)&x7=0x00002000
手算验证:
[*]~x6 = 0xFFFFEFFF 的二进制体现为:1111 1111 1111 1110 1111 1111 1111 1111。
[*]x7 = 0x00002000 的二进制体现为:0000 0000 0000 0010 0000 0000 0000 0000。
[*]x5=(~x6)&x7=0x00002000,即验证精确。
从波形图中可以看出, new_inst 指令将效果0x00002000写回到了5号寄存器中,故该指令实验正常。
五、实验效果
(一)COMB指令
指令对应的十六进制情势见下表:
指令
十六进制情势
阐明
lui x6,1
00001337
x6=0x00001000
lui x7,2
000023b7
x7=0x00002000
comb x5,x6,x7
027372b3
x5=cat(x6(31:16),x7(15:0))
从波形图中可以看出, comb 指令将拼接后的效果0x00002000写回到了5号寄存器中,故该指令实验正常。
效果验证:comb 指令的功能是将两个寄存器的值拼接成一个 32 位的整数。详细来说,comb 指令将 x6 的高 16 位和 x7 的低 16 位拼接在一起,形成一个新的 32 位值,并将效果写入 x5 寄存器。
[*]x6 = 0x00001000 的二进制体现为:00000000 00000001 00000000 00000000。
[*]x7 = 0x00002000 的二进制体现为:00000000 00000010 00000000 00000000。
将 x6 的高 16 位(00000000 00000001)与 x7 的低 16 位(00000000 00000000)拼接,得到的 32 位效果是:
x5 = cat(x6(31:16), x7(15:0)) = 0x00002000
因此,x5 的终极值是 0x00002000,指令实验精确。
(二)new_inst指令
指令对应的十六进制情势见下表:
指令
十六进制情势
阐明
lui x6,1
00001337
x6=0x00001000
lui x7,2
000023b7
x7=0x00002000
new_inst x5,x6,x7
01070333
x5=~(x6)&x7=0x00002000
从波形图中可以看出, new_inst 指令将效果0x00002000写回到了5号寄存器中,故该指令实验正常。
效果分析:new_inst 是一个 R 型指令,的功能是将 x6 的值按位取反后,与 x7 的值举行按位与操纵,并将效果存储到 x5 中。
[*]x6 = 0x00001000 的二进制体现为:00000000 00000001 00000000 00000000。
[*]对 x6 举行按位取反(~x6),得到 0xFFFFEFFF,即二进制体现为:11111111 11111111 11101111 11111111。
[*]x7 = 0x00002000 的二进制体现为:00000000 00000010 00000000 00000000。
[*]按位与操纵 ~x6 与 x7:
[*]11111111 11111111 11101111 11111111 (x6取反)
[*]AND
[*]00000000 00000010 00000000 00000000 (x7)
[*]--------------------------------------
[*]00000000 00000010 00000000 00000000 (效果)
得到的效果是 0x00002000,以是 x5 的值为 0x00002000,指令实验精确。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页:
[1]