經過漫長的戰斗以后,我們終於來到最后。對於普通人而言,頁讀寫就是一名戰士的墓碑(最終戰役) ... 然而,怕死的筆者想透過這個實驗告訴讀者,旅程的終點就是旅程的起點。一直以來,筆者都在煩惱“SDRAM是否應該成為儲存類?”SDRAM作為一介儲存資源(儲存器),它的好處就是大容量空間,壞處則就是麻煩的控制規則,還有中規中矩的溝通速率。
相比之下,片上內存無論是控制的難度,還是溝通的速率,它都遠遠領先SDRAM。俗語常說,愈是強力的資源愈是珍貴 ... 對此,片上內容的容量可謂是稀罕的程度。實驗二十二的要求非常單純:
”請問如何建立基於SDRAM儲存資源的FIFO存儲模塊呢?“,筆者問道。
圖22.1 SDRAM基礎模塊。
圖22.1是基於實驗十八修改而成的SDRAM基礎模塊,修改對象除了SDRAM控制模塊以外,SDRAM功能模塊保持實驗十八的狀態,即單字讀寫。SDRAM控制模塊,除了多出Tag以外,Addr的驅動也由該模塊負責。具體的內容,讓我們來看代碼吧:
1. module sdram_ctrlmod
2. (
3. input CLOCK,
4. input RESET,
5. input [1:0]iCall, // [1]Write, [0]Read
6. output [1:0]oDone,
7. output [3:0]oCall,
8. input iDone,
9. output [23:0]oAddr,
10. output [1:0]oTag
11. );
12. parameter WRITE = 4'd1, READ = 4'd4, REFRESH = 4'd7, INITIAL = 4'd8;
13. parameter TREF = 11'd1040;
14.
以上內容為相關的出入端聲明以及常量。其中多了24位寬的oAddr與2位寬的oTag。
15. reg [1:0]C7;
16. reg [1:0]isDo;
17.
18. always @ ( posedge CLOCK or negedge RESET ) // sub
19. if( !RESET )
20. begin
21. C7 <= 2'b10;
22. isDo <= 2'b00;
23. end
24. else
25. begin
26.
27. if( iCall[1] & C7[1] ) isDo[1] <= 1'b1;
28. else if( iCall[0] & C7[0] ) isDo[0] <= 1'b1;
29.
30. if( isDo[1] & isDone[1] ) isDo[1] <= 1'b0;
31. else if( isDo[0] & isDone[0] ) isDo[0] <= 1'b0;
32.
33. if( isDone ) C7 <= {isDo[0],isDo[1]};
34. else if( iCall ) C7 <= { C7[0], C7[1] };
35.
36. end
37.
以上內容為輪流協調的周邊操作。具體內容與實驗十七一樣。
38. reg [3:0]i;
39. reg [10:0]C1;
40. reg [3:0]isCall; //[3]Write [2]Read [1]A.Refresh [0]Initial
41. reg [1:0]isDone;
42. reg [23:0]D1;
43. reg [24:0]C2,C3; // N + 1
44.
45. always @ ( posedge CLOCK or negedge RESET ) // core
46. if( !RESET )
47. begin
48. i <= INITIAL; // Initial SDRam at first
49. C1 <= 11'd0;
50. isCall <= 4'b0000;
51. isDone <= 2'b00;
52. D1 <= 24'd0;
53. C2 <= 25'd0;
54. C3 <= 25'd0;
55. end
以上內容為相關的寄存器聲明與復位操作。其中C2是寫指針,C3是讀指針,位寬為oAddr + 1。D用來驅動oAddr。
56. else
57. case( i )
58.
59. 0: // IDLE
60. if( C1 >= TREF ) begin C1 <= 11'd0; i <= REFRESH; end
61. else if( isDo[1] ) begin C1 <= C1 + 1'b1; i <= WRITE; end
62. else if( isDo[0] ) begin C1 <= C1 + 1'b1; i <= READ; end
63. else begin C1 <= C1 + 1'b1; end
64.
65. /***********************/
66.
以上內容為部分核心內容。步驟0是待機狀態,其中61~62行改為 isDo[1] 與 isDo[2]。
67. 1: //Write
68. if( iDone ) begin isCall[3] <= 1'b0; C1 <= C1 + 1'b1; i <= i + 1'b1; end
69. else begin isCall[3] <= 1'b1; D1 <= C2[23:0]; C1 <= C1 + 1'b1; end
70.
71. 2:
72. begin C2 <= C2 + 1'b1; isDone[1] <= 1'b1; C1 <= C1 + 1'b1; i <= i + 1'b1; end
73.
74. 3:
75. begin isDone[1] <= 1'b0; C1 <= C1 + 1'b1; i <= 4'd0; end
76.
77. /***********************/
78.
以上內容為部分核心內容。步驟1~3是寫操作,步驟1將C2[23:0]的內容賦值D。步驟2~3產生完成信號之余也遞增C2.
79. 4: // Read
80. if( iDone ) begin isCall[2] <= 1'b0; C1 <= C1 + 1'b1; i <= i + 1'b1; end
81. else begin isCall[2] <= 1'b1; D1 <= C3[23:0]; C1 <= C1 + 1'b1; end
82.
83. 5:
84. begin C3 <= C3 + 1'b1; isDone[0] <= 1'b1; C1 <= C1 + 1'b1; i <= i + 1'b1; end
85.
86. 6:
87. begin isDone[0] <= 1'b0; C1 <= C1 + 1'b1; i <= 4'd0; end
88.
89. /***********************/
90.
以上內容為部分核心內容。步驟4~6是寫操作,步驟4將C3[23:0]的內容賦值D。步驟5~7產生完成信號之余也遞增C3.
91. 7: // Auto Refresh
92. if( iDone ) begin isCall[1] <= 1'b0; i <= 4'd0; end
93. else begin isCall[1] <= 1'b1; end
94.
95. /***********************/
96.
97. 8: // Initial
98. if( iDone ) begin isCall[0] <= 1'b0; i <= 4'd0; end
99. else begin isCall[0] <= 1'b1; end
100.
101. endcase
102.
以上內容為部分核心內容。步驟7~8保持不變。
103. assign oDone = isDone;
104. assign oCall = isCall;
105. assign oAddr = D1;
106. assign oTag[1] = ( C2[24]^C3[24] & C2[23:0] == C3[23:0] );
107. assign oTag[0] = ( C2 == C3 );
108.
109. endmodule
以上內容為相關的輸出驅動。D1驅動oAddr,第106~107行是寫滿狀態與讀空狀態的聲明。
sdram_funcmod.v
該功能模塊與實驗十八的內容一模一樣。
sdram_demo.v
該組合模塊的連線部署完全參照圖22.1。
1. module sdram_basemod
2. (
3. input CLOCK,
4. input RESET,
5.
6. output S_CKE, S_NCS, S_NRAS, S_NCAS, S_NWE,
7. output [1:0]S_BA,
8. output [12:0]S_A,
9. output [1:0]S_DQM,
10. inout [15:0]S_DQ,
11.
12. input [1:0]iCall,
13. output [1:0]oDone,
14. output [1:0]oTag,
15. input [15:0]iData,
16. output [15:0]oData
17. );
18. wire [3:0]CallU1; // [3]Refresh, [2]Read, [1]Write, [0]Initial
19. wire [23:0]AddrU2;
20.
21. sdram_ctrlmod U1
22. (
23. .CLOCK( CLOCK ),
24. .RESET( RESET ),
25. .iCall( iCall ), // < top ,[1]Write [0]Read
26. .oDone( oDone ), // > top ,[1]Write [0]Read
27. .oAddr( AddrU2 ), // > U2
28. .oTag( oTag ), // > top
29. .oCall( CallU1 ), // > U2
30. .iDone( DoneU2 ) // < U2
31. );
32.
33. wire DoneU2;
34.
35. sdram_funcmod U2
36. (
37. .CLOCK( CLOCK ),
38. .RESET( RESET ),
39. .S_CKE( S_CKE ), // > top
40. .S_NCS( S_NCS ), // > top
41. .S_NRAS( S_NRAS ), // > top
42. .S_NCAS( S_NCAS ), // > top
43. .S_NWE( S_NWE ), // > top
44. .S_BA( S_BA ), // > top
45. .S_A( S_A ), // > top
46. .S_DQM( S_DQM ), // > top
47. .S_DQ( S_DQ ), // <> top
48. .iCall( CallU1 ), // < U1
49. .oDone( DoneU2 ), // > U1
50. .iAddr( AddrU2 ), // < U1
51. .iData( iData ), // < top
52. .oData( oData ) // > top
53. );
54.
55. endmodule
連線內容請自己看着辦吧。
sdram_demo.v
圖22.3 實驗二十二的建模圖。
圖22.3是實驗二十二的建模圖,左邊的周邊操作負責寫入數據,右邊的核心操作負責讀取數據並且經由TXD發送出去。具體內容我們還是來看代碼吧。
1. module sdram_demo
2. (
3. input CLOCK,
4. input RESET,
5. output S_CLK,
6. output S_CKE, S_NCS, S_NRAS, S_NCAS, S_NWE,
7. output [12:0]S_A,
8. output [1:0]S_BA,
9. output [1:0]S_DQM,
10. inout [15:0]S_DQ,
11. output TXD
12. );
以上內容為相關的出入端聲明。
13. wire CLOCK1,CLOCK2;
14.
15. pll_module U1
16. (
17. .inclk0 ( CLOCK ), // 50Mhz
18. .c0 ( CLOCK1 ), // 133Mhz -210 degree phase
19. .c1 ( CLOCK2 ) // 133Mhz
20. );
21.
以上內容為PLL模塊的實例化。
22. wire [1:0]DoneU2;
23. wire [15:0]DataU2;
24. wire [1:0]TagU2;
25.
26. sdram_basemod U2
27. (
28. .CLOCK( CLOCK1 ),
29. .RESET( RESET ),
30. .S_CKE( S_CKE ),
31. .S_NCS( S_NCS ),
32. .S_NRAS( S_NRAS ),
33. .S_NCAS( S_NCAS ),
34. .S_NWE( S_NWE ),
35. .S_A( S_A ),
36. .S_BA( S_BA ),
37. .S_DQM( S_DQM ),
38. .S_DQ( S_DQ ),
39. .iCall( {isWR,isRD} ),
40. .oDone( DoneU2 ),
41. .iData( D2 ),
42. .oData( DataU2 ),
43. .oTag( TagU2 )
44. );
以上內容為SDRAM基礎模塊的實例化,注意第39行的iCall是由 isWR與isRD聯合驅動。此外,第43行也多了oTag。
46. reg [5:0]i;
47. reg [15:0]D2;
48. reg isWR;
49.
50. always @ ( posedge CLOCK1 or negedge RESET )
51. if( !RESET )
52. begin
53. i <= 6'd0;
54. D2 <= 16'hA000;
55. isWR <= 1'b0;
56. end
57. else
58. case( i )
59.
60. 0:
61. if( !TagU2[1] ) i <= i + 1'b1;
62.
63. 1:
64. if( DoneU2[1] ) begin isWR <= 1'b0; i <= i + 1'b1; end
65. else begin isWR <= 1'b1; end
66.
67. 2:
68. if( D2 == 16'hA1FF ) i <= i + 1'b1;
69. else begin D2[11:0] <= D2[11:0] + 1'b1; i <= 6'd0; end
70.
71. 3:
72. i <= i;
73.
74. endcase
75.
以上內容為寫作用的周邊操作。步驟0檢測是否寫滿,步驟1寫入數據,步驟2判斷是否寫滿512次,不是的話就遞增內容。步驟3是寫完發呆。
76. reg [5:0]j,Go;
77. reg [10:0]C1;
78. reg [15:0]D3;
79. reg [10:0]T;
80. reg isRD;
81. reg rTXD;
82.
83. parameter B115K2 = 11'd1157, TXFUNC = 6'd16;
84.
85. always @ ( posedge CLOCK1 or negedge RESET )
86. if( !RESET )
87. begin
88. j <= 6'd0;
89. Go <= 6'd0;
90. C1 <= 11'd0;
91. D3 <= 16'd0;
92. T <= 11'd0;
93. isRD <= 1'b0;
94. rTXD <= 1'b1;
95. end
96. else
97. case( j )
98.
99. 0:
100. if( !TagU2[0] ) j <= j + 1'b1;
101.
102. 1:
103. if( DoneU2[0] ) begin D3 <= DataU2; isRD <= 1'b0; j <= j + 1'b1; end
104. else begin isRD <= 1'b1; end
105.
106. 2:
107. begin T <= { 2'b11, D3[15:8], 1'b0 }; j <= TXFUNC; Go <= j + 1'b1; end
108.
109. 3:
110. begin T <= { 2'b11, D3[7:0], 1'b0 }; j <= TXFUNC; Go <= j + 1'b1; end
111.
112. 4:
113. if( D3 == 16'hA1FF ) j <= j + 1'b1;
114. else j <= 6'd0;
115.
116. 5:
117. j <= j;
118.
119. /******************************/
120.
以上內容為部分核心操作。步驟0檢測是否讀空,步驟1讀出數據,步驟2~3將數據發送數據,步驟4判斷是否執行512次?如果不是的話就返回步驟0,是的話就遞增i進入發呆的步驟5。
121. 16,17,18,19,20,21,22,23,24,25,26:
122. if( C1 == B115K2 -1 ) begin C1 <= 11'd0; j <= j + 1'b1; end
123. else begin rTXD <= T[j - 16]; C1 <= C1 + 1'b1; end
124.
125. 27:
126. j <= Go;
127.
128. endcase
129.
130. assign S_CLK = CLOCK2;
131. assign TXD = rTXD;
132.
133. endmodule
以上內容為核心操作以及輸出驅動·。綜合完畢便下載程序,如果串口調試軟件出現 A000~A1FF,表示實驗成功。
細節一:完整的個體模塊
本實驗的SDRAM基礎模塊已經准備就緒。
細節二:小談儲存類
FIFO機制的SDRAM很可能實用性不強。相對低級建模II而言,SDRAM已經脫離死板的印象,任何畸形的儲存方式,都有可能實現在任何儲存資源之上。舉例而言,片上內存儲存資源可以實現FIFO功能,IIC儲存資源也可以實現FIFO功能,SDRAM儲存資源也可以實現FIFO功能。
如果按照這條思路逆向思考的話,如果SDRAM儲存資源可以實現功能C,那么IIC儲存資源還有片上內存儲存資源同樣也可以實現功能C。如此一來,建模的自由度會大大增高,喜愛建模的孩子也會開心至極。如果讀者是變態,那么一塊小小的SDRAM儲存資源實現多功能讀寫如:單字讀寫,多字讀寫,頁讀寫,甚至FIFO讀寫,集於一身是有可能的。不管怎么樣,實驗二十二所要表達的信息已經非常清晰,即儲存類的伸縮性與可塑性。