CPLD(Complex Programmable Logic Device)復(fù)雜可編程邏輯器件,是從PAL和GAL器件發(fā)展出來的器件,相對而言規(guī)模大,結(jié)構(gòu)復(fù)雜,屬于大規(guī)模集成電路范圍。是一種用戶根據(jù)各自需要而自行構(gòu)造邏輯功能的數(shù)字集成電路。其基本設(shè)計(jì)方法是借助集成開發(fā)軟件平臺,用原理圖、硬件描述語言等方法,生成相應(yīng)的目標(biāo)文件,通過下載電纜(“在系統(tǒng)”編程)將代碼傳送到目標(biāo)芯片中,實(shí)現(xiàn)設(shè)計(jì)的數(shù)字系統(tǒng)。
CPLD主要是由可編程邏輯宏單元(MC,Macro Cell)圍繞中心的可編程互連矩陣單元組成。其中MC結(jié)構(gòu)較復(fù)雜,并具有復(fù)雜的I/O單元互連結(jié)構(gòu),可由用戶根據(jù)需要生成特定的電路結(jié)構(gòu),完成一定的功能。由于CPLD內(nèi)部采用固定長度的金屬線進(jìn)行各邏輯塊的互連,所以設(shè)計(jì)的邏輯電路具有時間可預(yù)測性,避免了分段式互連結(jié)構(gòu)時序不完全預(yù)測的缺點(diǎn)。
寫好CPLD的程序的經(jīng)驗(yàn)總結(jié)
一個簡單的數(shù)字電路中主要有三種基本單元:組合邏輯部分、觸發(fā)器和鎖存器。這些基本的單元通常都可以用FPGA內(nèi)部的通用邏輯資源LE來綜合得到,也就是說通過LE就能夠搭建出滿足需要的電路,然而用通用邏輯資源來做乘法、SRAM等非常消耗LE。所以FPGA中引入了SRAM、硬件乘法器和PLL等專用邏輯資源,滿足一些特殊的用途,調(diào)用它們能夠更加方便簡單的搭建出功能更加強(qiáng)大的電路出來。所以衡量一個FPGA的資源光看LE的個數(shù)是不行的,還要看其里面的專用邏輯資源等。
從上面的分析中,我們可以將FPGA中的通用邏輯資源看成單片機(jī)的內(nèi)核,其他的像乘法器等專用硬件資源可以看成是片內(nèi)的外設(shè),這樣可以和單片機(jī)的思想關(guān)聯(lián)起來:針對邏輯資源,我們可以用比較靈活的語言去組合成上述三種基本單元,而片內(nèi)的外設(shè)只要用專用的指令或者標(biāo)準(zhǔn)的庫去調(diào)用即可,其作為內(nèi)核的輔助單元。
其中,在內(nèi)核部分的編程中,組合邏輯部分可以用assign或者內(nèi)部邏輯判斷封閉的電平敏感always塊來描述;觸發(fā)器用邊緣敏感的always塊來描述;鎖存器用內(nèi)部邏輯判斷不封閉的電平敏感always塊來描述。
上面已經(jīng)給出了各種基本單元的verilog語言描述,不管一個多么復(fù)雜的電路,也可以用以上三種基本單元的基本描述方法來組合,就能夠組合出很復(fù)雜的電路描述出來。
上面我們講述了FPGA和單片機(jī)開發(fā)上的相似點(diǎn),但是他們還是有本質(zhì)上的區(qū)別:FPGA和單片機(jī)的思想不一樣,其語言是一種描述語言,包括行為級描述和門級描述,其不是一種設(shè)計(jì)語言,不能和C語言一樣創(chuàng)造出新的東西出來,只能借助于FPGA內(nèi)部的硬件資源,來表達(dá)電路的功能,只能按照米來下鍋,否則就可能寫出不能綜合的代碼,另外代碼執(zhí)行的方式也不一樣,不在贅述。
故寫FPGA程序的正確方法是:首先,我們要對所需求的電路怎么用硬件電路搭建有一個比較清楚的概念,然后再用規(guī)范的硬件描述語言去描述該電路,然后再去仿真分析。
ALTERA的LE結(jié)構(gòu)
要能寫出能夠綜合的代碼,首先要掌握LE的內(nèi)部邏輯資源。LE的結(jié)構(gòu)主要分為三部分:基于查表法的帶進(jìn)位鏈的四輸入函數(shù)發(fā)生器,包括時鐘編程(包括選擇以及使能)、同步置位(清零)、異步置位(清零)的可編程寄存器,以及一些可編程互聯(lián)線。
其中,可編程寄存器可以配置成各種觸發(fā)器,常見的D觸發(fā)器,或者也可以配置成鎖存器。有些需要注意的是:配置成觸發(fā)器的時候,要看其寄存器的輸出有沒有Q反,倘若有Q反,則可以寫成具有反向輸出的D觸發(fā)器,要是沒有,則并不能配置成具有反向輸出的D觸發(fā)器,其將占用兩個不同的邏輯單元來完成,這種寫法是不好的,采用邏輯取反更好。
reg data_reg, data_regn ;
always@(posedge clk)
begin
data_reg 《= data_in;
data_regn 《= ~ data_in;
end
這種寫法是不好的,應(yīng)該用邏輯取反獲得反向信號。
四輸入函數(shù)發(fā)生器可以配置成組合邏輯,也可以配置成同步RAM,同時函數(shù)運(yùn)算的結(jié)果可以通過后面的寄存器,也可以不通過后面的寄存器直接輸出,這樣從而可以方便的寫出純組合邏輯電路,也可以寫成時序電路。
一、延時的方法
1.在代碼中寫:
假設(shè)延時的路徑為a到b,dly為延時門:
reg dly/*synthesis keep*/;
always @(posedge clk)
begin
dly《=a;
b《=dly;
end
綜合時,上述寫法往往會被綜合器認(rèn)為是冗余邏輯而把dly優(yōu)化掉,為了保住dly,需要在聲明dly時加入/*synthesis keep*/注釋,需注意,這種注釋僅僅適用于Quartus自帶綜合器。若使用synplify(為/* synthesis syn_keep=1 */)等其他第三方綜合器請查閱該綜合器的文檔獲得顯式綜合保留聲明的方法。
2.用assignment editor約束編輯器實(shí)現(xiàn):
在from欄和to欄,用node finder選擇延時路徑的起點(diǎn)和終點(diǎn)。Assignment Name欄選擇LCELL Insertion,Value欄填入要加幾個門即可。
二、觸發(fā)器和鎖存器的標(biāo)準(zhǔn)寫法
1、D觸發(fā)器
//D觸發(fā)器
reg data_reg;
always@(posedge clk)
begin
data_reg 《= data_in;
end
(1)、always@(posedge clk or negedge clk) 不合法
需要注意的是一般都是單邊緣觸發(fā),不能上下沿同時計(jì)數(shù),雙邊緣觸發(fā)的需要特殊的硬件結(jié)構(gòu)的支持,但是一般需要注意的如果需要這種支持,則說明需要改設(shè)計(jì)。也就是說不能寫成always@(posedge clk or negedge clk)的形式,要么可以改成電平敏感的事件,或者就是錯誤的方式,當(dāng)要綜合成計(jì)數(shù)器是錯誤的,同時最好不要混合使用上升沿和下降沿的觸發(fā)器,最好統(tǒng)一用上升沿,或者下降沿。用D觸發(fā)器設(shè)計(jì)狀態(tài)機(jī)的時候,一定要注意采用同步的狀態(tài)機(jī)。
(2)也不要再一個always模塊中采用一信號的上升沿,在另一個模塊中采用其的下降沿。
(3)采用《=的非阻塞賦值的方式。采用非阻塞和阻塞賦值的原則一般為:
觸發(fā)器和鎖存器采用非阻塞賦值,純組合邏輯的always塊用阻塞賦值,既有組合邏輯又有時序邏輯的always塊要用非阻塞賦值,并且不要一個always塊內(nèi)部既用阻塞賦值又有非阻塞賦值。
// D觸發(fā)器,帶異步清零(置位)
reg data_reg;
always@(posedge clk or posedge clr)
begin
if(clr) data_reg《=0;
else data_reg 《= data_in;
end
// D觸發(fā)器,帶同步清零(置位)
reg data_reg;
always@(posedge clk)
begin
if(clr) data_reg《=0;
else data_reg 《= data_in;
end
(1)、異步和同步方式的區(qū)別就在于控制信號是否出現(xiàn)在敏感時間列表中
異步方式中,清零或置位等控制信號需要出現(xiàn)中敏感列表中,同步則不需要出現(xiàn)中敏感列表中,直接在模塊內(nèi)部用于條件判斷即可。由于FPGA的LE中的寄存器結(jié)構(gòu)中有異步加載等引線的結(jié)構(gòu),這樣直接導(dǎo)致同步和異步的綜合方式不一樣,異步清零或置位直接依靠寄存器的內(nèi)部結(jié)構(gòu)實(shí)現(xiàn)的,比較簡單,而同步的方式則是在輸入端添加控制信號邏輯來實(shí)現(xiàn)的,略復(fù)雜些。在下圖中可以清楚的看到不同。
圖1 AlTERA的LE內(nèi)部結(jié)構(gòu)簡明示意圖
(2)、異步清零或置位雖然是電平觸發(fā)的時間,但是為了和clk一致,也要寫成上升沿觸發(fā)的方式。
// D觸發(fā)器,帶時鐘使能
reg data_reg;
always@( posedge clk)
begin
if(clk_enable) data_reg 《 = data_in;
end
當(dāng)然,該語句和如下的例子是等價(jià)的:
reg data_reg;
assign clk2 = clk1 & clk_enable;
always@( posedge clk1)
begin
data_reg = data_in;
end
需要注意的是:
第二種寫法是沒有第一種好的,在LE的內(nèi)部,連接寄存器的有兩個時鐘線以及時鐘選擇使能信號線。當(dāng)采用一個時鐘線的時候,也將采用其時鐘使能信號線,也就是說時鐘的使能最好能使用內(nèi)部和寄存器直接相連的使能線,而不要采用另外的組合邏輯。
2、D鎖存器
reg data_latch;
always@(data_in or enable)
begin
if(enable) data_latch = data_in;
end
(1)、鎖存器的輸入信號也在敏感列表中出現(xiàn),這點(diǎn)和寄存器是不一樣的。
(2)、根據(jù)鎖存器的特性,要綜合成鎖存器,if語句中一定不要用一個封閉的判斷語句,如下面一個語句就不會綜合成鎖存器,而會綜合成組合邏輯。其實(shí),這一點(diǎn)也說明了always也能描述組合邏輯。
(3)、采用阻塞賦值的方式。
reg data_latch;
always@(data_in or enable)
begin
if(enable) data_latch = data_in;
else data_latch =0;
end
為了用always塊要描述組合邏輯,為了不要綜合成鎖存器,里面的if語句一定要寫成一個封閉的形式,采用case也要加default。
其實(shí),上述電路描述可以等效為:
reg data_latch;
always@(data_in or enable)
begin
data_latch = data_in & enable;
end
這個就是一個組合邏輯的描述方式,再這主要是為了說明if else 和邏輯運(yùn)算符&是等價(jià)的,兩者可以互換,但是邏輯運(yùn)算符更符合電路的描述方法,應(yīng)該更多的采用邏輯運(yùn)算符。
3、always描述組合邏輯
除了在上面講到要組成一個組合邏輯電路,最好用邏輯運(yùn)算符來描述,但是需要強(qiáng)調(diào)是,所有的參與賦值的信號都必須在always后的敏感列表中列出,倘若沒有列出就有可能引入透明的鎖存器:倘若信號A沒有在敏感列表中列出,A的變化只有在列出的變量變化的時候才能反映出來,這點(diǎn)對不對需要我們?nèi)ピ囼?yàn)驗(yàn)證。
所以寫好組合邏輯電路的兩點(diǎn):1、快內(nèi)的邏輯要封閉;2、賦值信號全部出現(xiàn)在敏感列表中。
評論
查看更多