diff --git a/rocketv/src/HellaCache.scala b/rocketv/src/HellaCache.scala index 0535f03f1..c57dd8736 100644 --- a/rocketv/src/HellaCache.scala +++ b/rocketv/src/HellaCache.scala @@ -25,7 +25,6 @@ import chisel3.util.{ PriorityEncoderOH, PriorityMux, RegEnable, - SRAM, SRAMInterface, UIntToOH } @@ -40,6 +39,7 @@ import org.chipsalliance.amba.axi4.bundle.{ W } import org.chipsalliance.dwbb.stdlib.queue.{Queue, QueueIO} +import chisel3.hack.util.SRAM object HellaCacheParameter { implicit def bitSetP: upickle.default.ReadWriter[BitSet] = upickle.default diff --git a/rocketv/src/ICache.scala b/rocketv/src/ICache.scala index 48ac2ea34..d8ff69958 100644 --- a/rocketv/src/ICache.scala +++ b/rocketv/src/ICache.scala @@ -12,6 +12,7 @@ import chisel3.util.random.LFSR import chisel3.util._ import org.chipsalliance.amba.axi4.bundle.{AXI4BundleParameter, AXI4ROIrrevocable, AXI4RWIrrevocable} import org.chipsalliance.dwbb.stdlib.queue.Queue +import chisel3.hack.util.SRAM case class ICacheParameter( useAsyncReset: Boolean, diff --git a/rocketv/src/PTW.scala b/rocketv/src/PTW.scala index 532ffed32..602669533 100644 --- a/rocketv/src/PTW.scala +++ b/rocketv/src/PTW.scala @@ -22,11 +22,11 @@ import chisel3.util.{ PriorityEncoder, PriorityEncoderOH, RegEnable, - SRAM, SRAMInterface, UIntToOH, Valid } +import chisel3.hack.util.SRAM object PTWParameter { implicit def rwP: upickle.default.ReadWriter[PTWParameter] = upickle.default.macroRW[PTWParameter] diff --git a/rocketv/src/SRAM.scala b/rocketv/src/SRAM.scala new file mode 100644 index 000000000..73d61fb43 --- /dev/null +++ b/rocketv/src/SRAM.scala @@ -0,0 +1,430 @@ +package chisel3.hack.util + +import chisel3._ +import chisel3.experimental.SourceInfo +import chisel3.experimental.hierarchy.{Instance, Instantiate} +import chisel3.internal.Builder +import chisel3.properties.{Path, Property} + +import chisel3.util.experimental.{CIRCTSRAMInterface, CIRCTSRAMParameter} +import chisel3.util.{log2Ceil, MemoryFile, SRAMDescription, SRAMInterface} +import firrtl.transforms.BlackBoxInlineAnno +import chisel3.experimental.ChiselAnnotation +import chisel3.experimental.hierarchy.core.Hierarchy.HierarchyBaseModuleExtensions + +class SRAMBlackbox(parameter: CIRCTSRAMParameter) extends FixedIOExtModule(new CIRCTSRAMInterface(parameter)) { self => + + private val verilogInterface: String = + (Seq.tabulate(parameter.write)(idx => + Seq( + s"// Write Port $idx", + s"input [${log2Ceil(parameter.depth) - 1}:0] W${idx}_addr", + s"input W${idx}_en", + s"input W${idx}_clk", + s"input [${parameter.width - 1}:0] W${idx}_data" + ) ++ + Option.when(parameter.masked)(s"input [${parameter.width / parameter.maskGranularity - 1}:0] W${idx}_mask") + ) ++ + Seq.tabulate(parameter.read)(idx => + Seq( + s"// Read Port $idx", + s"input [${log2Ceil(parameter.depth) - 1}:0] R${idx}_addr", + s"input R${idx}_en", + s"input R${idx}_clk", + s"output [${parameter.width - 1}:0] R${idx}_data" + ) + ) ++ + Seq.tabulate(parameter.readwrite)(idx => + Seq( + s"// ReadWrite Port $idx", + s"input [${log2Ceil(parameter.depth) - 1}:0] RW${idx}_addr", + s"input RW${idx}_en", + s"input RW${idx}_clk", + s"input RW${idx}_wmode", + s"input [${parameter.width - 1}:0] RW${idx}_wdata", + s"output [${parameter.width - 1}:0] RW${idx}_rdata" + ) ++ Option + .when(parameter.masked)( + s"input [${parameter.width / parameter.maskGranularity - 1}:0] RW${idx}_wmask" + ) + )).flatten.mkString(",\n") + + private val wLogic = Seq + .tabulate(parameter.write)(idx => + Seq( + s"reg [${log2Ceil(parameter.depth) - 1}:0] _R${idx}_addr;", + s"reg _R${idx}_en;" + ) ++ + Seq(s"always @(posedge R${idx}_clk) begin // RW${idx}") ++ + (if (parameter.masked) + Seq.tabulate(parameter.width / parameter.maskGranularity)(i => + s"if (W${idx}_en & W${idx}_wmask[${i}]) Memory[W${idx}_addr][${i * parameter.maskGranularity}+:${parameter.maskGranularity}] <= RW${idx}_wdata[${(i + 1) * parameter.maskGranularity - 1}:${i * parameter.maskGranularity}];" + ) + else + Seq(s"if (W${idx}) Memory[W${idx}_addr] <= W${idx}_data;")) ++ + Seq(s"end // RW${idx}") + ) + .flatten + + private val rLogic = Seq + .tabulate(parameter.read)(idx => + Seq( + s"reg [${log2Ceil(parameter.depth) - 1}:0] _R${idx}_en;", + s"reg _R${idx}_addr;" + ) ++ + Seq( + s"always @(posedge R${idx}_clk) begin // R${idx}", + s"_R${idx}_raddr <= R${idx}_addr;", + s"_R${idx}_ren <= R${idx}_ren;", + s"end // RW${idx}" + ) ++ + Some(s"R${idx}_data = _R${idx}_ren ? Memory[_R${idx}_raddr] : ${parameter.width}'bx;") + ) + .flatten + + private val rwLogic = Seq + .tabulate(parameter.readwrite)(idx => + Seq( + s"reg [${log2Ceil(parameter.depth) - 1}:0] _RW${idx}_raddr;", + s"reg _RW${idx}_ren;", + s"reg _RW${idx}_rmode;" + ) ++ + Seq(s"always @(posedge RW${idx}_clk) begin // RW${idx}") ++ + Seq( + s"_RW${idx}_raddr <= RW${idx}_addr;", + s"_RW${idx}_ren <= RW${idx}_ren;", + s"_RW${idx}_rmode <= RW${idx}_rmode;" + ) ++ + (if (parameter.masked) + Seq.tabulate(parameter.width / parameter.maskGranularity)(i => + s"if(RW${idx}_en & RW${idx}_wmask[${i}] & RW${idx}_wmode) Memory[RW${idx}_addr][${parameter.width / parameter.maskGranularity}'${i * parameter.maskGranularity}+:${parameter.maskGranularity}] <= RW${idx}_wdata[${(i + 1) * parameter.maskGranularity - 1}:${i * parameter.maskGranularity}];" + ) + else + Seq(s"if (RW${idx}) Memory[RW${idx}_addr] <= RW${idx}_data;")) ++ + Seq(s"end // RW${idx}") ++ + Seq(s"RW${idx}_rdata = _RW${idx}_ren ? Memory[_RW${idx}_raddr] : ${parameter.width}'bx;") + ) + .flatten + + private val logic = + (Seq(s"reg [${parameter.depth - 1}:0] Memory[0:${parameter.width - 1}];") ++ wLogic ++ rLogic ++ rwLogic) + .mkString("\n") + + override def desiredName = parameter.moduleName + + chisel3.experimental.annotate( + new ChiselAnnotation { + def toFirrtl = new BlackBoxInlineAnno( + self.toNamed, + parameter.moduleName, + s"""module ${parameter.moduleName}( + |${verilogInterface} + |); + |${logic} + |endmodule + |""".stripMargin + ) + } + ) +} + +/** This should be upstreamed to Chisel for implementing the InstanceChoice. + * - Here are different right for implementing the SRAM, bug chisel choose none of them. + * - SRAM Blackbox with metadata. + * - Intrinsic SRAM and then lower to + */ +object SRAM { + def apply[T <: Data]( + size: BigInt, + tpe: T, + numReadPorts: Int, + numWritePorts: Int, + numReadwritePorts: Int + )( + implicit sourceInfo: SourceInfo + ): SRAMInterface[T] = { + val clock = Builder.forcedClock + memInterface_impl( + size, + tpe, + Seq.fill(numReadPorts)(clock), + Seq.fill(numWritePorts)(clock), + Seq.fill(numReadwritePorts)(clock), + None, + None, + sourceInfo + ) + } + + def apply[T <: Data]( + size: BigInt, + tpe: T, + numReadPorts: Int, + numWritePorts: Int, + numReadwritePorts: Int, + memoryFile: MemoryFile + )( + implicit sourceInfo: SourceInfo + ): SRAMInterface[T] = { + val clock = Builder.forcedClock + memInterface_impl( + size, + tpe, + Seq.fill(numReadPorts)(clock), + Seq.fill(numWritePorts)(clock), + Seq.fill(numReadwritePorts)(clock), + Some(memoryFile), + None, + sourceInfo + ) + } + + def apply[T <: Data]( + size: BigInt, + tpe: T, + readPortClocks: Seq[Clock], + writePortClocks: Seq[Clock], + readwritePortClocks: Seq[Clock] + )( + implicit sourceInfo: SourceInfo + ): SRAMInterface[T] = + memInterface_impl( + size, + tpe, + readPortClocks, + writePortClocks, + readwritePortClocks, + None, + None, + sourceInfo + ) + + def apply[T <: Data]( + size: BigInt, + tpe: T, + readPortClocks: Seq[Clock], + writePortClocks: Seq[Clock], + readwritePortClocks: Seq[Clock], + memoryFile: MemoryFile + )( + implicit sourceInfo: SourceInfo + ): SRAMInterface[T] = + memInterface_impl( + size, + tpe, + readPortClocks, + writePortClocks, + readwritePortClocks, + Some(memoryFile), + None, + sourceInfo + ) + + def masked[T <: Data]( + size: BigInt, + tpe: T, + numReadPorts: Int, + numWritePorts: Int, + numReadwritePorts: Int + )( + implicit evidence: T <:< Vec[_], + sourceInfo: SourceInfo + ): SRAMInterface[T] = { + val clock = Builder.forcedClock + memInterface_impl( + size, + tpe, + Seq.fill(numReadPorts)(clock), + Seq.fill(numWritePorts)(clock), + Seq.fill(numReadwritePorts)(clock), + None, + Some(evidence), + sourceInfo + ) + } + + def masked[T <: Data]( + size: BigInt, + tpe: T, + numReadPorts: Int, + numWritePorts: Int, + numReadwritePorts: Int, + memoryFile: MemoryFile + )( + implicit evidence: T <:< Vec[_], + sourceInfo: SourceInfo + ): SRAMInterface[T] = { + val clock = Builder.forcedClock + memInterface_impl( + size, + tpe, + Seq.fill(numReadPorts)(clock), + Seq.fill(numWritePorts)(clock), + Seq.fill(numReadwritePorts)(clock), + Some(memoryFile), + Some(evidence), + sourceInfo + ) + } + + def masked[T <: Data]( + size: BigInt, + tpe: T, + readPortClocks: Seq[Clock], + writePortClocks: Seq[Clock], + readwritePortClocks: Seq[Clock] + )( + implicit evidence: T <:< Vec[_], + sourceInfo: SourceInfo + ): SRAMInterface[T] = + memInterface_impl( + size, + tpe, + readPortClocks, + writePortClocks, + readwritePortClocks, + None, + Some(evidence), + sourceInfo + ) + + def masked[T <: Data]( + size: BigInt, + tpe: T, + readPortClocks: Seq[Clock], + writePortClocks: Seq[Clock], + readwritePortClocks: Seq[Clock], + memoryFile: MemoryFile + )( + implicit evidence: T <:< Vec[_], + sourceInfo: SourceInfo + ): SRAMInterface[T] = + memInterface_impl( + size, + tpe, + readPortClocks, + writePortClocks, + readwritePortClocks, + Some(memoryFile), + Some(evidence), + sourceInfo + ) + + private def memInterface_impl[T <: Data]( + size: BigInt, + tpe: T, + readPortClocks: Seq[Clock], + writePortClocks: Seq[Clock], + readwritePortClocks: Seq[Clock], + memoryFile: Option[MemoryFile], + evidenceOpt: Option[T <:< Vec[_]], + sourceInfo: SourceInfo + ): SRAMInterface[T] = { + val numReadPorts = readPortClocks.size + val numWritePorts = writePortClocks.size + val numReadwritePorts = readwritePortClocks.size + val isVecMem = evidenceOpt.isDefined + val isValidSRAM = ((numReadPorts + numReadwritePorts) > 0) && ((numWritePorts + numReadwritePorts) > 0) + + if (!isValidSRAM) { + val badMemory = + if (numReadPorts + numReadwritePorts == 0) + "write-only SRAM (R + RW === 0)" + else + "read-only SRAM (W + RW === 0)" + Builder.error( + s"Attempted to initialize a $badMemory! SRAMs must have both at least one read accessor and at least one write accessor." + ) + } + + val mem = Instantiate( + new SRAMBlackbox( + new CIRCTSRAMParameter( + s"sram_${size}x${tpe.getWidth}", + numReadPorts, + numWritePorts, + numReadwritePorts, + size.intValue, + tpe.getWidth, + tpe match { + case vec: Vec[_] => vec.size + case _ => 0 + } + ) + ) + ) + + val sramReadPorts = Seq.tabulate(numReadPorts)(i => mem.io.R(i)) + val sramWritePorts = Seq.tabulate(numWritePorts)(i => mem.io.W(i)) + val sramReadwritePorts = Seq.tabulate(numReadwritePorts)(i => mem.io.RW(i)) + + val includeMetadata = Builder.includeUtilMetadata + + val out = Wire( + new SRAMInterface(size, tpe, numReadPorts, numWritePorts, numReadwritePorts, isVecMem, includeMetadata) + ) + + out.readPorts.zip(sramReadPorts).zip(readPortClocks).map { case ((intfReadPort, sramReadPort), readClock) => + sramReadPort.address := intfReadPort.address + sramReadPort.clock := readClock + intfReadPort.data := sramReadPort.data.asTypeOf(tpe) + sramReadPort.enable := intfReadPort.enable + } + out.writePorts.zip(sramWritePorts).zip(writePortClocks).map { case ((intfWritePort, sramWritePort), writeClock) => + sramWritePort.address := intfWritePort.address + sramWritePort.clock := writeClock + sramWritePort.data := intfWritePort.data.asUInt + sramWritePort.enable := intfWritePort.enable + sramWritePort.mask match { + case Some(mask) => mask := intfWritePort.mask.get.asUInt + case None => assert(intfWritePort.mask.isEmpty) + } + } + out.readwritePorts.zip(sramReadwritePorts).zip(readwritePortClocks).map { + case ((intfReadwritePort, sramReadwritePort), readwriteClock) => + sramReadwritePort.address := intfReadwritePort.address + sramReadwritePort.clock := readwriteClock + sramReadwritePort.enable := intfReadwritePort.enable + intfReadwritePort.readData := sramReadwritePort.readData.asTypeOf(tpe) + sramReadwritePort.writeData := intfReadwritePort.writeData.asUInt + sramReadwritePort.writeEnable := intfReadwritePort.isWrite + sramReadwritePort.writeMask match { + case Some(mask) => mask := intfReadwritePort.mask.get.asUInt + case None => assert(intfReadwritePort.mask.isEmpty) + } + } + + // Hack to ScalaDoc Bug, see [[LTLIntrinsicInstanceMethodsInternalWorkaround]] + implicit class SRAMDescriptionInstanceMethods(underlying: Instance[SRAMDescription]) { + implicit val mg: internal.MacroGenerated = new chisel3.internal.MacroGenerated {} + def depthIn: Property[BigInt] = underlying._lookup(_.depthIn) + def widthIn: Property[Int] = underlying._lookup(_.widthIn) + def maskedIn: Property[Boolean] = underlying._lookup(_.maskedIn) + def readIn: Property[Int] = underlying._lookup(_.readIn) + def writeIn: Property[Int] = underlying._lookup(_.writeIn) + def readwriteIn: Property[Int] = underlying._lookup(_.readwriteIn) + def maskGranularityIn: Property[Int] = underlying._lookup(_.maskGranularityIn) + def hierarchyIn: Property[Path] = underlying._lookup(_.hierarchyIn) + } + out.description.foreach { description => + val descriptionInstance: Instance[SRAMDescription] = Instantiate(new SRAMDescription) + descriptionInstance.depthIn := Property(size) + descriptionInstance.widthIn := Property(tpe.getWidth) + descriptionInstance.maskedIn := Property(isVecMem) + descriptionInstance.readIn := Property(numReadPorts) + descriptionInstance.writeIn := Property(numWritePorts) + descriptionInstance.readwriteIn := Property(numReadwritePorts) + descriptionInstance.maskGranularityIn := Property( + Option + .when(isVecMem)(tpe match { + case t: Vec[_] => t.sample_element.getWidth + }) + .getOrElse(0) + ) + descriptionInstance.hierarchyIn := Property(Path(mem.toRelativeTargetToHierarchy(None))) + description := descriptionInstance.getPropertyReference + } + out + } + +} diff --git a/t1/src/SRAM.scala b/t1/src/SRAM.scala new file mode 100644 index 000000000..73d61fb43 --- /dev/null +++ b/t1/src/SRAM.scala @@ -0,0 +1,430 @@ +package chisel3.hack.util + +import chisel3._ +import chisel3.experimental.SourceInfo +import chisel3.experimental.hierarchy.{Instance, Instantiate} +import chisel3.internal.Builder +import chisel3.properties.{Path, Property} + +import chisel3.util.experimental.{CIRCTSRAMInterface, CIRCTSRAMParameter} +import chisel3.util.{log2Ceil, MemoryFile, SRAMDescription, SRAMInterface} +import firrtl.transforms.BlackBoxInlineAnno +import chisel3.experimental.ChiselAnnotation +import chisel3.experimental.hierarchy.core.Hierarchy.HierarchyBaseModuleExtensions + +class SRAMBlackbox(parameter: CIRCTSRAMParameter) extends FixedIOExtModule(new CIRCTSRAMInterface(parameter)) { self => + + private val verilogInterface: String = + (Seq.tabulate(parameter.write)(idx => + Seq( + s"// Write Port $idx", + s"input [${log2Ceil(parameter.depth) - 1}:0] W${idx}_addr", + s"input W${idx}_en", + s"input W${idx}_clk", + s"input [${parameter.width - 1}:0] W${idx}_data" + ) ++ + Option.when(parameter.masked)(s"input [${parameter.width / parameter.maskGranularity - 1}:0] W${idx}_mask") + ) ++ + Seq.tabulate(parameter.read)(idx => + Seq( + s"// Read Port $idx", + s"input [${log2Ceil(parameter.depth) - 1}:0] R${idx}_addr", + s"input R${idx}_en", + s"input R${idx}_clk", + s"output [${parameter.width - 1}:0] R${idx}_data" + ) + ) ++ + Seq.tabulate(parameter.readwrite)(idx => + Seq( + s"// ReadWrite Port $idx", + s"input [${log2Ceil(parameter.depth) - 1}:0] RW${idx}_addr", + s"input RW${idx}_en", + s"input RW${idx}_clk", + s"input RW${idx}_wmode", + s"input [${parameter.width - 1}:0] RW${idx}_wdata", + s"output [${parameter.width - 1}:0] RW${idx}_rdata" + ) ++ Option + .when(parameter.masked)( + s"input [${parameter.width / parameter.maskGranularity - 1}:0] RW${idx}_wmask" + ) + )).flatten.mkString(",\n") + + private val wLogic = Seq + .tabulate(parameter.write)(idx => + Seq( + s"reg [${log2Ceil(parameter.depth) - 1}:0] _R${idx}_addr;", + s"reg _R${idx}_en;" + ) ++ + Seq(s"always @(posedge R${idx}_clk) begin // RW${idx}") ++ + (if (parameter.masked) + Seq.tabulate(parameter.width / parameter.maskGranularity)(i => + s"if (W${idx}_en & W${idx}_wmask[${i}]) Memory[W${idx}_addr][${i * parameter.maskGranularity}+:${parameter.maskGranularity}] <= RW${idx}_wdata[${(i + 1) * parameter.maskGranularity - 1}:${i * parameter.maskGranularity}];" + ) + else + Seq(s"if (W${idx}) Memory[W${idx}_addr] <= W${idx}_data;")) ++ + Seq(s"end // RW${idx}") + ) + .flatten + + private val rLogic = Seq + .tabulate(parameter.read)(idx => + Seq( + s"reg [${log2Ceil(parameter.depth) - 1}:0] _R${idx}_en;", + s"reg _R${idx}_addr;" + ) ++ + Seq( + s"always @(posedge R${idx}_clk) begin // R${idx}", + s"_R${idx}_raddr <= R${idx}_addr;", + s"_R${idx}_ren <= R${idx}_ren;", + s"end // RW${idx}" + ) ++ + Some(s"R${idx}_data = _R${idx}_ren ? Memory[_R${idx}_raddr] : ${parameter.width}'bx;") + ) + .flatten + + private val rwLogic = Seq + .tabulate(parameter.readwrite)(idx => + Seq( + s"reg [${log2Ceil(parameter.depth) - 1}:0] _RW${idx}_raddr;", + s"reg _RW${idx}_ren;", + s"reg _RW${idx}_rmode;" + ) ++ + Seq(s"always @(posedge RW${idx}_clk) begin // RW${idx}") ++ + Seq( + s"_RW${idx}_raddr <= RW${idx}_addr;", + s"_RW${idx}_ren <= RW${idx}_ren;", + s"_RW${idx}_rmode <= RW${idx}_rmode;" + ) ++ + (if (parameter.masked) + Seq.tabulate(parameter.width / parameter.maskGranularity)(i => + s"if(RW${idx}_en & RW${idx}_wmask[${i}] & RW${idx}_wmode) Memory[RW${idx}_addr][${parameter.width / parameter.maskGranularity}'${i * parameter.maskGranularity}+:${parameter.maskGranularity}] <= RW${idx}_wdata[${(i + 1) * parameter.maskGranularity - 1}:${i * parameter.maskGranularity}];" + ) + else + Seq(s"if (RW${idx}) Memory[RW${idx}_addr] <= RW${idx}_data;")) ++ + Seq(s"end // RW${idx}") ++ + Seq(s"RW${idx}_rdata = _RW${idx}_ren ? Memory[_RW${idx}_raddr] : ${parameter.width}'bx;") + ) + .flatten + + private val logic = + (Seq(s"reg [${parameter.depth - 1}:0] Memory[0:${parameter.width - 1}];") ++ wLogic ++ rLogic ++ rwLogic) + .mkString("\n") + + override def desiredName = parameter.moduleName + + chisel3.experimental.annotate( + new ChiselAnnotation { + def toFirrtl = new BlackBoxInlineAnno( + self.toNamed, + parameter.moduleName, + s"""module ${parameter.moduleName}( + |${verilogInterface} + |); + |${logic} + |endmodule + |""".stripMargin + ) + } + ) +} + +/** This should be upstreamed to Chisel for implementing the InstanceChoice. + * - Here are different right for implementing the SRAM, bug chisel choose none of them. + * - SRAM Blackbox with metadata. + * - Intrinsic SRAM and then lower to + */ +object SRAM { + def apply[T <: Data]( + size: BigInt, + tpe: T, + numReadPorts: Int, + numWritePorts: Int, + numReadwritePorts: Int + )( + implicit sourceInfo: SourceInfo + ): SRAMInterface[T] = { + val clock = Builder.forcedClock + memInterface_impl( + size, + tpe, + Seq.fill(numReadPorts)(clock), + Seq.fill(numWritePorts)(clock), + Seq.fill(numReadwritePorts)(clock), + None, + None, + sourceInfo + ) + } + + def apply[T <: Data]( + size: BigInt, + tpe: T, + numReadPorts: Int, + numWritePorts: Int, + numReadwritePorts: Int, + memoryFile: MemoryFile + )( + implicit sourceInfo: SourceInfo + ): SRAMInterface[T] = { + val clock = Builder.forcedClock + memInterface_impl( + size, + tpe, + Seq.fill(numReadPorts)(clock), + Seq.fill(numWritePorts)(clock), + Seq.fill(numReadwritePorts)(clock), + Some(memoryFile), + None, + sourceInfo + ) + } + + def apply[T <: Data]( + size: BigInt, + tpe: T, + readPortClocks: Seq[Clock], + writePortClocks: Seq[Clock], + readwritePortClocks: Seq[Clock] + )( + implicit sourceInfo: SourceInfo + ): SRAMInterface[T] = + memInterface_impl( + size, + tpe, + readPortClocks, + writePortClocks, + readwritePortClocks, + None, + None, + sourceInfo + ) + + def apply[T <: Data]( + size: BigInt, + tpe: T, + readPortClocks: Seq[Clock], + writePortClocks: Seq[Clock], + readwritePortClocks: Seq[Clock], + memoryFile: MemoryFile + )( + implicit sourceInfo: SourceInfo + ): SRAMInterface[T] = + memInterface_impl( + size, + tpe, + readPortClocks, + writePortClocks, + readwritePortClocks, + Some(memoryFile), + None, + sourceInfo + ) + + def masked[T <: Data]( + size: BigInt, + tpe: T, + numReadPorts: Int, + numWritePorts: Int, + numReadwritePorts: Int + )( + implicit evidence: T <:< Vec[_], + sourceInfo: SourceInfo + ): SRAMInterface[T] = { + val clock = Builder.forcedClock + memInterface_impl( + size, + tpe, + Seq.fill(numReadPorts)(clock), + Seq.fill(numWritePorts)(clock), + Seq.fill(numReadwritePorts)(clock), + None, + Some(evidence), + sourceInfo + ) + } + + def masked[T <: Data]( + size: BigInt, + tpe: T, + numReadPorts: Int, + numWritePorts: Int, + numReadwritePorts: Int, + memoryFile: MemoryFile + )( + implicit evidence: T <:< Vec[_], + sourceInfo: SourceInfo + ): SRAMInterface[T] = { + val clock = Builder.forcedClock + memInterface_impl( + size, + tpe, + Seq.fill(numReadPorts)(clock), + Seq.fill(numWritePorts)(clock), + Seq.fill(numReadwritePorts)(clock), + Some(memoryFile), + Some(evidence), + sourceInfo + ) + } + + def masked[T <: Data]( + size: BigInt, + tpe: T, + readPortClocks: Seq[Clock], + writePortClocks: Seq[Clock], + readwritePortClocks: Seq[Clock] + )( + implicit evidence: T <:< Vec[_], + sourceInfo: SourceInfo + ): SRAMInterface[T] = + memInterface_impl( + size, + tpe, + readPortClocks, + writePortClocks, + readwritePortClocks, + None, + Some(evidence), + sourceInfo + ) + + def masked[T <: Data]( + size: BigInt, + tpe: T, + readPortClocks: Seq[Clock], + writePortClocks: Seq[Clock], + readwritePortClocks: Seq[Clock], + memoryFile: MemoryFile + )( + implicit evidence: T <:< Vec[_], + sourceInfo: SourceInfo + ): SRAMInterface[T] = + memInterface_impl( + size, + tpe, + readPortClocks, + writePortClocks, + readwritePortClocks, + Some(memoryFile), + Some(evidence), + sourceInfo + ) + + private def memInterface_impl[T <: Data]( + size: BigInt, + tpe: T, + readPortClocks: Seq[Clock], + writePortClocks: Seq[Clock], + readwritePortClocks: Seq[Clock], + memoryFile: Option[MemoryFile], + evidenceOpt: Option[T <:< Vec[_]], + sourceInfo: SourceInfo + ): SRAMInterface[T] = { + val numReadPorts = readPortClocks.size + val numWritePorts = writePortClocks.size + val numReadwritePorts = readwritePortClocks.size + val isVecMem = evidenceOpt.isDefined + val isValidSRAM = ((numReadPorts + numReadwritePorts) > 0) && ((numWritePorts + numReadwritePorts) > 0) + + if (!isValidSRAM) { + val badMemory = + if (numReadPorts + numReadwritePorts == 0) + "write-only SRAM (R + RW === 0)" + else + "read-only SRAM (W + RW === 0)" + Builder.error( + s"Attempted to initialize a $badMemory! SRAMs must have both at least one read accessor and at least one write accessor." + ) + } + + val mem = Instantiate( + new SRAMBlackbox( + new CIRCTSRAMParameter( + s"sram_${size}x${tpe.getWidth}", + numReadPorts, + numWritePorts, + numReadwritePorts, + size.intValue, + tpe.getWidth, + tpe match { + case vec: Vec[_] => vec.size + case _ => 0 + } + ) + ) + ) + + val sramReadPorts = Seq.tabulate(numReadPorts)(i => mem.io.R(i)) + val sramWritePorts = Seq.tabulate(numWritePorts)(i => mem.io.W(i)) + val sramReadwritePorts = Seq.tabulate(numReadwritePorts)(i => mem.io.RW(i)) + + val includeMetadata = Builder.includeUtilMetadata + + val out = Wire( + new SRAMInterface(size, tpe, numReadPorts, numWritePorts, numReadwritePorts, isVecMem, includeMetadata) + ) + + out.readPorts.zip(sramReadPorts).zip(readPortClocks).map { case ((intfReadPort, sramReadPort), readClock) => + sramReadPort.address := intfReadPort.address + sramReadPort.clock := readClock + intfReadPort.data := sramReadPort.data.asTypeOf(tpe) + sramReadPort.enable := intfReadPort.enable + } + out.writePorts.zip(sramWritePorts).zip(writePortClocks).map { case ((intfWritePort, sramWritePort), writeClock) => + sramWritePort.address := intfWritePort.address + sramWritePort.clock := writeClock + sramWritePort.data := intfWritePort.data.asUInt + sramWritePort.enable := intfWritePort.enable + sramWritePort.mask match { + case Some(mask) => mask := intfWritePort.mask.get.asUInt + case None => assert(intfWritePort.mask.isEmpty) + } + } + out.readwritePorts.zip(sramReadwritePorts).zip(readwritePortClocks).map { + case ((intfReadwritePort, sramReadwritePort), readwriteClock) => + sramReadwritePort.address := intfReadwritePort.address + sramReadwritePort.clock := readwriteClock + sramReadwritePort.enable := intfReadwritePort.enable + intfReadwritePort.readData := sramReadwritePort.readData.asTypeOf(tpe) + sramReadwritePort.writeData := intfReadwritePort.writeData.asUInt + sramReadwritePort.writeEnable := intfReadwritePort.isWrite + sramReadwritePort.writeMask match { + case Some(mask) => mask := intfReadwritePort.mask.get.asUInt + case None => assert(intfReadwritePort.mask.isEmpty) + } + } + + // Hack to ScalaDoc Bug, see [[LTLIntrinsicInstanceMethodsInternalWorkaround]] + implicit class SRAMDescriptionInstanceMethods(underlying: Instance[SRAMDescription]) { + implicit val mg: internal.MacroGenerated = new chisel3.internal.MacroGenerated {} + def depthIn: Property[BigInt] = underlying._lookup(_.depthIn) + def widthIn: Property[Int] = underlying._lookup(_.widthIn) + def maskedIn: Property[Boolean] = underlying._lookup(_.maskedIn) + def readIn: Property[Int] = underlying._lookup(_.readIn) + def writeIn: Property[Int] = underlying._lookup(_.writeIn) + def readwriteIn: Property[Int] = underlying._lookup(_.readwriteIn) + def maskGranularityIn: Property[Int] = underlying._lookup(_.maskGranularityIn) + def hierarchyIn: Property[Path] = underlying._lookup(_.hierarchyIn) + } + out.description.foreach { description => + val descriptionInstance: Instance[SRAMDescription] = Instantiate(new SRAMDescription) + descriptionInstance.depthIn := Property(size) + descriptionInstance.widthIn := Property(tpe.getWidth) + descriptionInstance.maskedIn := Property(isVecMem) + descriptionInstance.readIn := Property(numReadPorts) + descriptionInstance.writeIn := Property(numWritePorts) + descriptionInstance.readwriteIn := Property(numReadwritePorts) + descriptionInstance.maskGranularityIn := Property( + Option + .when(isVecMem)(tpe match { + case t: Vec[_] => t.sample_element.getWidth + }) + .getOrElse(0) + ) + descriptionInstance.hierarchyIn := Property(Path(mem.toRelativeTargetToHierarchy(None))) + description := descriptionInstance.getPropertyReference + } + out + } + +} diff --git a/t1/src/vrf/VRF.scala b/t1/src/vrf/VRF.scala index cafaeb5e4..e7f2abf9b 100644 --- a/t1/src/vrf/VRF.scala +++ b/t1/src/vrf/VRF.scala @@ -23,6 +23,7 @@ import org.chipsalliance.t1.rtl.{ VRFWriteReport, VRFWriteRequest } +import chisel3.hack.util.SRAM sealed trait RamType object RamType {