在編寫Verilog代碼時(shí)最痛苦的事情便是例化模塊時(shí)端口的連接,這時(shí)候的你我便成了連線工程師,本節(jié)就在SpinalHDL中如何像軟件調(diào)用方法那樣優(yōu)雅地例化端口進(jìn)行探討。
前言
習(xí)慣了寫Verilog的小伙伴們?cè)谧龃笮凸こ虝r(shí)是否有遇到過(guò)連續(xù)數(shù)天時(shí)間化身“連線工程師”去例化模塊、為端口賦值連接的場(chǎng)景(關(guān)鍵是這些工作量老板他也不認(rèn))。盡管在SystemVerilog中提供了Interface接口的概念,但是從事FPGA的小伙伴都清楚無(wú)論是Xilinx的Vivado還是Intel Quartus雖然支持SystemVerilog但遠(yuǎn)沒(méi)有做到像軟件代碼編輯器那般做到自動(dòng)聯(lián)想與提示。最近分析一個(gè)Intel的大型源碼工程其中用到了大量的SystemVerilog中的interface及struct,但自動(dòng)關(guān)聯(lián)提示做的真是一團(tuán)糟,導(dǎo)致閱讀體驗(yàn)真是差的一匹…… 本文以一個(gè)簡(jiǎn)單的加法器的例子來(lái)看如何在SpinalHDL中如何避免成為連線工程師。 加法器端口列表如下所示:
端口名 | 方向 | 位寬 | 說(shuō)明 |
valid_in | input | 1 | 輸入有效標(biāo)志 |
data1 | input | 8 | 輸入數(shù)據(jù) |
data2 | input | 8 | 輸入數(shù)據(jù) |
sum | output | 8 | 和 |
sum_valid | output | 1 | 和有效標(biāo)志 |
初階
剛開(kāi)始接觸SpinalHDL時(shí)這個(gè)加法器我們可能會(huì)這么來(lái)寫:
class add(dataWidth:Int) extends Component{
val validIn=in Bool()
val data1=in UInt(dataWidth bits)
val data2=in UInt(dataWidth bits)
val sum=out UInt(dataWidth bits)
val sumValid=out Bool()
sum:=RegNextWhen(data1+data2,validIn)
sumValid:=RegNext(validIn,False)
}
這里針對(duì)端口的實(shí)現(xiàn)形式和我們?cè)赩erilog中的方式基本相同。那么當(dāng)我們?cè)诶@個(gè)模塊時(shí),我們可能會(huì)這么來(lái)寫:
class addInst(dataWidth:Int) extends Component {
val io=new Bundle{
val validIn_0=in Bool()
val data1_0=in UInt(dataWidth bits)
val data2_0=in UInt(dataWidth bits)
val sum_0=out UInt(dataWidth bits)
val sumValid_0=out Bool()
val validIn_1=in Bool()
val data1_1=in UInt(dataWidth bits)
val data2_1=in UInt(dataWidth bits)
val sum_1=out UInt(dataWidth bits)
val sumValid_1=out Bool()
}
val add0=new add(dataWidth)
val add1=new add(dataWidth)
add0.validIn<>io.validIn_0
add0.data1<>io.data1_0
add0.data2<>io.data2_0
add0.sum<>io.sum_0
add0.sumValid<>io.sumValid_0
add1.validIn<>io.validIn_1
add1.data1<>io.data1_1
add1.data2<>io.data2_1
add1.sum<>io.sum_1
add1.sumValid<>io.sumValid_1
}
這里例化了兩個(gè)加法器,可以看到,這里如同我們寫Verilog代碼般一根根連線,當(dāng)有眾多模塊需要去例化時(shí)還是蠻痛苦的。
中階
在SystemVerilog中提供了Interface的概念用于封裝接口,在SpinalHDL中,我們可以借助軟件面向?qū)ο蟮乃枷氚呀涌诮o抽象出來(lái):
case class sumPort(dataWidth:Int=8) extends Bundle with IMasterSlave{
case class dataPort(dataWidth:Int=8) extends Bundle{
val data1=UInt(dataWidth bits)
val data2=UInt(dataWidth bits)
}
val dataIn=Flow(dataPort(dataWidth))
val sum=Flow(UInt(dataWidth bits))
override def asMaster(): Unit = {
master(dataIn)
slave(sum)
}
}
這里我們將加法器的端口抽象成sumPort端口。其中包含兩個(gè)Flow類型:dataIn、sum。并聲明當(dāng)作為master端口時(shí)dataIn為master、sum為slave。這樣,我們的加法器便可以這么來(lái)寫:
case class add2(dataWidth:Int=8)extends Component{
val io=new Bundle{
val sumport=slave(sumPort(dataWidth))
}
io.sumport.sum.payload:=RegNextWhen(io.sumport.dataIn.data1+io.sumport.dataIn.data2,io.sumport.dataIn.valid)
io.sumport.sum.valid:=RegNext(io.sumport.dataIn.valid,False)
}
而我們?cè)诶瘯r(shí),便可以簡(jiǎn)潔地例化:
class addInst1(dataWidth:Int) extends Component{
val io=new Bundle{
val sumport0=slave(sumPort(dataWidth))
val sumport1=slave(sumPort(dataWidth))
}
val addInst_0=add2(dataWidth)
val addInst_1=add2(dataWidth)
io.sumport0<>addInst_0.io.sumport
io.sumport1<>addInst_1.io.sumport
}
如此我們便能簡(jiǎn)潔地例化加法器。雖然這里地做法思想和SystemVerilog中地思想基本一致,但好處是我們能夠在IDEA中像閱讀軟件代碼那般快速地跳轉(zhuǎn)和定位,相較于廠商工具中那樣分析工程地痛苦實(shí)在是好太多。
高階
在中階例,我們采用了類似SystemVerilog中Interface及struct概念,但可以發(fā)現(xiàn),我們這里依舊存在連線行為。一個(gè)模塊例化一次要連線一次,要例化N次還是要……
在軟件代碼中,調(diào)用一個(gè)方法或者模塊往往一行代碼了事:聲明調(diào)用函數(shù)并將參數(shù)放在括號(hào)列表里。那么在這里,我們能否像軟件調(diào)用那樣一行代碼搞定呢?
可以的!由于SpinalHDL是基于Scala的,因此我們可以將端口列表當(dāng)成參數(shù)列表來(lái)傳遞。這里我們先為我們的加法器定義一個(gè)伴生對(duì)象:
object add2{
def apply(dataWidth: Int,port Unit = {
val addInst=new add2(dataWidth)
addInst.io.sumport<>port
}
}
這里我們?yōu)榧臃ㄆ鱝dd2定義了一個(gè)伴生對(duì)象(伴生對(duì)象聲明為object,名字與類名相同)。并在其中定義了一個(gè)apply方法,傳入兩個(gè)參數(shù):位寬dataWidth及端口port,并在apply實(shí)現(xiàn)中完成模塊例化及端口連接(一次連線,終身使用)。隨后我們?cè)诶瘯r(shí)便可以像軟件調(diào)用方法那樣例化模塊了:
class addInst1(dataWidth:Int) extends Component{
val io=new Bundle{
val sumport0=slave(sumPort(dataWidth))
val sumport1=slave(sumPort(dataWidth))
}
add2(dataWidth,io.sumport0)
add2(dataWidth,io.sumport0)
}
一行代碼搞定一個(gè)模塊的一次例化和端口連接!
審核編輯 :李倩
-
加法器
+關(guān)注
關(guān)注
6文章
183瀏覽量
30609 -
代碼
+關(guān)注
關(guān)注
30文章
4887瀏覽量
70264 -
編輯器
+關(guān)注
關(guān)注
1文章
817瀏覽量
31775
原文標(biāo)題:SpinalHDL—像軟件調(diào)用方法般例化模塊
文章出處:【微信號(hào):zhuyandz,微信公眾號(hào):FPGA之家】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
cyusb3014插入usb2.0接口后,軟件調(diào)用cyapi.lib中的哪些函數(shù)完成初始化、讀寫操作?
研發(fā)排查問(wèn)題的利器:一款方法調(diào)用棧跟蹤工具

stm32的pcrop安全系數(shù)高不高,是否會(huì)像普通flash讀保護(hù)那樣被人家破解?
在Linux下安裝軟件有哪些方法
ShiMetaOS | 怎樣調(diào)用ShiMeta信息發(fā)布軟件快速打造數(shù)字標(biāo)牌產(chǎn)品

請(qǐng)問(wèn)ADS1256能像ADS1271那樣級(jí)聯(lián)嗎?
Verilog例化說(shuō)明

三相維也納電路,是否需要像單項(xiàng)那樣加一個(gè)pfc芯片?
ad中端口的幾種類型
請(qǐng)問(wèn)studio中可以像裸機(jī)一樣調(diào)用定時(shí)器中斷嗎?
鑒源實(shí)驗(yàn)室·ISO 26262中測(cè)試用例的得出方法-等價(jià)類的生成和分析

求助各位關(guān)于Verilog當(dāng)中模塊例化、端口與引腳 的問(wèn)題
python訓(xùn)練出的模型怎么調(diào)用
每次調(diào)用espconn_send之前,重新設(shè)置遠(yuǎn)程IP和端口的目的是什么?
動(dòng)態(tài)追溯方法:徹底革新軟件測(cè)試

評(píng)論