深圳大学-计算机系统(3)-实验三取指和指令译码设计

实验目标

设计完成一个连续取指令并进行指令译码的电路,从而掌握设计简单数据通路的基本方法。

实验内容

本实验分成三周(三次)完成: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,R6,lw 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.ChiselStage

class 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 操作信号
  })

  // 定义opcode
  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)

  io.add_op := false.B
  io.sub_op := false.B
  io.lw_op := false.B
  io.sw_op := false.B
  io.nop_op := true.B

  // 根据opcode和func判断指令类型
  when(opcode === "b000000".U(6.W)) { 
    when(func === ADD_OPCODE) {
      io.add_op := true.B
      io.nop_op := false.B
    }.elsewhen(func === SUB_OPCODE) {
      io.sub_op := true.B
      io.nop_op := false.B
    }
  }.elsewhen(opcode === LW_OPCODE) {
    io.lw_op := true.B
    io.nop_op := false.B
  }.elsewhen(opcode === SW_OPCODE) {
    io.sw_op := true.B
    io.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.AnyFlatSpec

class DecoderTest extends AnyFlatSpec with ChiselScalatestTester {
  behavior of "Decoder"

  it 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设计代码和仿真测试波形,观察RS1=5,RS2=8,WB_data=0x1234,Reg_WB=1的输出波形和受影响寄存器的值。
在这里插入图片描述
思路:
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.ChiselStage

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(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为5,RS2为8,设置WB_data为十六进制数0x1234。将 Reg_WB 设置为 true,启用写入。然后执行c.clock.step(1)来推进模拟时钟1个时钟周期。

RegisterFileTest.scala

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

class RegisterFileTest extends AnyFlatSpec with ChiselScalatestTester {
    behavior of "RegisterFile Module"

    it should "observe the effect on registers" in {
        test (new RegisterFile).withAnnotations(Seq(WriteVcdAnnotation)) { c =>
          // 设置输入信号
          // RS1 = 5
          c.io.RS1.poke(5.U) 
          // RS2 = 8
          c.io.RS2.poke(8.U) 
          // WB_data = 0x1234
          c.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,R6,lw R5,100(R2), sw R5,104(R2)。然后组合指令存储器、寄存器文件、译码电路,并结合PC更新电路(PC初值为0)、WB_data和Reg_WB信号产生电路,最终让电路能逐条指令取出、译码(不需要完成指令执行)。
给出Chisel设计代码和仿真测试波形,观察四条指令的执行过程波形,记录并解释其含义。
在这里插入图片描述
指令存储器设计思路:

  1. 定义接口:
    rdEna: 读使能信号,用于控制指令读取操作。
    rdData: 读取的数据输出,表示从内存读取的 32 位指令。
    wrEna: 写使能信号,用于控制指令写入操作。
    wrAddr: 写地址,用来指示写入操作的目标地址。
    wrData: 写入的数据输入,表示要写入的 32 位指令。
  2. 内存和程序计数器初始化:
    创建了一个名为 pcReg 的程序计数器寄存器,并将其初始化为 0。使用 SyncReadMem 创建一个同步读取的8位宽、128单元的内存,用来存储指令。
  3. 写内存:
    当wrEna为真时,通过写入操作将wrData的不同部分分别存储在指定地址及其后续三个地址中。
  4. 读内存:
    当rdEna为真时,从地址pcReg及其后续三个地址读取数据,分别存储在rdData0至rdData中。使用Cat函数将这四个字节的数据连接成一个32位的数据,以产生完整的指令。程序计数器pcReg每次读取后增加4,以指向下一条指令。
  5. 默认情况:
    当 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))
    })
    // 程序计数器,用于跟踪当前指令的读取位置,初始化为 0
    val 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))
    })
    // 程序计数器,用于跟踪当前指令的读取位置,初始化为 0
    val 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())
    })

    // opcode
    val 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)

    // 指令默认是 nop
    io.add_op := false.B
    io.sub_op := false.B
    io.lw_op := false.B
    io.sw_op := false.B
    io.nop_op := true.B

    // 译码
    when (opcode === AandS) {
        when (func === ADD_OPCODE){
        io.add_op := true.B
        io.nop_op := false.B
        }.elsewhen (func === SUB_OPCODE){
        io.sub_op := true.B
        io.nop_op := false.B
        }
    }.elsewhen (opcode === LW_OPCODE) {
        io.lw_op := true.B
        io.nop_op := false.B
    }.elsewhen (opcode === SW_OPCODE) {
        io.sw_op := true.B
        io.nop_op := false.B
    }
}



class Processor extends Module {
    val io = IO(new Bundle {
        // Decoder
        val 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())

        // RegisterFile
        val RS1_out = Output(UInt(32.W))
        val RS2_out = Output(UInt(32.W))

        // Instruction
        val 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_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

    // 连接控制信号
    instructionMemory.io.rdEna := io.rdEna
    instructionMemory.io.wrEna := io.wrEna
    instructionMemory.io.wrAddr := io.wrAddr
    instructionMemory.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.rdData
    io.Instr_word := instructionMemory.io.rdData

    // 初始化寄存器文件的控制信号
    registerFile.io.Reg_WB := false.B
    registerFile.io.WB_data := 0.U

    // 连接模块输出信号
    io.RS1_out := registerFile.io.RS1_out
    io.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.AnyFlatSpec

class ProcessorTest extends AnyFlatSpec with ChiselScalatestTester {
  behavior of "Processor"

  it should "execute instructions in memory correctly" in {
    test(new Processor).withAnnotations(Seq(WriteVcdAnnotation))  { c =>
      val instructions = Seq(
        "b00000000010000110000100000100000".U(32.W),  // ADD
        "b00000000101001100000000000100010".U(32.W),  // SUB
        "b10001100101000100000000000110010".U(32.W),  // LW
        "b10101100101000100000000000110100".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,R6,lw 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设计代码和仿真测试波形,观察RS1=5,RS2=8,WB_data=0x1234,Reg_WB=1的输出波形和受影响寄存器的值。
(1) 打开波形图,发现寄存器初值为其编号,RS1 = 5,RS2 = 8,WB_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的值为2,io_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的值为5,io_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的值为5,io_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的值为5,io_RS2_out的值为2,表示当前操作了R5,R2这2个寄存器。
(5) 指令全部执行之后,io_Instr_word为0,io_RS1_out的值为0,io_RS2_out的值为0,表示当前没有操作寄存器。
在这里插入图片描述

实验总结与体会

  1. 译码器的设计
    在实验的第一部分,我设计了译码器。译码器的主要作用是根据指令的操作码确定要执行的操作。通过实现一个简单的译码器,我学会了如何将机器指令解析成控制信号,并将这些控制信号进一步用于驱动后续硬件模块。
  2. 寄存器文件的实现
    实验的第二部分是设计寄存器文件,负责存储和读取寄存器数据。通过实现一个多端口的寄存器文件,我掌握了如何通过硬件访问和操作寄存器值。
  3. 指令存储器的设计与数据通路原型
    在最后的阶段,我设计了指令存储器,并将其与前面的译码器和寄存器文件连接,形成了一个简单的数据通路原型。这个阶段不仅是对之前设计的验证,也使我体会到了数据通路中各个模块之间的配合与协调。
    总的来说,这个实验为我今后深入学习计算机体系结构打下了良好的基础。

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

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

相关文章

【JDBC】数据库连接的艺术:深入解析数据库连接池、Apache-DBUtils与BasicDAO

文章目录 前言&#x1f30d; 一.连接池❄️1. 传统获取Conntion问题分析❄️2. 数据库连接池❄️3.连接池之C3P0技术&#x1f341;3.1关键特性&#x1f341;3.2配置选项&#x1f341;3.3使用示例 ❄️4. 连接池之Druid技术&#x1f341; 4.1主要特性&#x1f341; 4.2 配置选项…

Glary Utilities Pro 多语便携版系统优化工具 v6.21.0.25

Glary Utilities是一款功能强大的系统优化工具软件&#xff0c;旨在帮助用户清理计算机垃圾文件、修复系统错误、优化系统性能等。 软件功能 清理和修复&#xff1a;可以清理系统垃圾文件、无效注册表项、无效快捷方式等&#xff0c;修复系统错误和蓝屏问题。 优化和加速&…

QT调用OpenSceneGraph

OSG和osgQt编译教程&#xff0c;实测通过 一、下载OpenSceneGraph OpenSceneGraphhttps://github.com/openscenegraph/OpenSceneGraph 二、使用CMAKE编译OpenSceneGraph 1.打开cmake&#xff0c;配置源代码目录 2. CMAKE_INSTALL_PREFIX设置为install文件夹&#xff0c;生…

基于JAVA的微信点餐小程序设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

【Postgres_Python】使用python脚本批量创建和导入多个PG数据库

之前批量创建和导入数据库分为2个python脚本进行&#xff0c;现整合优化代码合并为一个python脚本&#xff0c;可同步实现数据库的创建和数据导入。之前的文章链接&#xff1a; 【Postgres_Python】使用python脚本批量创建PG数据库 【Postgres_Python】使用python脚本将多个.S…

数据结构——实验八·学生管理系统

嗨~~欢迎来到Tubishu的博客&#x1f338;如果你也是一名在校大学生&#xff0c;正在寻找各种编程资源&#xff0c;那么你就来对地方啦&#x1f31f; Tubishu是一名计算机本科生&#xff0c;会不定期整理和分享学习中的优质资源&#xff0c;希望能为你的编程之路添砖加瓦⭐&…

GitCode 助力 AutoTable:共创 MyBatis 生态的自动表格管理新篇章

项目仓库https://gitcode.com/dromara/auto-table 解放双手&#xff0c;专注业务&#xff1a;MyBatis 生态的“自动表格”创新 AutoTable 是一款致力于为 MyBatis 生态赋予“自动表格”功能的创新插件。其核心理念是通过 Java 实体类自动生成和维护数据库的表结构&#xff0c…

微信小程序使用picker根据接口给的省市区的数据实现省市区三级联动或者省市区街道等多级联动

接口数据如上图 省市区多级联动&#xff0c;都是使用的一个接口通过传参父类的code。返回我们想要的数据 比如获取省就直接不要参数。市就把省得code传给接口&#xff0c;区就把市的code作为参数。 <picker mode"multiSelector" :range"mulSelect1" …

nvm版本安装

安装 使用切换 MySQL5.7新安装 熟人命令 8.0 mysql -P3306 -uroot -p5.7 mysql -P3307 -uroot -p 记得用完关闭 navicat

rocketmq基本架构

简介 Name server 负责broker注册、心跳&#xff0c;路由等功能&#xff0c;类似Kafka的ZKname server节点之间不互相通信&#xff0c;broker需要和所有name server进行通信。扩容name server需要重启broker&#xff0c;不然broker不会和name server建立连接producer和consum…

k8s集成MinIo

本篇文章分享一下在 k8s怎么集成 minio做存储&#xff0c;并实现 PersistentVolume (PV)、PersistentVolumeClaim (PVC)、动态存储卷StorageClass&#xff0c;以及演示让pod使用这些存储卷的完整流程。 一、理论 1、PV概念 PV是对K8S存储资源的抽象&#xff0c;PV一般由运维…

PC端自动化测试实战教程-1-pywinauto 环境搭建(详细教程)

1.简介 之前总有人在群里或者私信留言问&#xff1a;Windows系统安装的软件如何自动化测试呢&#xff1f;因为没有接触过或者遇到过&#xff0c;所以说实话宏哥当时也不清楚怎么实现&#xff0c;怎么测试。然而在一次偶然的机会接触到了Python的一个模块说是它可以实现Microso…

STM32 GPIO工作模式

GPIO工作模式 1. GPIO简介2. GPIO工作模式2.1 输入浮空2.2 输入上拉2.3 输入下拉2.4 模拟2.5 开漏输出2.6 推挽输出2.7 开漏式复用功能2.8 推挽式复用功能 1. GPIO简介 GPIO 是通用输入输出端口的简称&#xff0c;简单来说就是 STM32 可控制的引脚&#xff0c;STM32 芯片的 GPI…

unity插件Excel转换Proto插件-ExcelToProtobufferTool

unity插件Excel转换Proto插件-ExcelToProtobufferTool **ExcelToProtobufTool 插件文档****1. 插件概述****2. 默认配置类&#xff1a;DefaultIProtoPathConfig****属性说明** **3. 自定义配置类****定义规则****示例代码** **4. 使用方式****4.1 默认路径****4.2 自定义路径**…

基于微信小程序的设备故障报修管理系统设计与实现(LW+源码+讲解)

专注于大学生项目实战开发,讲解,毕业答疑辅导&#xff0c;欢迎高校老师/同行前辈交流合作✌。 技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、小程序、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;…

LeRobot安装教程

LeRobot的下载与安装 使用Git下载源代码使用Anaconda安装LeRobot 使用Git下载源代码 使用Git从github仓库中远程下载项目&#xff1a;选择一个文件夹用于存放下载下来的项目&#xff08;可以新建一个空文件夹&#xff09;&#xff0c;然后在该文件夹内打开Git Bash Here,输入以…

HTML5使用favicon.ico图标

目录 1. 使用favicon.ico图标 1. 使用favicon.ico图标 favicon.ico一般用于作为网站标志&#xff0c;它显示在浏览器的地址栏或者标签上 制作favicon图标 选择一个png转ico的在线网站&#xff0c;这里以https://www.bitbug.net/为例。上传图片&#xff0c;目标尺寸选择48x48&a…

wx036基于springboot+vue+uniapp的校园快递平台小程序

开发语言&#xff1a;Java框架&#xff1a;springbootuniappJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#…

模拟算法习题篇

在算法中&#xff0c;模拟是一种通过计算机程序来模拟现实世界中的过程或系统行为的方法。它的核心思想是根据题目给定的规则和逻辑&#xff0c;按照步骤细致地重现事件的发展流程&#xff0c;从而获得最终结果。 解题时如何使用模拟算法&#xff1a; 理解题目规则&#xff1a;…

一文大白话讲清楚webpack基本使用——1——完成webpack的初步构建

文章目录 一文大白话讲清楚webpack基本使用——1——完成webpack的初步构建1. 先回忆webpack是个啥2. webpack四大核心2.1 Entry(入口)2.2 Output(输出)2.3 Loader(加载器)2.4 Plugin(插件) 3. 按部就班实现webpack3.1 初始化项目3.2 完成项目骨架搭建3.3 实现webpack构建 一文…