FIRRTLのコンパイルオプション・というか生成ファイルオプションには以下が存在する。
- none :
NoneCompiler()
を呼び出す。特に何もしない。 - hi :
HighFirrtlCompiler()
を呼び出す。 - low :
LowFirrtlCompiler()
を呼び出す。 - middle :
MiddleFirrtlCompiler()
を呼び出す。High, Low, Middle
の違いは良く分からない。一応生成されるコードが違う? - verilog :
VerilogCompiler()
を呼び出す。Verilogコードを生成する。 - mverilog :
MinimumVerilogCompiler()
を呼び出す。最適化されていないVerilogコードを生成する。DeadCodeElimination
が適用されない。 - sverilog :
SystemVerilogCompiler()
を呼び出す。実体はVerilogCompilerとどういうである。
それぞれ以下のように実装が分かれている。
src/main/scala/firrtl/stage/FirrtlAnnotations.scala
private [firrtl] def apply(compilerName: String): CompilerAnnotation = { val c = compilerName match { case "none" => new NoneCompiler() case "high" => new HighFirrtlCompiler() case "low" => new LowFirrtlCompiler() case "middle" => new MiddleFirrtlCompiler() case "verilog" => new VerilogCompiler() case "mverilog" => new MinimumVerilogCompiler() case "sverilog" => new SystemVerilogCompiler() case _ => throw new OptionsException(s"Unknown compiler name '$compilerName'! (Did you misspell it?)") } CompilerAnnotation(c) }
つまり、HighFirrtlCompiler()
, LowFirrtlCompiler()
, MiddleFirrtlCompiler()
のように、FIRRTLからFIRRTLへの変換のコンパイラも用意されているということだ。
実体はFirrtlEmitter
であり、この引数にあたるform
が変わる。
firrtl/Emitter.scala
sealed abstract class FirrtlEmitter(form: CircuitForm) extends Transform with Emitter { def inputForm = form def outputForm = form val outputSuffix: String = form.outputSuffix ...
firrtl/Compiler.scala
sealed abstract class CircuitForm(private val value: Int) extends Ordered[CircuitForm] { // Note that value is used only to allow comparisons def compare(that: CircuitForm): Int = this.value - that.value /** Defines a suffix to use if this form is written to a file */ def outputSuffix: String } ...
試しに、以下のようなFIRのコードを使ってコンパイル後のコードをチェックしてみる。これはtag_array_ext
のコードである。
tag_array.fir
circuit tag_array_ext : module tag_array_ext : input RW0_clk : Clock input RW0_addr : UInt<6> input RW0_wdata : UInt<80> output RW0_rdata : UInt<80> input RW0_en : UInt<1> input RW0_wmode : UInt<1> input RW0_wmask : UInt<4> inst mem_0_0 of rawr inst mem_0_1 of rawr inst mem_0_2 of rawr inst mem_0_3 of rawr mem_0_0.clk <= RW0_clk mem_0_0.addr <= RW0_addr node RW0_rdata_0_0 = bits(mem_0_0.dout, 19, 0) mem_0_0.din <= bits(RW0_wdata, 19, 0) mem_0_0.write_en <= and(and(RW0_wmode, bits(RW0_wmask, 0, 0)), UInt<1>("h1")) mem_0_1.clk <= RW0_clk mem_0_1.addr <= RW0_addr node RW0_rdata_0_1 = bits(mem_0_1.dout, 19, 0) mem_0_1.din <= bits(RW0_wdata, 39, 20) mem_0_1.write_en <= and(and(RW0_wmode, bits(RW0_wmask, 1, 1)), UInt<1>("h1")) mem_0_2.clk <= RW0_clk mem_0_2.addr <= RW0_addr node RW0_rdata_0_2 = bits(mem_0_2.dout, 19, 0) mem_0_2.din <= bits(RW0_wdata, 59, 40) mem_0_2.write_en <= and(and(RW0_wmode, bits(RW0_wmask, 2, 2)), UInt<1>("h1")) mem_0_3.clk <= RW0_clk mem_0_3.addr <= RW0_addr node RW0_rdata_0_3 = bits(mem_0_3.dout, 19, 0) mem_0_3.din <= bits(RW0_wdata, 79, 60) mem_0_3.write_en <= and(and(RW0_wmode, bits(RW0_wmask, 3, 3)), UInt<1>("h1")) node RW0_rdata_0 = cat(RW0_rdata_0_3, cat(RW0_rdata_0_2, cat(RW0_rdata_0_1, RW0_rdata_0_0))) RW0_rdata <= mux(UInt<1>("h1"), RW0_rdata_0, UInt<1>("h0")) extmodule rawr : input clk : Clock input addr : UInt<6> input din : UInt<32> output dout : UInt<32> input write_en : UInt<1> defname = rawr
./utils/bin/firrtl -td regress -i regress/tag_array.fir -X none ./utils/bin/firrtl -td regress -i regress/tag_array.fir -X high ./utils/bin/firrtl -td regress -i regress/tag_array.fir -X low ./utils/bin/firrtl -td regress -i regress/tag_array.fir -X middle ./utils/bin/firrtl -td regress -i regress/tag_array.fir -X mverilog ./utils/bin/firrtl -td regress -i regress/tag_array.fir -X sverilog
一応比較してみたが、あまり大きな違いは無かった。生成される行の位置が少し違うくらいかなあ。