1. 訊號的產生及always塊使用注意事項
1.1 不要在不同的always塊內為同一個變數賦值。即某個訊號出現在<=或=左邊時,只能在一個always塊內。(詳細解釋見 Verilog HDL與數字電路設計 P38)
所以注意,在產生一個訊號時,所有產生該訊號的條件都應放在一個always塊內考慮。
1.2 不要在同一個always塊內同時使用阻塞賦值(=)和非阻塞賦值(<=)。
1.3 使用always塊描述組合邏輯時使用阻塞賦值(=),在使用always塊描述時序邏輯時使用非阻塞賦值(<=)。簡單理解可以是,在電平敏感的always塊內使用阻塞賦值,在邊沿敏感的always塊內使用非阻塞賦值。
1.4 任何在always塊內被賦值的變數都必須是寄存器型(reg)。即<=或=左邊的訊號,必須是reg型,<=或=右邊的訊號可以是reg型也可以是wire型。
另,連接埠聲明中被聲明為input或inout型的連接埠,只能被定義為線網型(wire);被聲明為output型的連接埠,則可以被定義為線網型(wire)或者寄存器型(reg)。如果不定義,則預設為線網型(wire)。
1.5 always的敏感列表中可以同時包括多個電平敏感事件,也可以同時包括多個邊沿敏感事件,但不能同時有電平和邊沿敏感事件。另外,敏感列表中,同時包括一個訊號的上升沿敏感事件和下降沿敏感事件也是不允許的,因為這兩個事件可以合并為一個電平事件。
2. 總clk的使用
always敏感列表裡的邊沿觸發事件,就是一個clk訊號,所以在制定ucf時,邊沿觸發事件訊號都要被定義在clk IO連接埠上,有時隨意分配的clk IO連接埠在Implement時也會出錯。需要到ucf中用
NET "polin" CLOCK_DEDICATED_ROUTE = FALSE; //polin為邊沿觸發事件訊號
語句來規避錯誤。
所以在一個程式中,要盡量使用主clk作為always塊的邊沿觸發訊號。如果有些變數要通過某個訊號的邊沿觸發來產生,那盡量將這個邊沿觸發訊號做成一個判斷條件,然後在產生變數時仍用主clk觸發。
常式:要得到LCD大尺寸屏POL訊號的2分頻、8分頻、16分頻...,在控制板上撥動開關設定不同的狀態,輸出polout切換到不同的pol輸入的分頻訊號。
思路,定義一個counter(cnt_pol)對輸入pol訊號進行計數,則cnt_pol的bit0位與pol輸入訊號一致,cnt_pol的bit1位為pol訊號的2分頻,bit2位為pol的4分頻,bit3位為pol的8分頻,bit4位為pol的16分頻...
counter計數有兩種方法,一種是直接使用pol作為邊沿觸發事件計數:
reg [8:0] cnt_pol;
always @ (posedge polin or negedge rst)
if(!rst) cnt_pol <= 0;
else cnt_pol <= con_pol +1;
(程式中還有一個主clk訊號clkin作為其它訊號的主時鐘)
上面這種方法比較簡單,但是polin就作為了一個clk訊號,只能定義到FPGA的clk IO連接埠,並且實現時容易報錯。
另一種方法是,採用主時鐘訊號為cnt_pol計數的邊沿敏感事件:
reg [8:0] cnt_pol;
reg pold;
wire cnt_event;
always @ (posedge clkin or negedge rst)
if (!rst) pold <= 0;
else pold <= polin;
assign cnt_event = polin & pold;
always @ (posedge clkin or negedge rst)
if (!rst) cnt_pol <= 0;
else if (!cnt_event) ;
else cnt_pol <= cnt_pol +1;
這樣,程式比較多,但整個程式(包括其它部分)只有clkin是clk訊號,避免了上述問題。
對這段程式的解釋:cnt_pol計數的機理與第一種方法不同,先通過第一個always塊(寄存器),對polin訊號進行延遲,產生pold訊號,pold與polin在相位上差一個clkin周期。然後對pold和polin進行與操作並賦值給cnt_event訊號,這樣,cnt_event訊號的每個高電平,即代表一個polin周期。然後再在第二個always塊中,通過判斷cnt_event的狀態,來對cnt_pol計數。簡單講,就是將polin的上升沿,轉成一個訊號的電平狀態,然後通過判斷這個訊號電平的狀態來計數。整個過程使用的邊沿觸發訊號都是主clk。