html5 metro风格网站,苏州网站设计都选苏州聚尚网络,汕头h5模板建站,做网站售后几年实验目标
设计完成一个连续取指令并进行指令译码的电路#xff0c;从而掌握设计简单数据通路的基本方法。
实验内容
本实验分成三周#xff08;三次#xff09;完成#xff1a;1#xff09;首先完成一个译码器#xff08;30分#xff09;#xff1b;2#xff09;接…实验目标
设计完成一个连续取指令并进行指令译码的电路从而掌握设计简单数据通路的基本方法。
实验内容
本实验分成三周三次完成1首先完成一个译码器30分2接着实现一个寄存器文件30分3最后添加指令存储器和地址部件等将这些部件组合成一个数据通路原型40分。
实验环境
硬件桌面PC 软件Linux Chisel开发环境
实验步骤及说明
本次试验分为三个部分 1设计译码电路输入位32bit的一个机器字按照课本MIPS 指令格式完成add、sub、lw、sw指令译码其他指令一律译码成nop指令。输入信号名为Instr_word对上述四条指令义译码输出信号名为add_op、sub_op、lw_op和sw_op其余指令一律译码为nop 给出Chisel设计代码和仿真测试波形观察输入Instr_word为add R1,R2,R3; sub R0,R5,R6lw R5,100(R2), sw R5,104(R2)、JAL RA,100(R2)时对应的输出波形 思路 1.定义一个Chisel模块Decoder表示一个硬件模块用于译码输入的机器字。 2.创建接口io其中包含了输入和输出信号。输入信号是Instr_word代表32位的机器字。从输入信号Instr_word中提取操作码字段opcode和功能码字段func分别用于后续的匹配。 3.初始化所有的输出信号将它们都设置为false并将nop_op设置为true以表示默认情况下是nop指令。 4.使用条件语句来匹配操作码。当操作码匹配某个特定操作码时进入相应的条件分支。 5.在每个条件分支中继续使用条件语句来匹配功能码。如果功能码匹配某个指令的功能码那么将相应的输出信号设置为true表示这是一个有效的指令。同时将nop_op设置为false表示不是nop指令。
Decoder代码
import chisel3._
import chisel3.util._
import chisel3.stage.ChiselStageclass Decoder extends Module {val io IO(new Bundle {val Instr_word Input(UInt(32.W)) // 输入机器指令val add_op Output(Bool()) // add 操作信号val sub_op Output(Bool()) // sub 操作信号val lw_op Output(Bool()) // lw 操作信号val sw_op Output(Bool()) // sw 操作信号val nop_op Output(Bool()) // nop 操作信号})// 定义opcodeval ADD_OPCODE b100000.U(6.W)val SUB_OPCODE b100010.U(6.W)val LW_OPCODE b100011.U(6.W)val SW_OPCODE b101011.U(6.W)// 提取opcode和func字段val opcode io.Instr_word(31, 26)val func io.Instr_word(5, 0)io.add_op : false.Bio.sub_op : false.Bio.lw_op : false.Bio.sw_op : false.Bio.nop_op : true.B// 根据opcode和func判断指令类型when(opcode b000000.U(6.W)) { when(func ADD_OPCODE) {io.add_op : true.Bio.nop_op : false.B}.elsewhen(func SUB_OPCODE) {io.sub_op : true.Bio.nop_op : false.B}}.elsewhen(opcode LW_OPCODE) {io.lw_op : true.Bio.nop_op : false.B}.elsewhen(opcode SW_OPCODE) {io.sw_op : true.Bio.nop_op : false.B}
}object Decoder extends App {(new ChiselStage).emitVerilog(new Decoder(), Array(--target-dir,generated))
}
测试代码 使用ChiselTest和ScalaTest对Decoder模块进行单元测试。在测试中针对不同的指令ADD、SUB、LW、SW、JAL模拟输入并验证Decoder模块的输出是否符合预期检查各个操作类型的信号是否正确识别。通过逐步模拟时钟步进和检查输出信号的方式确认Decoder在解码不同指令时的行为是否正确。
DecoderTest.scala:
import chisel3._
import chiseltest._
import org.scalatest.flatspec.AnyFlatSpecclass DecoderTest extends AnyFlatSpec with ChiselScalatestTester {behavior of Decoderit should pass in {test(new Decoder()).withAnnotations(Seq(WriteVcdAnnotation)) { c // 定义测试指令val ADD b00000000010000110000100000100000.U(32.W) val SUB b00000000101001100000000000100010.U(32.W) val LW b10001100101000100000000000110010.U(32.W) val SW b10101100101000100000000000110100.U(32.W) val JAL b00001100001000100000000000110010.U(32.W) c.clock.setTimeout(0)// 测试 ADD 指令c.io.Instr_word.poke(ADD)c.clock.step(1)c.io.add_op.expect(true.B)c.io.sub_op.expect(false.B)c.io.lw_op.expect(false.B)c.io.sw_op.expect(false.B)c.io.nop_op.expect(false.B)// 测试 SUB 指令c.io.Instr_word.poke(SUB)c.clock.step(1)c.io.add_op.expect(false.B)c.io.sub_op.expect(true.B)c.io.lw_op.expect(false.B)c.io.sw_op.expect(false.B)c.io.nop_op.expect(false.B)// 测试 LW 指令c.io.Instr_word.poke(LW)c.clock.step(1)c.io.add_op.expect(false.B)c.io.sub_op.expect(false.B)c.io.lw_op.expect(true.B)c.io.sw_op.expect(false.B)c.io.nop_op.expect(false.B)// 测试 SW 指令c.io.Instr_word.poke(SW)c.clock.step(1)c.io.add_op.expect(false.B)c.io.sub_op.expect(false.B)c.io.lw_op.expect(false.B)c.io.sw_op.expect(true.B)c.io.nop_op.expect(false.B)// 测试 JAL 指令c.io.Instr_word.poke(JAL)c.clock.step(1)c.io.add_op.expect(false.B)c.io.sub_op.expect(false.B)c.io.lw_op.expect(false.B)c.io.sw_op.expect(false.B)c.io.nop_op.expect(true.B)}}
}2设计寄存器文件共32个32bit寄存器允许两读一写且0号寄存器固定读出位0。四个输入信号为RS1、RS2、WB_data、Reg_WB寄存器输出RS1_out和RS2_out寄存器内部保存的初始数值等同于寄存器编号 给出Chisel设计代码和仿真测试波形观察RS15,RS28WB_data0x1234,Reg_WB1的输出波形和受影响寄存器的值。 思路 1.定义一个名为 “RegisterFile” 的Chisel模块。使用val io IO(new Bundle()) 创建模块的输入和输出接口包括用于指定源寄存器索引、写入数据和写入使能信号以及用于输出源寄存器数据的输出端口。 2.创建一个包含32个32位寄存器值的寄存器数组初始值从0到31代表了模拟的寄存器文件。 3.使用 Mux 操作根据输入的源寄存器索引RS1和RS2的值从寄存器文件中选择要输出的数据。如果索引为0就输出0否则从寄存器文件中读取对应索引的寄存器的值。 4.当输入信号Reg_WB为真时表示要进行寄存器写入操作。使用Mux操作根据 RS1 和RS2的值将输入的WB_data写入寄存器文件的相应索引位置。
RegisterFile代码
import chisel3._
import chisel3.stage.ChiselStageclass RegisterFile extends Module {val io IO(new Bundle {val RS1 Input(UInt(5.W)) // RS1寄存器编号val RS2 Input(UInt(5.W)) // RS2寄存器编号val WB_data Input(UInt(32.W)) // 要写入寄存器的值val Reg_WB Input(Bool()) // 控制是否进行写操作val RS1_out Output(UInt(32.W)) // RS1寄存器值val RS2_out Output(UInt(32.W)) // RS2寄存器值})// 定义32个32位寄存器并将初始值设置为寄存器编号val regs RegInit(VecInit(Seq.tabulate(32)(i i.U(32.W))))// 处理RS1和RS2的读取操作io.RS1_out : Mux(io.RS1 0.U, 0.U, regs(io.RS1)) io.RS2_out : Mux(io.RS2 0.U, 0.U, regs(io.RS2)) // 处理写操作when(io.Reg_WB) { regs(io.RS1) : Mux(io.RS1 0.U, 0.U, io.WB_data)regs(io.RS2) : Mux(io.RS2 0.U, 0.U, io.WB_data)}
}object RegisterFile extends App {(new ChiselStage).emitVerilog(new RegisterFile(), Array(--target-dir,generated))
}测试代码 使用poke方法设置RS1为5RS2为8设置WB_data为十六进制数0x1234。将 Reg_WB 设置为 true启用写入。然后执行c.clock.step(1)来推进模拟时钟1个时钟周期。
RegisterFileTest.scala
import chisel3._
import chiseltest._
import org.scalatest.flatspec.AnyFlatSpecclass RegisterFileTest extends AnyFlatSpec with ChiselScalatestTester {behavior of RegisterFile Moduleit should observe the effect on registers in {test (new RegisterFile).withAnnotations(Seq(WriteVcdAnnotation)) { c // 设置输入信号// RS1 5c.io.RS1.poke(5.U) // RS2 8c.io.RS2.poke(8.U) // WB_data 0x1234c.io.WB_data.poke(0x1234.U) c.io.Reg_WB.poke(true) c.clock.step(1)}}
}3实现一个32个字的指令存储器从0地址分别存储4条指令add R1,R2,R3; sub R0,R5,R6lw R5,100(R2), sw R5,104(R2)。然后组合指令存储器、寄存器文件、译码电路并结合PC更新电路PC初值为0、WB_data和Reg_WB信号产生电路最终让电路能逐条指令取出、译码不需要完成指令执行。 给出Chisel设计代码和仿真测试波形观察四条指令的执行过程波形记录并解释其含义。 指令存储器设计思路
定义接口 rdEna: 读使能信号用于控制指令读取操作。 rdData: 读取的数据输出表示从内存读取的 32 位指令。 wrEna: 写使能信号用于控制指令写入操作。 wrAddr: 写地址用来指示写入操作的目标地址。 wrData: 写入的数据输入表示要写入的 32 位指令。内存和程序计数器初始化 创建了一个名为 pcReg 的程序计数器寄存器并将其初始化为 0。使用 SyncReadMem 创建一个同步读取的8位宽、128单元的内存用来存储指令。写内存 当wrEna为真时通过写入操作将wrData的不同部分分别存储在指定地址及其后续三个地址中。读内存 当rdEna为真时从地址pcReg及其后续三个地址读取数据分别存储在rdData0至rdData中。使用Cat函数将这四个字节的数据连接成一个32位的数据以产生完整的指令。程序计数器pcReg每次读取后增加4以指向下一条指令。默认情况 当 rdEna 为假时即没有读取请求输出设置为0.U表示没有有效数据。
InstructionMemory代码
// 指令存储器
class InstructionMemory extends Module {val io IO(new Bundle {// 读val rdEna Input(Bool())val rdData Output(UInt(32.W))// 写val wrEna Input(Bool())val wrAddr Input(UInt(10.W))val wrData Input(UInt(32.W))})// 程序计数器用于跟踪当前指令的读取位置初始化为 0val pcReg RegInit(0.U(32.W))// 创建一个同步读取的 8 位宽、128 单元的内存用来存储指令val mem SyncReadMem(128,UInt(8.W))// 写内存when (io.wrEna) {mem(io.wrAddr) : io.wrData(7, 0)mem(io.wrAddr 1.U) : io.wrData(15, 8)mem(io.wrAddr 2.U) : io.wrData(23, 16)mem(io.wrAddr 3.U) : io.wrData(31, 24)}// 读内存when (io.rdEna) {val rdData0 mem.read(pcReg)val rdData1 mem.read(pcReg 1.U)val rdData2 mem.read(pcReg 2.U)val rdData3 mem.read(pcReg 3.U)io.rdData : Cat(rdData3, rdData2, rdData1, rdData0) pcReg : pcReg 4.U}.otherwise{io.rdData : 0.U}
}组合指令存储器、寄存器文件、译码电路后的核心代码
import chisel3._
import chisel3.util._// 指令存储器
class InstructionMemory extends Module {val io IO(new Bundle {// 读val rdEna Input(Bool())val rdData Output(UInt(32.W))// 写val wrEna Input(Bool())val wrAddr Input(UInt(10.W))val wrData Input(UInt(32.W))})// 程序计数器用于跟踪当前指令的读取位置初始化为 0val pcReg RegInit(0.U(32.W))// 创建一个同步读取的 8 位宽、128 单元的内存用来存储指令val mem SyncReadMem(128,UInt(8.W))// 写内存when (io.wrEna) {mem(io.wrAddr) : io.wrData(7, 0)mem(io.wrAddr 1.U) : io.wrData(15, 8)mem(io.wrAddr 2.U) : io.wrData(23, 16)mem(io.wrAddr 3.U) : io.wrData(31, 24)}// 读内存when (io.rdEna) {val rdData0 mem.read(pcReg)val rdData1 mem.read(pcReg 1.U)val rdData2 mem.read(pcReg 2.U)val rdData3 mem.read(pcReg 3.U)io.rdData : Cat(rdData3, rdData2, rdData1, rdData0) pcReg : pcReg 4.U}.otherwise{io.rdData : 0.U}
}// 寄存器文件
class RegisterFile extends Module {val io IO(new Bundle {// 两读val RS1 Input(UInt(5.W))val RS2 Input(UInt(5.W))val RS1_out Output(UInt(32.W))val RS2_out Output(UInt(32.W))// 一写val Reg_WB Input(Bool())val WB_data Input(UInt(32.W))})val registers RegInit(VecInit((0 until 32).map(i i.U(32.W))))io.RS1_out : Mux(io.RS1 0.U, 0.U, registers(io.RS1))io.RS2_out : Mux(io.RS2 0.U, 0.U, registers(io.RS2))// 根据写信号选择要写入的寄存器when (io.Reg_WB) {registers(io.RS1) : Mux(io.RS1 0.U, 0.U, io.WB_data)registers(io.RS2) : Mux(io.RS2 0.U, 0.U, io.WB_data)}
}
// 译码器
class Decoder extends Module {val io IO(new Bundle {val Instr_word Input(UInt(32.W)) // 输入信号// 输出信号val add_op Output(Bool())val sub_op Output(Bool())val lw_op Output(Bool())val sw_op Output(Bool())val nop_op Output(Bool())})// opcodeval AandS b000000.U(6.W)val ADD_OPCODE b100000.U(6.W)val SUB_OPCODE b100010.U(6.W)val LW_OPCODE b100011.U(6.W)val SW_OPCODE b101011.U(6.W)// 取出指令的 opcode 和 func val opcode io.Instr_word(31, 26)val func io.Instr_word(5, 0)// 指令默认是 nopio.add_op : false.Bio.sub_op : false.Bio.lw_op : false.Bio.sw_op : false.Bio.nop_op : true.B// 译码when (opcode AandS) {when (func ADD_OPCODE){io.add_op : true.Bio.nop_op : false.B}.elsewhen (func SUB_OPCODE){io.sub_op : true.Bio.nop_op : false.B}}.elsewhen (opcode LW_OPCODE) {io.lw_op : true.Bio.nop_op : false.B}.elsewhen (opcode SW_OPCODE) {io.sw_op : true.Bio.nop_op : false.B}
}class Processor extends Module {val io IO(new Bundle {// Decoderval Instr_word Output(UInt(32.W))val add_op Output(Bool())val sub_op Output(Bool())val lw_op Output(Bool())val sw_op Output(Bool())val nop_op Output(Bool())// RegisterFileval RS1_out Output(UInt(32.W))val RS2_out Output(UInt(32.W))// Instructionval rdEna Input(Bool())val wrAddr Input(UInt(10.W))val wrData Input(UInt(32.W))val wrEna Input(Bool())})// 实例化模块val decoder Module(new Decoder)val registerFile Module(new RegisterFile)val instructionMemory Module(new InstructionMemory)// 连接指令信号io.add_op : decoder.io.add_opio.sub_op : decoder.io.sub_opio.lw_op : decoder.io.lw_opio.sw_op : decoder.io.sw_opio.nop_op : decoder.io.nop_op// 连接控制信号instructionMemory.io.rdEna : io.rdEnainstructionMemory.io.wrEna : io.wrEnainstructionMemory.io.wrAddr : io.wrAddrinstructionMemory.io.wrData : io.wrData// 连接输入数据registerFile.io.RS1 : instructionMemory.io.rdData(25,21)registerFile.io.RS2 : instructionMemory.io.rdData(20, 16)decoder.io.Instr_word : instructionMemory.io.rdDataio.Instr_word : instructionMemory.io.rdData// 初始化寄存器文件的控制信号registerFile.io.Reg_WB : false.BregisterFile.io.WB_data : 0.U// 连接模块输出信号io.RS1_out : registerFile.io.RS1_outio.RS2_out : registerFile.io.RS2_out
}// 生成 Verilog 代码
object Processor extends App {(new chisel3.stage.ChiselStage).emitVerilog(new Processor(), Array(--target-dir, generated))
}
实例化解码器、寄存器文件和指令存储器将各模块的信号进行连接实现了指令解码、寄存器读写控制以及数据传输功能。同时对寄存器文件进行了初始化并将相关输出信号连接至模块的输出端口构建了一个简单的处理器电路。 测试代码 测试这四条指令add R1,R2,R3; sub R0,R5,R6; lw R5,100(R2) ; sw R5,104(R2)
import chiseltest._
import chisel3._
import org.scalatest.flatspec.AnyFlatSpecclass ProcessorTest extends AnyFlatSpec with ChiselScalatestTester {behavior of Processorit should execute instructions in memory correctly in {test(new Processor).withAnnotations(Seq(WriteVcdAnnotation)) { c val instructions Seq(b00000000010000110000100000100000.U(32.W), // ADDb00000000101001100000000000100010.U(32.W), // SUBb10001100101000100000000000110010.U(32.W), // LWb10101100101000100000000000110100.U(32.W) // SW)c.clock.setTimeout(0)// 写入指令for ((instr, addr) - instructions.zipWithIndex) {c.io.wrEna.poke(true)c.io.wrAddr.poke((addr * 4).U)c.io.wrData.poke(instr)c.clock.step(1)}// 结束写入c.io.wrEna.poke(false)c.io.rdEna.poke(true)c.clock.step(10)}}
}
实验结果
1.设计译码电路观察输入Instr_word为add R1,R2,R3; sub R0,R5,R6lw R5,100(R2), sw R5,104(R2)、JAL RA,100(R2)时对应的输出波形 (1) add指令(add R1, R2, R3 的十六进制为00430820) (2) sub指令(sub R0, R5, R6 的十六进制为00A60022) (3) lw指令(lw R5, 100(R2)的十六进制为8CA20032) (4) sw指令(sw R5, 104(R2) 的十六进制为ACA20034) (5) jal指令(jal 100 的十六进制为0C220032) 2. 设计寄存器文件给出Chisel设计代码和仿真测试波形观察RS15,RS28WB_data0x1234,Reg_WB1的输出波形和受影响寄存器的值。 (1) 打开波形图发现寄存器初值为其编号RS1 5RS2 8WB_data 0x1234 (2) 观察到在后面的时钟周期中0x1234写入R5和R8。 3. 设计指令存储器然后组合指令存储器、寄存器文件、译码电路, 最终让电路能逐条指令取出、译码。 (1) 第1条指令add R1,R2,R3: 此时io_Instr_word 00430820是add指令对应的16进制表达。io_add_op置为1表示该操作为为add操作。io_RS1_out的值为2io_RS2_out的值为3表示当前操作了R2,R3这2个寄存器。 (2) 第2条指令sub R0,R5,R6: 此时io_Instr_word 00A60022是sub指令对应的16进制表达。io_sub_op置为1表示该操作为为sub操作。io_RS1_out的值为5io_RS2_out的值为6表示当前操作了R5,R6这2个寄存器。 (3) 第3条指令lw R5,100(R2): 此时io_Instr_word 8CA20032是lw指令对应的16进制表达。io_lw_op置为1表示该操作为为lw操作。io_RS1_out的值为5io_RS2_out的值为2表示当前操作了R5,R2这2个寄存器。 (4) 第4条指令sw R5,104(R2): 此时io_Instr_word ACA20034是sw指令对应的16进制表达。io_sw_op置为1表示该操作为为sw操作。io_RS1_out的值为5io_RS2_out的值为2表示当前操作了R5,R2这2个寄存器。 (5) 指令全部执行之后io_Instr_word为0io_RS1_out的值为0io_RS2_out的值为0表示当前没有操作寄存器。
实验总结与体会
译码器的设计 在实验的第一部分我设计了译码器。译码器的主要作用是根据指令的操作码确定要执行的操作。通过实现一个简单的译码器我学会了如何将机器指令解析成控制信号并将这些控制信号进一步用于驱动后续硬件模块。寄存器文件的实现 实验的第二部分是设计寄存器文件负责存储和读取寄存器数据。通过实现一个多端口的寄存器文件我掌握了如何通过硬件访问和操作寄存器值。指令存储器的设计与数据通路原型 在最后的阶段我设计了指令存储器并将其与前面的译码器和寄存器文件连接形成了一个简单的数据通路原型。这个阶段不仅是对之前设计的验证也使我体会到了数据通路中各个模块之间的配合与协调。 总的来说这个实验为我今后深入学习计算机体系结构打下了良好的基础。