FIRでAsync Resetの作り方が分かったので、練習問題としてNegative EdgeのAsynchronous Resetを作ってみようかと思う。
Async Resetは専用の型が定義されていたので、同様にNegative EdgeのAsync Resetの型を定義してみる。
firrtl/src/main/scala/firrtl/ir/IR.scala
case object AsyncResetNType extends GroundType { val width = IntWidth(1) def serialize: String = "AsyncResetN" def mapWidth(f: Width => Width): Type = this def foreachWidth(f: Width => Unit): Unit = Unit }
AsyncResetType
に対して、AsyncResetNType
を定義した。これはAsyncReset同様に、AsyncResetNとして使用する。
circuit SimpleCircuit : module SimpleCircuit : ... input arstn : AsyncResetN
FIRRTL.g4も改造して、AsyncResetN
を扱えるように変更した。
src/main/antlr4/FIRRTL.g4
diff --git a/src/main/antlr4/FIRRTL.g4 b/src/main/antlr4/FIRRTL.g4 index be15ab7c..269e101c 100644 --- a/src/main/antlr4/FIRRTL.g4 +++ b/src/main/antlr4/FIRRTL.g4 @@ -52,6 +52,7 @@ type | 'Fixed' ('<' intLit '>')? ('<' '<' intLit '>' '>')? | 'Clock' | 'AsyncReset' + | 'AsyncResetN' | 'Reset' | 'Analog' ('<' intLit '>')? | '{' field* '}' // Bundle @@ -251,6 +252,7 @@ primop | 'pad(' | 'asUInt(' | 'asAsyncReset(' + | 'asAsyncResetN(' | 'asSInt(' | 'asClock(' | 'shl('
色々と実装を追加しなければならないのだが、Verilogのalways
ルーチンを生成する仕組みは、
- FFが定義されているとそれを3種類に分類する。
- Syncronous Resetのタイプ
- Asynchronous Reset(Posedge)のタイプ
- Asynchronous Reset(Negedge)のタイプ
always
文で囲んだルーチンを生成し、そこに上記のFFごとに実装を貼り付けていく。- Reset信号による初期化ルーチンは、上記の分類作業中に最後にMuxで挿入してくと、Verilog生成時に先頭に生成されるようになる。
ではまずは分類するルーチンを改造していく。
firrtl/src/main/scala/firrtl/SystemVerilogEmitter.scala
def regUpdate(r: Expression, clk: Expression, reset: Expression, init: Expression) = { def addUpdate(expr: Expression, tabs: String): Seq[Seq[Any]] = expr match { ... if (weq(init, r)) { // Synchronous Reset noResetAlwaysBlocks.getOrElseUpdate(clk, ArrayBuffer[Seq[Any]]()) ++= addUpdate(netlist(r), "") } else { // Asynchronous Reset assert(reset.tpe == AsyncResetType || reset.tpe == AsyncResetNType, "Error! Synchronous reset should have been removed!") val tv = init val fv = netlist(r) if (reset.tpe == AsyncResetType) { asyncResetAlwaysBlocks += ((clk, reset, addUpdate(Mux(reset, tv, fv, mux_type_and_widths(tv, fv)), ""))) } else { asyncResetNAlwaysBlocks += ((clk, reset, addUpdate(Mux(DoPrim(Not, Seq(reset), Nil, BoolType), tv, fv, mux_type_and_widths(tv, fv)), ""))) } }
この実装を見つけだすまでに結構な時間がかかったのだが、要するにSynchronous Resetの時はnoResetAlwaysBlocks
に代入し、Asynchronous Reset(Posedge)の場合はasyncResetAlwaysBlocks
にFF動作を代入しておき、Asynchronous Reset(Negedge)の場合はasyncResetNAlwaysBlocks
に代入するという仕組みである。
Negative EdgeのAsyncResetを作るためにかなり苦労したのだが、
if (!reset) begin
という文法を生成するためにどうやったかというと、Muxの条件の所を単純なreset
信号ではなく、DoPrim
を使って演算子を挿入している。
asyncResetNAlwaysBlocks += ((clk, reset, addUpdate(Mux(DoPrim(Not, Seq(reset), Nil, BoolType), tv, fv, mux_type_and_widths(tv, fv)), "")))
そして、最後にVerilogを生成するにあたりこの3種類で生成Verilogルーチンを切り分ける。
firrtl/src/main/scala/firrtl/SystemVerilogEmitter.scala
def emit_streams(): Unit = { description match { ... for ((clk, content) <- noResetAlwaysBlocks if content.nonEmpty) { emit(Seq(tab, "always_ff @(posedge ", clk, ") begin")) for (line <- content) emit(Seq(tab, tab, line)) emit(Seq(tab, "end")) } for ((clk, reset, content) <- asyncResetAlwaysBlocks if content.nonEmpty) { emit(Seq(tab, "always_ff @(posedge ", clk, ", posedge ", reset, ") begin")) for (line <- content) emit(Seq(tab, tab, line)) emit(Seq(tab, "end")) } for ((clk, reset, content) <- asyncResetNAlwaysBlocks if content.nonEmpty) { emit(Seq(tab, "always_ff @(posedge ", clk, ", negedge ", reset, ") begin")) for (line <- content) emit(Seq(tab, tab, line)) emit(Seq(tab, "end")) }
それ以外にも少しずつ改造しておりやっとこの形になっている。生成時のAssertionのコードとか改造したのだが、おおむね上記のコードで行ける。
テストコードを流してみる。
circuit SimpleCircuit : module SimpleCircuit : input clk : Clock input rst : UInt<1> input arst : AsyncReset input arstn : AsyncResetN ... reg myreg3 : UInt<32>, clk with : (reset => (arstn, UInt<32>("h0"))) when en : myreg3 <= io.in skip when clr : myreg3 <= UInt<32>("h1") skip myreg3_out <= myreg3
これでどうなるか。
module SimpleCircuit( input logic clk, input logic rst, input logic arst, input logic arstn, ... always_ff @(posedge clk, negedge arstn) begin if (~ arstn) begin myreg3 <= 32'h0; end else if (clr) begin myreg3 <= 32'h1; end else if (en) begin myreg3 <= io_in; end end endmodule
想定通りのコードが生成された。よさそうだ。