From 5d8f47c0694b49801b9d7ae687bbb1313419365b Mon Sep 17 00:00:00 2001 From: CGH0S7 <776459475@qq.com> Date: Wed, 25 Dec 2024 08:42:07 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=B8=B8=E9=87=8F=E5=92=8C?= =?UTF-8?q?=E9=A1=B6=E5=B1=82=E6=A8=A1=E5=9D=97=E7=BC=96=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .scalafmt.conf | 2 + src/main/scala/common/Consts.scala | 73 ++++++++++++++++++++++++ src/main/scala/common/Instructions.scala | 47 +++++++++++++++ src/main/scala/gcd/DecoupledGCD.scala | 73 ------------------------ src/main/scala/gcd/GCD.scala | 46 --------------- src/main/scala/micore/Core.scala | 15 +++++ src/main/scala/micore/Memory.scala | 43 ++++++++++++++ src/main/scala/micore/Top.scala | 28 +++++++++ src/test/scala/gcd/GCDSpec.scala | 68 ---------------------- src/test/scala/micore/MicoreSpec.scala | 7 +++ 10 files changed, 215 insertions(+), 187 deletions(-) create mode 100644 .scalafmt.conf create mode 100644 src/main/scala/common/Consts.scala create mode 100644 src/main/scala/common/Instructions.scala delete mode 100644 src/main/scala/gcd/DecoupledGCD.scala delete mode 100644 src/main/scala/gcd/GCD.scala create mode 100644 src/main/scala/micore/Core.scala create mode 100644 src/main/scala/micore/Memory.scala create mode 100644 src/main/scala/micore/Top.scala delete mode 100644 src/test/scala/gcd/GCDSpec.scala create mode 100644 src/test/scala/micore/MicoreSpec.scala diff --git a/.scalafmt.conf b/.scalafmt.conf new file mode 100644 index 0000000..ee7753a --- /dev/null +++ b/.scalafmt.conf @@ -0,0 +1,2 @@ +version = "3.7.15" +runner.dialect = scala213 \ No newline at end of file diff --git a/src/main/scala/common/Consts.scala b/src/main/scala/common/Consts.scala new file mode 100644 index 0000000..b094cbe --- /dev/null +++ b/src/main/scala/common/Consts.scala @@ -0,0 +1,73 @@ +package common + +import chisel3._ +import chisel3.util._ + +object Consts { + // 数据宽度和地址 + val WORD_LEN = 32 // 指令和数据的宽度为32位 + val START_ADDR = 0.U(WORD_LEN.W) // 起始地址,设为0 + val BUBBLE = 0x00000000.U(WORD_LEN.W) // 用于冒泡的指令 [NOP] + val UNIMP = "x_c0000000".U(WORD_LEN.W) // 未实现指令 [SLL $0, $0, 0] + + // 寄存器地址长度 + val ADDR_LEN = 5 // rs1、rs2和写回寄存器的地址宽度为5位 + + // 执行功能定义 + val EXE_FUN_LEN = 5 // 执行功能的编码长度为5位 + val ALU_X = 0.U(EXE_FUN_LEN.W) // 未定义ALU操作 + val ALU_ADD = 1.U(EXE_FUN_LEN.W) // 加法操作 + val ALU_SUB = 2.U(EXE_FUN_LEN.W) // 减法操作 + val ALU_AND = 3.U(EXE_FUN_LEN.W) // 位与操作 + val ALU_OR = 4.U(EXE_FUN_LEN.W) // 位或操作 + val ALU_XOR = 5.U(EXE_FUN_LEN.W) // 位异或操作 + val ALU_SLL = 6.U(EXE_FUN_LEN.W) // 左移操作 + val ALU_SRL = 7.U(EXE_FUN_LEN.W) // 逻辑右移操作 + val ALU_SRA = 8.U(EXE_FUN_LEN.W) // 算术右移操作 + val ALU_SLT = 9.U(EXE_FUN_LEN.W) // 有符号比较小于操作 + val ALU_SLTU = 10.U(EXE_FUN_LEN.W) // 无符号比较小于操作 + val BR_BEQ = 11.U(EXE_FUN_LEN.W) // 分支相等 + val BR_BNE = 12.U(EXE_FUN_LEN.W) // 分支不等 + val ALU_JALR = 13.U(EXE_FUN_LEN.W) // JALR跳转 + val ALU_COPY1 = 14.U(EXE_FUN_LEN.W) // 复制操作 + + // 操作数选择 + val OP1_LEN = 2 // 操作数1的选择宽度为2位 + val OP1_RS1 = 0.U(OP1_LEN.W) // 选择rs1 + val OP1_PC = 1.U(OP1_LEN.W) // 选择PC + val OP1_X = 2.U(OP1_LEN.W) // 未定义操作数1 + + val OP2_LEN = 3 // 操作数2的选择宽度为3位 + val OP2_X = 0.U(OP2_LEN.W) // 未定义操作数2 + val OP2_RS2 = 1.U(OP2_LEN.W) // 选择rs2 + val OP2_IMI = 2.U(OP2_LEN.W) // 立即数操作数2(immI) + val OP2_IMS = 3.U(OP2_LEN.W) // 立即数操作数2(immS) + val OP2_IMJ = 4.U(OP2_LEN.W) // 立即数操作数2(immJ) + val OP2_IMU = 5.U(OP2_LEN.W) // 立即数操作数2(immU) + + // 内存访问使能信号 + val MEN_LEN = 2 // 内存访问控制宽度为2位 + val MEN_X = 0.U(MEN_LEN.W) // 未定义 + val MEN_S = 1.U(MEN_LEN.W) // 存储操作 + + // 寄存器使能信号 + val REN_LEN = 2 // 寄存器使能信号宽度为2位 + val REN_X = 0.U(REN_LEN.W) // 未定义 + val REN_S = 1.U(REN_LEN.W) // 标量寄存器写回使能 + + // 写回选择 + val WB_SEL_LEN = 3 // 写回选择信号宽度为3位 + val WB_X = 0.U(WB_SEL_LEN.W) // 未定义 + val WB_ALU = 1.U(WB_SEL_LEN.W) // ALU结果写回 + val WB_MEM = 2.U(WB_SEL_LEN.W) // 内存数据写回 + val WB_PC = 3.U(WB_SEL_LEN.W) // PC写回 + + // 内存宽度 + val MW_LEN = 3 // 内存宽度控制信号宽度为3位 + val MW_X = 0.U(MW_LEN.W) // 未定义 + val MW_W = 1.U(MW_LEN.W) // 32位字访问 + val MW_H = 2.U(MW_LEN.W) // 16位半字访问 + val MW_B = 3.U(MW_LEN.W) // 8位字节访问 + val MW_HU = 4.U(MW_LEN.W) // 16位无符号半字访问 + val MW_BU = 5.U(MW_LEN.W) // 8位无符号字节访问 +} diff --git a/src/main/scala/common/Instructions.scala b/src/main/scala/common/Instructions.scala new file mode 100644 index 0000000..e235e0b --- /dev/null +++ b/src/main/scala/common/Instructions.scala @@ -0,0 +1,47 @@ +package common + +import chisel3._ +import chisel3.util._ + +object Instructions { + // * 加载 / 存储 + val LW = BitPat("b100011????????????0000000000000000") // lw rt, offset(rs) + val SW = BitPat("b101011????????????0000000000000000") // sw rt, offset(rs) + + // * 算术运算 + val ADD = BitPat("b000000???????????????00000100000") // add rd, rs, rt + val ADDI = BitPat( + "b001000????????????0000000000000000" + ) // addi rt, rs, immediate + val SUB = BitPat("b000000???????????????00000100010") // sub rd, rs, rt + + // * 逻辑运算 + val AND = BitPat("b000000???????????????00000100100") // and rd, rs, rt + val OR = BitPat("b000000???????????????00000100101") // or rd, rs, rt + val XOR = BitPat("b000000???????????????00000100110") // xor rd, rs, rt + val ANDI = BitPat( + "b001100????????????0000000000000000" + ) // andi rt, rs, immediate + val ORI = BitPat( + "b001101????????????0000000000000000" + ) // ori rt, rs, immediate + + // * 比较 + val SLT = BitPat("b000000???????????????00000101010") // slt rd, rs, rt + + // * 条件分支 + val BEQ = BitPat("b000100????????????0000000000000000") // beq rs, rt, offset + val BNE = BitPat("b000101????????????0000000000000000") // bne rs, rt, offset + + // * 移位 + val SLL = BitPat("b00000000000??????????????000000") // sll rd, rt, shamt + val SRL = BitPat("b00000000000??????????????000010") // srl rd, rt, shamt + val SRA = BitPat("b00000000000??????????????000011") // sra rd, rt, shamt + + // * 跳转 + val JR = BitPat("b000000????????????0000000000001000") // jr rs + val JAL = BitPat("b00001100000000000000000000000000") // jal target + + // * 立即数加载 + val LUI = BitPat("b00111100000????????????0000000000") // lui rt, immediate +} diff --git a/src/main/scala/gcd/DecoupledGCD.scala b/src/main/scala/gcd/DecoupledGCD.scala deleted file mode 100644 index 1cd2993..0000000 --- a/src/main/scala/gcd/DecoupledGCD.scala +++ /dev/null @@ -1,73 +0,0 @@ -// See README.md for license details. - -package gcd - -import chisel3._ -import chisel3.util.Decoupled - -class GcdInputBundle(val w: Int) extends Bundle { - val value1 = UInt(w.W) - val value2 = UInt(w.W) -} - -class GcdOutputBundle(val w: Int) extends Bundle { - val value1 = UInt(w.W) - val value2 = UInt(w.W) - val gcd = UInt(w.W) -} - -/** - * Compute Gcd using subtraction method. - * Subtracts the smaller from the larger until register y is zero. - * value input register x is then the Gcd. - * Unless first input is zero then the Gcd is y. - * Can handle stalls on the producer or consumer side - */ -class DecoupledGcd(width: Int) extends Module { - val input = IO(Flipped(Decoupled(new GcdInputBundle(width)))) - val output = IO(Decoupled(new GcdOutputBundle(width))) - - val xInitial = Reg(UInt()) - val yInitial = Reg(UInt()) - val x = Reg(UInt()) - val y = Reg(UInt()) - val busy = RegInit(false.B) - val resultValid = RegInit(false.B) - - input.ready := ! busy - output.valid := resultValid - output.bits := DontCare - - when(busy) { - when(x > y) { - x := x - y - }.otherwise { - y := y - x - } - when(x === 0.U || y === 0.U) { - when(x === 0.U) { - output.bits.gcd := y - }.otherwise { - output.bits.gcd := x - } - - output.bits.value1 := xInitial - output.bits.value2 := yInitial - resultValid := true.B - - when(output.ready && resultValid) { - busy := false.B - resultValid := false.B - } - } - }.otherwise { - when(input.valid) { - val bundle = input.deq() - x := bundle.value1 - y := bundle.value2 - xInitial := bundle.value1 - yInitial := bundle.value2 - busy := true.B - } - } -} diff --git a/src/main/scala/gcd/GCD.scala b/src/main/scala/gcd/GCD.scala deleted file mode 100644 index 42c5e80..0000000 --- a/src/main/scala/gcd/GCD.scala +++ /dev/null @@ -1,46 +0,0 @@ -// See README.md for license details. - -package gcd - -import chisel3._ -// _root_ disambiguates from package chisel3.util.circt if user imports chisel3.util._ -import _root_.circt.stage.ChiselStage - -/** - * Compute GCD using subtraction method. - * Subtracts the smaller from the larger until register y is zero. - * value in register x is then the GCD - */ -class GCD extends Module { - val io = IO(new Bundle { - val value1 = Input(UInt(16.W)) - val value2 = Input(UInt(16.W)) - val loadingValues = Input(Bool()) - val outputGCD = Output(UInt(16.W)) - val outputValid = Output(Bool()) - }) - - val x = Reg(UInt()) - val y = Reg(UInt()) - - when(x > y) { x := x - y } - .otherwise { y := y - x } - - when(io.loadingValues) { - x := io.value1 - y := io.value2 - } - - io.outputGCD := x - io.outputValid := y === 0.U -} - -/** - * Generate Verilog sources and save it in file GCD.v - */ -object GCD extends App { - ChiselStage.emitSystemVerilogFile( - new GCD, - firtoolOpts = Array("-disable-all-randomization", "-strip-debug-info") - ) -} diff --git a/src/main/scala/micore/Core.scala b/src/main/scala/micore/Core.scala new file mode 100644 index 0000000..80a7af1 --- /dev/null +++ b/src/main/scala/micore/Core.scala @@ -0,0 +1,15 @@ +package micore + +import chisel3._ +import chisel3.util._ +import common.Consts._ +import common.Instructions._ +import os.list + +class Core extends Module { + val io = IO(new Bundle { + val imem = Flipped(new ImemPortIo) + val dmem = Flipped(new DmemPortIo) + val exit = Output(Bool()) + }) +} diff --git a/src/main/scala/micore/Memory.scala b/src/main/scala/micore/Memory.scala new file mode 100644 index 0000000..0a6eaa4 --- /dev/null +++ b/src/main/scala/micore/Memory.scala @@ -0,0 +1,43 @@ +package micore + +import chisel3._ +import chisel3.util._ +import chisel3.util.experimental.loadMemoryFromFileInline +import common.Consts._ + +class ImemPortIo extends Bundle { + val addr = Input(UInt(WORD_LEN.W)) + val inst = Output(UInt(WORD_LEN.W)) +} + +class DmemPortIo extends Bundle { + val addr = Input(UInt(WORD_LEN.W)) + val rdata = Output(UInt(WORD_LEN.W)) + val wen = Input(Bool()) + val wdata = Input(UInt(WORD_LEN.W)) +} + +class Memory extends Module { + val io = IO(new Bundle { + val imem = new ImemPortIo + val dmem = new DmemPortIo + }) + + val mem = Mem(8192, UInt(8.W)) + + loadMemoryFromFileInline(mem, "src/hex/mem.hex") + + io.imem.inst := Cat( + mem(io.imem.addr), + mem(io.imem.addr + 1.U(WORD_LEN.W)), + mem(io.imem.addr + 2.U(WORD_LEN.W)), + mem(io.imem.addr + 3.U(WORD_LEN.W)) + ) + + when(io.dmem.wen) { + mem(io.dmem.addr) := io.dmem.wdata(31, 24) + mem(io.dmem.addr + 1.U(WORD_LEN.W)) := io.dmem.wdata(23, 16) + mem(io.dmem.addr + 2.U(WORD_LEN.W)) := io.dmem.wdata(15, 8) + mem(io.dmem.addr + 3.U(WORD_LEN.W)) := io.dmem.wdata(7, 0) + } +} diff --git a/src/main/scala/micore/Top.scala b/src/main/scala/micore/Top.scala new file mode 100644 index 0000000..342e1a4 --- /dev/null +++ b/src/main/scala/micore/Top.scala @@ -0,0 +1,28 @@ +package micore + +import chisel3._ +// _root_ disambiguates from package chisel3.util.circt if user imports chisel3.util._ +import _root_.circt.stage.ChiselStage +import common.Consts._ + +class Top extends Module { + val io = IO(new Bundle { + val exit = Output(Bool()) + }) + val core = Module(new Core) + val memory = Module(new Memory) + + core.io.imem <> memory.io.imem + core.io.dmem <> memory.io.dmem + + io.exit := core.io.exit +} + +/** Generate Verilog sources and save it in file + */ +object Top extends App { + ChiselStage.emitSystemVerilogFile( + new Top, + firtoolOpts = Array("-disable-all-randomization", "-strip-debug-info") + ) +} diff --git a/src/test/scala/gcd/GCDSpec.scala b/src/test/scala/gcd/GCDSpec.scala deleted file mode 100644 index e8c7ce8..0000000 --- a/src/test/scala/gcd/GCDSpec.scala +++ /dev/null @@ -1,68 +0,0 @@ -// See README.md for license details. - -package gcd - -import chisel3._ -import chisel3.experimental.BundleLiterals._ -import chisel3.simulator.EphemeralSimulator._ -import org.scalatest.freespec.AnyFreeSpec -import org.scalatest.matchers.must.Matchers - -/** - * This is a trivial example of how to run this Specification - * From within sbt use: - * {{{ - * testOnly gcd.GCDSpec - * }}} - * From a terminal shell use: - * {{{ - * sbt 'testOnly gcd.GCDSpec' - * }}} - * Testing from mill: - * {{{ - * mill %NAME%.test.testOnly gcd.GCDSpec - * }}} - */ -class GCDSpec extends AnyFreeSpec with Matchers { - - "Gcd should calculate proper greatest common denominator" in { - simulate(new DecoupledGcd(16)) { dut => - val testValues = for { x <- 0 to 10; y <- 0 to 10} yield (x, y) - val inputSeq = testValues.map { case (x, y) => (new GcdInputBundle(16)).Lit(_.value1 -> x.U, _.value2 -> y.U) } - val resultSeq = testValues.map { case (x, y) => - (new GcdOutputBundle(16)).Lit(_.value1 -> x.U, _.value2 -> y.U, _.gcd -> BigInt(x).gcd(BigInt(y)).U) - } - - dut.reset.poke(true.B) - dut.clock.step() - dut.reset.poke(false.B) - dut.clock.step() - - var sent, received, cycles: Int = 0 - while (sent != 100 && received != 100) { - assert(cycles <= 1000, "timeout reached") - - if (sent < 100) { - dut.input.valid.poke(true.B) - dut.input.bits.value1.poke(testValues(sent)._1.U) - dut.input.bits.value2.poke(testValues(sent)._2.U) - if (dut.input.ready.peek().litToBoolean) { - sent += 1 - } - } - - if (received < 100) { - dut.output.ready.poke(true.B) - if (dut.output.valid.peekValue().asBigInt == 1) { - dut.output.bits.gcd.expect(BigInt(testValues(received)._1).gcd(testValues(received)._2)) - received += 1 - } - } - - // Step the simulation forward. - dut.clock.step() - cycles += 1 - } - } - } -} diff --git a/src/test/scala/micore/MicoreSpec.scala b/src/test/scala/micore/MicoreSpec.scala new file mode 100644 index 0000000..e575df0 --- /dev/null +++ b/src/test/scala/micore/MicoreSpec.scala @@ -0,0 +1,7 @@ +package micore + +import chisel3._ +import chisel3.experimental.BundleLiterals._ +import chisel3.simulator.EphemeralSimulator._ +import org.scalatest.freespec.AnyFreeSpec +import org.scalatest.matchers.must.Matchers