文章目錄
聲明:本文為原創作品,著作權歸akuei2及黑金動力社區(http://www.heijin.org)共同所有,如需轉載,請註明出處http://www.cnblogs.com/kingst/
4.3 命令式的仿順序操作
什麼是 Verilog HDL 式的仿順序操作!?在明白這東西之前,我們先看幾個例子:
假設我要建立 可以產生 SSS,S0S,0S0,000 這四種模組。如果模仿C語言函數會是如下:
//基礎函數
S_Function(){...}
O_Function(){...}
//基於基礎函數建立的函數
SSS_Function()
{
S_Function(); S_Function(); S_Function();
}
SOS_Fucntion()
{
S_Function(); O_Function(); S_Function();
}
OSO_Fucntion()
{
O_Function(); S_Function(); O_Function();
}
OOO_Fucntion()
{
O_Function(); O_Function(); O_Function();
}
我們會很自然的,以S_Function() 和 O_Fucntion() 為基礎,再建立出四個新的 SOS_Function(), SSS_Function(), OSO_Function(), 和 OOO_Fucntion()。在仿順序語言上(如C語言),這樣的方法當然,沒有問題,但是在 Verilog HDL上呢?
筆者說過“仿順序操作”歸根究底不是單純模仿順序操作而已,而是利用Verilog HDL語言本身的特質,去模仿順序操作。4.1章~4.2章的實驗,雖然達到以上這一點,但這隻是僅限於“少個函數”而已。
我們來看看,如果以 4.1章~4.2章為基礎,來模仿以上的順序操作的話,會是什麼樣一個結果。
上!可怕吧!逐漸建立 SSS組合模組,SOS組合模組,OSO組合模組和OOO模組。然而每一個組合模組都包含各自的 “控制模組”,“S摩斯碼模組”,“O模式碼模組”
和“選取器”。最後,每一個組合模組的輸出,還需要一個輸出選取器來協調操作。
我們從另一個方面來分析它的缺點:
(一)模組的重複,資源的消耗。
(二)建模量多,連線設計繁多。
(三)模組調用的難度。
諸如以上等。如果你有筆者這樣的耐性,當然沒有問題,但是實際上筆者也覺得非常“猥瑣”,而且建模的設計也很苦難。所以我們需要另一種“仿順序操作”的方法,畢竟4-1章 和 4-2章的方法,只適合小規模的“仿順序操作”。
那個方法即是“命令式的仿順序操作”。
何謂“命令式的仿順序操作?”我們來看看下面的一張圖,就可以知道個大概:
如!結果我們可以把如上的建模精簡到這樣的程度, 當然在各個所包含的內容中,代碼的結構也是有保障的。如果繼續引入以上的例子,那麼:
function_module.v
module function_module
(
......
Function_Start_Sig,
Function_Done_Sig,
Pin_Out
);
......
input [1:0]Function_Start_Sig;
input Function_Done_Sig;
output Pin_Out;
/******************************************/
//定時器和延時器
......
/*****************************************/
reg [3:0]i;
reg rPin_Out;
reg isDone;
......
always @ ( posedge or CLK or negedge RSTn )
if( !RSTn )
begin
i <= 4'd0;
rPin_Out <= 1'b0;
isDone <= 1'b0;
......
end
===> else if( Function_Start_Sig[1] )
case( i )
// S摩斯碼產生
......
4'd 9:
begin isDone <= 1'b1; i <= i + 1'b1; end
4'd10:
begin isDone <= 1'b0; i <= 4'd0;
endcase
===> else if( Function_Start_Sig[0] )
case( i )
// 0摩斯碼產生
......
4'd 9:
begin isDone <= 1'b1; i <= i + 1'b1; end
4'd10:
begin isDone <= 1'b0; i <= 4'd0;
endcase
/*************************************************/
assign Function_Done_Sig = isDone;
assign Pin_Out = rPin_Out;
/*************************************************/
endmodule
關於 function_module.v 關鍵的部分是 Function_Start_Sig 的位寬和 else if 部分。
Function_Start_Sig 的每一位“位寬”都代表不同的“Start_Sig”。
Function_Start_Sig[ 1..0 ]
位命令
功能
10
S莫斯碼產生
01
O摩斯碼產生
然而 “Done_Sig” 和以往一樣,沒有任何變化。假設我要產生 S模式碼,那麼我只要往 Function_Start_Sig 輸入 2'b10 即可。
cmd_control_module.v
module cmd_control_module
(
......
Command_Start_Sig,
Commnad_Done_Sig,
Function_Start_Sig,
Fucntion_Done_Sig
);
input [3:0]Command_Start_Sig;
output Command_Done_Sig;
output [1:0]Function_Start_Sig;
input Function_Done_Sig;
/********************************************/
reg [3:0]i;
reg [1:0]isStart;
reg isDone;
always @ ( posedge CLK or negedge RSTn )
if( !RSTn )
begin
i <= 4'd0;
isStart <= 2'b00;
isDone <= 1'b0;
end
==> else if( Start_Sig[3] ) // 產生SSS
case( i )
4'd0, 4'd1, 4'd2 :
if( Fucntion_Done_Sig ) begin isStart <= 2'b00; i <= i + 1'b1; end
else isStart <= 2'b10;
4'd3:
begin isDone <= 1'b1; i <= i + 1'b1; end
4'd4:
begin isDone <= 1'b0; i <= i + 1'b1; end
endcase
==> else if( Start_Sig[2] ) // 產生SOS
case( i )
4'd0, 4'd2 :
if( Fucntion_Done_Sig ) begin isStart <= 2'b00; i <= i + 1'b1; end
else isStart <= 2'b10;
4'd1 :
if( Fucntion_Done_Sig ) begin isStart <= 2'b00; i <= i + 1'b1; end
else isStart <= 2'b01;
4'd3:
begin isDone <= 1'b1; i <= i + 1'b1; end
4'd4:
begin isDone <= 1'b0; i <= i + 1'b1; end
endcase
==> else if( Start_Sig[1] ) // 產生OSO
case( i )
4'd0, 4'd2 :
if( Fucntion_Done_Sig ) begin isStart <= 2'b00; i <= i + 1'b1; end
else isStart <= 2'b01;
4'd1 :
if( Fucntion_Done_Sig ) begin isStart <= 2'b00; i <= i + 1'b1; end
else isStart <= 2'b10;
4'd3:
begin isDone <= 1'b1; i <= i + 1'b1; end
4'd4:
begin isDone <= 1'b0; i <= i + 1'b1; end
endcase
==> else if( Start_Sig[1] ) // 產生OOO
case( i )
4'd0, 4'd1, 4'd2 :
if( Fucntion_Done_Sig ) begin isStart <= 2'b00; i <= i + 1'b1; end
else isStart <= 2'b01;
4'd3:
begin isDone <= 1'b1; i <= i + 1'b1; end
4'd4:
begin isDone <= 1'b0; i <= i + 1'b1; end
endcase
/**********************************************************/
assign Function_Start_Sig = isStart;
assign Command_Done_Sig = isDone;
/**********************************************************/
endmodule
和 function_module.v 一樣 command_control_module 最關鍵的部分同樣是 Command_Start_Sig 的“位寬”和 else if 部分,位寬分配如下:
Command_Start_Sig[ 3..0 ]
位命令
功能
1000
產生SSS
0100
產生SOS
0010
產生OSO
0001
產生OOO
假設我輸入 Command_Start_Sig 是 0100,
一、對 function_module.v 輸入 2'b10 , 產生 S摩斯碼 ,返回 Function_Done_Sig。
二、對 function_module.v 輸入 2'b01 , 產生 O摩斯碼 ,返回 Function_Done_Sig。
三、對 function_module.v 輸入 2'b10 , 產生 S摩斯碼 ,返回 Function_Done_Sig。
四、返回Command_Done_Sig 。
“命令式仿順序操作”的基本思路就那麼簡單,下一章我們以一個實驗來說明。
實驗十三:DS1302 系統時鐘驅動
雖說有關 DS1302 的資料都是網路滿天飛,在這裡我還是介紹一點點吧。系統時鐘晶片,大家應該明白是什麼吧,就是一種控制時鐘的晶片。一旦初始化後,它就會隨著現實的時鐘一隻計數。要明白DS1302晶片最主要的關鍵,就是“時序”和晶片本身的“寄存器”啦。
嗯!是DS1302晶片寫操作的時序圖。第一個位元組是“訪問寄存器的地址”,第二位元組是“寫資料”。在寫操作的時候,都是“上升沿有效”,然而還有一個條件,就是CE(/RST)訊號必須拉高。(資料都是從LSB開始發送,亦即最低位開始最高位結束)
是DS1302晶片讀操作的時序圖。基本上和寫操作的時序圖大同小異,區別的地方就是在第二個位元組時“讀資料”的動作。同樣,在第二位元組讀資料的開始時,SCLK訊號都是“下降沿有效”。嗯,別忘了CE(/RST)訊號同樣是必須拉高。(第一節資料是從LSB開始輸出,第二節資料是從LSB開始讀入)
無論是讀操作還是寫操作,在時序圖中,第一個位元組都是“訪問寄存器的地址”,然而這一位元組資料有自己的格式。
BIT 7 固定。
BIT 6 表示是訪問寄存器本身,還是訪問RAM空間。
BIT 5 .. 1 表示是寄存器|RAM空間的地址。
BIT 0 表示是訪問寄存器本身是寫操作,還是讀操作。
是寄存器地址的全家福。啊有一點,我必須強調,Verilog HDL語言有的是很強的位操作,“訪問寄存器的地址”可以這樣表示:
{ 2'b10 , 5'd Addr, 1'b RD/W }
(這樣就可以再度提高解讀性)我們知道BIT 7是固定的位,然而BIT 6表示“訪問RAM空間還是訪問寄存器”。在寄存器地址的全家福中,BIT 6 都是清一色的為“邏輯0”。
假設要寫秒寄存器,那麼我可以如此輸入:
{ 2'b10, 5'd0, 1'b0 }
再假設我要讀秒寄存器,那麼我可以這樣表示:
{ 2'b10, 5'd0, 1'b1 }
表達了每一個寄存器的位元組配置。秒寄存器(第一個),前高四位(BIT7除外),表示“秒的十位”,低四位表示“秒的個位”。其他的寄存器的位元組配置也是如此。但是有3個寄存器比較特別,那就是“秒寄存器”,“時寄存器”,“控制寄存器”(最後第二個)。
秒寄存器的最高位(BIT7),如果寫入“邏輯0”DS1302晶片就開始啟動,反之就關閉。
時寄存器的最高位(BIT7),表示了“邏輯1是 12小時進位”,“邏輯0 24小時進位”,筆者保守的認為,還是24小時進位比較方便工作。
控制寄存器的最高位(BIT7),如果寫入“邏輯0”表示關閉防寫保護,寫入“邏輯1”表示開啟防寫保護。所以呀,每當要變更寄存器的內容之前,就要關閉防寫保護。
是RAM的全家福。RAM的空間有 2^5 - 2 = 0~30 , 亦即 31 words x 8 bits 的空間。由於是訪問RAM,所以“訪問寄存器的地址”的BIT6必須是邏輯1。RAM地址的範圍如下:
{ 2'b11 , 5'd0 , 1'b RD/W } ~ { 2'b11 , 5'd30 , 1'b RD/W }
如果我要關閉防寫保護,那麼我需要的操作如下:
{ 2'b10, 5'd 7, 1'b0 }
{ 8'h00 }
如果我要在時寄存器寫入 10 十進位( 如果以24小時進位 ),那麼我需要如下的操作:
{ 2'b10, 5'd2, 1'b0 }
{ 4'h1, 4'h0 }
如果我要在分寄存器寫入 20 十進位,那麼我需要的操作如下:
{ 2'b10, 5'd1, 1'b0 }
{ 4'h2, 4'h0 }
如果我要在秒寄存器寫入 33 十進位,那麼我需要的操作如下:
{ 2'b10, 5'd0, 1'b0 }
{ 4'h3, 4'h3 }
(在這裡我們知道,秒寄存器的最高位,控制著DS1302晶片的啟動和關閉,所以秒鐘寄存器的配置都是留在最後才操作。因為變更秒寄存器如同啟動DS1302晶片)
如果我要在地址20, RAM空間寫入 0xff的資料,那麼我需要如下的操作:
{ 2'b11, 5'd 20, 1'b0 }
{ 8'hff }
如果我要在時寄存器讀出的“十位和個位”( 如果以24小時進位 ),那麼我需要如下的操作:
{ 2'b10, 5'd2, 1'b1 }
{ 4'h讀出時十位, 4'h讀出時個位 }
如果我要在秒寄存器讀出“十位和個位”,那麼我需要的操作如下:
{ 2'b10, 5'd1, 1'b1 }
{ 4'h讀出秒十位, 4'h讀出秒個位}
如果我要在地址20, RAM空間讀出資料,那麼我需要如下的操作:
{ 2'b11, 5'd 20, 1'b1}
{ 8'h讀出資料 }
我們要建立的組合模組 ds1302_module.v 基本上如。首先我們先把焦點放在 function_module.v 。我們知道如果以“命令式仿順序操作”,函數模組,必須包含“兩個最基本的函數”,亦即“寫位元組函數”和“讀位元組函數”。
在我們可見 函數模組 的開始訊號 Access_Start_Sig 的位寬有兩位,它們分別是:
Access_Start_Sig [ 1..0 ]
位命令
功能
10
寫位元組操作
01
讀位元組操作
此外,還有由上層模組輸入的 Words_Addr 和 Write_Data ,亦即“寫一位元組操作”所要求的“第一位元組”和“第二位元組”資料。Read_Data 和 Access_Done_Sig 分別是返回的“讀出資料”和“完成訊號”。
具體的操作,我們還是直接看代碼吧!
function_module.v
第1~23行表示了該模組輸入輸出口,注意 SIO 是IO口(11行)。說道IO,
左圖是一個IO的硬體設計。如果要該IO輸出,這時候 isOut必須拉高,同時間 Data_Out 的資料就會輸出。如果要該IO為輸入,這時候我需要拉低 isOut,然而三態門會輸出高阻態將“輸出”載止 , 從IO口輸入的資料就會經向Data_In。如果使用 Verilog HDL來表示:
assign IO = isOut ? Data_Out : 1'bz;
assign Data_In = IO;
在142行,定義了 SDA 這個 IO口,是由 isOut 這個寄存器控制著“輸入輸出”。
當 isOut為邏輯1時,該IO口是輸出狀態,反之是輸入狀態。然而輸入只直接在操作中調用(122行)。當然我們也可以這樣:
wire SIO_In;
assign SIO_In = SIO;
assign SIO = isOut ? rSIO : 1'bz;
然後在調用的地方,可以這樣寫, 結果也是一樣。
rData[ ( i >> 1 ) - 9 ] = SDA_In;
在45~51行定義了相關的寄存器,i是指示著執行步驟,rData用來暫存資料,rSCLK用來驅動 SCLK ,rRST用來驅動 RST , rSIO 用來驅動 SIO的輸出,isOut用來控制 IO口的方向,最後的 isDone 是完成標誌,亦即用來反饋完成資訊。
在65~98行也是Start_Sig 為 2'b10 的時候,亦即是“寫位元組操作”。在這段內容之中,代碼完全是按照時序圖執行。
在步驟0的時候,對rData,rSCLK,rRST(CE),isOut等寄存器進行初始化,這一點很重要(68行)。
然後在步驟1~16之中,將“第一個位元組資料”,亦即“訪問寄存器地址位元組”發送出去。傳輸規則和SPI有點相似,都是時間下降沿設定資料,時間上升沿鎖存資料。(70~76行)此外在步驟17再一次對rData設定為“第二個位元組資料”。(79行)
然後重複如同步驟 1~16那樣(81~87行),將“第二位元組資料”發送出去。在步驟34,對rRST (CE) 拉低,以示“寫位元組操作”已經結束。最後在步驟35~36反饋完成訊號。
在99~133行也是Start_Sig 為 2'b01 的時候,亦即是“讀位元組操作”。在讀操作中,第一位元組和第二位元組資料顯然,對時間沿的敏感不同。
在步驟0的時候,對rData,rSCLK,rRST(CE),isOut等寄存器進行初始化,這一點很重要(103行)。
然後在步驟1~16之中,將“第一個位元組資料”,亦即“訪問寄存器地址位元組”發送出去。這時的資料鎖存發生在時間的上升沿。(105~111行)在步驟17對IO口的方向,改變為輸入,亦即將isOut設定為邏輯0。(114行)
在步驟18~33之間是“讀取一個位元組資料的操作”,該動作時時間的下降沿,對SIO信後讀取資料(116~122行)。在這裡我再強調一下,DS1302晶片,資料的傳輸都是從LSB開始到MSB結束。
最後在步驟35對rRST的拉低,以示“讀位元組資料”操作已經結束。然後恢複IO口為輸出,亦即拉高isOut寄存器(125行),然後產生一個完成訊號(127~131行)。
在122行,從DS1302晶片讀取的資料會暫存在rData這個寄存器,然後該寄存器會驅動Read_Data 這個訊號線(137行)。
接下來我們要探討的就是 cmd_control_module.v, 從“圖形”看來 cmd_control_module.v 是 function_module.v 的上層模組。從順序操作上看來, cmd_control_module.v的功能如下(以下只是虛擬碼,希望讀者不要太認真,為了給讀者一個感知的認識):
Function_Module( Command, Addr, Data )
{
case( Command )
{
2'b10 : Write_Function( Addr, Data ); // Write operation
2'b11 : Read_Fucntion( Addr ) { return Data; } // Read operation
}
}
CMD_Control_Module( Command )
{
case( Command )
{
8'b10000000 : Function_module( 2'b10, {2'b10,5'd0,1'b0}, 8'h00 ); // Unprotect
8'b01000000 : Function_module( 2'b10, {2'b10,5'd2,1'b0}, 8'h00 ); // Write hour
8'b00100000 : Function_module( 2'b10, {2'b10,5'd1,1'b0}, 8'h00 ); // Write minit
8'b00010000 : Function_module( 2'b10, {2'b10,5'd0,1'b0}, 8'h00 ); // Write second
8'b00001000 : Function_module( 2'b10, {2'b10,5'd0,1'b0}, 8'h80 ); // Protect
8'b00000100 : Function_module( 2'b01, {2'b10,5'd2,1'b1} ); // Read hour
8'b00000010 : Function_module( 2'b01, {2'b10,5'd1,1'b1} ); // Read minit
8'b00000001 : Function_module( 2'b01, {2'b10,5'd0,1'b1} ); // Read second
}
}
從上面的虛擬碼看來 CMD_Control_Module 反應出 cmd_control_module 是利用 Start_Sig 的8位位寬來定義8中不同的操作。而且在這8個不同的操作之中,都對function_module.v 都有不同的操作。
(位寬對命令分配如下)
Start_Sig[ 7..0 ]
位命令
功能
1000_0000
關閉防寫保護
0100_0000
變更時寄存器
0010_0000
變更分寄存器
0001_0000
變更秒寄存器
0000_1000
開啟防寫保護
0000_0100
讀取時寄存器
0000_0010
讀取分寄存器
0000_0001
讀取秒寄存器
cmd_control_module.v
第2~34行的介面定義和“圖形”是一致的。在38~39行定義了針對Words_Addr, 和 Write_Data 的rAddr 和 rData 暫存的寄存器。換句話說,rAddr寄存器是用來驅動 Words_Addr訊號,rData寄存器是用來驅動 Write_Data訊號。
我們知道在8位 Start_Sig 的位寬之中,Start_Sig[7..3] 是寫操作,反之 Start_Sig[2..0]是讀操作。在DS1302晶片的時序中“寫操作”的第一個位元組需要“訪問寄存器的地址”,第二個位元組是“寫資料”。
然而在48~74行之中,針對這一內容對於rAddr 和 rData寄存器執行賦值。如在8'b1000_0000 的時候,是“關閉防寫保護”的操作,換句話說就是要往“控制寄存器”寫入“資料8'h00”。故對 rAddr和 rData 賦值 {2'b10, 5'd7, 1'b0},8'h00。再舉一個例子,當Start_Sig 等價於 8'b0100_0000的時候,即表示對“時寄存器”,“寫入資料”。這時候對 rAddr 賦予早已經預定好的值,亦即 { 2'b10, 5'd2, 1'b0 }。然而不同的是,rData被賦予的值,是從上層發來的 Time_Write_Data。
至於 Start_Sig[2..0] 是表示“讀操作”,在DS1302晶片的時序中,讀操作只需要寫入“第一位元組資料”,第二位元組資料是從DS1302讀來的。舉個例子,如當 Start_Sig 等價於 8’b0000_0001 是表示從“秒寄存器讀出資料”,所以關於這個操作rAddr被賦予{ 2'b10, 5'd0, 1'b1 }。
在76~120行,是該模組的具體操作。i寄存器表示執行步驟,rRead寄存器是讀出資料的暫存寄存器,isStart寄存器是用於驅動 Access_Start_Sig ,亦即是對 function_module.v 的控制寄存器(78~80行)。
在這裡我再重申一下 Start_Sig[7..3] 是“寫操作”,Start_Sig[2..0]是“讀操作”。為了避免跟多無謂的資源浪費,在91~118行採用了複用的寫法。
假設一個請款,當Start_Sig 等價於 8'b1000_0000 的時候,我們知道這是“關閉防寫保護”的操作。在同一時間rAddr和rData都會被賦值(51行)。然後91行的if條件就會成立。那麼一次性行為的寫操作就會發生(94~102行),當一次性的寫操作完成後,它會反饋完成訊號。
我們再假設一個情況,當Start_Sig 等價於 8'b0000_0001的時候,這表示“從秒寄存器讀取資料”。在同一個瞬間 rAddr 會被賦予相關的值(72行),然後在 105行 if條件就會成立,在108~116行就會完成一次的“讀位元組資料”的操作。
當完成一次性的“讀位元組資料”,讀取到的資料就會被暫存在 rRead寄存器(109行),最後反饋一個完成訊號。以示上一層模組“一次性的讀資料操作”已經完成。
嗯!終於完成對 function_module.v 和 cmd_control_module.v 的解釋了。最後的工作就是把它們組合成為 ds1302_module.v。
ds1302_module.v
組合模組ds1302_module.v 基本和“圖形”沒有什麼大不同,自己看著辦吧。
實驗十三說明:
在實驗十三中 function_module.v 包含了底層的操作,如果從順序操作的角度看來 function_module.v 包含了底層函數。然而 cmd_control_module.v 不是以“函數的形式”,基於底層函數去建立更高層的函數,相反的 cmd_control_module.v 是以“位命令的形式”去“控制著每一個更高層函數的執行步驟和操作”。
完成後的擴充圖:
實驗十三結論:
這個實驗和以往的實驗很不同,主要是混合了Verilog HDL語言本身的位操作和仿順序操作。實驗十三重點不是在於 DS1302晶片的驅動,而是“命令式仿順序操作”的設計。
筆者為了使筆記精簡僅僅為 cmd_control_module.v 配置8個命令而已,實際上可以達到更多,這要讀者自己看著辦。
實驗十三示範:
這個示範主要是示範對 ds1302_module.v 的調用。控制模組具體操作如下
一、關閉防寫保護,亦即發送命令 8'b1000_0000;
二、變更時寄存器,亦即發送命令 8'b0100_0000;
三、變更分寄存器,亦即發送命令 8'b0010_0000;
四、變更秒寄存器,亦即發送命令 8'b0001_0000;
五、最後永遠讀取秒寄存器的值,亦即發送命令 8'b0000_0001。然後將秒個位往
四位LED資源發送。
exp13_demo.v
在步驟0(35~37行),該模組向 ds1302_module.v 發送關閉防寫保護的命令,已經8'b1000_000。然後步驟1~3分別對,時寄存器,分寄存器和秒寄存器寫入資料,分別是寫入12時,22分,22秒(39~49行)。在步驟4,會一直從秒寄存器讀取,系統時鐘的秒值,換句話說該模組會一直對 ds1302_module.v 發送 8'b0000_0001的命令。每當完成“一次讀位元組操作”,就會對rLED賦予“秒個位”的值。最後由rLED寄存器驅動LED訊號。
實驗十三示範說明:
筆者開始懶惰了,所以只是讀取“秒個位”往LED輸出。啊哈哈!
實驗十三示範結論:
ds1302_module.v 的調用示範。
總結:
當你把筆記看到這裡,筆者只能說恭喜你了。因為經第二章到第四章,讀者已經瞭解都是“低級建模”所敘述的概念。筆記第二章的內容是表示“低級建模”的結構和Verilog HDL語言特性的概念。筆記的第三章是表示“低級建模”的基礎建模執行個體。然而筆記的第四章是說“仿順序操作”這一概念。
無論是筆記的那一章,都是“低級建模”重要的一部分,誰也不可缺少誰。可能讀者會在第二章瞭解到 Verilog HDL 語言容易被忽略的小細節。讀者又有可從第三章瞭解到“低級建模”一直強調的“準則”和“模組性質”的重要性等細節。筆記第四章中的“仿順序操作”作為“低級建模”的一部分,是不可或缺的。
因為“仿順序操作”作為“步驟概念”是 Verilog HDL語言不可缺少的一個部分,就像筆者在第二章中的說所,Verilog HDL是一個擁有並行性質的語言,多少對於“順序操作”都會有點不足。筆者所強調的“仿順序操作”,就是有效“利用Verilog HDL本身的特質”去“模仿順序操作”,在同一時間將設計的效果推向更高,既有可能超出“順序操作”本身的極限。
估計當讀者完成13個實驗後,讀者基本上已經掌握筆者所說的“低級建模”的Verilog HDL的建模技巧了。但是有一點筆者必須強調的說:“低級建模的精彩,不僅為前期建模的帶來結構性的優勢,而且還為後期的建模帶來許多方便。”,換句話說“低級建模”真正精彩的部分是在後期的建模。
在第一章中,筆者不是說了嗎,在筆者的眼中Verilog HDL語言 和 FPGA 是一堆樂高積木,不知道多少讀到這裡的筆者能瞭解其中的含義?筆者還說過,對於 Verilog HDL 和 FPGA 語言的學習,必須有形狀!?然而幾乎從第一個實驗到第十三個實驗,筆者完全把 FPGA 和 Verilog HDL語言當成“控制器”來使用。
還有一點,筆者一直很在意的就是“代碼風格”和“建模結構”,這就是筆者在第一章提及的“建模手段”,而對於這個所謂的“建模手段”亦即這本筆記的重點 -“低級建模”。可能讀者經筆者這樣一提,似乎可能明白第一章中筆者所言的“FPGA+Verilog HDL語言是一堆樂高積木”這一回事了吧!
至於什麼是“後期的建模”,這一點筆者還是停留在實驗的階段。畢竟還存在許多不成熟的地方。好了,筆者也不再說什麼了!但是,別忘了“低級建模”還沒有結束,只是目前告一段落!
下一章筆記開始又是一個新的旅程!