實驗一:流水燈模塊
對於發展商而言,動土儀式無疑是最重要的任務。為此,流水燈實驗作為低級建模II的動土儀式再適合不過了。廢話少說,我們還是開始實驗吧。
圖1.1 實驗一建模圖。
如圖1.1 所示,實驗一有名為 led_funcmod的功能模塊。如果無視環境信號(時鍾信號還有復位信號),該功能模塊只有一組輸出端,亦即4位LED信號。接下來讓我們來看具體內容:
led_funcmod.v
1. module led_funcmod
2. (
3. input CLOCK, RESET,
4. output [3:0]LED
5. );
以上內容為出入端聲明。
6. parameter T1S = 26'd50_000_000; //1Hz
7. parameter T100MS = 26'd5_000_000; //10Hz
8. parameter T10MS = 26'd500_000; //100Hz
9. parameter T1MS = 26'd50_000; //1000Hz
10.
以上內容為常量聲明。分別是1秒至1毫秒。
11. reg [3:0]i;
12. reg [25:0]C1;
13. reg [3:0]D;
14. reg [25:0]T;
15. reg [3:0]isTag;
16.
17. always @ ( posedge CLOCK or negedge RESET )
18. if( !RESET )
19. begin
20. i <= 4'd0;
21. C1 <= 26'd0;
22. D <= 4'b0001;
23. T <= T1S;
24. isTag <= 4'b0001;
25. end
26. else
以上內容是相關的寄存器聲明以及復位操作。寄存器i用來指向步驟,寄存器C1用來計數,寄存器D用來暫存結果和驅動輸出,寄存器T用來暫存計數量,isTag則用來暫存延遲標簽。
27. case( i )
28.
29. 0:
30. if( C1 == T -1) begin C1 <= 26'd0; i <= i + 1'b1; end
31. else begin C1 <= C1 + 1'b1; D <= 4'b0001; end
32.
33. 1:
34. if( C1 == T -1) begin C1 <= 26'd0; i <= i + 1'b1; end
35. else begin C1 <= C1 + 1'b1; D <= 4'b0010; end
36.
37. 2:
38. if( C1 == T -1) begin C1 <= 26'd0; i <= i + 1'b1; end
39. else begin C1 <= C1 + 1'b1; D <= 4'b0100; end
40.
41. 3:
42. if( C1 == T -1) begin C1 <= 26'd0; i <= i + 1'b1; end
43. else begin C1 <= C1 + 1'b1; D <= 4'b1000; end
44.
45. 4:
46. begin isTag <= { isTag[2:0], isTag[3] }; i <= i + 1'b1; end
47.
48. 5:
49. if( isTag[0] ) begin T <= T1S; i <= 4'd0; end
50. else if( isTag[1] ) begin T <= T100MS; i <= 4'd0; end
51. else if( isTag[2] ) begin T <= T10MS; i <= 4'd0; end
52. else if( isTag[3] ) begin T <= T1MS; i <= 4'd0; end
53.
54. endcase
55.
56. assign LED = D;
57.
58. endmodule
以上內容為是核心操作以及輸出驅動聲明。步驟0~3用來實現流水燈效果。最初,每個步驟的停留時間是 1秒,然后步驟0~3按順序執行便會產生流水效果。步驟4是用來切換模式,步驟5則是根據isTag的內容再為 T寄存器載入不同的延遲內容,如 [0] 延遲1秒,[1] 延遲100毫妙,[2] 延遲10毫妙,[3] 延遲1毫妙。默認下為模式0(第24行),既延遲1秒(第23行)。
這個實驗所在乎的內容根本不是實驗結果而是低級建模II本身。假若比較《建模篇》的流水實驗,低級建模I與低級建模II之間是有明顯的差距。首先,低級建模II再也不見計數器或者定時器等周邊操作。再者,低級建模II的整合度很高,例如步驟0~3:
1. 0:
2. if( C1 == T -1) begin C1 <= 26'd0; i <= i + 1'b1; end
3. else begin C1 <= C1 + 1'b1; D <= 4'b0001; end
4. 1:
5. if( C1 == T -1) begin C1 <= 26'd0; i <= i + 1'b1; end
6. else begin C1 <= C1 + 1'b1; D<= 4'b0010; end
7. 2:
8. if( C1 ==T -1) begin C1 <= 26'd0; i <= i + 1'b1; end
9. else begin C1 <= C1 + 1'b1; D <= 4'b0100; end
10. 3:
11. if( C1 == T -1) begin C1 <= 26'd0; i <= i + 1'b1; end
12. else begin C1 <= C1 + 1'b1; D <= 4'b1000; end
代碼1.1
內容如代碼1.1所示,步驟0~3每個步驟示意一段完整的小操作,例如步驟0為4’b0001保持一段時間,步驟1為4’b0010保持一段時間,步驟2~3也是如此。其中 -1也考慮了步驟切換的時間。假設流水間隔要求1毫妙,那么每個步驟都會准確無誤停留50000個時鍾。事實上,步驟0也可以換成比較方便的寫法,如代碼1.2所示:
1. reg [3:0] D = 4’b0001;
2. ......
3. 0,1,2,3:
4. if( C1 == T -1) begin D <= { D[2:0], D[3] }; C1 <= 26'd0; i <= i + 1'b1; end
5. else begin C1 <= C1 + 1'b1; end
代碼1.2
代碼1.2表示,只要寄存器D准備好初值,例如 4’b0001,那么步驟0~3都可以共享同樣的操作,如此一來會大大減少行數,節省空間。好奇的同學一定覺得疑惑,既然代碼1.2的寫法那么方便,反之筆者為何要選擇代碼1.1的寫法呢?原因很單純,那是為了清晰模塊內容,以致我們容易腦補時序。感覺上,兩者雖然都差不多,但是我們只要仔看,我們便會發現 ... 代碼1.2它雖然書寫方便,可是細節模糊而且內容也不直觀。
在此,筆者需要強調!低級建模II雖然有整合技巧讓操作變得更加便捷,不過比起整合它更加注重表達能力以及清晰度。這樣做的關鍵是為了發揮主動思想,以便擺脫無謂的仿真。所以說,如果讀者想和低級建模II作朋友,建模之前應該優先考慮內容的清晰度,而不是內容的精簡性。如果內容即精簡又直觀,這種情況當然是最好的結果。
至於實驗一是沒有仿真的必要,因為內容足夠直白,這種程度足以腦補時序。最后筆者還要說道,實驗一雖然沒有什么學習的價值,但是實驗一要表達的信息也非常清楚,即低級建模II是注重清晰,直觀的建模技巧。此外,實驗一也可以作為學習的熱身。