FPGA開発日記

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

FIRRTLに入門する (7. FIRRTLにSystemVerilogCompilerを追加してみる)

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

現在FIRRTLでは出力オプションに-X sverilogを追加することができ、これは一応SystemVerilog出力用のコンパイラであるのだが、実際に適用してみると、

------------------------------------------------------------------------------
Warning: SystemVerilog Compiler behaves the same as the Verilog Compiler!
------------------------------------------------------------------------------

こんな感じで実はVerilogコンパイラと全く同じものを出してくる。

逆にいえばこのあたりはまだ試しようがあるということで、では本当にSystemVerilogCompilerを追加するためにはどうすればよいのか考えてみる。

ただし、すべてSystemVerilogの機能を適用することはもちろんできなくて、例えばSystemVerilog Interfaceなどは便利な機能ではあるのだが、FIRがそのようなインタフェースはサポートしていないし、Chiselから変換した場合でもFIRに変換された時点でインタフェースは消えてしまっているので適用できない。ここでは、制御構文や、ちょっとしたSystem Verilogっぽい記述を追加するためにはどうすればよいのか考えてみる。

まず、SystemVerilog向けのコンパイラクラスを作る。これは、Verilogコンパイラクラスを適当にコピーして作る。

  • src/main/scala/firrtl/SystemVerilogEmitter.scala
class SystemVerilogEmitter extends VerilogEmitter with Emitter {

  class SystemVerilogRender(description: Description,
                      portDescriptions: Map[String, Description],
                      m: Module,
                      moduleMap: Map[String, DefModule])(implicit writer: Writer) {

    def this(m: Module, moduleMap: Map[String, DefModule])(implicit writer: Writer) {
...

SystemVerilogRenderVerilogRenderをベースに拡張したかったのだが、Scalaの文法が良く分かっていないのでエラーを解消できなかった。仕方がないのでSystemVerilogRenderを作ってVerilogRenderをそのままコピーしてしまった。これでとりあえずSystemVerilogCompiler専用のRenderを作ることができる。

手始めに非常に小さなところから改造してみる。まずは、wireregの記法が非常に古臭いので、格好よくlogicを使ってみることにする。以下のようにSystemVerilogCompilerの記述を変更した。

  • src/main/scala/firrtl/SystemVerilogEmitter.scala
...
      // Turn directions into strings (and AnalogType into inout)
      val dirs = m.ports map { case Port(_, name, dir, tpe) =>
        (dir, tpe) match {
          case (_, AnalogType(_)) => "inout " // padded to length of output
          case (Input, _) => "input logic "
          case (Output, _) => "output logic"
        }
      }
...
...
     withoutDescription match {
        case sx@Connect(info, loc@WRef(_, _, PortKind | WireKind | InstanceKind, _), expr) =>
          assign(loc, expr, info)
        case sx: DefWire =>
          declare("logic", sx.name, sx.tpe, sx.info)
        case sx: DefRegister =>
          declare("logic", sx.name, sx.tpe, sx.info)
          val e = wref(sx.name, sx.tpe)
          regUpdate(e, sx.clock, sx.reset, sx.init)
          initialize(e, sx.reset, sx.init)
        case sx: DefNode =>
          declare("logic", sx.name, sx.value.tpe, sx.info)
          assign(WRef(sx.name, sx.value.tpe, NodeKind, SourceFlow), sx.value, sx.info)
        case sx: Stop =>
...

この状態でビルドしてみる。

sbt assembly
./utils/bin/firrtl -td regress -i regress/tag_array.fir -X sverilog
  • regress/tag_array.sv
module tag_array_ext(
  input logic         RW0_clk,
  input logic  [5:0]  RW0_addr,
  input logic  [79:0] RW0_wdata,
  output logic [79:0] RW0_rdata,
  input logic         RW0_en,
  input logic         RW0_wmode,
  input logic  [3:0]  RW0_wmask
);
  logic  mem_0_0_clk;
  logic [5:0] mem_0_0_addr;
  logic [31:0] mem_0_0_din;
  logic [31:0] mem_0_0_dout;
  logic  mem_0_0_write_en;
  logic  mem_0_1_clk;
  logic [5:0] mem_0_1_addr;
  logic [31:0] mem_0_1_din;
  logic [31:0] mem_0_1_dout;
...

一応、wire, reglogicに変更することができた。これ以外の変更できる点を探していく。