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