編 者 按
無論是FPGA還是ASIC,系統設計中總會存在配置寄存器總線的使用,我們會將各種功能、調試寄存器掛載在寄存器總線上使用。在SpinalHDL中,BusIf那套總線模型庫寫的還是相當不錯的,能夠同時生成對應的代碼和文檔(在公司里也做過一些修改,能夠直接生成整個系統的寄存器文檔而不僅僅是單個模塊的寄存器文檔)。今天就手把手來基于SpinalHDL中的BusIf來看如何根據自己設計中的寄存器配置總線定義來生成一套寄存器配置模版。
HPI總線
今天以下面一套簡單的寄存器總線為例進行設計:
caseclass Hpi(addrWidth:Int,dataWidth:Int,useStrb:Boolean) extends Bundle with IMasterSlave { val wr,rd=Bool() val addr=UInt(addrWidth bits) val wdata=Bits(dataWidth bits) val strb= useStrb generate(Bits(dataWidth/8bits)) val rvalid=Bool() val rdata=Bits(dataWidth bits) overridedef asMaster(): Unit = { out(wr,rd,addr,wdata) in(rvalid,rdata) if(useStrb) out(strb) } }
HPI總線很簡單,wr用于標識寫指令、rd用于標識讀指令。addr用于指示讀/寫地址。wdata用于輸入待寫入的數據。strb如果使能則用于標識對應寫指令的位選信號。而rvalid則用于指示讀返回數據有效,rdata則表示讀返回數據(這里就不畫時序圖了)。
設計一套自己的HpiInterface
針對上面的HPI總線,這里我們基于BusIf設計一套自己的配置寄存器總線模板。
BusIf都做了什么
對于寄存器配置總線,其總線信號定義,協議交互定義總是各有千秋的,BusIf無法對寄存器配置總線的這些定義做提前預知。然而對于寄存器配置總線的用途,則相對明確,無非就是實現不同數據類型(像UVM中定義的寄存器模型)的讀寫操作。那么,當我們基于BusIf定義一套自己的配置寄存器模板時,所需要做的就無非是:
實現配置寄存器交互協議的邏輯實現
告訴BusIf如何什么情況下觸發了某個寄存器的讀/寫操作
HpiInterface
這里先貼上一個完整的代碼,隨后進行逐行解析
這里我們定義的寄存器總線相對來講較為簡單,故只有30~31行是用來進行配置寄存器總線協議時序的處理的。
line3
override defgetModuleName:String = moduleName.name
利用隱式參數獲取模塊名,BusIf中并無顯示使用。
line5~6
override defwriteAddress():UInt = bus.addr override defreadAddress():UInt = bus.addr
這里用來告知BusIf配置寄存器總線的寫地址,讀地址分別是什么,在HPI總線中均為bus.addr
line8~9
override defreadHalt():Unit = {} override defwriteHalt():Unit = {}
這里用來填寫發生讀阻塞或者寫阻塞時的響應動作,HPI總線中沒有阻塞的概念,直接不做任何處理即可(由于配置寄存器總線接口層協議我們會自己實現,BusIf中當前版本內部無使用的地方,故直接填空即可)。
line11
override defbusDataWidth:Int = bus.dataWidth
這里用來告知BusIf配置寄存器總線的數據位寬
line13~18
override val withStrb:Boolean = bus.useStrb val wstrb:Bits = withStrb generate(Bits(strbWidth bit)) val wmask: Bits = withStrb generate(Bits(busDataWidth bit)) val wmaskn: Bits = withStrb generate(Bits(busDataWidth bit)) initStrbMasks() if(bus.useStrb){wstrb:=bus.strb}
這里用于設置配置寄存器總線是否有使用掩碼功能。line13用于告知BusIf是否使用掩碼,而line14~17則是一套針對掩碼的BusIf設置(直接copy即可)。在line18行如果使能了掩碼功能,則通過用bus.strb來驅動 wstrb來告知BusIf配置寄存器總線對應的寫掩碼。
line20~26
overrideval askRead: Bool = bus.rd overrideval askWrite: Bool = bus.wr overrideval doWrite: Bool = bus.wr overrideval doRead: Bool = bus.rd overrideval readData: Bits = Bits(bus.dataWidth bits) overrideval writeData: Bits =bus.wdata overrideval readError: Bool = Bool()
askRead:告知BusIf什么情況下配置寄存器產生了讀請求(如果總線類型是Stream那種握手型的,則只需填valid即可,可參照regif下的AxiLite4BusInterface)。在當前版本中,askRead在BusIf中并未有使用
askWrite:告知BusIf什么情況下配置寄存器產生了寫請求,同上
doWrite:告知BusIf當前時鐘是否出發了寄存器寫操作
doRead:告知BusIf當前時鐘是否發生了寄存器讀操作
readData:為BusIf聲明一個對應總線數據位寬的信號,BusIf會將讀結果返回到當前信號上。
writeData:告知BusIf如果發生寫數據,寫入的數據是什么
readError:聲明一個Bool類型,BusIf會將是否有讀錯誤發生通過該信號進行表示。
line28
setReservedAddressReadValue(BigInt("deaddead",16))
此處用于設置當總線讀了未使用的地址時,應當返回何值。當然也可以不必在這里進行統一設置,可以在真正的例化位置為每個模塊設置一個不同的值。
line30~31
bus.rdata:=RegNext(readData) bus.rvalid:=RegNext(bus.rd,False)
該處則用于處理Hpi總線的協議時序,我們僅需處理接口層面上的時序即可。
使用HpiInterface
定義好之后,使用就和regif文檔里的例子一樣了,下面給出一個例子:
這里定義了data0,data1,data2三個寄存器。data0,data1可讀可寫,data2只讀。
值得注意的是這里針對data0,data1采用了不同形式的API,對于data0,使用field來注冊會生成reg0_data0,其是帶有復位處理的,這里不希望其有復位邏輯,故這里添加了removeInitAssignments()
DIY時間
來看下在BusIf中針對讀邏輯是怎么處理的:
從FPGA的角度來看的話,這個askRead判斷是沒有必要,徒增延遲,看起來不那么優雅,而且在Hpi總線使用時往往是要求地址按位寬對齊的,所以完全可以將地址判斷抹去低比特,由于這里readGenerator定義成了private,外部無法重載,這里給出一個在HpiInterface中進行DIY的例子:
val discardAddrWidth = log2Up(busDataWidth / 8) def hitDoWriteOverride() = { orderdRegInsts.foreach(regInst => { regInst.hitDoWrite.removeAssignments() regInst.hitDoWrite := writeAddress()(bus.addrWidth - 1downto discardAddrWidth) === regInst.addr / (busDataWidth / 8) && doWrite }) } def reworkReadGenerate() = { readError.removeAssignments() readData.removeAssignments() switch(readAddress()(bus.addrWidth - 1downto discardAddrWidth)) { orderdRegInsts.foreach(regInst => { if(!regInst.allIsNA) { is(regInst.addr / (busDataWidth / 8)) { readData := regInst.readBits readError := Bool(regInst.haveWO) } } }) default{ readData := getReservedAddressReadValue readError := True } } } component.addPrePopTask(() => { hitDoWriteOverride() reworkReadGenerate() })
這里對讀和寫均做了一些優化。對于寫操作進行地址判定時將會抹去地址相應地比特的判斷。而對于讀操作,也會抹去相應地址位,同時也刪除了askRead的判斷。
感興趣的小伙伴可自行擴展定制,比如將HPI的讀返回拆成兩排分級譯碼以獲取更好的時序等等。
-
FPGA
+關注
關注
1630文章
21796瀏覽量
605996 -
寄存器
+關注
關注
31文章
5363瀏覽量
121158 -
UVM
+關注
關注
0文章
182瀏覽量
19227 -
HPI
+關注
關注
0文章
36瀏覽量
14545
原文標題:手把手創建自己的寄存器配置模版
文章出處:【微信號:Spinal FPGA,微信公眾號:Spinal FPGA】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論