1. int main() 2. { 3. int A; 4. A = 16; 5. }
代碼17.1
話題為進入之前,首先讓我們來聊聊一些題外話。那些學過軟核NIOS的朋友可曾記得,軟核NIOS可利用片上內存作為儲存資源,而且它也能利用SDRAM作為儲存資源,然而問題是在這里 ... 如代碼17.1所示,筆者先建立變量A,然后變量A賦值16。如果站在高級語言上的角度去思考,無論是建立變量A還是為變量A賦值,我們沒有必要去理解變量A利用什么儲存資源,然后賦值變量A又是利用怎樣的儲存功能去實現。
我們只要負責輕松的表層工作,然而那些辛苦的底層工作統統交由編譯器處理。讀者也許會認為那是高級語言的溫柔,不過這股溫柔卻不適合描述語言,還不如說那是一種抹殺性的傷害。這種感覺好比父母親過度溺愛自己的孩子,嬌生慣養的孩子最終只會失去可能性而已。
筆者曾在前面說過,儲存模塊基本上可以分為“儲存資源”還有“儲存方式”。默認下,描述語言可用的儲存資源有寄存器還有片上內存,然而兩者都是內在資源。換之,實驗十六卻利用外在的儲存資源,IIC儲存器。
圖17.1 IIC儲存模塊與RAM儲存模塊。
如圖17.1所示,那是實驗十六的IIC儲存模塊,然而圖17.1也表示IIC儲存模塊的調用方法也不僅近似 RAM儲存模塊,而且只有一方調用它而已。這種感覺好比順序語言的主函數調用某個儲存函數一樣,結果如代碼17.2所示:
1. int ram_func( int Addr, int WrData ) { ... } 2. 3. int main() 4. { 5. ram_func( 0,20 ); 6. ... 7. }
代碼17.2
如代碼17.2所示,第1行聲明函數 ram_func,然后主函數在第5行將其調用,並且傳遞地址參數0,數據參數 20。高級語言是一位順序又寂寞的家伙,函數永遠只能被一方調用而已 ... 換之,描述語言是一位並行又多愁的家伙,模塊有可能同時被兩方以上調用,情況宛如兩位男生同時追求一位少女,對此少女會煩惱選擇誰。哎~這是奢侈的少女憂愁。
圖17.2 雙口RAM儲存模塊。
如圖17.2所示,RAM儲存模塊同時被兩方調用,周邊操作為它寫入數據,核心操作則為它讀出數據。圖17.2也是俗稱的雙口RAM。對此,我們也可以說雙口RAM儲存模是RAM儲存模塊的亞種。
圖17.3 基於雙口RAM儲存模塊的FIFO儲存模塊。
此外,雙口RAM儲存模塊只要稍微更換一下馬甲,然后加入一些先進先出的機制,隨之基於雙口RAM儲存模塊的FIFO儲存模塊便完成,結果如圖17.3所示。對此,我們可以說FIFO儲存模塊是雙口RAM儲存模塊的亞種。
那么問題來了:
“請問,實驗十六的IIC儲存模塊是否也能成為雙口IIC儲存模塊?”,筆者問道。
“再請問,它還可以成為有FIFO機制的儲存模塊呢?”,筆者再問道。
沒錯,上述問題就是實驗十七的主要目的。如果這些要求可以成真,我們便可以斷定描述語言不僅不遜色與順序語言,描述語言也充滿許多可能性,而且儲存類作為一個模塊類有着舉足輕重的地位。廢話少說,我們還是開始實驗吧,因為筆者已經壓抑不了蛋蛋的沖動!
首先我們要明白,片上內存是效率又優秀的儲存資源,基本上只要1個時鍾就可完成讀寫操作,而且讀寫也可以同時進行,兩方也可以同時調用,不過就是不能隨意擴充。反之,IIC儲存器雖然可以隨意擴充,但是又笨又麻煩的它,讀寫操作不僅用時很長,而且不能也同時進行,對此造就兩方不能同時調用的問題。為此,我們必須先解決這個問題。
圖17.4 寫操作與讀操作。
實驗十六告訴我們,IIC儲存模塊有兩位 Call 信號,其中 Call[1] 表示寫操作,Call[0]表示讀操作。不過不管是寫操作還是讀操作,IIC儲存模塊都必須調用IIC儲存器,而且讀寫操作一次也只能進行其中一項。如圖17.4,假設左邊的周邊操作負責寫操作,右邊的核心操作負責讀操作 ... 如果兩者同時拉高 Call 信號就會發生多義性的問題,對此筆者該如何協調呢?
圖17.5 輪流協調。
為了公平起見,筆者采取輪流的方式來協調多義性的問題。圖17.5所是輪流協調的概念圖,一般Call兼職“提問與使能“,即Call一拉高操作便執行。如今Call信號作為第一層提問,isDo則作為第二層使能 ... 換句話說,不管 Call 拉不拉高,只要isDo不拉高,操作也不會執行。如圖17.5所示,isDo位寬有3表示模塊有3種操作,或者說3個操作共享一個模塊資源。至於右邊是稱為使能指針的箭頭,它的作用是給予使能權。
圖17.6 輪流協調例子①。
如圖17.6所示,假設 Call[2] 拉高以示提問,但是指針並沒有指向它,所以它沒有使能權也不能執行操作。這種情況好比舉手的學生沒被老師點名,這位學生就不能隨意開口。當然,使能指針也不是靜止不動,只要遇見有人舉手提問,它便會按照順序檢測各個對象。
圖17.7 輪流協調例子②。
如圖17.7所示,當指針來到Call[2]的面前並且給予使能權,isDo[2]立即拉高使能操作,直至操作完成之前,該操作都享有模塊的使用權。(灰度指針為過去,黑色指針為現在)
圖17.8 輪流協調例子③。
如圖17.8所示,操作執行完畢之際,模塊便會反饋完成信號以示結束操作,isDo[2] 還有Call[2] 都會經由完成信號拉低內容。此外,指針也會立即指向下一個對象。
圖17.9 輪流協調例子④。
如圖17.9所示,假設 Call[2] 還有 Call[1] 同時提問,由於指針沒有指向它們,所以Call[2] 與 Call[1] 都沒有使能權。此刻,指針開始一動。
圖17.10 輪流協調例子⑤。
首先,Call[1] 會得到使能權,isDo[1]因此拉高並且開始執行操作,直至操作結束之前,isDo[1]都獨占模塊,結果如圖17.10所示。
圖17.11 輪流協調例子⑥。
如圖17.11所示,當操作執行完畢,模塊便會反饋完成信號,隨之isDo[1] 還有 Call[1]都會拉低內容,而且指針也會指向下一個對象。對此,isDo[2] 得到使能權,並且開始執行操作 ... 直至操作結束之前,它都獨占模塊。
圖17.12 輪流協調例子⑦。
操作結束之際,模塊便會反饋完成信號,isDo[2] 還有 Call[2] 隨之也會拉低內容,然后指針指向另一個對象,結果如圖17.12所示。輪流協調的概念基本上就是這樣而已,即單純也非常邏輯。接下來,讓我們來看看Verilog 如何描述輪流協調,結果如代碼17.3所示:
1. module iic_savemod 2. ( 3. input [1:0]iCall, 4. output [1:0]oDone, 5. ); 6. reg [1:0]C7; 7. reg [1:0]isDo; 8. 9. always @ ( posedge CLOCK or negedge RESET ) 10. if( !RESET ) 11. begin 12. C7 <= 2’b10; 13. isDo <= 2’b00; 14. end 15. else 16. begin 17. if( iCall[1] & C7[1] ) isDo[1] <= 1’b1; 18. else if( iCall[0] & C7[0] ) isDo[0] <= 1’b1; 19. 20. if( isDo[1] & isDone[1] ) isDo[1] <= 1’b0; 21. else if( isDo[0] & isDone[0] ) isDo[0] <= 1’b0; 22. 23. if( isDone ) C7 <= { isDo[0], isDo[1] }; 24. else if( iCall ) C7 <= { C7[0], C7[1] }; 25. end 26.
代碼17.3
第3~4行是相關的出入端聲明, 其中 Call 還有 Done 均為兩位。第6~7行是輪流協調作用的寄存器isDo與C7,C7為使能指針。第10~14行則是這些寄存器的初始化,注意C7默認下指向 Call[1]。第16~25行則是輪流協調的主要操作,第17~18行是提問與使能,其中 iCall[N] & C7[N] 表示提問並且被指針指向,isDo[N] 表示給予使能權。
第20~21行是消除提問和使能,其中 isDo[N] & isDone[N] 表示相關的完成信號對應相關的操作,然后 isDo[N] 表示消除使能。第24行的表示有提問,指針就立即移動。第23行表示結束操作,指針便指向下一個對象。
27. reg [4:0]i; 28. reg [1:0]isDone; 29. 30. always @ ( posedge CLOCK or negedge RESET ) 31. if( !RESET ) 32. begin 33. ... 34. i <= 5’d0; 35. isDone <= 2’b00; 36. end 37. else if( isDo[1] ) 38. case( i ) 39. ... 40. 5: begin isDone[1] <= 1’b1; i <= i + 1’b1; end 41. 6: begin isDone[1] <= 1’b0; i <= 5’d0; end 42. endcase 43. else if( isDo[0] ) 44. case( i ) 45. ... 46. 7: begin isDone[0] <= 1’b1; i <= i + 1’b1; end 47. 8: begin isDone[0] <= 1’b0; i <= 5’d0; end 48. endcase 49. 50. endmodule
代碼17.3
第27~28行只核心操作相關的寄存器,第31~36行則是這些寄存器的復位操作。第37行表示 isDo[1] 拉高才執行操作1。第40~41行表示操作1反饋完成信號。第43行表示 isDo[0] 拉高才指向操作0。第46~47行表示操作0反饋完成信號。如此一來,問答信號便有輪流協調,接下來就是為IIC儲存模塊加入FIFO機制。
圖17.13 有FIFO機制的IIC儲存模塊。
如圖17.13所示,那是擁有FIFO機制的IIC儲存模塊,它那畸形的儲存功能,可謂是IIC儲存模塊的亞種。其中 Call/Done[1] 表示寫入調用,Tag[1] 表示寫滿狀態,反之既然。由於目前的IIC儲存模塊是FIFO的關系,所以寫入地址還有讀出地址都是在里邊建立。為此,Verilog可以這樣描述,結果如代碼17.4所示:
1. module iic_savemod 2. ( 3. input [1:0]iCall, 4. output [1:0]oDone, 5. input [7:0]iData, 6. output [7:0]oData, 7. output [1:0]oTag 8. ); 9. always @ ( posedge CLOCK or negedge RESET ) // 輪流協調的周邊操作 10. ... 11. 12. reg [8:0]C2,C3; // C2 Write Pointer, C3 Read Pointer; 13. 14. always @ ( posedge CLOCK or negedge RESET ) 15. if( !RESET ) 16. begin 17. C2 <= 9’d0; 18. C3 <= 9’d0; 19. end 20. else if( isDo[1] ) 21. case( i ) 22. ... 23. 2: // Wirte Word Addr 24. begin D1 <= C2[7:0]; i <= FF_Write1; Go <= i + 1'b1; end 25. ... 26. 5: 27. begin C2 <= C2 + 1'b1; isDone[1] <= 1'b1; i <= i + 1'b1; end 28. ... 29. endcase 30. else if( isDo[0] ) 31. case( i ) 32. ... 33. 2: // Wirte Word Addr 34. begin D1 <= C3[7:0]; i <= FF_Write2; Go <= i + 1'b1; end 35. ... 36. 7: 37. begin C3 <= C3 + 1'b1; isDone[0] <= 1'b1; i <= i + 1'b1; end 38. ... 39. endcase 40. 41. ... 42. assign oTag[1] = ( (C2[8]^C3[8]) && (C2[7:0] == C3[7:0]) ); 43. assign oTag[0] = ( C2 == C3 ); 44. 45. endmodule
代碼17.4
如代碼17.4所示,第3~7行是相關的出入端聲明。第12行建立相關的寄存器,C2為寫指針,C3為讀指針,位寬為 N + 1。第23~24行表示C2[7:0]為寫數據地址。第26~27行表示C2遞增。第33~34行表示C3[7:0]為讀數據地址。第36~37行表示C3遞增。第42行表示寫滿狀態,第43行則表示讀空狀態。完后,我們便可以開始建模了。
圖17.14 實驗十七的建模圖。
圖17.14是實驗十七的建模圖,周邊操作為 IIC 儲存模塊寫入數據,核心操作則從哪里讀取數據,並且將讀出的數據驅動數碼管基礎模塊。
iic_savemod.v
圖17.15 IIC儲存模塊。
圖17.15是IIC儲存模塊的建模圖,左方是寫入操作,右邊是讀出操作,上方則是鏈接至頂層信號 SCL 與 SDA。
1. module iic_savemod 2. ( 3. input CLOCK, RESET, 4. output SCL, 5. inout SDA, 6. input [1:0]iCall, 7. output [1:0]oDone, 8. input [7:0]iData, 9. output [7:0]oData, 10. output [1:0]oTag 11. ); 12. parameter FCLK = 10'd125, FHALF = 10'd62, FQUARTER = 10'd31; //(1/400E+3)/(1/50E+6) 13. parameter THIGH = 10'd30, TLOW = 10'd65, TR = 10'd15, TF = 10'd15; 14. parameter THD_STA = 10'd30, TSU_STA = 10'd30, TSU_STO = 10'd30; 15. parameter FF_Write1 = 5'd7; 16. parameter FF_Write2 = 5'd9, FF_Read = 5'd19; 17. 18. /***************/ 19. 20. reg [1:0]C7; 21. reg [1:0]isDo; 22. 23. always @ ( posedge CLOCK or negedge RESET ) 24. if( !RESET ) 25. begin 26. C7 <= 2'b10; 27. isDo <= 2'b00; 28. end 29. else 30. begin 31. 32. if( iCall[1] & C7[1] ) isDo[1] <= 1'b1; 33. else if( iCall[0] & C7[0] ) isDo[0] <= 1'b1; 34. 35. if( isDo[1] & isDone[1] ) isDo[1] <= 1'b0; 36. else if( isDo[0] & isDone[0] ) isDo[0] <= 1'b0; 37. 38. if( isDone ) C7 <= {isDo[0],isDo[1]}; 39. else if( iCall ) C7 <= { C7[0], C7[1] }; 40. 41. end 42. 43. 44. /***************/ 45. 46. reg [4:0]i; 47. reg [4:0]Go; 48. reg [9:0]C1; 49. reg [7:0]D1; 50. reg [1:0]isDone; 51. reg [8:0]C2,C3; // C2 Write Pointer, C3 Read Pointer 52. reg rSCL,rSDA; 53. reg isAck,isQ; 54. 55. always @ ( posedge CLOCK or negedge RESET ) 56. if( !RESET ) 57. begin 58. { i,Go } <= { 5'd0,5'd0 }; 59. C1 <= 10'd0; 60. D1 <= 8'd0; 61. isDone <= 2'd0; 62. { C2, C3 } <= 18'd0; 63. { rSCL,rSDA,isAck,isQ } <= 4'b1111; 64. end 65. else if( isDo[1] ) 66. case( i ) 67. 68. 0: // Call 69. begin 70. isQ = 1; 71. rSCL <= 1'b1; 72. 73. if( C1 == 0 ) rSDA <= 1'b1; 74. else if( C1 == (TR + THIGH) ) rSDA <= 1'b0; 75. 76. if( C1 == (FCLK) -1) begin C1 <= 10'd0; i <= i + 1'b1; end 77. else C1 <= C1 + 1'b1; 78. end 79. 80. 1: // Write Device Addr 81. begin D1 <= {4'b1010, 3'b000, 1'b0}; i <= 5'd7; Go <= i + 1'b1; end 82. 83. 2: // Wirte Word Addr 84. begin D1 <= C2[7:0]; i <= FF_Write1; Go <= i + 1'b1; end 85. 86. 3: // Write Data 87. begin D1 <= iData; i <= FF_Write1; Go <= i + 1'b1; end 88. 89. /*************************/ 90. 91. 4: // Stop 92. begin 93. isQ = 1'b1; 94. 95. if( C1 == 0 ) rSCL <= 1'b0; 96. else if( C1 == FQUARTER ) rSCL <= 1'b1; 97. 98. if( C1 == 0 ) rSDA <= 1'b0; 99. else if( C1 == (FQUARTER + TR + TSU_STO ) ) rSDA <= 1'b1; 100. 101. if( C1 == (FQUARTER + FCLK) -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end 102. else C1 <= C1 + 1'b1; 103. end 104. 105. 5: 106. begin C2 <= C2 + 1'b1; isDone[1] <= 1'b1; i <= i + 1'b1; end 107. 108. 6: 109. begin isDone[1] <= 1'b0; i <= 5'd0; end 110. 111. /*******************************/ //function 112. 113. 7,8,9,10,11,12,13,14: 114. begin 115. isQ = 1'b1; 116. rSDA <= D1[14-i]; 117. 118. if( C1 == 0 ) rSCL <= 1'b0; 119. else if( C1 == (TF + TLOW) ) rSCL <= 1'b1; 120. 121. if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end 122. else C1 <= C1 + 1'b1; 123. end 124. 125. 15: // waiting for acknowledge 126. begin 127. isQ = 1'b0; 128. if( C1 == FHALF ) isAck <= SDA; 129. 130. if( C1 == 0 ) rSCL <= 1'b0; 131. else if( C1 == FHALF ) rSCL <= 1'b1; 132. 133. if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end 134. else C1 <= C1 + 1'b1; 135. end 136. 137. 16: 138. if( isAck != 0 ) i <= 5'd0; 139. else i <= Go; 140. 141. /*******************************/ // end function 142. 143. endcase 144. 145. else if( isDo[0] ) 146. case( i ) 147. 148. 0: // Call 149. begin 150. isQ = 1; 151. rSCL <= 1'b1; 152. 153. if( C1 == 0 ) rSDA <= 1'b1; 154. else if( C1 == (TR + THIGH) ) rSDA <= 1'b0; 155. 156. if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end 157. else C1 <= C1 + 1'b1; 158. end 159. 160. 1: // Write Device Addr 161. begin D1 <= {4'b1010, 3'b000, 1'b0}; i <= 5'd9; Go <= i + 1'b1; end 162. 163. 2: // Wirte Word Addr 164. begin D1 <= C3[7:0]; i <= FF_Write2; Go <= i + 1'b1; end 165. 166. 3: // Start again 167. begin 168. isQ = 1'b1; 169. 170. if( C1 == 0 ) rSCL <= 1'b0; 171. else if( C1 == FQUARTER ) rSCL <= 1'b1; 172. else if( C1 == (FQUARTER + TR + TSU_STA + THD_STA + TF) ) rSCL <= 1'b0; 173. 174. if( C1 == 0 ) rSDA <= 1'b0; 175. else if( C1 == FQUARTER ) rSDA <= 1'b1; 176. else if( C1 == ( FQUARTER + TR + THIGH) ) rSDA <= 1'b0; 177. 178. if( C1 == (FQUARTER + FCLK + FQUARTER) -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end 179. else C1 <= C1 + 1'b1; 180. end 181. 182. 4: // Write Device Addr ( Read ) 183. begin D1 <= {4'b1010, 3'b000, 1'b1}; i <= 5'd9; Go <= i + 1'b1; end 184. 185. 5: // Read Data 186. begin D1 <= 8'd0; i <= FF_Read; Go <= i + 1'b1; end 187. 188. 6: // Stop 189. begin 190. isQ = 1'b1; 191. 192. if( C1 == 0 ) rSCL <= 1'b0; 193. else if( C1 == FQUARTER ) rSCL <= 1'b1; 194. 195. if( C1 == 0 ) rSDA <= 1'b0; 196. else if( C1 == (FQUARTER + TR + TSU_STO) ) rSDA <= 1'b1; 197. 198. if( C1 == (FCLK + FQUARTER) -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end 199. else C1 <= C1 + 1'b1; 200. end 201. 202. 7: 203. begin C3 <= C3 + 1'b1; isDone[0] <= 1'b1; i <= i + 1'b1; end 204. 205. 8: 206. begin isDone[0] <= 1'b0; i <= 5'd0; end 207. 208. /*******************************/ //function 209. 210. 9,10,11,12,13,14,15,16: 211. begin 212. isQ = 1'b1; 213. 214. rSDA <= D1[16-i]; 215. 216. if( C1 == 0 ) rSCL <= 1'b0; 217. else if( C1 == (TF + TLOW) ) rSCL <= 1'b1; 218. 219. if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end 220. else C1 <= C1 + 1'b1; 221. end 222. 223. 17: // waiting for acknowledge 224. begin 225. isQ = 1'b0; 226. 227. if( C1 == FHALF ) isAck <= SDA; 228. 229. if( C1 == 0 ) rSCL <= 1'b0; 230. else if( C1 == FHALF ) rSCL <= 1'b1; 231. 232. if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end 233. else C1 <= C1 + 1'b1; 234. end 235. 236. 18: 237. if( isAck != 0 ) i <= 5'd0; 238. else i <= Go; 239. 240. /*****************************/ 241. 242. 19,20,21,22,23,24,25,26: // Read 243. begin 244. isQ = 1'b0; 245. if( C1 == FHALF ) D1[26-i] <= SDA; 246. 247. if( C1 == 0 ) rSCL <= 1'b0; 248. else if( C1 == FHALF ) rSCL <= 1'b1; 249. 250. if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= i + 1'b1; end 251. else C1 <= C1 + 1'b1; 252. end 253. 254. 27: // no acknowledge 255. begin 256. isQ = 1'b1; 257. //if( C1 == 100 ) isAck <= SDA; 258. 259. if( C1 == 0 ) rSCL <= 1'b0; 260. else if( C1 == FHALF ) rSCL <= 1'b1; 261. 262. if( C1 == FCLK -1 ) begin C1 <= 10'd0; i <= Go; end 263. else C1 <= C1 + 1'b1; 264. end 265. 266. /*************************************/ // end fucntion 267. 268. endcase 269. 270. /***************************************/ 271. 272. assign SCL = rSCL; 273. assign SDA = isQ ? rSDA : 1'bz; 274. assign oDone = isDone; 275. assign oData = D1; 276. assign oTag[1] = ( (C2[8]^C3[8]) && (C2[7:0] == C3[7:0]) ); 277. assign oTag[0] = ( C2 == C3 ); 278. 279. /***************************************/ 280. 281. endmodule
具體內容筆者也懶得解釋了,讀者自己看着辦吧。
iic_demo.v
連線部署請參考圖17.14。
1. module iic_demo 2. ( 3. input CLOCK, RESET, 4. output SCL, 5. inout SDA, 6. output [7:0]DIG, 7. output [5:0]SEL 8. ); 以上內容為相關的出入端聲明。 9. reg [3:0]j; 10. reg [7:0]D1; 11. reg isWR; 12. 13. always @ ( posedge CLOCK or negedge RESET ) 14. if( !RESET ) 15. begin 16. j <= 4'd0; 17. D1 <= 8'd0; 18. isWR <= 1'b0; 19. end 20. else 21. case( j ) 22. 23. 0: 24. if( !TagU1[1] ) j <= j + 1'b1; 25. 26. 1: 27. if( DoneU1[1] ) begin isWR <= 1'b0; j <= j + 1'b1; end 28. else begin isWR <= 1'b1; D1 <= 8'hAB; end 29. 30. 2: 31. if( !TagU1[1] ) j <= j + 1'b1; 32. 33. 3: 34. if( DoneU1[1] ) begin isWR <= 1'b0; j <= j + 1'b1; end 35. else begin isWR <= 1'b1; D1 <= 8'hCD; end 36. 37. 4: 38. if( !TagU1[1] ) j <= j + 1'b1; 39. 40. 5: 41. if( DoneU1[1] ) begin isWR <= 1'b0; j <= j + 1'b1; end 42. else begin isWR <= 1'b1; D1 <= 8'hEF; end 43. 44. 6: 45. i <= i; 46. 47. endcase 48.
以上內容為寫入作用的周邊操作,操作過程如下:
步驟0,判斷是否寫滿狀態。
步驟1,寫入數據 8’hAB;
步驟2,判斷是否寫滿狀態。
步驟3,寫入數據 8’hCD;
步驟4,判斷是否寫滿狀態。
步驟5,寫入數據 8’hEF;
步驟6,發呆。
49. wire [7:0]DataU1; 50. wire [1:0]DoneU1; 51. wire [1:0]TagU1; 52. 53. iic_savemod U1 54. ( 55. .CLOCK( CLOCK ), 56. .RESET( RESET ), 57. .SCL( SCL ), // > top 58. .SDA( SDA ), // <> top 59. .iCall( { isWR, isRD } ), // < sub & core 60. .oDone( DoneU1 ), // > core 61. .iData( D1 ), // < core 62. .oData( DataU1 ), // > core 63. .oTag( TagU1 ) 64. ); 65.
以上內容為IIC儲存模塊的實例化。第59行表示 isWR 為 Call[1],isRD 為 Call[0]。第61行表示 D1 驅動該輸入。
66. reg [3:0]i; 67. reg [23:0]D2; 68. reg isRD; 69. 70. always @ ( posedge CLOCK or negedge RESET ) // core 71. if( !RESET ) 72. begin 73. i <= 4'd0; 74. D2 <= 24'd0; 75. isRD <= 1'b0; 76. end 77. else 78. case( i ) 79. 80. 0: 81. if( !TagU1[0] ) i <= i + 1'b1; 82. 83. 1: 84. if( DoneU1[0] ) begin D2[23:16] <= DataU1; isRD <= 1'b0; i <= i + 1'b1; end 85. else isRD <= 1'b1; 86. 87. 2: 88. if( !TagU1[0] ) i <= i + 1'b1; 89. 90. 3: 91. if( DoneU1[0] ) begin D2[15:8] <= DataU1; isRD <= 1'b0; i <= i + 1'b1; end 92. else isRD <= 1'b1; 93. 94. 4: 95. if( !TagU1[0] ) i <= i + 1'b1; 96. 97. 5: 98. if( DoneU1[0] ) begin D2[7:0] <= DataU1; isRD <= 1'b0; i <= i + 1'b1; end 99. else isRD <= 1'b1; 100. 101. 6: 102. i <= i; 103. 104. endcase 105.
以上內容為讀出數據並且驅動數碼管基礎模塊的核心操作,操作過程如下:
步驟0,判斷是否為讀空狀態。
步驟1,讀出數據 8’hAB,並且暫存至 D2[23:16]。
步驟2,判斷是否為讀空狀態。
步驟3,讀出數據 8’hCD,並且暫存至 D2[15:8]。
步驟4,判斷是否為讀空狀態。
步驟5,讀出數據 8’hEF,並且暫存至 D2[7:0]。
步驟6,發呆。
106. smg_basemod U2 107. ( 108. .CLOCK( CLOCK ), 109. .RESET( RESET ), 110. .DIG( DIG ), // > top 111. .SEL( SEL ), // > top 112. .iData( D2 ) // < core 113. ); 114. 115. endmodule
第106~113行是數碼管基礎模塊的實例化,第112行表示D2驅動該輸入。編譯完畢並且下載程序,如果數碼管自左向右顯示“ABCDEF”表示實驗成功。
細節一: 完整的個體模塊
實驗十七的IIC儲存模塊隨時可以使用。