diff --git a/firrtl/src/main/scala/firrtl/annotations/JsonProtocol.scala b/firrtl/src/main/scala/firrtl/annotations/JsonProtocol.scala index cb2017900af..3b55fd9c60e 100644 --- a/firrtl/src/main/scala/firrtl/annotations/JsonProtocol.scala +++ b/firrtl/src/main/scala/firrtl/annotations/JsonProtocol.scala @@ -15,6 +15,7 @@ import org.json4s.native.Serialization import org.json4s.native.Serialization.{read, write, writePretty} import scala.collection.mutable +import java.io.{StringWriter, Writer} trait HasSerializationHints { // For serialization of complicated constructor arguments, let the annotation @@ -167,13 +168,21 @@ object JsonProtocol extends LazyLogging { }) .distinct - def serializeTry(annos: Seq[Annotation]): Try[String] = { - val tags = getTags(annos) + def serializeTry(annos: Seq[Annotation]): Try[String] = serializeTry(annos, new StringWriter).map(_.toString) + + /** Serialize annotations to a [[java.io.Writer]] + * + * @param annos Annotations to serialize + * @param out Writer to which the serialized annotations will be written + * @return + */ + def serializeTry[W <: Writer](annos: Iterable[Annotation], out: W): Try[W] = { + val tags = getTags(annos.toSeq) implicit val formats = jsonFormat(tags) - Try(writePretty(annos)).recoverWith { + Try(writePretty(annos, out)).recoverWith { case e: org.json4s.MappingException => - val badAnnos = findUnserializeableAnnos(annos) + val badAnnos = findUnserializeableAnnos(annos.toSeq) Failure(if (badAnnos.isEmpty) e else UnserializableAnnotationException(badAnnos)) } } diff --git a/firrtl/src/main/scala/firrtl/options/phases/WriteOutputAnnotations.scala b/firrtl/src/main/scala/firrtl/options/phases/WriteOutputAnnotations.scala index c28da8d65f4..5bb36da515d 100644 --- a/firrtl/src/main/scala/firrtl/options/phases/WriteOutputAnnotations.scala +++ b/firrtl/src/main/scala/firrtl/options/phases/WriteOutputAnnotations.scala @@ -126,7 +126,7 @@ class WriteOutputAnnotations extends Phase { case None => case Some(file) => val pw = new PrintWriter(sopts.getBuildFileName(file, Some(".anno.json"))) - pw.write(JsonProtocol.serialize(serializable)) + JsonProtocol.serializeTry(serializable, pw).get // .get to throw any exceptions pw.close() } } diff --git a/firrtl/src/test/scala/firrtl/JsonProtocolSpec.scala b/firrtl/src/test/scala/firrtl/JsonProtocolSpec.scala index 3e07542bb3d..27c8877c583 100644 --- a/firrtl/src/test/scala/firrtl/JsonProtocolSpec.scala +++ b/firrtl/src/test/scala/firrtl/JsonProtocolSpec.scala @@ -80,4 +80,24 @@ class JsonProtocolSpec extends AnyFlatSpec { val deserAnno = JsonProtocol.deserialize(serializedAnno).head assert(anno == deserAnno) } + + "JsonProtocol" should "support serializing directly to a Java Writer" in { + val anno = SimpleAnnotation("hello") + class NaiveWriter extends java.io.Writer { + private var contents: String = "" + def value: String = contents + def close(): Unit = contents = "" + def flush(): Unit = contents = "" + def write(cbuff: Array[Char], off: Int, len: Int): Unit = { + for (i <- off until off + len) { + contents += cbuff(i) + } + } + } + val w = new NaiveWriter + JsonProtocol.serializeTry(Seq(anno), w) + val ser1 = w.value + val ser2 = JsonProtocol.serialize(Seq(anno)) + assert(ser1 == ser2) + } } diff --git a/src/main/scala/chisel3/simulator/package.scala b/src/main/scala/chisel3/simulator/package.scala index de942f17f4f..354a5b56573 100644 --- a/src/main/scala/chisel3/simulator/package.scala +++ b/src/main/scala/chisel3/simulator/package.scala @@ -2,6 +2,8 @@ package chisel3 import svsim._ import chisel3.reflect.DataMirror +import scala.collection.mutable +import java.nio.file.{Files, Path, Paths} package object simulator { @@ -107,37 +109,46 @@ package object simulator { ) ) - // Move the files indicated by a filelist. - def moveFiles(filename: String) = { - val filelist = new java.io.BufferedReader(new java.io.FileReader(filename)) - try { - filelist.lines().forEach { immutableFilename => - var filename = immutableFilename - /// Some files are provided as absolute paths - if (filename.startsWith(workspace.supportArtifactsPath)) { - filename = filename.substring(workspace.supportArtifactsPath.length + 1) - } - java.nio.file.Files.move( - java.nio.file.Paths.get(s"${workspace.supportArtifactsPath}/$filename"), - java.nio.file.Paths.get(s"${workspace.primarySourcesPath}/$filename") - ) + // Move the files indicated by a filelist. No-op if the file has already + // been moved. + val movedFiles = mutable.HashSet.empty[Path] + val supportArtifactsPath = Paths.get(workspace.supportArtifactsPath) + def moveFiles(filelist: Path) = + // Extract all lines (files) from the filelist. + Files + .lines(filelist) + .map(Paths.get(_)) + // Convert the files to an absolute version and a relative version. + .map { + case file if file.startsWith(supportArtifactsPath) => + (file, file.subpath(supportArtifactsPath.getNameCount(), -1)) + case file => (supportArtifactsPath.resolve(file), file) + } + // Normalize the absolute path so it can be checked if it has already + // been moved. + .map { case (abs, rel) => (abs.normalize(), rel) } + // Move the file into primarySourcesPath if it has not already been moved. + .forEach { + case (abs, _) if movedFiles.contains(abs) => + case (abs, rel) => + Files.move( + abs, + Paths.get(workspace.primarySourcesPath).resolve(rel) + ) + movedFiles += abs } - } finally { - filelist.close() - } - } - // Move a file in a filelist which may not exist. - def maybeMoveFiles(filename: String) = try { - moveFiles(filename) - } catch { - case _ @(_: java.nio.file.NoSuchFileException | _: java.io.FileNotFoundException) => + // Move a file in a filelist which may not exist. Do nothing if the + // filelist does not exist. + def maybeMoveFiles(filelist: Path): Unit = filelist match { + case _ if Files.exists(filelist) => moveFiles(filelist) + case _ => } // Move files indicated by 'filelist.f' (which must exist). Move files // indicated by a black box filelist (which may exist). - moveFiles(s"${workspace.supportArtifactsPath}/filelist.f") - maybeMoveFiles(s"${workspace.supportArtifactsPath}/firrtl_black_box_resource_files.f") + moveFiles(supportArtifactsPath.resolve("filelist.f")) + maybeMoveFiles(supportArtifactsPath.resolve("firrtl_black_box_resource_files.f")) // Initialize Module Info val dut = someDut.get diff --git a/src/main/scala/chisel3/util/experimental/BoringUtils.scala b/src/main/scala/chisel3/util/experimental/BoringUtils.scala index 632196d74bc..a916b341644 100644 --- a/src/main/scala/chisel3/util/experimental/BoringUtils.scala +++ b/src/main/scala/chisel3/util/experimental/BoringUtils.scala @@ -224,9 +224,14 @@ object BoringUtils { else if (DataMirror.hasOuterFlip(source)) Flipped(chiselTypeOf(source)) else chiselTypeOf(source) def purePortType = createProbe match { - case Some(pi) if pi.writable => RWProbe(purePortTypeBase) - case Some(pi) => Probe(purePortTypeBase) - case None => purePortTypeBase + case Some(pi) => + // If the source is already a probe, don't double wrap it in a probe. + purePortTypeBase.probeInfo match { + case Some(_) => purePortTypeBase + case None if pi.writable => RWProbe(purePortTypeBase) + case None => Probe(purePortTypeBase) + } + case None => purePortTypeBase } def isPort(d: Data): Boolean = d.topBindingOpt match { case Some(PortBinding(_)) => true diff --git a/src/test/scala/chiselTests/BoringUtilsTapSpec.scala b/src/test/scala/chiselTests/BoringUtilsTapSpec.scala index 87f3625ae44..cc0c52ac632 100644 --- a/src/test/scala/chiselTests/BoringUtilsTapSpec.scala +++ b/src/test/scala/chiselTests/BoringUtilsTapSpec.scala @@ -495,4 +495,21 @@ class BoringUtilsTapSpec extends ChiselFlatSpec with ChiselRunners with Utils wi val verilog = circt.stage.ChiselStage.emitSystemVerilog(new Foo) } + it should "allow tapping a probe" in { + class Bar extends RawModule { + val a = IO(probe.Probe(Bool())) + } + class Foo extends RawModule { + val b = IO(probe.Probe(Bool())) + val bar = Module(new Bar) + probe.define(b, BoringUtils.tap(bar.a)) + } + + val chirrtl = circt.stage.ChiselStage.emitCHIRRTL(new Foo) + + matchesAndOmits(chirrtl)( + "define b = bar.a" + )() + } + } diff --git a/svsim/src/main/scala/verilator/Backend.scala b/svsim/src/main/scala/verilator/Backend.scala index de6c843835d..8e2dc18419e 100644 --- a/svsim/src/main/scala/verilator/Backend.scala +++ b/svsim/src/main/scala/verilator/Backend.scala @@ -120,7 +120,7 @@ final class Backend( case OptimizationStyle.OptimizeForCompilationSpeed => Seq("-O1") }, - Seq("-std=c++11"), + Seq("-std=c++14"), additionalHeaderPaths.map { path => s"-I${path}" },