APBのChisel実装を観察してみる。
今回読んだのは、TL2APBというChiselで書かれた実装だ。TL(TileLink)からAMBAに変換するモジュールについては、
- ToAPB
- ToAHB
- ToAXI4
の3種類があり、まずは一番簡単そうなAPBを観察する。
ちなみにSiFiveが以下のような資料を出していたが、あまり内容は濃くない。インタフェースだけの解説だ。
- SiFive TileLink To APB Bridge (TL2APB)
https://static.dev.sifive.com/pdfjs/web/viewer.html?file=https://static.dev.sifive.com/TL2APB.pdf
TileLinkをAPBに変換するコードは以下のようになっている。
TLのリクエストを受け付けるためのQueueが実装されている。
// We need an irrevocable input for APB to stall val a = Queue(in.a, 1, flow = aFlow, pipe = !aFlow)
TLがRead Responseを受け付けるためのQueueも用意されている。
val d = Wire(in.d) in.d <> Queue(d, 1, flow = true)
APBに関するa_sel, a_enable, a_write
の生成については、以下のように実装されていた。
val a_enable = RegInit(Bool(false)) val a_sel = a.valid && RegNext(!in.d.valid || in.d.ready) val a_write = edgeIn.hasData(a.bits) when (a_sel) { a_enable := Bool(true) } when (d.fire()) { a_enable := Bool(false) }
a_sel
がAssertされるとEnableが立ち上がる。
a_sel
はdチャネル側の受け入れ準備が出来ていないと立ち上がらない。
a_write
はa.bits
にデータが入っていないと立ち上がらない。
APBの出力は以下のようになっている。 これは普通の実装に見える。
out.psel := a_sel
out.penable := a_enable
out.pwrite := a_write
out.paddr := a.bits.address
out.pprot := PROT_DEFAULT
out.pwdata := a.bits.data
out.pstrb := Mux(a_write, a.bits.mask, UInt(0))
読み込みデータが戻ってくるときの実装は以下のようになっている。
まず、DチャネルのOPコードは、読み込みの場合はAccessAck
, 書き込みの場合はAccessAckData
が返される。
ID信号とSizeデータはTL2APBユニット内で保持されており、それがそのままキープされる。
データ信号はAPBからのデータがそのまま返される。
d.bits.opcode := Mux(d_write, TLMessages.AccessAck, TLMessages.AccessAckData) d.bits.param := UInt(0) d.bits.size := d_size d.bits.source := d_source d.bits.sink := UInt(0) d.bits.denied := d_write && out.pslverr d.bits.data := out.prdata d.bits.corrupt := !d_write && out.pslverr