學(xué)習(xí)FPGA,最關(guān)鍵的是學(xué)什么?有讀者學(xué)了大半年時間的FPGA,學(xué)了串口就只懂串口的設(shè)計,學(xué)了SPI就只懂SPI接口的設(shè)計。每個接口、每個功能,都只是學(xué)一個懂一個。換個功能需求,或者對接口做一個小小的改動,就無從下手了。 設(shè)計代碼,從來都只是模仿,或者不斷地調(diào)試修改,湊代碼。設(shè)計出的代碼也沒有任何規(guī)律,相同的功能,今天設(shè)計和明天設(shè)計都不一樣。這就如學(xué)功夫,今天學(xué)下少林,明天學(xué)下武當(dāng),后天又學(xué)下華山,在這樣的情況下,能成長為高手,那就奇怪了。 在明德?lián)P看來,FPGA設(shè)計應(yīng)該有一套通用的設(shè)計方法。該方法能夠應(yīng)付所有的功能設(shè)計,無論功能怎么變,都可以用該方法來套用。明德?lián)P發(fā)明的這套方法就是至簡設(shè)計法。 至簡設(shè)計法從宏觀上,適應(yīng)所有的功能設(shè)計需求。例如,無論是什么功能,我們都先將其轉(zhuǎn)化成需求波形。然后在此基礎(chǔ)上設(shè)計模塊架構(gòu);在模塊架構(gòu)基礎(chǔ)上設(shè)計信號。這步驟都是通用的、是固化的。 至簡設(shè)計法在微觀上,則制定得實用的規(guī)范。詳細到,要不要添加信號;怎么添加信號;添加信號的名字規(guī)范等,我們都做了詳細的規(guī)定。 下面我們用4個經(jīng)典例子,講述了至簡設(shè)計法的使用技巧。其他復(fù)雜功能,無論怎么變,都是這4個經(jīng)典案例的變種。讀者只需要強化、鞏固技巧,多訓(xùn)練,多應(yīng)用,逐步成長為高手。 至簡設(shè)計法經(jīng)典案例1案例1. 當(dāng)收到en=1后,dout產(chǎn)生一個寬度為10個時鐘周期的高電平脈沖。 需要說明,根據(jù)看波形規(guī)則,在第3個時鐘上沿的時候,看到en==1,根據(jù)功能要求,上升沿之后dout就會變?yōu)?/font>1。10個時鐘周期后,dout將變?yōu)?/font>0。 從功能要求中,看到數(shù)字10,我們就知道要計數(shù),并且是dout==1的次數(shù)為10個。所以我們計算的是dout==1的時鐘次數(shù),并且是10次。為此,補充一個計數(shù)器信號cnt,更新后的波形如下圖。 計數(shù)器cnt要遵守如下原則。 初值一定為0。 除了最后一個,在時鐘上升沿,看到dout==1,就將cnt值加1 在時鐘上升沿時看到dout==1,并且是最后一個時,cnt值不加1,直接清零。 從功能要求和波形圖,我們確認,計數(shù)器cnt是對dout==1進行計數(shù),并且一共數(shù)10個。為此,在GVIM編輯器中輸入“Jsq”并回車,將出現(xiàn)如下代碼。 在第13行,輸入dout==1,在第14行代碼中,輸入10-1,這樣就完成了計數(shù)器設(shè)計。 代碼解釋:第1至第11行,是一個時序always的代碼。該代碼要描述的功能是: 在時鐘clk上升沿或者復(fù)位rst_n的下降沿的時候,always就對cnt判斷條件并變化一次。具體變化過程如下: 如果是rst_n==0,則將cnt變?yōu)?/font>0。 否則(即rst_n==1),如果add_cnt有效,也就是為1的時候。繼續(xù)判斷條件并執(zhí)行。 如果end_cnt有效,即end_cnt==1,則將cnt變?yōu)?/font>0。 否則(即end_cnt==0),cnt就自加1。 否則(即rst_n==1且add_cnt==0的時鐘),cnt保持不變。 上面代碼中add_cnt表示計數(shù)器加1條件,end_cnt表示計數(shù)器數(shù)到最后一個。 上面代碼描述過于復(fù)雜,其實概括起來,功能就是:時鐘上升沿時,如果計數(shù)器加1條件有效,并且是數(shù)到最后一個,則計數(shù)器清零;如果計數(shù)器加1條件有效,但不是最后一個,則計數(shù)器就加1;其他時候,計數(shù)器就保持不變。 那么加1條件,即add_cnt是什么呢?在第13行進行了定義。該行代碼表示,dout==1就是計數(shù)器的加1條件。 那么結(jié)束條件,即end_cnt是什么呢?在第14行進行了定義。該行代碼表示,數(shù)到10個就結(jié)束。其中我們關(guān)注的是那個數(shù)字10,而-1是固定的格式。 add_cnt && cnt==10-1,含義是表示“數(shù)到第10個的時候”,add_cnt &&cnt==x-1表示“數(shù)到第x個的時候”。記住這個規(guī)則。end_cnt==1也表示數(shù)完了。 設(shè)計好計數(shù)器cnt后,我們就可以設(shè)計輸出信號dout了。仔細分析dout,該信號有兩個變化點:變1和變0。我們分析原因,dout變1是由于收到en==1;dout變0,則是數(shù)到了10個或者是數(shù)完了。所以綜上所述,dout的代碼是: 至此,我們完成了主體程序的設(shè)計,接下來補充module的其他部分。 將module的名稱定義為my_ex1。并且我們已經(jīng)知道該模塊有4個信號:clk、rst_n、en和dout。為此,代碼如下: 其中clk、rst_n和en是輸入信號,dout是輸出信號,并且4個信號都是1比特的,根據(jù)這些信息,我們補充輸入輸出端口定義。代碼如下: 接下來定義信號類型。 cnt是用always產(chǎn)生的信號,因此類型為reg。cnt計數(shù)的最大值為9,需要用4根線表示,即位寬是4位。add_cnt和end_cnt都是用assign方式設(shè)計的,因此類型為wire。并且其值是0或者1,1個線表示即可。因此代碼如下: dout是用always方式設(shè)計的,因此類型為reg。并且其值是0或者1,1根線表示即可。因此代碼如下: 至此,整個代碼的設(shè)計工作已經(jīng)完成。整體代碼如下: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | module my_ex1( clk , rst_n , en , dout ); input clk ; input rst_n ; input en ; output dout ;
reg [ 3:0] cnt ; wire add_cnt ; wire end_cnt ; reg dout ;
always @(posedge clk or negedge rst_n)begin if(!rst_n)begin cnt <= 0; end else if(add_cnt)begin if(end_cnt) cnt <= 0; else cnt <= cnt + 1; end end
assign add_cnt = (dout==1); assign end_cnt = add_cnt && cnt==10 -1 ;
always @(posedge clk or negedge rst_n)begin if(rst_n==1'b0)begin dout <= 0; end else if(en==1)begin dout <= 1; end else if(add_cnt && cnt==10-1)begin dout <= 0; end end
endmodule |
如果你覺得有用的話,就請你回個貼或者贊,證明我的付出沒有白費,大家都不容易,q328908175,讓我們共同學(xué)習(xí)。
|