diff options
| -rw-r--r-- | build.sbt | 11 | ||||
| -rw-r--r-- | project/plugins.sbt | 4 | ||||
| -rw-r--r-- | src/main/proto/firrtl.proto | 58 | ||||
| -rw-r--r-- | src/main/scala/firrtl/Driver.scala | 16 | ||||
| -rw-r--r-- | src/main/scala/firrtl/ExecutionOptionsManager.scala | 7 | ||||
| -rw-r--r-- | src/main/scala/firrtl/WIR.scala | 4 | ||||
| -rw-r--r-- | src/main/scala/firrtl/proto/FromProto.scala | 304 | ||||
| -rw-r--r-- | src/main/scala/firrtl/proto/ToProto.scala | 413 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/DriverSpec.scala | 36 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/FirrtlSpec.scala | 6 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/ProtoBufSpec.scala | 140 | ||||
| -rw-r--r-- | test/integration/GCDTester.pb | bin | 0 -> 4410 bytes |
12 files changed, 981 insertions, 18 deletions
@@ -68,6 +68,17 @@ libraryDependencies += "net.jcazevedo" %% "moultingyaml" % "0.4.0" libraryDependencies += "org.json4s" %% "json4s-native" % "3.5.3" +// Java PB + +enablePlugins(ProtobufPlugin) + +sourceDirectory in ProtobufConfig := baseDirectory.value / "src" / "main" / "proto" + +protobufRunProtoc in ProtobufConfig := (args => + com.github.os72.protocjar.Protoc.runProtoc("-v351" +: args.toArray)) + +javaSource in ProtobufConfig := (sourceManaged in Compile).value + // Assembly assemblyJarName in assembly := "firrtl.jar" diff --git a/project/plugins.sbt b/project/plugins.sbt index 40490350..1a7cb8a6 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -19,3 +19,7 @@ addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.5.1") addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.6") addSbtPlugin("com.simplytyped" % "sbt-antlr4" % "0.8.1") + +addSbtPlugin("com.github.gseitz" % "sbt-protobuf" % "0.6.3") + +libraryDependencies += "com.github.os72" % "protoc-jar" % "3.5.1.1" diff --git a/src/main/proto/firrtl.proto b/src/main/proto/firrtl.proto index 3e2d848c..7be042ab 100644 --- a/src/main/proto/firrtl.proto +++ b/src/main/proto/firrtl.proto @@ -34,9 +34,15 @@ message Firrtl { oneof source_info { None none = 1; Position position = 2; + string text = 3; } } + message BigInt { + // 2's complement binary representation + bytes value = 1; + } + message Top { // Required. string name = 1; @@ -49,9 +55,20 @@ message Firrtl { message Module { message ExternalModule { + message Parameter { + string id = 1; + oneof value { + BigInt integer = 2; + double double = 3; + string string = 4; + string raw_string = 5; + } + } // Required. string id = 1; repeated Port port = 2; + string defined_name = 3; + repeated Parameter parameter = 4; } message UserModule { @@ -100,6 +117,7 @@ message Firrtl { uint32 read_latency = 5; repeated string reader_id = 6; repeated string writer_id = 7; + repeated string readwriter_id = 8; } message CMemory { @@ -107,6 +125,8 @@ message Firrtl { string id = 1; // Required. Type.VectorType type = 2; + // Required. + bool sync_read = 3; } message Instance { @@ -141,7 +161,7 @@ message Firrtl { message Printf { // Required. - bytes value = 1; + string value = 1; repeated Expression arg = 2; // Required. Expression clk = 3; @@ -195,6 +215,10 @@ message Firrtl { Expression expression = 5; } + message Attach { + repeated Expression expression = 1; + } + // Required. oneof statement { Wire wire = 1; @@ -211,6 +235,7 @@ message Firrtl { PartialConnect partial_connect = 16; IsInvalid is_invalid = 17; MemoryPort memory_port = 18; + Attach attach = 20; } SourceInfo source_info = 19; @@ -256,13 +281,24 @@ message Firrtl { uint32 size = 2; } + message FixedType { + Width width = 1; + Width point = 2; + } + + message AnalogType { + Width width = 3; + } + // Required. oneof type { UIntType uint_type = 2; SIntType sint_type = 3; ClockType clock_type = 4; - RecordType record_type = 5; + BundleType bundle_type = 5; VectorType vector_type = 6; + FixedType fixed_type = 7; + AnalogType analog_type = 8; } } @@ -306,6 +342,12 @@ message Firrtl { Width width = 2; } + message FixedLiteral { + BigInt value = 1; + Width width = 2; + Width point = 3; + } + message ValidIf { // Required. Expression condition = 1; @@ -376,6 +418,13 @@ message Firrtl { OP_AS_UINT = 28; OP_AS_SINT = 29; OP_EXTRACT_BITS = 30; + OP_AS_CLOCK = 31; + OP_AS_FIXED_POINT = 32; + OP_AND_REDUCE = 33; + OP_OR_REDUCE = 34; + OP_SHIFT_BINARY_POINT_LEFT = 35; + OP_SHIFT_BINARY_POINT_RIGHT = 36; + OP_SET_BINARY_POINT = 37; } // Required. @@ -384,13 +433,16 @@ message Firrtl { repeated IntegerLiteral const = 3; } + reserved 5; + // Required. oneof expression { Reference reference = 1; UIntLiteral uint_literal = 2; SIntLiteral sint_literal = 3; + FixedLiteral fixed_literal = 11; ValidIf valid_if = 4; - ExtractBits extract_bits = 5; + //ExtractBits extract_bits = 5; Mux mux = 6; SubField sub_field = 7; SubIndex sub_index = 8; diff --git a/src/main/scala/firrtl/Driver.scala b/src/main/scala/firrtl/Driver.scala index 97d0ce4e..2315ad55 100644 --- a/src/main/scala/firrtl/Driver.scala +++ b/src/main/scala/firrtl/Driver.scala @@ -148,6 +148,16 @@ object Driver { LegacyAnnotation.convertLegacyAnnos(annos) } + private sealed trait FileExtension + private case object FirrtlFile extends FileExtension + private case object ProtoBufFile extends FileExtension + + private def getFileExtension(filename: String): FileExtension = + filename.drop(filename.lastIndexOf('.')) match { + case ".pb" => ProtoBufFile + case _ => FirrtlFile // Default to FIRRTL File + } + // Useful for handling erros in the options case class OptionsException(msg: String) extends Exception(msg) @@ -183,7 +193,11 @@ object Driver { } val inputFileName = firrtlConfig.getInputFileName(optionsManager) try { - Parser.parseFile(inputFileName, firrtlConfig.infoMode) + // TODO What does InfoMode mean to ProtoBuf? + getFileExtension(inputFileName) match { + case ProtoBufFile => proto.FromProto.fromFile(inputFileName) + case FirrtlFile => Parser.parseFile(inputFileName, firrtlConfig.infoMode) + } } catch { case _: FileNotFoundException => diff --git a/src/main/scala/firrtl/ExecutionOptionsManager.scala b/src/main/scala/firrtl/ExecutionOptionsManager.scala index f8caf842..bb5e68a3 100644 --- a/src/main/scala/firrtl/ExecutionOptionsManager.scala +++ b/src/main/scala/firrtl/ExecutionOptionsManager.scala @@ -225,14 +225,15 @@ extends ComposableOptions { } } - /** - * build the input file name, taking overriding parameters + /** Get the name of the input file * + * @note Does not implicitly add a file extension to the input file * @param optionsManager this is needed to access build function and its common options * @return a properly constructed input file name */ def getInputFileName(optionsManager: ExecutionOptionsManager): String = { - optionsManager.getBuildFileName("fir", inputFileNameOverride) + if (inputFileNameOverride.nonEmpty) inputFileNameOverride + else optionsManager.getBuildFileName("fir", inputFileNameOverride) } /** Get the user-specified [[OutputConfig]] * diff --git a/src/main/scala/firrtl/WIR.scala b/src/main/scala/firrtl/WIR.scala index 20da680f..29a14406 100644 --- a/src/main/scala/firrtl/WIR.scala +++ b/src/main/scala/firrtl/WIR.scala @@ -275,7 +275,7 @@ case class CDefMemory( name: String, tpe: Type, size: Int, - seq: Boolean) extends Statement { + seq: Boolean) extends Statement with HasInfo { def serialize: String = (if (seq) "smem" else "cmem") + s" $name : ${tpe.serialize} [$size]" + info.serialize def mapExpr(f: Expression => Expression): Statement = this @@ -289,7 +289,7 @@ case class CDefMPort(info: Info, tpe: Type, mem: String, exps: Seq[Expression], - direction: MPortDir) extends Statement { + direction: MPortDir) extends Statement with HasInfo { def serialize: String = { val dir = direction.serialize s"$dir mport $name = $mem[${exps.head.serialize}], ${exps(1).serialize}" + info.serialize diff --git a/src/main/scala/firrtl/proto/FromProto.scala b/src/main/scala/firrtl/proto/FromProto.scala new file mode 100644 index 00000000..dda2099c --- /dev/null +++ b/src/main/scala/firrtl/proto/FromProto.scala @@ -0,0 +1,304 @@ +// See LICENSE for license details. + +package firrtl +package proto + +import java.io.{File, FileInputStream, InputStream} + +import collection.JavaConverters._ +import FirrtlProtos._ +import com.google.protobuf.CodedInputStream + +object FromProto { + + /** Deserialize ProtoBuf representation of [[ir.Circuit]] + * + * @param filename Name of file containing ProtoBuf representation + * @return Deserialized FIRRTL Circuit + */ + def fromFile(filename: String): ir.Circuit = { + fromInputStream(new FileInputStream(new File(filename))) + } + + /** Deserialize ProtoBuf representation of [[ir.Circuit]] + * + * @param is InputStream containing ProtoBuf representation + * @return Deserialized FIRRTL Circuit + */ + def fromInputStream(is: InputStream): ir.Circuit = { + val cistream = CodedInputStream.newInstance(is) + cistream.setRecursionLimit(Integer.MAX_VALUE) // Disable recursion depth check + val pb = firrtl.FirrtlProtos.Firrtl.parseFrom(cistream) + proto.FromProto.convert(pb) + } + + // Convert from ProtoBuf message repeated Statements to FIRRRTL Block + private def compressStmts(stmts: Seq[ir.Statement]): ir.Statement = stmts match { + case Seq() => ir.EmptyStmt + case Seq(stmt) => stmt + case multiple => ir.Block(multiple) + } + + def convert(info: Firrtl.SourceInfo): ir.Info = + info.getSourceInfoCase.getNumber match { + case Firrtl.SourceInfo.POSITION_FIELD_NUMBER => + val pos = info.getPosition + val str = s"${pos.getFilename} ${pos.getLine}:${pos.getColumn}" + ir.FileInfo(ir.StringLit(str)) + case Firrtl.SourceInfo.TEXT_FIELD_NUMBER => + ir.FileInfo(ir.StringLit(info.getText)) + // NONE_FIELD_NUMBER or anything else + case _ => ir.NoInfo + } + + val convert: Map[Firrtl.Expression.PrimOp.Op, ir.PrimOp] = + ToProto.convert.map { case (k, v) => v -> k } + + def convert(literal: Firrtl.Expression.IntegerLiteral): BigInt = + BigInt(literal.getValue) + + def convert(bigint: Firrtl.BigInt): BigInt = BigInt(bigint.getValue.toByteArray) + + def convert(uint: Firrtl.Expression.UIntLiteral): ir.UIntLiteral = { + val width = if (uint.hasWidth) convert(uint.getWidth) else ir.UnknownWidth + ir.UIntLiteral(convert(uint.getValue), width) + } + + def convert(sint: Firrtl.Expression.SIntLiteral): ir.SIntLiteral = { + val width = if (sint.hasWidth) convert(sint.getWidth) else ir.UnknownWidth + ir.SIntLiteral(convert(sint.getValue), width) + } + + def convert(fixed: Firrtl.Expression.FixedLiteral): ir.FixedLiteral = { + val width = if (fixed.hasWidth) convert(fixed.getWidth) else ir.UnknownWidth + val point = if (fixed.hasPoint) convert(fixed.getPoint) else ir.UnknownWidth + ir.FixedLiteral(convert(fixed.getValue), width, point) + } + + def convert(subfield: Firrtl.Expression.SubField): ir.SubField = + ir.SubField(convert(subfield.getExpression), subfield.getField, ir.UnknownType) + + def convert(index: Firrtl.Expression.SubIndex): ir.SubIndex = + ir.SubIndex(convert(index.getExpression), convert(index.getIndex).toInt, ir.UnknownType) + + def convert(access: Firrtl.Expression.SubAccess): ir.SubAccess = + ir.SubAccess(convert(access.getExpression), convert(access.getIndex), ir.UnknownType) + + def convert(primop: Firrtl.Expression.PrimOp): ir.DoPrim = { + val args = primop.getArgList.asScala.map(convert(_)) + val consts = primop.getConstList.asScala.map(convert(_)) + ir.DoPrim(convert(primop.getOp), args, consts, ir.UnknownType) + } + + def convert(mux: Firrtl.Expression.Mux): ir.Mux = + ir.Mux(convert(mux.getCondition), convert(mux.getTValue), convert(mux.getFValue), ir.UnknownType) + + def convert(expr: Firrtl.Expression): ir.Expression = { + import Firrtl.Expression._ + expr.getExpressionCase.getNumber match { + case REFERENCE_FIELD_NUMBER => ir.Reference(expr.getReference.getId, ir.UnknownType) + case SUB_FIELD_FIELD_NUMBER => convert(expr.getSubField) + case SUB_INDEX_FIELD_NUMBER => convert(expr.getSubIndex) + case SUB_ACCESS_FIELD_NUMBER => convert(expr.getSubAccess) + case UINT_LITERAL_FIELD_NUMBER => convert(expr.getUintLiteral) + case SINT_LITERAL_FIELD_NUMBER => convert(expr.getSintLiteral) + case FIXED_LITERAL_FIELD_NUMBER => convert(expr.getFixedLiteral) + case PRIM_OP_FIELD_NUMBER => convert(expr.getPrimOp) + case MUX_FIELD_NUMBER => convert(expr.getMux) + } + } + + def convert(con: Firrtl.Statement.Connect, info: Firrtl.SourceInfo): ir.Connect = + ir.Connect(convert(info), convert(con.getLocation), convert(con.getExpression)) + + def convert(con: Firrtl.Statement.PartialConnect, info: Firrtl.SourceInfo): ir.PartialConnect = + ir.PartialConnect(convert(info), convert(con.getLocation), convert(con.getExpression)) + + def convert(wire: Firrtl.Statement.Wire, info: Firrtl.SourceInfo): ir.DefWire = + ir.DefWire(convert(info), wire.getId, convert(wire.getType)) + + def convert(reg: Firrtl.Statement.Register, info: Firrtl.SourceInfo): ir.DefRegister = + ir.DefRegister(convert(info), reg.getId, convert(reg.getType), convert(reg.getClock), + convert(reg.getReset), convert(reg.getInit)) + + def convert(node: Firrtl.Statement.Node, info: Firrtl.SourceInfo): ir.DefNode = + ir.DefNode(convert(info), node.getId, convert(node.getExpression)) + + def convert(inst: Firrtl.Statement.Instance, info: Firrtl.SourceInfo): ir.DefInstance = + ir.DefInstance(convert(info), inst.getId, inst.getModuleId) + + def convert(when: Firrtl.Statement.When, info: Firrtl.SourceInfo): ir.Conditionally = { + val conseq = compressStmts(when.getConsequentList.asScala.map(convert(_))) + val alt = compressStmts(when.getOtherwiseList.asScala.map(convert(_))) + ir.Conditionally(convert(info), convert(when.getPredicate), conseq, alt) + } + + def convert(cmem: Firrtl.Statement.CMemory, info: Firrtl.SourceInfo): ir.Statement = { + val vtpe = convert(cmem.getType) + CDefMemory(convert(info), cmem.getId, vtpe.tpe, vtpe.size, cmem.getSyncRead) + } + + import Firrtl.Statement.MemoryPort.Direction._ + def convert(mportdir: Firrtl.Statement.MemoryPort.Direction): MPortDir = mportdir match { + case MEMORY_PORT_DIRECTION_INFER => MInfer + case MEMORY_PORT_DIRECTION_READ => MRead + case MEMORY_PORT_DIRECTION_WRITE => MWrite + case MEMORY_PORT_DIRECTION_READ_WRITE => MReadWrite + } + + def convert(port: Firrtl.Statement.MemoryPort, info: Firrtl.SourceInfo): CDefMPort = { + val exprs = Seq(convert(port.getMemoryIndex), convert(port.getExpression)) + CDefMPort(convert(info), port.getId, ir.UnknownType, port.getMemoryId, exprs, convert(port.getDirection)) + } + + def convert(printf: Firrtl.Statement.Printf, info: Firrtl.SourceInfo): ir.Print = { + val args = printf.getArgList.asScala.map(convert(_)) + val str = ir.StringLit(printf.getValue) + ir.Print(convert(info), str, args, convert(printf.getClk), convert(printf.getEn)) + } + + def convert(stop: Firrtl.Statement.Stop, info: Firrtl.SourceInfo): ir.Stop = + ir.Stop(convert(info), stop.getReturnValue, convert(stop.getClk), convert(stop.getEn)) + + def convert(mem: Firrtl.Statement.Memory, info: Firrtl.SourceInfo): ir.DefMemory = { + val dtype = convert(mem.getType) + val rs = mem.getReaderIdList.asScala + val ws = mem.getWriterIdList.asScala + val rws = mem.getReadwriterIdList.asScala + ir.DefMemory(convert(info), mem.getId, dtype, mem.getDepth, mem.getWriteLatency, mem.getReadLatency, + rs, ws, rws, None) + } + + def convert(attach: Firrtl.Statement.Attach, info: Firrtl.SourceInfo): ir.Attach = { + val exprs = attach.getExpressionList.asScala.map(convert(_)) + ir.Attach(convert(info), exprs) + } + + def convert(stmt: Firrtl.Statement): ir.Statement = { + import Firrtl.Statement._ + val info = stmt.getSourceInfo + stmt.getStatementCase.getNumber match { + case NODE_FIELD_NUMBER => convert(stmt.getNode, info) + case CONNECT_FIELD_NUMBER => convert(stmt.getConnect, info) + case PARTIAL_CONNECT_FIELD_NUMBER => convert(stmt.getPartialConnect, info) + case WIRE_FIELD_NUMBER => convert(stmt.getWire, info) + case REGISTER_FIELD_NUMBER => convert(stmt.getRegister, info) + case WHEN_FIELD_NUMBER => convert(stmt.getWhen, info) + case INSTANCE_FIELD_NUMBER => convert(stmt.getInstance, info) + case PRINTF_FIELD_NUMBER => convert(stmt.getPrintf, info) + case STOP_FIELD_NUMBER => convert(stmt.getStop, info) + case MEMORY_FIELD_NUMBER => convert(stmt.getMemory, info) + case IS_INVALID_FIELD_NUMBER => + ir.IsInvalid(convert(info), convert(stmt.getIsInvalid.getExpression)) + case CMEMORY_FIELD_NUMBER => convert(stmt.getCmemory, info) + case MEMORY_PORT_FIELD_NUMBER => convert(stmt.getMemoryPort, info) + case ATTACH_FIELD_NUMBER => convert(stmt.getAttach, info) + } + } + + /** Converts ProtoBuf width to FIRRTL width + * + * @note Checks for UnknownWidth must be done on the parent object + * @param width ProtoBuf width representation + * @return IntWidth equivalent of the ProtoBuf width, default is 0 + */ + def convert(width: Firrtl.Width): ir.IntWidth = ir.IntWidth(width.getValue) + + def convert(ut: Firrtl.Type.UIntType): ir.UIntType = { + val w = if (ut.hasWidth) convert(ut.getWidth) else ir.UnknownWidth + ir.UIntType(w) + } + + def convert(st: Firrtl.Type.SIntType): ir.SIntType = { + val w = if (st.hasWidth) convert(st.getWidth) else ir.UnknownWidth + ir.SIntType(w) + } + + def convert(fixed: Firrtl.Type.FixedType): ir.FixedType = { + val w = if (fixed.hasWidth) convert(fixed.getWidth) else ir.UnknownWidth + val p = if (fixed.hasPoint) convert(fixed.getPoint) else ir.UnknownWidth + ir.FixedType(w, p) + } + + def convert(analog: Firrtl.Type.AnalogType): ir.AnalogType = { + val w = if (analog.hasWidth) convert(analog.getWidth) else ir.UnknownWidth + ir.AnalogType(w) + } + + def convert(field: Firrtl.Type.BundleType.Field): ir.Field = { + val flip = if (field.getIsFlipped) ir.Flip else ir.Default + ir.Field(field.getId, flip, convert(field.getType)) + } + + def convert(vtpe: Firrtl.Type.VectorType): ir.VectorType = + ir.VectorType(convert(vtpe.getType), vtpe.getSize) + + def convert(tpe: Firrtl.Type): ir.Type = { + import Firrtl.Type._ + tpe.getTypeCase.getNumber match { + case UINT_TYPE_FIELD_NUMBER => convert(tpe.getUintType) + case SINT_TYPE_FIELD_NUMBER => convert(tpe.getSintType) + case FIXED_TYPE_FIELD_NUMBER => convert(tpe.getFixedType) + case CLOCK_TYPE_FIELD_NUMBER => ir.ClockType + case ANALOG_TYPE_FIELD_NUMBER => convert(tpe.getAnalogType) + case BUNDLE_TYPE_FIELD_NUMBER => + ir.BundleType(tpe.getBundleType.getFieldList.asScala.map(convert(_))) + case VECTOR_TYPE_FIELD_NUMBER => convert(tpe.getVectorType) + } + } + + def convert(dir: Firrtl.Port.Direction): ir.Direction = { + dir match { + case Firrtl.Port.Direction.PORT_DIRECTION_IN => ir.Input + case Firrtl.Port.Direction.PORT_DIRECTION_OUT => ir.Output + } + } + + def convert(port: Firrtl.Port): ir.Port = { + val dir = convert(port.getDirection) + val tpe = convert(port.getType) + ir.Port(ir.NoInfo, port.getId, dir, tpe) + } + + def convert(param: Firrtl.Module.ExternalModule.Parameter): ir.Param = { + import Firrtl.Module.ExternalModule.Parameter._ + val name = param.getId + param.getValueCase.getNumber match { + case INTEGER_FIELD_NUMBER => ir.IntParam(name, convert(param.getInteger)) + case DOUBLE_FIELD_NUMBER => ir.DoubleParam(name, param.getDouble) + case STRING_FIELD_NUMBER => ir.StringParam(name, ir.StringLit(param.getString)) + case RAW_STRING_FIELD_NUMBER => ir.RawStringParam(name, param.getRawString) + } + } + + def convert(module: Firrtl.Module.UserModule): ir.Module = { + val name = module.getId + val ports = module.getPortList.asScala.map(convert(_)) + val stmts = module.getStatementList.asScala.map(convert(_)) + ir.Module(ir.NoInfo, name, ports, ir.Block(stmts)) + } + + def convert(module: Firrtl.Module.ExternalModule): ir.ExtModule = { + val name = module.getId + val ports = module.getPortList.asScala.map(convert(_)) + val defname = module.getDefinedName + val params = module.getParameterList.asScala.map(convert(_)) + ir.ExtModule(ir.NoInfo, name, ports, defname, params) + } + + def convert(module: Firrtl.Module): ir.DefModule = + if (module.hasUserModule) convert(module.getUserModule) + else { + require(module.hasExternalModule, "Module must have Module or ExtModule") + convert(module.getExternalModule) + } + + def convert(proto: Firrtl): ir.Circuit = { + require(proto.getCircuitCount == 1, "Only 1 circuit is currently supported") + val c = proto.getCircuit(0) + require(c.getTopCount == 1, "Only 1 top is currently supported") + val modules = c.getModuleList.asScala.map(convert(_)) + val top = c.getTop(0).getName + ir.Circuit(ir.NoInfo, modules, top) + } +} diff --git a/src/main/scala/firrtl/proto/ToProto.scala b/src/main/scala/firrtl/proto/ToProto.scala new file mode 100644 index 00000000..b3fb9a0c --- /dev/null +++ b/src/main/scala/firrtl/proto/ToProto.scala @@ -0,0 +1,413 @@ +// See LICENSE for license details. + +package firrtl +package proto + +import java.io.{BufferedOutputStream, OutputStream} + +import FirrtlProtos._ +import Firrtl.Expression.PrimOp.Op +import com.google.protobuf.{CodedOutputStream, WireFormat} +import firrtl.PrimOps._ + +import scala.collection.JavaConverters._ + +object ToProto { + + + /** Serialize a FIRRTL Circuit to an Output Stream as a ProtoBuf message + * + * @param ostream Output stream that will be written + * @param circuit The Circuit to serialize + */ + def writeToStream(ostream: OutputStream, circuit: ir.Circuit): Unit = { + writeToStreamFast(ostream, circuit.info, circuit.modules.map(() => _), circuit.main) + } + + /** Serialized a deconstructed Circuit with lazy Modules + * + * This serializer allows intermediate objects to be garbage collected during serialization + * to save time and memory + * + * @param ostream Output stream that will be written + * @param info Info of Circuit + * @param modules Functions to generate Modules lazily + * @param main Top-level module of the Circuit + */ + // Note this function is sensitive to changes to the Firrtl and Circuit protobuf message definitions + def writeToStreamFast( + ostream: OutputStream, + info: ir.Info, + modules: Seq[() => ir.DefModule], + main: String + ): Unit = { + val costream = CodedOutputStream.newInstance(ostream) + + // Write each module for the circuit + val ostreamInner = new java.io.ByteArrayOutputStream() + val costreamInner = CodedOutputStream.newInstance(ostreamInner) + for (mod <- modules) { + costreamInner.writeMessage(Firrtl.Circuit.MODULE_FIELD_NUMBER, convert(mod()).build) + } + val top = Firrtl.Top.newBuilder().setName(main).build + costreamInner.writeMessage(Firrtl.Circuit.TOP_FIELD_NUMBER, top) + + // Write Circuit header first + costream.writeTag(Firrtl.CIRCUIT_FIELD_NUMBER, WireFormat.WIRETYPE_LENGTH_DELIMITED) + costream.writeUInt32NoTag(costreamInner.getTotalBytesWritten) + costream.flush() + + // Write Modules + costreamInner.flush() + ostreamInner.writeTo(ostream) + ostreamInner.flush() + } + + val convert: Map[ir.PrimOp, Op] = Map( + Add -> Op.OP_ADD, + Sub -> Op.OP_SUB, + Mul -> Op.OP_TIMES, + Div -> Op.OP_DIVIDE, + Rem -> Op.OP_REM, + Lt -> Op.OP_LESS, + Leq -> Op.OP_LESS_EQ, + Gt -> Op.OP_GREATER, + Geq -> Op.OP_GREATER_EQ, + Eq -> Op.OP_EQUAL, + Neq -> Op.OP_NOT_EQUAL, + Pad -> Op.OP_PAD, + AsUInt -> Op.OP_AS_UINT, + AsSInt -> Op.OP_AS_SINT, + AsClock -> Op.OP_AS_CLOCK, + AsFixedPoint -> Op.OP_AS_FIXED_POINT, + Shl -> Op.OP_SHIFT_LEFT, + Shr -> Op.OP_SHIFT_RIGHT, + Dshl -> Op.OP_DYNAMIC_SHIFT_LEFT, + Dshr -> Op.OP_DYNAMIC_SHIFT_RIGHT, + Cvt -> Op.OP_CONVERT, + Neg -> Op.OP_NEG, + Not -> Op.OP_BIT_NOT, + And -> Op.OP_BIT_AND, + Or -> Op.OP_BIT_OR, + Xor -> Op.OP_BIT_XOR, + Andr -> Op.OP_AND_REDUCE, + Orr -> Op.OP_OR_REDUCE, + Xorr -> Op.OP_XOR_REDUCE, + Cat -> Op.OP_CONCAT, + Bits -> Op.OP_EXTRACT_BITS, + Head -> Op.OP_HEAD, + Tail -> Op.OP_TAIL, + BPShl -> Op.OP_SHIFT_BINARY_POINT_LEFT, + BPShr -> Op.OP_SHIFT_BINARY_POINT_RIGHT, + BPSet -> Op.OP_SET_BINARY_POINT + ) + + def convertToIntegerLiteral(value: BigInt): Firrtl.Expression.IntegerLiteral.Builder = { + Firrtl.Expression.IntegerLiteral.newBuilder() + .setValue(value.toString) + } + + def convertToBigInt(value: BigInt): Firrtl.BigInt.Builder = { + Firrtl.BigInt.newBuilder() + .setValue(com.google.protobuf.ByteString.copyFrom(value.toByteArray)) + } + + def convert(info: ir.Info): Firrtl.SourceInfo.Builder = { + val ib = Firrtl.SourceInfo.newBuilder() + info match { + case ir.NoInfo => + ib.setNone(Firrtl.SourceInfo.None.newBuilder) + case ir.FileInfo(ir.StringLit(text)) => + ib.setText(text) + // TODO properly implement MultiInfo + case ir.MultiInfo(infos) => + val x = if (infos.nonEmpty) infos.head else ir.NoInfo + convert(x) + } + } + + def convert(expr: ir.Expression): Firrtl.Expression.Builder = { + val eb = Firrtl.Expression.newBuilder() + expr match { + case ir.Reference(name, _) => + val rb = Firrtl.Expression.Reference.newBuilder() + .setId(name) + eb.setReference(rb) + case ir.SubField(e, name, _) => + val sb = Firrtl.Expression.SubField.newBuilder() + .setExpression(convert(e)) + .setField(name) + eb.setSubField(sb) + case ir.SubIndex(e, value, _) => + val sb = Firrtl.Expression.SubIndex.newBuilder() + .setExpression(convert(e)) + .setIndex(convertToIntegerLiteral(value)) + eb.setSubIndex(sb) + case ir.SubAccess(e, index, _) => + val sb = Firrtl.Expression.SubAccess.newBuilder() + .setExpression(convert(e)) + .setIndex(convert(index)) + eb.setSubAccess(sb) + case ir.UIntLiteral(value, width) => + val ub = Firrtl.Expression.UIntLiteral.newBuilder() + .setValue(convertToIntegerLiteral(value)) + convert(width).foreach(ub.setWidth) + eb.setUintLiteral(ub) + case ir.SIntLiteral(value, width) => + val sb = Firrtl.Expression.SIntLiteral.newBuilder() + .setValue(convertToIntegerLiteral(value)) + convert(width).foreach(sb.setWidth) + eb.setSintLiteral(sb) + case ir.FixedLiteral(value, width, point) => + val fb = Firrtl.Expression.FixedLiteral.newBuilder() + .setValue(convertToBigInt(value)) + convert(width).foreach(fb.setWidth) + convert(point).foreach(fb.setPoint) + eb.setFixedLiteral(fb) + case ir.DoPrim(op, args, consts, _) => + val db = Firrtl.Expression.PrimOp.newBuilder() + .setOp(convert(op)) + consts.foreach(c => db.addConst(convertToIntegerLiteral(c))) + args.foreach(a => db.addArg(convert(a))) + eb.setPrimOp(db) + case ir.Mux(cond, tval, fval, _) => + val mb = Firrtl.Expression.Mux.newBuilder() + .setCondition(convert(cond)) + .setTValue(convert(tval)) + .setFValue(convert(fval)) + eb.setMux(mb) + } + } + + def convert(dir: MPortDir): Firrtl.Statement.MemoryPort.Direction = { + import Firrtl.Statement.MemoryPort.Direction._ + dir match { + case MInfer => MEMORY_PORT_DIRECTION_INFER + case MRead => MEMORY_PORT_DIRECTION_READ + case MWrite => MEMORY_PORT_DIRECTION_WRITE + case MReadWrite => MEMORY_PORT_DIRECTION_READ_WRITE + } + } + + def convert(stmt: ir.Statement): Seq[Firrtl.Statement.Builder] = { + stmt match { + case ir.Block(stmts) => stmts.flatMap(convert(_)) + case ir.EmptyStmt => Seq.empty + case other => + val sb = Firrtl.Statement.newBuilder() + other match { + case ir.DefNode(_, name, expr) => + val nb = Firrtl.Statement.Node.newBuilder() + .setId(name) + .setExpression(convert(expr)) + sb.setNode(nb) + case ir.DefWire(_, name, tpe) => + val wb = Firrtl.Statement.Wire.newBuilder() + .setId(name) + .setType(convert(tpe)) + sb.setWire(wb) + case ir.DefRegister(_, name, tpe, clock, reset, init) => + val rb = Firrtl.Statement.Register.newBuilder() + .setId(name) + .setType(convert(tpe)) + .setClock(convert(clock)) + .setReset(convert(reset)) + .setInit(convert(init)) + sb.setRegister(rb) + case ir.DefInstance(_, name, module) => + val ib = Firrtl.Statement.Instance.newBuilder() + .setId(name) + .setModuleId(module) + sb.setInstance(ib) + case ir.Connect(_, loc, expr) => + val cb = Firrtl.Statement.Connect.newBuilder() + .setLocation(convert(loc)) + .setExpression(convert(expr)) + sb.setConnect(cb) + case ir.PartialConnect(_, loc, expr) => + val cb = Firrtl.Statement.PartialConnect.newBuilder() + .setLocation(convert(loc)) + .setExpression(convert(expr)) + sb.setPartialConnect(cb) + case ir.Conditionally(_, pred, conseq, alt) => + val cs = convert(conseq) + val as = convert(alt) + val wb = Firrtl.Statement.When.newBuilder() + .setPredicate(convert(pred)) + cs.foreach(wb.addConsequent) + as.foreach(wb.addOtherwise) + sb.setWhen(wb) + case ir.Print(_, string, args, clk, en) => + val pb = Firrtl.Statement.Printf.newBuilder() + .setValue(string.string) + .setClk(convert(clk)) + .setEn(convert(en)) + args.foreach(a => pb.addArg(convert(a))) + sb.setPrintf(pb) + case ir.Stop(_, ret, clk, en) => + val stopb = Firrtl.Statement.Stop.newBuilder() + .setReturnValue(ret) + .setClk(convert(clk)) + .setEn(convert(en)) + sb.setStop(stopb) + case ir.IsInvalid(_, expr) => + val ib = Firrtl.Statement.IsInvalid.newBuilder() + .setExpression(convert(expr)) + sb.setIsInvalid(ib) + case ir.DefMemory(_, name, dtype, depth, wlat, rlat, rs, ws, rws, _) => + val mem = Firrtl.Statement.Memory.newBuilder() + .setId(name) + .setType(convert(dtype)) + .setDepth(depth) + .setWriteLatency(wlat) + .setReadLatency(rlat) + mem.addAllReaderId(rs.asJava) + mem.addAllWriterId(ws.asJava) + mem.addAllReadwriterId(rws.asJava) + sb.setMemory(mem) + case CDefMemory(_, name, tpe, size, seq) => + val tpeb = convert(ir.VectorType(tpe, size)) + val mb = Firrtl.Statement.CMemory.newBuilder() + .setId(name) + .setType(tpeb) + .setSyncRead(seq) + sb.setCmemory(mb) + case CDefMPort(_, name, _, mem, exprs, dir) => + val pb = Firrtl.Statement.MemoryPort.newBuilder() + .setId(name) + .setMemoryId(mem) + .setMemoryIndex(convert(exprs.head)) + .setExpression(convert(exprs(1))) + .setDirection(convert(dir)) + sb.setMemoryPort(pb) + case ir.Attach(_, exprs) => + val ab = Firrtl.Statement.Attach.newBuilder() + exprs.foreach(e => ab.addExpression(convert(e))) + sb.setAttach(ab) + } + stmt match { + case hasInfo: ir.HasInfo => sb.setSourceInfo(convert(hasInfo.info)) + case _ => // Do nothing + } + Seq(sb) + } + } + + def convert(field: ir.Field): Firrtl.Type.BundleType.Field.Builder = { + val b = Firrtl.Type.BundleType.Field.newBuilder() + .setId(field.name) + .setIsFlipped(field.flip == ir.Flip) + .setType(convert(field.tpe)) + b + } + + /** Converts a Width to a ProtoBuf Width Builder + * + * @param width Input width + * @return Option width where None means the width field should be cleared in the parent object + */ + def convert(width: ir.Width): Option[Firrtl.Width.Builder] = width match { + case ir.IntWidth(w) => Some(Firrtl.Width.newBuilder().setValue(w.toInt)) + case ir.UnknownWidth => None + } + + def convert(vtpe: ir.VectorType): Firrtl.Type.VectorType.Builder = + Firrtl.Type.VectorType.newBuilder() + .setType(convert(vtpe.tpe)) + .setSize(vtpe.size) + + def convert(tpe: ir.Type): Firrtl.Type.Builder = { + val tb = Firrtl.Type.newBuilder() + tpe match { + case ir.UIntType(width) => + val ut = Firrtl.Type.UIntType.newBuilder() + convert(width).foreach(ut.setWidth) + tb.setUintType(ut) + case ir.SIntType(width) => + val st = Firrtl.Type.SIntType.newBuilder() + convert(width).foreach(st.setWidth) + tb.setSintType(st) + case ir.FixedType(width, point) => + val ft = Firrtl.Type.FixedType.newBuilder() + convert(width).foreach(ft.setWidth) + convert(point).foreach(ft.setPoint) + tb.setFixedType(ft) + case ir.ClockType => + val ct = Firrtl.Type.ClockType.newBuilder() + tb.setClockType(ct) + case ir.AnalogType(width) => + val at = Firrtl.Type.AnalogType.newBuilder() + convert(width).foreach(at.setWidth) + tb.setAnalogType(at) + case ir.BundleType(fields) => + val bt = Firrtl.Type.BundleType.newBuilder() + fields.foreach(f => bt.addField(convert(f))) + tb.setBundleType(bt) + case vtpe: ir.VectorType => + val vtb = convert(vtpe) + tb.setVectorType(vtb) + } + } + + def convert(direction: ir.Direction): Firrtl.Port.Direction = direction match { + case ir.Input => Firrtl.Port.Direction.PORT_DIRECTION_IN + case ir.Output => Firrtl.Port.Direction.PORT_DIRECTION_OUT + } + + def convert(port: ir.Port): Firrtl.Port.Builder = { + Firrtl.Port.newBuilder() + .setId(port.name) + .setDirection(convert(port.direction)) + .setType(convert(port.tpe)) + } + + def convert(param: ir.Param): Firrtl.Module.ExternalModule.Parameter.Builder = { + import Firrtl.Module.ExternalModule._ + val pb = Parameter.newBuilder() + .setId(param.name) + param match { + case ir.IntParam(_, value) => + pb.setInteger(convertToBigInt(value)) + case ir.DoubleParam(_, value) => + pb.setDouble(value) + case ir.StringParam(_, ir.StringLit(value)) => + pb.setString(value) + case ir.RawStringParam(_, value) => + pb.setRawString(value) + } + } + + def convert(module: ir.DefModule): Firrtl.Module.Builder = { + val ports = module.ports.map(convert(_)) + val b = Firrtl.Module.newBuilder() + module match { + case mod: ir.Module => + val stmts = convert(mod.body) + val mb = Firrtl.Module.UserModule.newBuilder() + .setId(mod.name) + ports.foreach(mb.addPort) + stmts.foreach(mb.addStatement) + b.setUserModule(mb) + case ext: ir.ExtModule => + val eb = Firrtl.Module.ExternalModule.newBuilder() + .setId(ext.name) + .setDefinedName(ext.defname) + ports.foreach(eb.addPort) + val params = ext.params.map(convert(_)) + params.foreach(eb.addParameter) + b.setExternalModule(eb) + } + } + + def convert(circuit: ir.Circuit): Firrtl = { + val moduleBuilders = circuit.modules.map(convert(_)) + val cb = Firrtl.Circuit.newBuilder + .addTop(Firrtl.Top.newBuilder().setName(circuit.main)) + for (m <- moduleBuilders) { + cb.addModule(m) + } + Firrtl.newBuilder() + .addCircuit(cb.build()) + .build() + } +} diff --git a/src/test/scala/firrtlTests/DriverSpec.scala b/src/test/scala/firrtlTests/DriverSpec.scala index 4afd5674..4cc7bc90 100644 --- a/src/test/scala/firrtlTests/DriverSpec.scala +++ b/src/test/scala/firrtlTests/DriverSpec.scala @@ -2,22 +2,18 @@ package firrtlTests -import java.io.{File, FileWriter} -import org.scalatest.{FreeSpec, Matchers} +import java.io.{File, FileInputStream, FileWriter} +import org.scalatest.{FreeSpec, Matchers} import firrtl.passes.{InlineAnnotation, InlineInstances} -import firrtl.passes.memlib.{ - InferReadWrite, - InferReadWriteAnnotation, - ReplSeqMem, - ReplSeqMemAnnotation -} +import firrtl.passes.memlib.{InferReadWrite, InferReadWriteAnnotation, ReplSeqMem, ReplSeqMemAnnotation} import firrtl.transforms.BlackBoxTargetDirAnno import firrtl._ import firrtl.annotations._ import firrtl.util.BackendCompilationUtilities -import scala.util.{Try, Success, Failure} +import scala.io.Source +import scala.util.{Failure, Success, Try} class ExceptingTransform extends Transform { def inputForm = HighForm @@ -416,6 +412,28 @@ class DriverSpec extends FreeSpec with Matchers with BackendCompilationUtilities } } + "The Driver is sensitive to the file extension of input files" - { + val design = "GCDTester" + val outputDir = createTestDirectory("DriverFileExtensionSensitivity") + val verilogFromFir = new File(outputDir, s"$design.fromfir.v") + val verilogFromPb = new File(outputDir, s"$design.frompb.v") + val commonArgs = Array("-X", "verilog", "--info-mode", "use") + ".fir means FIRRTL file" in { + val inFile = new File(getClass.getResource(s"/integration/$design.fir").getFile) + val args = Array("-i", inFile.getAbsolutePath, "-o", verilogFromFir.getAbsolutePath) ++ commonArgs + Driver.execute(args) + } + ".pb means ProtoBuf file" in { + val inFile = new File(getClass.getResource(s"/integration/$design.pb").getFile) + val args = Array("-i", inFile.getAbsolutePath, "-o", verilogFromPb.getAbsolutePath) ++ commonArgs + Driver.execute(args) + } + "Both paths do the same thing" in { + val s1 = Source.fromFile(verilogFromFir).mkString + val s2 = Source.fromFile(verilogFromPb).mkString + s1 should equal (s2) + } + } "Directory deleter is handy for cleaning up after tests" - { "for example making a directory tree, and deleting it looks like" in { diff --git a/src/test/scala/firrtlTests/FirrtlSpec.scala b/src/test/scala/firrtlTests/FirrtlSpec.scala index 861d1745..01ae0431 100644 --- a/src/test/scala/firrtlTests/FirrtlSpec.scala +++ b/src/test/scala/firrtlTests/FirrtlSpec.scala @@ -101,6 +101,12 @@ trait FirrtlMatchers extends Matchers { require(!s.contains("\n")) s.replaceAll("\\s+", " ").trim } + /** Helper to make circuits that are the same appear the same */ + def canonicalize(circuit: Circuit): Circuit = { + import firrtl.Mappers._ + def onModule(mod: DefModule) = mod.map(firrtl.Utils.squashEmpty) + circuit.map(onModule) + } def parse(str: String) = Parser.parse(str.split("\n").toIterator, UseInfo) /** Helper for executing tests * compiler will be run on input then emitted result will each be split into diff --git a/src/test/scala/firrtlTests/ProtoBufSpec.scala b/src/test/scala/firrtlTests/ProtoBufSpec.scala new file mode 100644 index 00000000..2a60eab5 --- /dev/null +++ b/src/test/scala/firrtlTests/ProtoBufSpec.scala @@ -0,0 +1,140 @@ +// See LICENSE for license details. + +package firrtlTests + +import java.io.{ByteArrayInputStream, ByteArrayOutputStream} + +import firrtl.FirrtlProtos.Firrtl +import firrtl._ +import firrtl.ir._ +import firrtl.Mappers._ + +class ProtoBufSpec extends FirrtlFlatSpec { + + /** Tests in src/test/resource/ in .fir format + * + * @note These tests rely on the ANTLR Parser + */ + case class FirrtlResourceTest(name: String, resourceDir: String) + + val firrtlResourceTests = List( + FirrtlResourceTest("GCDTester", "/integration"), + FirrtlResourceTest("RightShiftTester", "/integration"), + FirrtlResourceTest("MemTester", "/integration"), + FirrtlResourceTest("PipeTester", "/integration"), + FirrtlResourceTest("Rob", "/regress"), + FirrtlResourceTest("RocketCore", "/regress"), + FirrtlResourceTest("ICache", "/regress"), + FirrtlResourceTest("FPU", "/regress") + ) + + for (FirrtlResourceTest(name, dir) <- firrtlResourceTests) { + s"$name" should "work with Protobuf serialization and deserialization" in { + val stream = getClass.getResourceAsStream(s"$dir/$name.fir") + val circuit = parse(scala.io.Source.fromInputStream(stream).getLines.mkString("\n")) + + // Test ToProto and FromProto + val protobuf = proto.ToProto.convert(circuit) + val pcircuit = proto.FromProto.convert(protobuf) + canonicalize(circuit).serialize should equal(canonicalize(pcircuit).serialize) + + // Test ProtoBuf generated serialization and deserialization + val ostream = new java.io.ByteArrayOutputStream() + protobuf.writeTo(ostream) + val istream = new java.io.ByteArrayInputStream(ostream.toByteArray) + val cistream = com.google.protobuf.CodedInputStream.newInstance(istream) + cistream.setRecursionLimit(Integer.MAX_VALUE) + val protobuf2 = firrtl.FirrtlProtos.Firrtl.parseFrom(cistream) + protobuf2 should equal (protobuf) + + // Test that our faster serialization matches generated serialization + val ostream2 = new java.io.ByteArrayOutputStream + proto.ToProto.writeToStream(ostream2, circuit) + ostream2.toByteArray.toList should equal (ostream.toByteArray.toList) + } + } + + // ********** Focused Tests ********** + // The goal is to fill coverage holes left after the above + + behavior of "ProtoBuf serialization and deserialization" + import firrtl.proto._ + + it should "support UnknownWidth" in { + // Note that this has to be handled in the parent object so we need to test everything that has a width + val uint = ir.UIntType(ir.UnknownWidth) + FromProto.convert(ToProto.convert(uint).build) should equal (uint) + + val sint = ir.SIntType(ir.UnknownWidth) + FromProto.convert(ToProto.convert(sint).build) should equal (sint) + + val ftpe = ir.FixedType(ir.UnknownWidth, ir.UnknownWidth) + FromProto.convert(ToProto.convert(ftpe).build) should equal (ftpe) + + val atpe = ir.AnalogType(ir.UnknownWidth) + FromProto.convert(ToProto.convert(atpe).build) should equal (atpe) + + val ulit = ir.UIntLiteral(123, ir.UnknownWidth) + FromProto.convert(ToProto.convert(ulit).build) should equal (ulit) + + val slit = ir.SIntLiteral(-123, ir.UnknownWidth) + FromProto.convert(ToProto.convert(slit).build) should equal (slit) + + val flit = ir.FixedLiteral(-123, ir.UnknownWidth, ir.UnknownWidth) + FromProto.convert(ToProto.convert(flit).build) should equal (flit) + } + + it should "support all Primops" in { + val builtInOps = PrimOps.listing.map(PrimOps.fromString(_)) + for (op <- builtInOps) { + val expr = DoPrim(op, List.empty, List.empty, ir.UnknownType) + FromProto.convert(ToProto.convert(expr).build) should equal (expr) + } + } + + it should "support all ExtModule features (except info which isn't yet supported by Chisel)" in { + val ports = Seq( + Port(ir.NoInfo, "port1", ir.Input, ir.UIntType(ir.IntWidth(8))), + Port(ir.NoInfo, "port2", ir.Output, ir.SIntType(ir.IntWidth(8))) + ) + val params = Seq( + IntParam("param1", BigInt(Long.MinValue)), + DoubleParam("param2", Double.NegativeInfinity), + StringParam("param3", ir.StringLit("quite the string!")), + RawStringParam("param4", "get some raw strings") + ) + val ext = ir.ExtModule(ir.NoInfo, "MyModule", ports, "DefNameHere", params) + FromProto.convert(ToProto.convert(ext).build) should equal (ext) + } + + it should "supported FixedType" in { + val ftpe = ir.FixedType(IntWidth(8), IntWidth(4)) + FromProto.convert(ToProto.convert(ftpe).build) should equal (ftpe) + } + + it should "supported FixedLiteral" in { + val flit = ir.FixedLiteral(3, IntWidth(8), IntWidth(4)) + FromProto.convert(ToProto.convert(flit).build) should equal (flit) + } + + it should "support Analog and Attach" in { + val analog = ir.AnalogType(IntWidth(8)) + FromProto.convert(ToProto.convert(analog).build) should equal (analog) + + val attach = ir.Attach(ir.NoInfo, Seq(Reference("hi", ir.UnknownType))) + FromProto.convert(ToProto.convert(attach).head.build) should equal (attach) + } + + // Regression tests were generated before Chisel could emit else + it should "support whens with elses" in { + val expr = Reference("hi", ir.UnknownType) + val stmt = Connect(ir.NoInfo, expr, expr) + val when = ir.Conditionally(ir.NoInfo, expr, stmt, stmt) + FromProto.convert(ToProto.convert(when).head.build) should equal (when) + } + + it should "support SIntLiteral with a width" in { + val slit = ir.SIntLiteral(-123) + FromProto.convert(ToProto.convert(slit).build) should equal (slit) + } +} diff --git a/test/integration/GCDTester.pb b/test/integration/GCDTester.pb Binary files differnew file mode 100644 index 00000000..fc7c2ba4 --- /dev/null +++ b/test/integration/GCDTester.pb |
