今天群友遇到一個在綜合的時(shí)候報(bào)錯ambiguous clock in event control的問題,我們就來看看一個always塊會生成什么樣的電路。
案例一:
首先從最簡單的一段代碼來看:
always @(posedge clk ) begin c <= b; end
在上面的代碼里面敏感信號只有一個posedge clk,begin end中間也只有一個賦值語句,僅僅是一個打拍的操作,那么我們來看一下,他生成的電路是什么樣子的。
可以看到,clk和b經(jīng)過一個IBUF后接入觸發(fā)器的C端(時(shí)鐘端)和D端(數(shù)據(jù)輸入端),然后輸出Q端經(jīng)過一個OBUF連接到c。其中IBUF和OBUF以及BUFG是vivado自動幫我們插入的,當(dāng)信號是頂層信號的時(shí)候vivado就會幫我們自動插入IBUF和OBUF,時(shí)鐘信號幫我們自動掛到了時(shí)鐘樹上面。
案例二:
那么vivado是怎么認(rèn)出來我們代碼里面寫的clk就是時(shí)鐘信號呢,是靠clk這個名字嗎,讓我們把clk換成rst來看一下,代碼如下:
always @(posedge rst ) begin c <= 1'b1; end
可以看到上面代碼生成的電路和案例一中的電路是一樣的,vivado也是把posedge rst認(rèn)為是時(shí)鐘信號接到了觸發(fā)器的時(shí)鐘端,可見vivado并不是靠名字來識別哪個信號是時(shí)鐘,哪個信號是復(fù)位的。
案例三:
那么我們應(yīng)該怎么生成一個復(fù)位信號呢,先看一下同步復(fù)位的情況,代碼如下:
always @(posedge clk ) begin if(rst == 1'b1)begin c <= 1'b0; end else begin c <= b; end end
可以看到復(fù)位端rst接到了觸發(fā)器的復(fù)位信號上。注意在上述代碼中是高電平復(fù)位的,那么我們再看一下如果是低電平復(fù)位會產(chǎn)生什么樣子的電路,代碼如下:
always @(posedge clk ) begin if(rst == 1'b0)begin c <= 1'b0; end else begin c <= b; end end
可以看到在rst信號之后多了一個LUT,這個LUT的目的是將rst信號取反,也就是說我們想讓rst為0的時(shí)候進(jìn)行復(fù)位,但是vivado在生成電路的時(shí)候會將其取反變?yōu)?之后接到觸發(fā)器的復(fù)位端,這里在復(fù)位端插入一級LUT便會影響我們的時(shí)序,這也是為什么我們常說FPGA更推薦同步高復(fù)位的原因之一。
在UG901中有相關(guān)的描述如下:
案例四:
在案例三中展示了同步復(fù)位的情況,注意這里生成的觸發(fā)器都是FDRE,在xilinx的FPGA中一共有四種觸發(fā)器,在UG901中有說明,如下圖:
可以看到對于同步復(fù)位的觸發(fā)器有FDSE和FDRE兩種,兩者的區(qū)別就是FDSE在復(fù)位的時(shí)候輸出是1,F(xiàn)DRE是0。,這也是為什么在案例三中無論是高復(fù)位還是低復(fù)位生成的都是FDRE。
那么我們改變一下代碼,讓c在復(fù)位的時(shí)候變?yōu)?,也就是c <= 1'b1,來看看生成的電路是什么樣子的,代碼如下:
always @(posedge clk ) begin if(rst == 1'b1)begin c <= 1'b1; end else begin c <= b; end end
可以看到在這種情況下,我們的代碼就映射到了一個FDSE上。
案例五:
第五個案例就來看一下vivado是怎么把rst認(rèn)為是復(fù)位信號的。我們先來看如下代碼:
always @(posedge clk ) begin if(rst == 1'b1)begin c <= 1'b1; end if (a == 1'b1)begin c <= b; end end
可以發(fā)現(xiàn)復(fù)位信號沒有了,F(xiàn)DRE是復(fù)位端直接接了地,那么我們有理由懷疑是不是vivado在處理if else的時(shí)候會把if里面的信號認(rèn)為是復(fù)位信號呢。上述代碼里面兩個if語句并列,生成的電路也是a b rst經(jīng)過一大段組合邏輯之后接入到FDRE是D端。
案例六:
將案例五的代碼稍將改變,也就是在第二個if前面增加一個else,代碼如下
always @(posedge clk ) begin if(rst == 1'b1)begin c <= 1'b1; end else if (a == 1'b1)begin c <= b; end end
可以看到我們的復(fù)位端又重新回來了,那么也就印證了在案例五中的猜想,他是靠if else, if elseif這樣的結(jié)構(gòu)來識別是不是復(fù)位的,怎么映射過去的,需要注意的是案例六中因?yàn)槲覀儗懥薬==1時(shí)才把b的值給到c,那么a就被接入到了觸發(fā)器的CE端,當(dāng)a的值是0時(shí),CE端為0,Q端保持上一次的值不變。注意在時(shí)序電路里面,我們不寫else也不會生成latch。
案例七:
上面代碼都是在敏感信號里面只有一個posedge clk,那么如果我們寫多個敏感信號呢,會變成什么樣子。
always @(posedge clk or posedge rst) begin if(rst == 1'b1)begin c <= 1'b1; end if (a == 1'b1)begin c <= b; end end
我們先來看一下上述代碼,敏感列表里面有兩個信號,posedge clk和posedge rst。并且在begin end里面也沒有if else或者if else if這種結(jié)構(gòu)的語句,按照我們同步復(fù)位的幾個案例的推斷,他會把clk和rst都推斷為時(shí)鐘信號,而實(shí)際上我們不可能給一個xilinx的FPGA的觸發(fā)器同時(shí)接兩個時(shí)鐘信號,這個時(shí)候vivado在綜合的時(shí)候就給我們報(bào)錯了。
因?yàn)槲覀冎酪粋€觸發(fā)器只能有一個時(shí)鐘信號,如果有多個時(shí)鐘信號我們也需要做時(shí)鐘切換電路,來確保在同一時(shí)刻只有一個時(shí)鐘接到上面。vivado推斷不出來上述代碼究竟哪個信號是時(shí)鐘信號,那他只能報(bào)錯了,告訴我們當(dāng)前時(shí)鐘信號是模棱兩可的。
案例八:
那么案例七中的代碼怎么改呢,第一種選擇案例五中的方式,將敏感列表變?yōu)橐粋€,那么時(shí)鐘信號自然就明確了。
第二種就是敏感列表中另一個信號變?yōu)閺?fù)位信號。這兩種改法取決于我們想要實(shí)現(xiàn)的邏輯是什么樣子的。
always @(posedge clk or posedge rst) begin if(rst == 1'b1)begin c <= 1'b1; end else begin c <= b; end end
第二種改法被綜合為一個FDPE。
案例九:
always @(posedge clk) begin if(rst == 1'b1)begin c <= 1'b1; end else begin c <= b; end if(a == 1'b1)begin c <= d; end end
我們再來看一段代碼:
有一組if else,如果只有這一組的話,他就應(yīng)該和案例4一樣生成一個FDSE,但是我們下面又給他加了一句if,那么他便不會將rst認(rèn)為是一個復(fù)位信號了,而是和其他if else一起生成一大堆組合邏輯。
案例十:
在案例九里面是對同一個信號進(jìn)行賦值,如果我們對不同的信號進(jìn)行賦值呢。
always @(posedge clk) begin if(rst == 1'b1)begin c <= 1'b1; end else begin c <= b; end if(a == 1'b1)begin e <= d; end end
上述代碼和案例九里面,只有當(dāng)a==1時(shí)的操作不同,一個是對c進(jìn)行賦值,另一個是對新的寄存器e進(jìn)行賦值,那么vivado就會對c和e兩個寄存器分別處理,生成如下電路:
這個代碼和我們分成兩個always寫是一樣的:
always @(posedge clk) begin if(rst == 1'b1)begin c <= 1'b1; end else begin c <= b; end end always @(posedge clk) begin if(a == 1'b1)begin e <= d; end end
案例十一:
如果我案例九中的代碼換成異步復(fù)位呢,會發(fā)生什么:
always @(posedge clk or negedge rst) begin if(rst == 1'b1)begin c <= 1'b1; end else begin c <= b; end if(a == 1'b1)begin c <= d; end end
綜合完直接報(bào)錯了,那么為什么案例九中沒報(bào)錯呢。是因?yàn)槲覀兠舾辛斜砝锩嬗衏lk和rst,他們肯定不是復(fù)位就是時(shí)鐘信號嘛,vivado也會這么考慮,但是在begin end里面寫的代碼塊,按第一個if else結(jié)構(gòu)應(yīng)該生成帶復(fù)位的觸發(fā)器,而第二個if結(jié)構(gòu),他又不應(yīng)該生成,vivado就傻眼了,只能報(bào)錯啊。而案例九里面,rst不在敏感列表里面,vivado還有一種選擇就是將其當(dāng)成普通信號,所以案例九生成了一大堆的組合邏輯。
案例十二:
也許會有一種想法就是把案例十一的代碼像案例十那樣改,也就是對兩個寄存器進(jìn)行賦值,如下代碼:
always @(posedge clk or posedge rst) begin if(rst == 1'b1)begin c <= 1'b1; end else begin c <= b; end if(a == 1'b1)begin e <= d; end end
不過不好意思,這種寫法也是錯誤的,會報(bào)錯
這是因?yàn)槲覀冊诿舾辛斜砝锩鎸憙蓚€信號,但是對于e這個寄存器又都沒有使用,不會生成復(fù)位信號,那可不就接著報(bào)ambiguous clock in event control了。在上述代碼中對c的操作是不會出錯的。
案例十三:
看到這里不知道大家有沒有注意到,在異步復(fù)位里面,我們都是posedge rst和if(rst == 1'b1)也就是高電平復(fù)位,注意這兩個是需要匹配的,如果我們寫個posedge rst但是緊接著寫if(rst == 1'b0),這樣寫是會報(bào)錯的。
always @(posedge clk or posedge rst) begin if(rst == 1'b0)begin c <= 1'b1; end else begin c <= b; end end
如上代碼和報(bào)錯,vivado也不知道他應(yīng)該是生成一個高電平復(fù)位還是一個低電平復(fù)位的電路了,所以他報(bào)錯了。
關(guān)于異步復(fù)位還是同步復(fù)位可以參考UG949中的描述:
-
觸發(fā)器
+關(guān)注
關(guān)注
14文章
2039瀏覽量
62069 -
代碼
+關(guān)注
關(guān)注
30文章
4895瀏覽量
70555 -
時(shí)鐘信號
+關(guān)注
關(guān)注
4文章
468瀏覽量
29194 -
Vivado
+關(guān)注
關(guān)注
19文章
834瀏覽量
68608 -
復(fù)位信號
+關(guān)注
關(guān)注
0文章
67瀏覽量
6566
原文標(biāo)題:小議觸發(fā)器
文章出處:【微信號:FPGA開源工坊,微信公眾號:FPGA開源工坊】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
JK觸發(fā)器 D觸發(fā)器 RS觸發(fā)器 T觸發(fā)器 真值表
JK觸發(fā)器,JK觸發(fā)器是什么意思
D觸發(fā)器,D觸發(fā)器是什么意思
什么是RS觸發(fā)器,RS觸發(fā)器的工作原理是什么?
施密特觸發(fā)器,施密特觸發(fā)器是什么意思
觸發(fā)器的分類, 觸發(fā)器的電路
什么是邊沿觸發(fā)器_邊沿D觸發(fā)器介紹

觸發(fā)器的作用_觸發(fā)器的特點(diǎn)介紹
電平觸發(fā)器,脈沖觸發(fā)器和邊沿觸發(fā)器的觸發(fā)因素是什么

評論