diff --git a/chisel/playground/src/CpuConfig.scala b/chisel/playground/src/CpuConfig.scala index 0e566cb..b969222 100644 --- a/chisel/playground/src/CpuConfig.scala +++ b/chisel/playground/src/CpuConfig.scala @@ -26,3 +26,29 @@ case class CpuConfig( case class BranchPredictorConfig( val bhtDepth: Int = 5, val phtDepth: Int = 6) + +case class CacheConfig( + nway: Int = 2, // 路数 + nbank: Int = 8, // bank数 + nset: Int, + bankWidth: Int // bytes per bank +) { + val config = CpuConfig() + val indexWidth = log2Ceil(nset) // 6 + val bankIndexWidth = log2Ceil(nbank) // 3 + val bankOffsetWidth = log2Ceil(bankWidth) // 3 + val offsetWidth = bankIndexWidth + bankOffsetWidth // 6 + val tagWidth = 32 - indexWidth - offsetWidth // 20 + val tagvWidth = tagWidth + 1 // 21 + val bankWidthBits = bankWidth * 8 // 64 + val burstSize = 16 + val ninst = config.instFetchNum // TODO:改成可随意修改的参数 + require(isPow2(nset)) + require(isPow2(nway)) + require(isPow2(nbank)) + require(isPow2(bankWidth)) + require( + tagWidth + indexWidth + bankIndexWidth + bankOffsetWidth == 32, + "basic request calculation" + ) +} diff --git a/chisel/playground/src/cache/memory/LUTRam.scala b/chisel/playground/src/cache/memory/LUTRam.scala new file mode 100644 index 0000000..1382e17 --- /dev/null +++ b/chisel/playground/src/cache/memory/LUTRam.scala @@ -0,0 +1,64 @@ +package cache.memory + +import chisel3._ +import chisel3.util._ +import cpu.CpuConfig + +/** LUT ram for XPM, one port for read/write, one port for read + * @param depth + * how many lines there are in the bank + * @param width + * how wide in bits each line is + * @param config + * implicit configuration to control generate ram for simulation or elaboration + */ +class LUTRam(depth: Int, width: Int)(implicit val config: CpuConfig) extends Module { + require(isPow2(depth)) + val waddridth = log2Ceil(depth) + val io = IO(new Bundle { + val raddr = Input(UInt(waddridth.W)) + val rdata = Output(UInt(width.W)) + + val waddr = Input(UInt(waddridth.W)) + val wdata = Input(UInt(width.W)) + val wen = Input(Bool()) + val writeOutput = Output(UInt(width.W)) + }) + + if (config.build) { + val bank = Module( + new LUTRamIP( + wdataidth = width, + waddridth = waddridth, + byteWriteWidth = width, + numberOfLines = depth, + ), + ) + bank.io.clka := clock + bank.io.clkb := clock + bank.io.rsta := reset + bank.io.rstb := reset + + bank.io.regcea := false.B + bank.io.regceb := false.B + bank.io.ena := true.B + bank.io.enb := true.B + + bank.io.addra := io.waddr + bank.io.wea := io.wen + bank.io.dina := io.wdata + io.writeOutput := DontCare + + bank.io.addrb := io.raddr + io.rdata := bank.io.doutb + } else { + val bank = RegInit(VecInit(Seq.fill(depth)(0.U(width.W)))) + io.rdata := bank(io.raddr) + io.writeOutput := DontCare + when(io.wen) { + bank(io.waddr) := io.wdata + }.otherwise { + io.writeOutput := bank(io.waddr) + } + } +} diff --git a/chisel/playground/src/cache/memory/LUTRamIP.scala b/chisel/playground/src/cache/memory/LUTRamIP.scala new file mode 100644 index 0000000..91aac49 --- /dev/null +++ b/chisel/playground/src/cache/memory/LUTRamIP.scala @@ -0,0 +1,65 @@ +package cache.memory + +import chisel3._ +import chisel3.util.log2Ceil + +/** XPM 2019.2 XPM_MEMORY_DPDISTRAM, at page 124 of UG953(2019.2) by default, this is initialized to + * all 0 + * + * @param wdataidth + * : the size of the data to store in each line, in bits + * @param waddridth + * : the width of request + * @param byteWriteWidth + * : addressable size of write + * @param numberOfLines + * : how many **bits** there are in the memory + */ +class LUTRamIP(wdataidth: Int, waddridth: Int, byteWriteWidth: Int, numberOfLines: Int) + extends BlackBox( + Map( + "ADDR_WIDTH_A" -> waddridth, + "ADDR_WIDTH_B" -> waddridth, + "MEMORY_SIZE" -> numberOfLines * wdataidth, + "WRITE_DATA_WIDTH_A" -> wdataidth, + "READ_DATA_WIDTH_A" -> wdataidth, + "READ_DATA_WIDTH_B" -> wdataidth, + "BYTE_WRITE_WIDTH_A" -> byteWriteWidth, + "READ_LATENCY_A" -> 0, + "READ_LATENCY_B" -> 0, + "READ_RESET_VALUE_A" -> 0, + "READ_RESET_VALUE_B" -> 0, + "CLOCKING_MODE" -> "common_clock", + ), + ) { + override def desiredName: String = "xpm_memory_dpdistram" + require( + waddridth == log2Ceil(numberOfLines), + "request width should be log 2 of number of lines to request all", + ) + require( + wdataidth - (wdataidth / byteWriteWidth) * byteWriteWidth == 0, + "data width should be a multiple of byte write width", + ) + require(waddridth <= 20, "request width should be 1 to 20") + val io = IO(new Bundle { + val clka = Input(Clock()) + val clkb = Input(Clock()) + val rsta = Input(Reset()) + val rstb = Input(Reset()) + + val ena = Input(Bool()) + val enb = Input(Bool()) + val regcea = Input(Bool()) + val regceb = Input(Bool()) + + val dina = Input(UInt(wdataidth.W)) + val addra = Input(UInt(waddridth.W)) + val addrb = Input(UInt(waddridth.W)) + + val wea = Input(UInt((wdataidth / byteWriteWidth).W)) + + val douta = Output(UInt(wdataidth.W)) + val doutb = Output(UInt(wdataidth.W)) + }) +} diff --git a/chisel/playground/src/cache/memory/PortDefinitions.scala b/chisel/playground/src/cache/memory/PortDefinitions.scala new file mode 100644 index 0000000..f7ed75e --- /dev/null +++ b/chisel/playground/src/cache/memory/PortDefinitions.scala @@ -0,0 +1,37 @@ +package cache.memory + +import chisel3._ +import chisel3.util._ +import cpu.CacheConfig + +class ReadOnlyPort[+T <: Data](gen: T)(implicit cacheConfig: CacheConfig) extends Bundle { + val addr = Input(UInt(log2Ceil(cacheConfig.nset * cacheConfig.nbank).W)) + val data = Output(gen) +} + +class WriteOnlyPort[+T <: Data](gen: T)(implicit cacheConfig: CacheConfig) extends Bundle { + val addr = Input(UInt(log2Ceil(cacheConfig.nset * cacheConfig.nbank).W)) + val en = Input(Bool()) + val data = Input(gen) +} + +class WriteOnlyMaskPort[+T <: Data](gen: T)(implicit cacheConfig: CacheConfig) extends Bundle { + val addr = Input(UInt(log2Ceil(cacheConfig.nset * cacheConfig.nbank).W)) + val en = Input(UInt(cacheConfig.bankWidth.W)) + val data = Input(gen) +} + + +class ReadWritePort[+T <: Data](gen: T)(implicit cacheConfig: CacheConfig) extends Bundle { + val addr = Input(UInt(log2Ceil(cacheConfig.nset * cacheConfig.nbank).W)) + val en = Input(Bool()) + val wdata = Input(gen) + val rdata = Output(gen) +} + +class MaskedReadWritePort[+T <: Data](gen: T)(implicit cacheConfig: CacheConfig) extends Bundle { + val addr = Input(UInt(log2Ceil(cacheConfig.nset * cacheConfig.nbank).W)) + val writeMask = Input(UInt(cacheConfig.bankWidth.W)) + val wdata = Input(gen) + val rdata = Output(gen) +} diff --git a/chisel/playground/src/cache/memory/SimpleDualPortRam.scala b/chisel/playground/src/cache/memory/SimpleDualPortRam.scala new file mode 100644 index 0000000..44ef47a --- /dev/null +++ b/chisel/playground/src/cache/memory/SimpleDualPortRam.scala @@ -0,0 +1,91 @@ +package cache.memory + +import chisel3._ +import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage} +import chisel3.util._ +import cpu.CpuConfig + +/** simple dual port ram, with a port for reading and a port for writing + * + * @param depth + * how many lines there are in the ram + * @param width + * how wide in bits each line is + * @param byteAddressable + * is it byte addressable? + * @param cpuCfg + * the implicit configuration for simulation and elaboration + */ +class SimpleDualPortRam(depth: Int, width: Int, byteAddressable: Boolean)(implicit + val config: CpuConfig, +) extends Module { + require(isPow2(depth)) + require( + width % 8 == 0 || !byteAddressable, + "if memory is byte addressable, then the adderss width must be a multiple of 8", + ) + val waddridth = log2Ceil(depth) + + val io = IO(new Bundle { + val raddr = Input(UInt(waddridth.W)) + val ren = Input(Bool()) + val rdata = Output(UInt(width.W)) + + val waddr = Input(UInt(waddridth.W)) + val wen = Input(Bool()) + val wstrb = Input(UInt((if (byteAddressable) width / 8 else 1).W)) + val wdata = Input(UInt(width.W)) + }) + + if (config.build) { + val memory = Module( + new SimpleDualPortRamIP( + wdataidth = width, + byteWriteWidth = if (byteAddressable) 8 else width, + numberOfLines = depth, + waddridth = waddridth, + ), + ) + memory.io.clka := clock + memory.io.clkb := clock + memory.io.rstb := reset + + memory.io.addra := io.waddr + memory.io.ena := io.wen + memory.io.dina := io.wdata + memory.io.wea := io.wstrb + + memory.io.addrb := io.raddr + memory.io.enb := io.ren + memory.io.regceb := false.B + io.rdata := memory.io.doutb + } else { + assert( + io.wstrb.orR || !io.wen, + "when write port enable is high, write vector cannot be all 0", + ) + if (byteAddressable) { + val bank = SyncReadMem(depth, Vec(width / 8, UInt(8.W))) + when(io.ren) { + io.rdata := bank(io.raddr).asTypeOf(io.rdata) + }.otherwise { + io.rdata := DontCare + } + when(io.wen) { + bank.write(io.waddr, io.wdata.asTypeOf(Vec(width / 8, UInt(8.W))), io.wstrb.asBools) + } + } else { + val bank = SyncReadMem(depth, UInt(width.W)) + + when(io.ren) { + io.rdata := bank.read(io.raddr) + }.otherwise { + io.rdata := 0.U(32.W) + } + + when(io.wen) { + bank.write(io.waddr, io.wdata) + } + } + } +} diff --git a/chisel/playground/src/cache/memory/SimpleDualPortRamIP.scala b/chisel/playground/src/cache/memory/SimpleDualPortRamIP.scala new file mode 100644 index 0000000..b697a00 --- /dev/null +++ b/chisel/playground/src/cache/memory/SimpleDualPortRamIP.scala @@ -0,0 +1,68 @@ +package cache.memory + +import chisel3._ +import chisel3.util.log2Ceil + +/** simple dual port ram + * + * @param wdataidth + * : width of every data line + * @param byteWriteWidth + * : how many bits to write per mask + * @param numberOfLines + * : how many lines of data are in the ram + * @param waddridth + * : how wide is the request (to cover all lines) + * @param memoryPrimitive + * : should I use auto, block ram or distributed ram + */ +class SimpleDualPortRamIP( + wdataidth: Int = 32, + byteWriteWidth: Int = 8, + numberOfLines: Int, + waddridth: Int, + memoryPrimitive: String = "block", +) extends BlackBox( + Map( + "ADDR_WIDTH_A" -> waddridth, + "ADDR_WIDTH_B" -> waddridth, + "WRITE_DATA_WIDTH_A" -> wdataidth, + "READ_DATA_WIDTH_B" -> wdataidth, + "BYTE_WRITE_WIDTH_A" -> byteWriteWidth, + "CLOCKING_MODE" -> "common_clock", + "READ_LATENCY_B" -> 1, + "MEMORY_SIZE" -> numberOfLines * wdataidth, + "MEMORY_PRIMITIVE" -> memoryPrimitive, + ), + ) { + override def desiredName: String = "xpm_memory_sdpram" + require(waddridth <= 20, "request width should be 1 to 20") + require( + wdataidth - (wdataidth / byteWriteWidth) * byteWriteWidth == 0, + "data width should be a multiple of byte write width", + ) + require( + List("auto", "block", "distributed", "ultra").contains(memoryPrimitive), + "memory primitive should be auto, block ram, dist ram or ultra ram", + ) + require( + waddridth == log2Ceil(numberOfLines), + "request width should be log 2 of number of lines to request all", + ) + val io = IO(new Bundle { + // clock and reset + val clka = Input(Clock()) + val clkb = Input(Clock()) + val rstb = Input(Reset()) + + val addra = Input(UInt(waddridth.W)) + val dina = Input(UInt(wdataidth.W)) + val ena = Input(Bool()) + val wea = Input(UInt((wdataidth / byteWriteWidth).W)) + + val addrb = Input(UInt(waddridth.W)) + val enb = Input(Bool()) + val regceb = Input(Bool()) + val doutb = Output(UInt(wdataidth.W)) + }) +}