@@ -18,6 +18,7 @@ import chisel3.properties.Class.ClassDefinitionOps
1818import chisel3 .properties .{Class , ClassType , Path , Property }
1919
2020import scala .collection .immutable .{ListMap , VectorMap }
21+ import chisel3 .util .experimental .{CIRCTSRAMInterface , CIRCTSRAMParameter }
2122
2223/** A bundle of signals representing a memory read port.
2324 *
@@ -200,6 +201,120 @@ case class BinaryMemoryFile(path: String) extends MemoryFile(MemoryLoadFileType.
200201 */
201202case class HexMemoryFile (path : String ) extends MemoryFile (MemoryLoadFileType .Hex )
202203
204+ class SRAMBlackbox (parameter : CIRCTSRAMParameter )
205+ extends FixedIOExtModule (new CIRCTSRAMInterface (parameter))
206+ with HasExtModuleInline { self =>
207+
208+ private val verilogInterface : String =
209+ (Seq .tabulate(parameter.write)(idx =>
210+ Seq (
211+ s " // Write Port $idx" ,
212+ s " input [ ${log2Ceil(parameter.depth) - 1 }:0] W ${idx}_addr " ,
213+ s " input W ${idx}_en " ,
214+ s " input W ${idx}_clk " ,
215+ s " input [ ${parameter.width - 1 }:0] W ${idx}_data "
216+ ) ++
217+ Option .when(parameter.masked)(s " input [ ${parameter.width / parameter.maskGranularity - 1 }:0] W ${idx}_mask " )
218+ ) ++
219+ Seq .tabulate(parameter.read)(idx =>
220+ Seq (
221+ s " // Read Port $idx" ,
222+ s " input [ ${log2Ceil(parameter.depth) - 1 }:0] R ${idx}_addr " ,
223+ s " input R ${idx}_en " ,
224+ s " input R ${idx}_clk " ,
225+ s " output [ ${parameter.width - 1 }:0] R ${idx}_data "
226+ )
227+ ) ++
228+ Seq .tabulate(parameter.readwrite)(idx =>
229+ Seq (
230+ s " // ReadWrite Port $idx" ,
231+ s " input [ ${log2Ceil(parameter.depth) - 1 }:0] RW ${idx}_addr " ,
232+ s " input RW ${idx}_en " ,
233+ s " input RW ${idx}_clk " ,
234+ s " input RW ${idx}_wmode " ,
235+ s " input [ ${parameter.width - 1 }:0] RW ${idx}_wdata " ,
236+ s " output [ ${parameter.width - 1 }:0] RW ${idx}_rdata "
237+ ) ++ Option
238+ .when(parameter.masked)(
239+ s " input [ ${parameter.width / parameter.maskGranularity - 1 }:0] RW ${idx}_wmask "
240+ )
241+ )).flatten.mkString(" ,\n " )
242+
243+ private val rLogic = Seq
244+ .tabulate(parameter.read) { idx =>
245+ val prefix = s " R ${idx}"
246+ Seq (
247+ s " reg _ ${prefix}_en; " ,
248+ s " reg [ ${log2Ceil(parameter.depth) - 1 }:0] _ ${prefix}_addr; "
249+ ) ++
250+ Seq (
251+ s " always @(posedge ${prefix}_clk) begin // ${prefix}" ,
252+ s " _ ${prefix}_en <= ${prefix}_en; " ,
253+ s " _ ${prefix}_addr <= ${prefix}_addr; " ,
254+ s " end // ${prefix}"
255+ ) ++
256+ Some (s " assign ${prefix}_data = _ ${prefix}_en ? Memory[_ ${prefix}_addr] : ${parameter.width}'bx; " )
257+ }
258+ .flatten
259+
260+ private val wLogic = Seq
261+ .tabulate(parameter.write) { idx =>
262+ val prefix = s " W ${idx}"
263+ Seq (s " always @(posedge ${prefix}_clk) begin // ${prefix}" ) ++
264+ (if (parameter.masked)
265+ Seq .tabulate(parameter.width / parameter.maskGranularity)(i =>
266+ s " if ( ${prefix}_en & ${prefix}_mask[ ${i}]) Memory[ ${prefix}_addr][ ${i * parameter.maskGranularity} +: ${parameter.maskGranularity}] <= ${prefix}_data[ ${(i + 1 ) * parameter.maskGranularity - 1 }: ${i * parameter.maskGranularity}]; "
267+ )
268+ else
269+ Seq (s " if ( ${prefix}_en) Memory[ ${prefix}_addr] <= ${prefix}_data; " )) ++
270+ Seq (s " end // ${prefix}" )
271+ }
272+ .flatten
273+
274+ private val rwLogic = Seq
275+ .tabulate(parameter.readwrite) { idx =>
276+ val prefix = s " RW ${idx}"
277+ Seq (
278+ s " reg [ ${log2Ceil(parameter.depth) - 1 }:0] _ ${prefix}_raddr; " ,
279+ s " reg _ ${prefix}_ren; " ,
280+ s " reg _ ${prefix}_rmode; "
281+ ) ++
282+ Seq (s " always @(posedge ${prefix}_clk) begin // ${prefix}" ) ++
283+ Seq (
284+ s " _ ${prefix}_raddr <= ${prefix}_addr; " ,
285+ s " _ ${prefix}_ren <= ${prefix}_en; " ,
286+ s " _ ${prefix}_rmode <= ${prefix}_wmode; "
287+ ) ++
288+ (if (parameter.masked)
289+ Seq .tabulate(parameter.width / parameter.maskGranularity)(i =>
290+ s " if( ${prefix}_en & ${prefix}_wmask[ ${i}] & ${prefix}_wmode) Memory[ ${prefix}_addr][ ${i * parameter.maskGranularity} +: ${parameter.maskGranularity}] <= ${prefix}_wdata[ ${(i + 1 ) * parameter.maskGranularity - 1 }: ${i * parameter.maskGranularity}]; "
291+ )
292+ else
293+ Seq (s " if ( ${prefix}_en & ${prefix}_wmode) Memory[ ${prefix}_addr] <= ${prefix}_wdata; " )) ++
294+ Seq (s " end // ${prefix}" ) ++
295+ Seq (
296+ s " assign ${prefix}_rdata = _ ${prefix}_ren & ~_ ${prefix}_rmode ? Memory[_ ${prefix}_raddr] : ${parameter.width}'bx; "
297+ )
298+ }
299+ .flatten
300+
301+ private val logic =
302+ (Seq (s " reg [ ${parameter.width - 1 }:0] Memory[0: ${parameter.depth - 1 }]; " ) ++ wLogic ++ rLogic ++ rwLogic)
303+ .mkString(" \n " )
304+
305+ override def desiredName = parameter.moduleName
306+
307+ setInline(
308+ desiredName + " .sv" ,
309+ s """ module ${parameter.moduleName}(
310+ | ${verilogInterface}
311+ |);
312+ | ${logic}
313+ |endmodule
314+ | """ .stripMargin
315+ )
316+ }
317+
203318object SRAM {
204319
205320 /** Generates a memory within the current module, connected to an explicit number
@@ -502,6 +617,113 @@ object SRAM {
502617 sourceInfo
503618 )
504619
620+ private def memInterface_blackbox_impl [T <: Data ](
621+ size : BigInt ,
622+ tpe : T ,
623+ readPortClocks : Seq [Clock ],
624+ writePortClocks : Seq [Clock ],
625+ readwritePortClocks : Seq [Clock ],
626+ memoryFile : Option [MemoryFile ],
627+ evidenceOpt : Option [T <:< Vec [_]],
628+ sourceInfo : SourceInfo
629+ ): SRAMInterface [T ] = {
630+ val numReadPorts = readPortClocks.size
631+ val numWritePorts = writePortClocks.size
632+ val numReadwritePorts = readwritePortClocks.size
633+ val enableMask = evidenceOpt.isDefined
634+ val isValidSRAM = ((numReadPorts + numReadwritePorts) > 0 ) && ((numWritePorts + numReadwritePorts) > 0 )
635+ val maskGranularity = tpe match {
636+ case vec : Vec [_] if enableMask => vec.sample_element.getWidth
637+ case _ => 0
638+ }
639+
640+ if (! isValidSRAM) {
641+ val badMemory =
642+ if (numReadPorts + numReadwritePorts == 0 )
643+ " write-only SRAM (R + RW === 0)"
644+ else
645+ " read-only SRAM (W + RW === 0)"
646+ Builder .error(
647+ s " Attempted to initialize a $badMemory! SRAMs must have both at least one read accessor and at least one write accessor. "
648+ )
649+ }
650+
651+ val mem = Instantiate (
652+ new SRAMBlackbox (
653+ new CIRCTSRAMParameter (
654+ s " sram_ ${numReadPorts}R_ ${numWritePorts}W_ ${numReadwritePorts}RW_ ${maskGranularity}M_ ${size}x ${tpe.getWidth}" ,
655+ numReadPorts,
656+ numWritePorts,
657+ numReadwritePorts,
658+ size.intValue,
659+ tpe.getWidth,
660+ maskGranularity
661+ )
662+ )
663+ )
664+
665+ implicit class SRAMInstanceMethods (underlying : Instance [SRAMBlackbox ]) {
666+ implicit val mg : internal.MacroGenerated = new chisel3.internal.MacroGenerated {}
667+ def io = underlying._lookup(_.io)
668+ }
669+
670+ val sramReadPorts = Seq .tabulate(numReadPorts)(i => mem.io.R (i))
671+ val sramWritePorts = Seq .tabulate(numWritePorts)(i => mem.io.W (i))
672+ val sramReadwritePorts = Seq .tabulate(numReadwritePorts)(i => mem.io.RW (i))
673+
674+ val includeMetadata = Builder .includeUtilMetadata
675+
676+ val out = Wire (
677+ new SRAMInterface (size, tpe, numReadPorts, numWritePorts, numReadwritePorts, enableMask, includeMetadata)
678+ )
679+
680+ out.readPorts.zip(sramReadPorts).zip(readPortClocks).map {
681+ case ((intfReadPort, sramReadPort), readClock) =>
682+ sramReadPort.address := intfReadPort.address
683+ sramReadPort.clock := readClock
684+ intfReadPort.data := sramReadPort.data.asTypeOf(tpe)
685+ sramReadPort.enable := intfReadPort.enable
686+ }
687+ out.writePorts.zip(sramWritePorts).zip(writePortClocks).map {
688+ case ((intfWritePort, sramWritePort), writeClock) =>
689+ sramWritePort.address := intfWritePort.address
690+ sramWritePort.clock := writeClock
691+ sramWritePort.data := intfWritePort.data.asUInt
692+ sramWritePort.enable := intfWritePort.enable
693+ sramWritePort.mask match {
694+ case Some (mask) => mask := intfWritePort.mask.get.asUInt
695+ case None => assert(intfWritePort.mask.isEmpty)
696+ }
697+ }
698+ out.readwritePorts.zip(sramReadwritePorts).zip(readwritePortClocks).map {
699+ case ((intfReadwritePort, sramReadwritePort), readwriteClock) =>
700+ sramReadwritePort.address := intfReadwritePort.address
701+ sramReadwritePort.clock := readwriteClock
702+ sramReadwritePort.enable := intfReadwritePort.enable
703+ intfReadwritePort.readData := sramReadwritePort.readData.asTypeOf(tpe)
704+ sramReadwritePort.writeData := intfReadwritePort.writeData.asUInt
705+ sramReadwritePort.writeEnable := intfReadwritePort.isWrite
706+ sramReadwritePort.writeMask match {
707+ case Some (mask) => mask := intfReadwritePort.mask.get.asUInt
708+ case None => assert(intfReadwritePort.mask.isEmpty)
709+ }
710+ }
711+
712+ out.description.foreach { description =>
713+ val descriptionInstance : Instance [SRAMDescription ] = Instantiate (new SRAMDescription )
714+ descriptionInstance.depthIn := Property (size)
715+ descriptionInstance.widthIn := Property (tpe.getWidth)
716+ descriptionInstance.maskedIn := Property (enableMask)
717+ descriptionInstance.readIn := Property (numReadPorts)
718+ descriptionInstance.writeIn := Property (numWritePorts)
719+ descriptionInstance.readwriteIn := Property (numReadwritePorts)
720+ descriptionInstance.maskGranularityIn := Property (maskGranularity)
721+ descriptionInstance.hierarchyIn := Property (Path (mem.toTarget))
722+ description := descriptionInstance.getPropertyReference
723+ }
724+ out
725+ }
726+
505727 private def memInterface_impl [T <: Data ](
506728 size : BigInt ,
507729 tpe : T ,
@@ -512,6 +734,18 @@ object SRAM {
512734 evidenceOpt : Option [T <:< Vec [_]],
513735 sourceInfo : SourceInfo
514736 ): SRAMInterface [T ] = {
737+ if (Builder .useSRAMBlackbox)
738+ return memInterface_blackbox_impl(
739+ size,
740+ tpe,
741+ readPortClocks,
742+ writePortClocks,
743+ readwritePortClocks,
744+ memoryFile,
745+ evidenceOpt,
746+ sourceInfo
747+ )
748+
515749 val numReadPorts = readPortClocks.size
516750 val numWritePorts = writePortClocks.size
517751 val numReadwritePorts = readwritePortClocks.size
@@ -609,18 +843,6 @@ object SRAM {
609843 assignMask(firrtlReadwritePort.wmask, memReadwritePort.mask)
610844 }
611845
612- // Hack to ScalaDoc Bug, see [[LTLIntrinsicInstanceMethodsInternalWorkaround]]
613- implicit class SRAMDescriptionInstanceMethods (underlying : Instance [SRAMDescription ]) {
614- implicit val mg : internal.MacroGenerated = new chisel3.internal.MacroGenerated {}
615- def depthIn : Property [BigInt ] = underlying._lookup(_.depthIn)
616- def widthIn : Property [Int ] = underlying._lookup(_.widthIn)
617- def maskedIn : Property [Boolean ] = underlying._lookup(_.maskedIn)
618- def readIn : Property [Int ] = underlying._lookup(_.readIn)
619- def writeIn : Property [Int ] = underlying._lookup(_.writeIn)
620- def readwriteIn : Property [Int ] = underlying._lookup(_.readwriteIn)
621- def maskGranularityIn : Property [Int ] = underlying._lookup(_.maskGranularityIn)
622- def hierarchyIn : Property [Path ] = underlying._lookup(_.hierarchyIn)
623- }
624846 _out.description.foreach { description =>
625847 val descriptionInstance : Instance [SRAMDescription ] = Instantiate (new SRAMDescription )
626848 descriptionInstance.depthIn := Property (size)
@@ -643,6 +865,24 @@ object SRAM {
643865 _out
644866 }
645867
868+ // There appears to be a bug in ScalaDoc where you cannot use macro-generated methods in the same
869+ // compilation unit as the macro-generated type. This means that any use of Definition[_] or
870+ // Instance[_] of the above classes within this compilation unit breaks ScalaDoc generation. This
871+ // issue appears to be similar to https://stackoverflow.com/questions/42684101 but applying the
872+ // specific mitigation did not seem to work. As a workaround, we simply write the extension methods
873+ // that are generated by the @instantiable macro so that we can use them here.
874+ implicit class SRAMDescriptionInstanceMethods (underlying : Instance [SRAMDescription ]) {
875+ implicit val mg : internal.MacroGenerated = new chisel3.internal.MacroGenerated {}
876+ def depthIn : Property [BigInt ] = underlying._lookup(_.depthIn)
877+ def widthIn : Property [Int ] = underlying._lookup(_.widthIn)
878+ def maskedIn : Property [Boolean ] = underlying._lookup(_.maskedIn)
879+ def readIn : Property [Int ] = underlying._lookup(_.readIn)
880+ def writeIn : Property [Int ] = underlying._lookup(_.writeIn)
881+ def readwriteIn : Property [Int ] = underlying._lookup(_.readwriteIn)
882+ def maskGranularityIn : Property [Int ] = underlying._lookup(_.maskGranularityIn)
883+ def hierarchyIn : Property [Path ] = underlying._lookup(_.hierarchyIn)
884+ }
885+
646886 /** Assigns a given SRAM-style mask to a FIRRTL memory port mask. The FIRRTL
647887 * memory port mask is tied to 1 for unmasked SRAMs.
648888 *
0 commit comments