【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验二十二:SDRAM模块⑤ — FIFO读写

来源:互联网 时间:1970-01-01

 

经过漫长的战斗以后,我们终于来到最后。对于普通人而言,页读写就是一名战士的墓碑(最终战役) ... 然而,怕死的笔者想透过这个实验告诉读者,旅程的终点就是旅程的起点。一直以来,笔者都在烦恼“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读写,集于一身是有可能的。不管怎么样,实验二十二所要表达的信息已经非常清晰,即储存类的伸缩性与可塑性。


相关阅读:
Top