編 者 按
無論是FPGA還是ASIC,系統(tǒng)設(shè)計(jì)中總會(huì)存在配置寄存器總線的使用,我們會(huì)將各種功能、調(diào)試寄存器掛載在寄存器總線上使用。在SpinalHDL中,BusIf那套總線模型庫寫的還是相當(dāng)不錯(cuò)的,能夠同時(shí)生成對(duì)應(yīng)的代碼和文檔(在公司里也做過一些修改,能夠直接生成整個(gè)系統(tǒng)的寄存器文檔而不僅僅是單個(gè)模塊的寄存器文檔)。今天就手把手來基于SpinalHDL中的BusIf來看如何根據(jù)自己設(shè)計(jì)中的寄存器配置總線定義來生成一套寄存器配置模版。
HPI總線
今天以下面一套簡(jiǎn)單的寄存器總線為例進(jìn)行設(shè)計(jì):
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總線很簡(jiǎn)單,wr用于標(biāo)識(shí)寫指令、rd用于標(biāo)識(shí)讀指令。addr用于指示讀/寫地址。wdata用于輸入待寫入的數(shù)據(jù)。strb如果使能則用于標(biāo)識(shí)對(duì)應(yīng)寫指令的位選信號(hào)。而rvalid則用于指示讀返回?cái)?shù)據(jù)有效,rdata則表示讀返回?cái)?shù)據(jù)(這里就不畫時(shí)序圖了)。
設(shè)計(jì)一套自己的HpiInterface
針對(duì)上面的HPI總線,這里我們基于BusIf設(shè)計(jì)一套自己的配置寄存器總線模板。
BusIf都做了什么
對(duì)于寄存器配置總線,其總線信號(hào)定義,協(xié)議交互定義總是各有千秋的,BusIf無法對(duì)寄存器配置總線的這些定義做提前預(yù)知。然而對(duì)于寄存器配置總線的用途,則相對(duì)明確,無非就是實(shí)現(xiàn)不同數(shù)據(jù)類型(像UVM中定義的寄存器模型)的讀寫操作。那么,當(dāng)我們基于BusIf定義一套自己的配置寄存器模板時(shí),所需要做的就無非是:
實(shí)現(xiàn)配置寄存器交互協(xié)議的邏輯實(shí)現(xiàn)
告訴BusIf如何什么情況下觸發(fā)了某個(gè)寄存器的讀/寫操作
HpiInterface
這里先貼上一個(gè)完整的代碼,隨后進(jìn)行逐行解析
這里我們定義的寄存器總線相對(duì)來講較為簡(jiǎn)單,故只有30~31行是用來進(jìn)行配置寄存器總線協(xié)議時(shí)序的處理的。
line3
override defgetModuleName:String = moduleName.name
利用隱式參數(shù)獲取模塊名,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 = {}
這里用來填寫發(fā)生讀阻塞或者寫阻塞時(shí)的響應(yīng)動(dòng)作,HPI總線中沒有阻塞的概念,直接不做任何處理即可(由于配置寄存器總線接口層協(xié)議我們會(huì)自己實(shí)現(xiàn),BusIf中當(dāng)前版本內(nèi)部無使用的地方,故直接填空即可)。
line11
override defbusDataWidth:Int = bus.dataWidth
這里用來告知BusIf配置寄存器總線的數(shù)據(jù)位寬
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}
這里用于設(shè)置配置寄存器總線是否有使用掩碼功能。line13用于告知BusIf是否使用掩碼,而line14~17則是一套針對(duì)掩碼的BusIf設(shè)置(直接copy即可)。在line18行如果使能了掩碼功能,則通過用bus.strb來驅(qū)動(dòng) wstrb來告知BusIf配置寄存器總線對(duì)應(yīng)的寫掩碼。
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什么情況下配置寄存器產(chǎn)生了讀請(qǐng)求(如果總線類型是Stream那種握手型的,則只需填valid即可,可參照regif下的AxiLite4BusInterface)。在當(dāng)前版本中,askRead在BusIf中并未有使用
askWrite:告知BusIf什么情況下配置寄存器產(chǎn)生了寫請(qǐng)求,同上
doWrite:告知BusIf當(dāng)前時(shí)鐘是否出發(fā)了寄存器寫操作
doRead:告知BusIf當(dāng)前時(shí)鐘是否發(fā)生了寄存器讀操作
readData:為BusIf聲明一個(gè)對(duì)應(yīng)總線數(shù)據(jù)位寬的信號(hào),BusIf會(huì)將讀結(jié)果返回到當(dāng)前信號(hào)上。
writeData:告知BusIf如果發(fā)生寫數(shù)據(jù),寫入的數(shù)據(jù)是什么
readError:聲明一個(gè)Bool類型,BusIf會(huì)將是否有讀錯(cuò)誤發(fā)生通過該信號(hào)進(jìn)行表示。
line28
setReservedAddressReadValue(BigInt("deaddead",16))
此處用于設(shè)置當(dāng)總線讀了未使用的地址時(shí),應(yīng)當(dāng)返回何值。當(dāng)然也可以不必在這里進(jìn)行統(tǒng)一設(shè)置,可以在真正的例化位置為每個(gè)模塊設(shè)置一個(gè)不同的值。
line30~31
bus.rdata:=RegNext(readData) bus.rvalid:=RegNext(bus.rd,False)
該處則用于處理Hpi總線的協(xié)議時(shí)序,我們僅需處理接口層面上的時(shí)序即可。
使用HpiInterface
定義好之后,使用就和regif文檔里的例子一樣了,下面給出一個(gè)例子:
這里定義了data0,data1,data2三個(gè)寄存器。data0,data1可讀可寫,data2只讀。
值得注意的是這里針對(duì)data0,data1采用了不同形式的API,對(duì)于data0,使用field來注冊(cè)會(huì)生成reg0_data0,其是帶有復(fù)位處理的,這里不希望其有復(fù)位邏輯,故這里添加了removeInitAssignments()
DIY時(shí)間
來看下在BusIf中針對(duì)讀邏輯是怎么處理的:
從FPGA的角度來看的話,這個(gè)askRead判斷是沒有必要,徒增延遲,看起來不那么優(yōu)雅,而且在Hpi總線使用時(shí)往往是要求地址按位寬對(duì)齊的,所以完全可以將地址判斷抹去低比特,由于這里readGenerator定義成了private,外部無法重載,這里給出一個(gè)在HpiInterface中進(jìn)行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() })
這里對(duì)讀和寫均做了一些優(yōu)化。對(duì)于寫操作進(jìn)行地址判定時(shí)將會(huì)抹去地址相應(yīng)地比特的判斷。而對(duì)于讀操作,也會(huì)抹去相應(yīng)地址位,同時(shí)也刪除了askRead的判斷。
感興趣的小伙伴可自行擴(kuò)展定制,比如將HPI的讀返回拆成兩排分級(jí)譯碼以獲取更好的時(shí)序等等。
-
FPGA
+關(guān)注
關(guān)注
1626文章
21678瀏覽量
602006 -
寄存器
+關(guān)注
關(guān)注
31文章
5325瀏覽量
120052 -
UVM
+關(guān)注
關(guān)注
0文章
181瀏覽量
19144 -
HPI
+關(guān)注
關(guān)注
0文章
35瀏覽量
14502
原文標(biāo)題:手把手創(chuàng)建自己的寄存器配置模版
文章出處:【微信號(hào):Spinal FPGA,微信公眾號(hào):Spinal FPGA】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論