Skip to content

Commit 0e46fe1

Browse files
committed
Merge remote-tracking branch 'origin/main' into tmckay/sim-construct-disable-warnings
2 parents 06bbd08 + ba19b6d commit 0e46fe1

File tree

14 files changed

+477
-114
lines changed

14 files changed

+477
-114
lines changed

core/src/main/scala/chisel3/experimental/hierarchy/core/Definition.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ object Definition extends SourceInfoDoc {
111111
context.throwOnFirstError,
112112
context.useLegacyWidth,
113113
context.includeUtilMetadata,
114+
context.useSRAMBlackbox,
114115
context.warningFilters,
115116
context.sourceRoots,
116117
Some(context.globalNamespace),

core/src/main/scala/chisel3/internal/Builder.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,7 @@ private[chisel3] class DynamicContext(
481481
val throwOnFirstError: Boolean,
482482
val useLegacyWidth: Boolean,
483483
val includeUtilMetadata: Boolean,
484+
val useSRAMBlackbox: Boolean,
484485
val warningFilters: Seq[WarningFilter],
485486
val sourceRoots: Seq[File],
486487
val defaultNamespace: Option[Namespace],
@@ -975,6 +976,8 @@ private[chisel3] object Builder extends LazyLogging {
975976

976977
def includeUtilMetadata: Boolean = dynamicContextVar.value.map(_.includeUtilMetadata).getOrElse(false)
977978

979+
def useSRAMBlackbox: Boolean = dynamicContextVar.value.map(_.useSRAMBlackbox).getOrElse(false)
980+
978981
// Builds a RenameMap for all Views that do not correspond to a single Data
979982
// These Data give a fake ReferenceTarget for .toTarget and .toReferenceTarget that the returned
980983
// RenameMap can split into the constituent parts

core/src/main/scala/chisel3/util/circt/LTLIntrinsics.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ private[chisel3] object UnaryLTLIntrinsic {
3737
)(
3838
implicit sourceInfo: SourceInfo
3939
): Bool =
40-
BaseIntrinsic(f"ltl_$intrinsicName", Bool(), params)(_in).suggestName(intrinsicName)
40+
BaseIntrinsic(f"ltl_$intrinsicName", Bool(), params)(_in).suggestName(f"ltl_$intrinsicName")
4141
}
4242

4343
/** Base instrinsic for all binary intrinsics with `lhs`, `rhs`, and `out` ports. */
@@ -50,7 +50,7 @@ private[chisel3] object BinaryLTLIntrinsic {
5050
)(
5151
implicit sourceInfo: SourceInfo
5252
): Bool =
53-
BaseIntrinsic(f"ltl_$intrinsicName", Bool(), params)(lhs, rhs).suggestName(intrinsicName)
53+
BaseIntrinsic(f"ltl_$intrinsicName", Bool(), params)(lhs, rhs).suggestName(f"ltl_$intrinsicName")
5454
}
5555

5656
/** A wrapper intrinsic for the CIRCT `ltl.and` operation. */

etc/circt.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"version": "firtool-1.96.0"
2+
"version": "firtool-1.97.1"
33
}

src/main/scala-2/chisel3/util/SRAM.scala

Lines changed: 252 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import chisel3.properties.Class.ClassDefinitionOps
1818
import chisel3.properties.{Class, ClassType, Path, Property}
1919

2020
import 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
*/
201202
case 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+
203318
object 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
*

src/main/scala/chisel3/stage/ChiselAnnotations.scala

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,3 +503,18 @@ case object IncludeUtilMetadata extends NoTargetAnnotation with ChiselOption wit
503503
)
504504
)
505505
}
506+
507+
/** Use Blackbox implementation for SRAM
508+
*
509+
* Use as CLI option `--use-sram-blackbox`.
510+
*/
511+
case object UseSRAMBlackbox extends NoTargetAnnotation with ChiselOption with HasShellOptions with Unserializable {
512+
513+
val options = Seq(
514+
new ShellOption[Unit](
515+
longOption = "use-sram-blackbox",
516+
toAnnotationSeq = _ => Seq(UseSRAMBlackbox),
517+
helpText = "Use Blackbox implementation for SRAM"
518+
)
519+
)
520+
}

0 commit comments

Comments
 (0)