【计算机组成与设计】Chisel取指和指令译码设计

本次试验分为三个部分:

目录

设计译码电路

设计寄存器文件

实现一个32个字的指令存储器


设计译码电路

输入位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,R6,lw R5,100(R2), sw R5,104(R2)、JAL RA,100(R2)时,对应的输出波形

Decode.scala

import chisel3._

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())
  })
  // 定义操作码
  val OPCODE_ADD = "b000000".U
  val OPCODE_SUB = "b000000".U
  val OPCODE_LW = "b100011".U
  val OPCODE_SW = "b101011".U
  //定义功能码
  val FUNCT_ADD = "b100000".U
  val FUNCT_SUB = "b100010".U
  // 提取MIPS指令的操作码
  val opcode = io.Instr_word(31, 26)
  //提取MIPS指令的功能码
  val funct = io.Instr_word(5, 0)
  // 译码
  io.add_op := opcode === OPCODE_ADD && funct === FUNCT_ADD
  io.sub_op := opcode === OPCODE_SUB && funct === FUNCT_SUB
  io.lw_op := opcode === OPCODE_LW
  io.sw_op := opcode === OPCODE_SW
  io.nop_op := !(io.add_op || io.sub_op || io.lw_op || io.sw_op)
}

object Decoder extends App {
  (new chisel3.stage.ChiselStage).emitVerilog(new Decoder())
}

 DecoderTest.scala

import chiseltest._
import org.scalatest.flatspec.AnyFlatSpec
import chisel3._

class DecoderTest extends AnyFlatSpec with ChiselScalatestTester {
  behavior of "Decoder"
  it should "correctly decode instructions" in {
    test(new Decoder).withAnnotations(Seq(WriteVcdAnnotation)) { c =>
      // Test instructions
      val addInstruction = "b000000_00010_00011_00001_00000_100000".U
      val subInstruction = "b000000_00101_00110_00000_00000_100010".U
      val lwInstruction = "b100011_00010_00101_0000000001100100".U
      val swInstruction = "b101011_00010_00101_0000000001101000".U
      val jalInstruction = "b000011_00000_00000000000000000000".U
      // Set the input instruction and evaluate the decoder
      c.io.Instr_word.poke(addInstruction)
      c.clock.step()
      c.io.add_op.expect(true)
      c.io.sub_op.expect(false)
      c.io.lw_op.expect(false)
      c.io.sw_op.expect(false)
      c.io.nop_op.expect(false)

      c.io.Instr_word.poke(subInstruction)
      c.clock.step()
      c.io.add_op.expect(false)
      c.io.sub_op.expect(true)
      c.io.lw_op.expect(false)
      c.io.sw_op.expect(false)
      c.io.nop_op.expect(false)

      c.io.Instr_word.poke(lwInstruction)
      c.clock.step()
      c.io.add_op.expect(false)
      c.io.sub_op.expect(false)
      c.io.lw_op.expect(true)
      c.io.sw_op.expect(false)
      c.io.nop_op.expect(false)

      c.io.Instr_word.poke(swInstruction)
      c.clock.step()
      c.io.add_op.expect(false)
      c.io.sub_op.expect(false)
      c.io.lw_op.expect(false)
      c.io.sw_op.expect(true)
      c.io.nop_op.expect(false)

      c.io.Instr_word.poke(jalInstruction)
      c.clock.step()
      c.io.add_op.expect(false)
      c.io.sub_op.expect(false)
      c.io.lw_op.expect(false)
      c.io.sw_op.expect(false)
      c.io.nop_op.expect(true)
    }
  }
}

设计寄存器文件

共32个32bit寄存器,允许两读一写,且0号寄存器固定读出位0。四个输入信号为RS1、RS2、WB_data、Reg_WB,寄存器输出RS1_out和RS2_out;寄存器内部保存的初始数值等同于寄存器编号

给出Chisel设计代码和仿真测试波形,观察RS1=5,RS2=8,WB_data=0x1234,Reg_WB=1的输出波形和受影响寄存器的值。

Register.scala

import chisel3._
import chisel3.util._

class 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(UInt(5.W)) // 选择写入数据的寄存器
    val RS1_out = Output(UInt(32.W)) // RS1输出数据
    val RS2_out = Output(UInt(32.W)) // RS2输出数据
  })

  val registers = RegInit(VecInit((0 until 32).map(_.U(32.W)))) // 32个32位寄存器,初始值等于寄存器编号
  registers(io.Reg_WB) := io.WB_data // 写入数据到寄存器
  io.RS1_out := Mux(io.RS1 === 0.U, 0.U, registers(io.RS1)) // RS1输出数据,0号寄存器固定读出位0
  io.RS2_out := Mux(io.RS2 === 0.U, 0.U, registers(io.RS2)) // RS2输出数据,0号寄存器固定读出位0
}

object RegisterFile extends App {
  (new chisel3.stage.ChiselStage).emitVerilog(new RegisterFile())
}

RegisterTest.scala

import chisel3._
import chiseltest._
import org.scalatest.flatspec.AnyFlatSpec
import chisel3.util._

class RegisterFileTest extends AnyFlatSpec with ChiselScalatestTester {
  behavior of "RegisterFile"
  it should "correctly update and read registers" in {
    test(new RegisterFile).withAnnotations(Seq(WriteVcdAnnotation)) { c =>
      // 设置输入信号
      c.io.RS1.poke(5.U)
      c.io.RS2.poke(8.U)
      c.io.WB_data.poke(0x1234.U)
      c.io.Reg_WB.poke(1.U)

      c.clock.step()
      c.io.RS1_out.expect(5.U)
      c.io.RS2_out.expect(8.U)
    }
  }
}

实现一个32个字的指令存储器

从0地址分别存储4条指令add R1,R2,R3; sub R0,R5,R6,lw R5,100(R2), sw R5,104(R2)。然后组合指令存储器、寄存器文件、译码电路,并结合PC更新电路(PC初值为0)、WB_data和Reg_WB信号产生电路,最终让电路能逐条指令取出、译码(不需要完成指令执行)。

给出Chisel设计代码和仿真测试波形,观察四条指令的执行过程波形,记录并解释其含义。

InstructionMemory.scala

import chisel3._

class InstructionMemory extends Module {
  val io = IO(new Bundle {
    val address = Input(UInt(5.W)) // 32个字,需要5位地址
    val instruction = Output(UInt(32.W))
  })
  // 创建一个32个字的指令存储器
  val mem = Mem(32, UInt(32.W))
  // 初始化存储器,存储MIPS指令
  mem.write(0.U, "b000000_00010_00011_00001_00000_100000".U) // add R1, R2, R3
  mem.write(1.U, "b000000_00101_00110_00000_00000_100010".U) // sub R0, R5, R6
  mem.write(2.U, "b100011_00010_00101_0000000001100100".U) // lw R5, 100(R2)
  mem.write(3.U, "b101011_00010_00101_0000000001101000".U) // sw R5, 104(R2)
  // 从存储器中读取指令
  io.instruction := mem.read(io.address)
}

Circuit.scala

import chisel3._
import chisel3.util._

class Circuit extends Module {
  val io = IO(new Bundle {
    // 寄存器的输入输出
    val WB_data = Input(UInt(32.W)) // 写入数据信号,用于写入寄存器
    val Reg_WB = Input(UInt(5.W)) // 选择写入数据的寄存器
    val RS1_out = Output(UInt(32.W))
    val RS2_out = 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())
  })

  val instructionMemory = Module(new InstructionMemory)
  val registerFile = Module(new RegisterFile)
  val decoder = Module(new Decoder)
  val pc = RegInit(0.U(5.W))
  // 根据pc的值取出指令寄存器相应指令
  instructionMemory.io.address := pc
  decoder.io.Instr_word := instructionMemory.io.instruction
  registerFile.io.RS1 := instructionMemory.io.instruction(25, 21)
  registerFile.io.RS2 := instructionMemory.io.instruction(20, 16)
  registerFile.io.WB_data := (0.U(32.W))
  registerFile.io.Reg_WB := (0.U(5.W))
  // 更新输出
  io.RS1_out := registerFile.io.RS1_out
  io.RS2_out := registerFile.io.RS2_out
  io.add_op := decoder.io.add_op
  io.sub_op := decoder.io.sub_op
  io.lw_op := decoder.io.lw_op
  io.sw_op := decoder.io.sw_op
  io.nop_op := decoder.io.nop_op
  // 更新PC
  pc := pc + 1.U
}

object Circuit extends App {
  (new chisel3.stage.ChiselStage).emitVerilog(new Circuit())
}

Circuit.scala

import chiseltest._
import org.scalatest.flatspec.AnyFlatSpec
import chisel3._

class CircuitTest extends AnyFlatSpec with ChiselScalatestTester {
  behavior of "Circuit"
  it should "correct circuit" in {
    test(new Circuit).withAnnotations(Seq(WriteVcdAnnotation)) { c =>
      c.clock.step()
      c.clock.step()
      c.clock.step()
      c.clock.step()
    }
  }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/117460.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【UE 材质】简单的闪闪发光材质

效果 节点 参考视频: https://www.bilibili.com/video/BV1uK411y737/?vd_source36a3e35639c44bb339f59760641390a8

【Liunx系统编程】命令模式3

目录 一,zip/unzip压缩指令 二,tar打包/压缩/解包指令 三,uname获取系统信息指令 四,Liunx下常用且重要的按键和关机指令 五,文件之间的互传 1,Windows与Linux之间的互传 2,Linux系统之间…

0xGame Web 2023

0xGame Web 2023 [Week 1] signin 这题直接看源码就行,easy [Week 1] baby_php OST /?aQNKCDZO&b240610708 HTTP/1.1 Host: 120.27.148.152:50014 Content-Length: 11 Pragma: no-cache Cache-Control: no-cache Upgrade-Insecure-Requests: 1 Origin: htt…

K8s学习笔记——认识理解篇

1. K8s诞生背景 回顾应用的部署,经历了以下几个阶段: 传统部署:物理服务器上运行应用程序。虚拟机部署:物理服务器上安装虚拟机,在虚拟机上运行应用程序。容器部署:物理服务器上安装容器运行时&#xff0…

android studio 编译Telegram源码经验总结(2023-11-05)

前言 Telegram是一款强大的端到端加密IM,专注于安全性和速度,支持Android/IOS/Windows/macOS等平台,功能丰富,运行流畅,免费开源,代码具有学习和研究意义。 一、android telegram源码下载地址: …

MQTT协议零基础快速入门

MQTT协议零基础快速入门 MQTT(Message Queuing Telemetry Transport)是一种轻量级的发布/订阅消息传输协议,广泛应用于物联网(IoT)和机器对机器(M2M)通信场景。它具有简单、开放、易于实现等优…

2014年亚太杯APMCM数学建模大赛C题公共基础课教师专业化培养方式研究求解全过程文档及程序

2014年亚太杯APMCM数学建模大赛 C题 公共基础课教师专业化培养方式研究 原题再现 近年来,世界基础工业、信息产业、服务业的跨越式发展引发了大量人才需求,导致了职业教育的飞速发展,除原有专科层次高等职业教育院校外,大量普通…

0002Java安卓程序设计-基于Uniapp+springboot菜谱美食饮食健康管理App

文章目录 开发环境 《[含文档PPT源码等]精品基于Uniappspringboot饮食健康管理App》该项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程、包运行成功 编程技术交流、源码分享、模板分享、网课教程 🐧裙:776871563 功能介绍&#xff…

饥荒联机版 Don‘t Starve Together(WinMac)最新中文学习版

《饥荒联机版》是由Klei自主开发的开放世界冒险游戏。在这个游戏中,玩家将扮演各种各样的人物,这些人物不幸来到了一个神秘的异世界。在旅行中,玩家会邂逅性格各异、能力独特的同伴们,并和他们一起生存下去并征服异世界。游戏中的…

Windows Server 2019安装docker服务

离线状态下,使用脚本在Windows Server 2019环境中安装docker服务。 1.安装脚本 在微软官方网站中,找到脚本并下载: 准备 Windows 操作系统容器 | Microsoft Learn 脚本地址: https://raw.githubusercontent.com/microsoft/Win…

MySQL索引优化与查询优化

1. 索引失效案例 MySQL中提高性能的一个最有效的方式是对数据表设计合理的索引。索引提供了访问高效数据的方法,并且加快查询的速度,因此索引对查询的速度有着至关重要的影响。 使用索引可以快速地定位表中的某条记录,从而提高数据库查询的速…

【Linux】 shutdown 命令使用

shutdown 命令可以用来进行关机程序,并且在关机以前传送讯息给所有使用者正在执行的程序,shutdown 也可以用来重开机。使用权限:系统管理者。 语法 shutdown [选项] 时间 [警告信息] 命令选项及作用 执行令 man shutdown 执行命令结果 参…

字符串函数的模拟实现

今天我们来了解以下一些字符串函数的模拟实现: strlen strcpy strcat strcmp strlen函数的模拟实现 首先我们转到cplusplus中查找strlen的官方解释: 通过查找我们了解到,strlen是用来测量一个字符串长度的函数,函数的返回值就是…

【六、http】go的http的客户端重定向

一、http的重定向 重定向过程:客户浏览器发送http请求----》web服务器接受后发送302状态码响应及对应新的location给客户浏览器–》客户浏览器发现是302响应,则自动再发送一个新的http请求,请求url是新的location地址----》服务器根据此请求寻…

【嵌入式开发工具】STM32+Keil实现软件工程搭建与开发调试

本篇文章介绍了使用Keil来对STM32F103C8芯片进行初始工程搭建,以及开发与工程调试的完整过程,帮助读者能够在实战中体会到Keil这个开发环境的使用方法,了解一个嵌入式工程从无到有的过程,并且具备快速搭建一个全新芯片对应最小软件…

软件测试工作流程

流程体系介绍 在以往的项目工作中,我参与过,需求评审、测试计划制定、测试用例编写、测试用例执行、测试脚本编写、测试脚本的执行,进行回归测试、验收测试、编写阶段性测试报告等工作 需求分析,需求评审(RPD、产品原…

【ArcGIS Pro二次开发】(74):Python、C#实现Excel截图导出图片

以村庄规划制图为例,通过对现状和规划用地的统计,生成Excel格式的【空间功能结构调整表】后,需要进一步将表格导出成图片,并嵌入到图集中,这样可以实现全流程不用手动参与,让制图的流程完全自动化。 关于E…

深度学习服务器(Linux)开发环境搭建教程

当你拿到一台服务器的使用权时,最头疼的莫过于登陆服务区并配置开发环境。本文将从0开始,讲述一台刚申请的服务器远程登陆并配置开发环境的全过程。希望对你有所帮助 1.登陆服务器 打开MobaXterm软件,创建一个新的Session,选择S…

Java操作redis常见类型数据存储

目录 一、Java连接Redis 1.1 导入pom依赖 1.2 建立连接 二、Java使用Redis 2.1 字符串 String 2.2 哈希 Hash 2.3 列表 List 2.4 集合 Set 2.5 有序集合 Sorted Set 三、Redis的实际应用场景 一、Java连接Redis redis与mysq都是数据库,java操作redis其实跟…

S32K324 UDS Bootloader开发-下位机篇-Bootload软件(1)

文章目录 前言启动过程Bootloader开发链接文件编译文件跳转函数CAN收发相关发送接收初始化及使能CAN周期函数总结前言 上一篇文章介绍了S32K324 -UDS Bootlodaer开发中的需求,本文根据需求开发Bootloader软件。 本文参考NXP官网的S32K324 UBL,其中有一些Bug,也有一些和上位机…