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
一応比較してみたが、あまり大きな違いは無かった。生成される行の位置が少し違うくらいかなあ。