FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

FIRRTLに入門する (12. 配列参照のコードを残したままVerilogを出したい2)

https://raw.githubusercontent.com/freechipsproject/firrtl/master/doc/images/firrtl_logo.svg?sanitize=true

Chiselで配列を出力するために試行錯誤してみる。 まず、試行する中で余計な処理をするコードを片っ端から省いていく。ここは最適化に影響しそうなところだが、とりあえず無視。

diff --git a/src/main/scala/firrtl/Emitter.scala b/src/main/scala/firrtl/Emitter.scala
index 42101e4e..d9d29295 100644
--- a/src/main/scala/firrtl/Emitter.scala
+++ b/src/main/scala/firrtl/Emitter.scala
@@ -960,7 +971,6 @@ class VerilogEmitter extends SeqTransform with Emitter {
     new BlackBoxSourceHelper,
     new ReplaceTruncatingArithmetic,
     new FlattenRegUpdate,
-    new DeadCodeElimination,
     passes.VerilogModulusCleanup,
     new VerilogRename,
     passes.VerilogPrep,
diff --git a/src/main/scala/firrtl/LoweringCompilers.scala b/src/main/scala/firrtl/LoweringCompilers.scala
index 05cdbe96..4af78394 100644
--- a/src/main/scala/firrtl/LoweringCompilers.scala
+++ b/src/main/scala/firrtl/LoweringCompilers.scala
@@ -62,7 +62,6 @@ class HighFirrtlToMiddleFirrtl extends CoreTransform {
     passes.PullMuxes,
     passes.ReplaceAccesses,
     passes.ExpandConnects,
-    passes.RemoveAccesses,
     passes.Uniquify,
     passes.ExpandWhens,
     passes.CheckInitialization,
@@ -114,8 +113,7 @@ class LowFirrtlOptimization extends CoreTransform {
     new firrtl.transforms.ConstantPropagation,
     passes.SplitExpressions,
     new firrtl.transforms.CombineCats,
-    passes.CommonSubexpressionElimination,
-    new firrtl.transforms.DeadCodeElimination)
+    passes.CommonSubexpressionElimination)
 }
 /** Runs runs only the optimization passes needed for Verilog emission */
 class MinimumLowFirrtlOptimization extends CoreTransform {

次に、配列参照のコードを出力するために、stringifyを拡張する。

diff --git a/src/main/scala/firrtl/SystemVerilogEmitter.scala b/src/main/scala/firrtl/SystemVerilogEmitter.scala
index 478f2bd2..46cb95c7 100644
--- a/src/main/scala/firrtl/SystemVerilogEmitter.scala
+++ b/src/main/scala/firrtl/SystemVerilogEmitter.scala
@@ -266,6 +266,7 @@ class SystemVerilogEmitter extends VerilogEmitter with Emitter {
       // Turn types into strings, all ports must be GroundTypes
       val tpes = m.ports map {
         case Port(_, _, _, tpe: GroundType) => stringify(tpe)
+        case Port(_, _, _, tpe: VectorType) => stringify(tpe)
         case port: Port => error(s"Trying to emit non-GroundType Port $port")
       }

ここのコード変換がミソになる。VectorTypeに使用する型を_: UIntType | _: SIntType | _: AnalogTypeであれば、通常通り信号を生成し、そのあとに[${tpe.size}]を接続することができる。これで、2次元配列を生成できる。

diff --git a/src/main/scala/firrtl/Emitter.scala b/src/main/scala/firrtl/Emitter.scala
index 42101e4e..d9d29295 100644
--- a/src/main/scala/firrtl/Emitter.scala
+++ b/src/main/scala/firrtl/Emitter.scala
@@ -219,6 +219,17 @@ class VerilogEmitter extends SeqTransform with Emitter {
     case ClockType | AsyncResetType | AsyncResetNType => ""
     case _ => throwInternalError(s"trying to write unsupported type in the Verilog Emitter: $tpe")
   }
+  def stringify(tpe: VectorType): String = {
+    val ground_type = tpe.tpe
+    val elem_type = ground_type match {
+      case (_: UIntType | _: SIntType | _: AnalogType) =>
+        val wx = bitWidth(tpe) - 1
+        if (wx > 0) s"[$wx:0]" else ""
+      case ClockType | AsyncResetType | AsyncResetNType => ""
+      case _ => throwInternalError(s"trying to write unsupported type in the Verilog Emitter: $tpe")
+    }
+    elem_type + s"[${tpe.size}]"
+  }
   def emit(x: Any)(implicit w: Writer): Unit = { emit(x, 0) }
   def emit(x: Any, top: Int)(implicit w: Writer): Unit = {

配列のアクセスに関しては、lowerNameで一度配列名だけ文字列に変換しておき、VectorTypeの時はそれを無理に分解せずにそのまま落とし込む。

diff --git a/src/main/scala/firrtl/passes/LowerTypes.scala b/src/main/scala/firrtl/passes/LowerTypes.scala
index f52e1e6b..0ee6b5e2 100644
--- a/src/main/scala/firrtl/passes/LowerTypes.scala
+++ b/src/main/scala/firrtl/passes/LowerTypes.scala
@@ -36,6 +36,8 @@ object LowerTypes extends Transform {
     case e: WRef => e.name
     case e: WSubField => s"${loweredName(e.expr)}$delim${e.name}"
     case e: WSubIndex => s"${loweredName(e.expr)}$delim${e.value}"
+    case e: WSubAccess => s"${loweredName(e.expr)}"
   }
   def loweredName(s: Seq[String]): String = s mkString delim
   def renameExps(renames: RenameMap, n: String, t: Type, root: String): Seq[String] =
@@ -52,11 +54,10 @@ object LowerTypes extends Transform {
       renames.rename(root + e.serialize, subNames)
       subNames
     }
-    case (t: VectorType) => (0 until t.size).flatMap { i =>
-      val subNames = renameExps(renames, WSubIndex(e, i, t.tpe,flow(e)), root)
-      renames.rename(root + e.serialize, subNames)
-      subNames
-    }
+    case (t: VectorType) =>
+      val name = root + loweredName(e)
+      Seq(name)
   }
@@ -139,6 +140,7 @@ object LowerTypes extends Transform {
   def lowerTypesExp(memDataTypeMap: MemDataTypeMap,
       info: Info, mname: String)(e: Expression): Expression = e match {
     case e: WRef => e
+    case e: WSubAccess => e map lowerTypesExp(memDataTypeMap, info, mname)
     case (_: WSubField | _: WSubIndex) => kind(e) match {
       case InstanceKind =>
         val (root, tail) = splitRef(e)

これでテストコードを流してみた。

circuit VecModules :
  module VecModules :
    input in: UInt<1>[4]
    input sel: UInt<2>
    output out : UInt<1>
    out <= in[sel]

生成コードは以下のようになる。配列アクセスが生成された!

module VecModules(
  input logic  [3:0][4] in,
  input logic  [1:0]    sel,
  output logic          out
);
  assign out = in[sel];
endmodule