实验二十:SDRAM模块③ — 页读写 α
完成单字读写与多字读写以后,接下来我们要实验页读写。丑话当前,实验二十的页读写只是实验性质的东西,其中不存在任何实用价值,笔者希望读者可以把它当成页读写的热身运动。
表示20.1 Mode Register的内容。
Mode Register |
||||||||||||
A12 |
A11 |
A10 |
A9 |
A8 |
A7 |
A6 |
A5 |
A4 |
A3 |
A2 |
A1 |
A0 |
0 |
0 |
OP Code |
0 |
0 |
CAS Latency |
BT |
Burst Length |
|
|
||||||||||||||||||||||||||||||||||||||
|
|
所谓页读写就是全列读写,而且表20.1告诉我们,页读写必须将 A2~A0设置为3’b111。然而,Verilog的描述结果如代码20.1所示:
7: // Send LMR Cmd. Burst Read & Write, 3'b010 mean CAS latecy = 3, Sequential,Full Page
begin rCMD <= _LMR; rBA <= 2'b11; rA <= { 3'd0, 1'b0, 2'd0, 3'b011, 1'b0, 3'b111 }; i <= i + 1'b1; end
代码20.1
如果我们一页一页的叫,基本上“一页”的定义是非常暧昧的,因为“一页”所指定的范围会随着该存储器的容量而有所改变。举例HY57V2562GTR 这只SDRAM,地址的指定范围有 BA1~BA0,R12~R0,C8~C0,其中“一页”是全列,亦即C8~C0。根据计算,C8~C0等价29 = 512,或者说页读写有512的地址偏移量。
页写操作:
图20.1 页写操作的理想时序图。
图20.1是笔者自定义的页写操作的理想时序图,其中C1是为了控制读写的次数。页读写相较字读写,前者好比一只不会停下冲锋的山猪。一旦读写开始,SDRAM内部的计数器就会从0开始计数,计数结果为511又会从0重新计数。因为如此,页读写需要利用BSTP命令禁止山猪继续冲锋。
此外,自动预充对页读写来说是无效的东西,因此A10拉不拉高都没有关系,而且页写操作也不需要满足 TWR/TDPL与TPR。图20.1大致的时序过程如下:
l T1,发送ACT命令,BANK地址与行地址;
l T1半周期,SDRAM读取;
l T2,满足TRCD;
l T3,发送WR命令,BANK地址与列地址,还有第0数据;
l T3半周期,SDRAM读取
l T4,发送第1~511数据,然后发送BSTP命令结束页写。
Verilog则可以这样描述,结果如代码20.2所示:
1. 1: // Send Active Command with Bank and Row address
2. begin rCMD <= _ACT; rBA <= iAddr[23:22]; rA <= iAddr[21:9]; i <= i + 1'b1; end
3.
4. 2: // wait TRCD 20ns
5. if( C1 == TRCD -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
6. else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end
7.
8. 3: // Send Write command with row address
9. begin rCMD <= _WR; rBA <= iAddr[23:22]; rA <= { 4'b0010, iAddr[8:0] }; D1 <= iData; i <= i + 1'b1; end
10.
11. 4: // continue write until end and send BSTP
12. if( C1 == 512 -1 ) begin rCMD <= _BSTP; C1 <= 14'd0; i <= i + 1'b1 ;end
13. else begin rCMD <= _NOP; C1 <= C1 + 1'b1; D1 <= D1 + 1'b1; end
代码20.2
如代码20.2所示,步骤3写第0数据,步骤4则写入第1~511数据并且发送 BSTP命令。
页读操作:
图20.2 页读操作的理想时序图。
图20.2也是笔者自定义的理想时序图。同样,页读操也是一只不断冲锋的山猪,因此它需要BSTP这支停下的告示牌。除此之外,页读也没有自行预充电的必要,而且TPR也不用满足。实验二十要实验的页读比较单纯,我们读取第0数据以后立即发送BSTP命令来结束也操作。图20.2大致的时序过程如下:
l T1,发送ACT命令,BANK地址与行地址;
l T1半周期,SDRAM读取;
l T2,满足TRCD;
l T3,发送RD命令,BANK地址与列地址;
l T3半周期,SDRAM读取命令。
l T4,满足 CAS Latency。
l T5,读取第0数据,然后发送BSTP命令。
Verilog则可以这样描述,结果如代码20.3所示:
1. 1: // Send Active command with Bank and Row address
2. begin rCMD <= _ACT; rBA <= iAddr[23:22]; rA <= iAddr[21:9]; i <= i + 1'b1; end
3.
4. 2: // wait TRCD 20ns
5. if( C1 == TRCD -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
6. else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end
7.
8. 3: // Send Read command and column address
9. begin rCMD <= _RD; rBA <= iAddr[23:22]; rA <= { 4'b0010, iAddr[8:0]}; i <= i + 1'b1; end
10.
11. 4: // wait CL 3 clock
12. if( C1 == CL -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
13. else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end
14.
15. 5: // Read Data
16. begin D1 <= S_DQ; rCMD <= _BSTP; i <= i + 1'b1; end
代码20.3
如代码20.3所示,步骤5读取数据以后立即发送 BSTP命令以示结束页读操作。理解完毕以后我们便可以开始建模了。
图20.3 SDRAM基础模块的建模图。
图20.3是SDRAM基础模块的建模图,外表上和实验十八差不多,不过SDRAM功能模块的内容却有一些改变。
sdram_funcmod.v
1. module sdram_funcmod
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 [3:0]iCall,
13. output oDone,
14. input [23:0]iAddr, // [23:22]BA,[21:9]Row,[8:0]Column
15. input [15:0]iData,
16. output [15:0]oData
17. );
第3~16行是相关的输入端声明。
18. parameter T100US = 14'd13300;
19. // tRP 20ns, tRRC 63ns, tRCD 20ns, tMRD 2CLK, tWR/tDPL 2CLK, CAS Latency 3CLK
20. parameter TRP = 14'd3, TRRC = 14'd9, TMRD = 14'd2, TRCD = 14'd3, TWR = 14'd2, CL = 14'd3;
21. parameter _INIT = 5'b01111, _NOP = 5'b10111, _ACT = 5'b10011, _RD = 5'b10101, _WR = 5'b10100,
22. _BSTP = 5'b10110, _PR = 5'b10010, _AR = 5'b10001, _LMR = 5'b10000;
23.
第18~22行是相关的常量声明。
24. reg [4:0]i;
25. reg [13:0]C1;
26. reg [15:0]D1;
27. reg [4:0]rCMD;
28. reg [1:0]rBA;
29. reg [12:0]rA;
30. reg [1:0]rDQM;
31. reg isOut;
32. reg isDone;
33.
34. always @ ( posedge CLOCK or negedge RESET )
35. if( !RESET )
36. begin
37. i <= 4'd0;
38. C1 <= 14'd0;
39. D1 <= 16'd0;
40. rCMD <= _NOP;
41. rBA <= 2'b11;
42. rA <= 13'h1fff;
43. rDQM <= 2'b00;
44. isOut <= 1'b1;
45. isDone <= 1'b0;
46. end
第24~46行是相关的寄存器声明与复位操作。
47. else if( iCall[3] )
48. case( i )
49.
50. 0: // Set IO to output State
51. begin isOut <= 1'b1; i <= i + 1'b1; end
52.
53. 1: // Send Active Command with Bank and Row address
54. begin rCMD <= _ACT; rBA <= iAddr[23:22]; rA <= iAddr[21:9]; i <= i + 1'b1; end
55.
56. 2: // wait TRCD 20ns
57. if( C1 == TRCD -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
58. else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end
59.
60. /*********************************************/
61.
62. 3: // Send Write command with row address
63. begin rCMD <= _WR; rBA <= iAddr[23:22]; rA <= { 4'b0010, iAddr[8:0] }; D1 <= iData; i <= i + 1'b1; end
64.
65. 4: // continue write until end and send BSTP
66. if( C1 == 512 -1 ) begin rCMD <= _BSTP; C1 <= 14'd0; i <= i + 1'b1 ;end
67. else begin rCMD <= _NOP; C1 <= C1 + 1'b1; D1 <= D1 + 1'b1; end
68.
69. /**********************************************/
70.
71. 5: // Generate done signal
72. begin rCMD <= _NOP; isDone <= 1'b1; i <= i + 1'b1; end
73.
74. 6:
75. begin isDone <= 1'b0; i <= 4'd0; end
76.
77. endcase
以上内容为页写操作,注意步骤3写入第0数据,步骤4则写入第1~511数据并且发送BSTP命令。
78. else if( iCall[2] )
79. case( i )
80.
81. 0:
82. begin isOut <= 1'b0; D1 <= 16'd0; i <= i + 1'b1; end
83.
84. 1: // Send Active command with Bank and Row address
85. begin rCMD <= _ACT; rBA <= iAddr[23:22]; rA <= iAddr[21:9]; i <= i + 1'b1; end
86.
87. 2: // wait TRCD 20ns
88. if( C1 == TRCD -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
89. else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end
90.
91. /********************/
92.
93. 3: // Send Read command and column address
94. begin rCMD <= _RD; rBA <= iAddr[23:22]; rA <= { 4'b0010, iAddr[8:0]}; i <= i + 1'b1; end
95.
96. 4: // wait CL 3 clock
97. if( C1 == CL -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
98. else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end
99.
100. /********************/
101.
102. 5: // Read Data
103. begin D1 <= S_DQ; rCMD <= _BSTP; i <= i + 1'b1; end
104.
105. /********************/
106.
107. 6: // Generate done signal
108. begin rCMD <= _NOP; isDone <= 1'b1; i <= i + 1'b1; end
109.
110. 7:
111. begin isDone <= 1'b0; i <= 4'd0; end
112.
113. endcase
以上内容为页读操作,注意步骤5是读取第0数据并且发送 BSTP命令。
114. else if( iCall[1] )
115. case( i )
116.
117. 0: // Send Precharge Command
118. begin rCMD <= _PR; i <= i + 1'b1; end
119.
120. 1: // wait TRP 20ns
121. if( C1 == TRP -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
122. else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end
123.
124. 2: // Send Auto Refresh Command
125. begin rCMD <= _AR; i <= i + 1'b1; end
126.
127. 3: // wait TRRC 63ns
128. if( C1 == TRRC -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
129. else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end
130.
131. 4: // Send Auto Refresh Command
132. begin rCMD <= _AR; i <= i + 1'b1; end
133.
134. 5: // wait TRRC 63ns
135. if( C1 == TRRC -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
136. else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end
137.
138. /********************/
139.
140. 6: // Generate done signal
141. begin isDone <= 1'b1; i <= i + 1'b1; end
142.
143. 7:
144. begin isDone <= 1'b0; i <= 4'd0; end
145.
146. endcase
以上内容是刷新操作。
147. else if( iCall[0] )
148. case( i )
149.
150. 0: // delay 100us
151. if( C1 == T100US -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
152. else begin C1 <= C1 + 1'b1; end
153.
154. /********************/
155.
156. 1: // Send Precharge Command
157. begin rCMD <= _PR; { rBA, rA } <= 15'h3fff; i <= i + 1'b1; end
158.
159. 2: // wait TRP 20ns
160. if( C1 == TRP -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
161. else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end
162.
163. 3: // Send Auto Refresh Command
164. begin rCMD <= _AR; i <= i + 1'b1; end
165.
166. 4: // wait TRRC 63ns
167. if( C1 == TRRC -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
168. else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end
169.
170. 5: // Send Auto Refresh Command
171. begin rCMD <= _AR; i <= i + 1'b1; end
172.
173. 6: // wait TRRC 63ns
174. if( C1 == TRRC -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
175. else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end
176.
177. /********************/
178.
179. 7: // Send LMR Cmd. Burst Read & Write, 3'b010 mean CAS latecy = 3, Sequential,Full Page
180. begin rCMD <= _LMR; rBA <= 2'b11; rA <= { 3'd0, 1'b0, 2'd0, 3'b011, 1'b0, 3'b111 }; i <= i + 1'b1; end
181.
182. 8: // Send 2 nop CLK for tMRD
183. if( C1 == TMRD -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
184. else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end
185.
186. /********************/
187.
188. 9: // Generate done signal
189. begin isDone <= 1'b1; i <= i + 1'b1; end
190.
191. 10:
192. begin isDone <= 1'b0; i <= 4'd0; end
193.
194. endcase
195.
以上内容是初始化,注意步骤7的Mode Register 内容,Busrt Length 为 3’b111。
196. assign { S_CKE, S_NCS, S_NRAS, S_NCAS, S_NWE } = rCMD;
197. assign { S_BA, S_A } = { rBA, rA };
198. assign S_DQM = rDQM;
199. assign S_DQ = isOut ? D1 : 16'hzzzz;
200. assign oDone = isDone;
201. assign oData = D1;
202.
203. endmodule
第196~201行是相关的输出驱动。
sdram_ctrlmod.v
该控制模块的内容与实验十八一致。
sdram_basemod.v
该组合模块的内容也与实验十八一致
sdram_demo.v
图20.4 实验二十的建模图。
图20.4是实验二十的建模图,外观上与实验十八一样,不过核心操作的内容却有所不同,具体内容我们还是来看代码吧。
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.
25. sdram_basemod U2
26. (
27. .CLOCK( CLOCK1 ),
28. .RESET( RESET ),
29. .S_CKE( S_CKE ),
30. .S_NCS( S_NCS ),
31. .S_NRAS( S_NRAS ),
32. .S_NCAS( S_NCAS ),
33. .S_NWE( S_NWE ),
34. .S_A( S_A ),
35. .S_BA( S_BA ),
36. .S_DQM( S_DQM ),
37. .S_DQ( S_DQ ),
38. .iCall( isCall ),
39. .oDone( DoneU2 ),
40. .iAddr( D1 ),
41. .iData( D2 ),
42. .oData( DataU2 )
43. );
44.
以上内容为SDRAM基础模块的实例化。
45. parameter B115K2 = 11'd1157, TXFUNC = 6'd16;
46.
47. reg [5:0]i,Go;
48. reg [10:0]C1;
49. reg [23:0]D1;
50. reg [15:0]D2,D3;
51. reg [10:0]T;
52. reg [1:0]isCall;
53. reg rTXD;
54.
55. always @ ( posedge CLOCK1 or negedge RESET )
56. if( !RESET )
57. begin
58. i <= 6'd0;
59. Go <= 6'd0;
60. C1 <= 11'd0;
61. D1 <= 24'd0;
62. D2 <= 16'd0;
63. D3 <= 16'd0;
64. T <= 11'd0;
65. isCall <= 2'b00;
66. rTXD <= 1'b1;
67. end
68. else
以上内容为相关的寄存器声明还有复位操作。第45行是波特率还有伪函数入口的常量声明。
69. case( i )
70.
71. 0:
72. if( DoneU2[1] ) begin isCall[1] <= 1'b0; i <= i + 1'b1; end
73. else begin isCall[1] <= 1'b1; D1 <= 24'd0; D2 <= 16'hA000; end
74.
75. 1:
76. if( DoneU2[0] ) begin D3 <= DataU2; isCall[0] <= 1'b0; i <= i + 1'b1; end
77. else begin isCall[0] <= 1'b1; end
78.
79. 2:
80. begin T <= { 2'b11, D3[15:8], 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end
81.
82. 3:
83. begin T <= { 2'b11, D3[7:0], 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end
84.
85. 4:
86. if( D1 == 24'd511 ) i <= i + 1'b1;
87. else begin D1 <= D1 + 1'b1; i <= 6'd1; end
88.
89. 5:
90. i <= i;
91.
92. /******************************/
93.
以上内容为部分核心操作。步骤0将数据 16’hA×××从地址0写至地址511,其中×××会经由页写而自行递增。换句话说,数据16’hA000~16’hA1FF从地址0写至地址511。
步骤1则用来读取数据,步骤2~3将读出的数据一一发送出去。步骤4用来递增地址,从0~511,然后返回步骤1,直至人为页读结束。
94. 16,17,18,19,20,21,22,23,24,25,26:
95. if( C1 == B115K2 -1 ) begin C1 <= 11'd0; i <= i + 1'b1; end
96. else begin rTXD <= T[i - 16]; C1 <= C1 + 1'b1; end
97.
98. 27:
99. i <= Go;
100.
101. endcase
102.
103. assign S_CLK = CLOCK2;
104. assign TXD = rTXD;
105.
106. endmodule
以上内容为部分核心操作。步骤16~27是发送一帧数据的伪函数。第103~104行则是相关的输出驱动。综合完毕并且下载程序,如果串口调试软件出现数据 A000~A1FF表示实验成功。