【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验十七:IIC储存模块 - FIFO读写

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储存模块随时可以使用。

时间: 2024-08-04 23:45:49

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验十七:IIC储存模块 - FIFO读写的相关文章

【黑金原创教程】【FPGA那些事儿-驱动篇I 】连载导读

前言: 无数昼夜的来回轮替以后,这本<驱动篇I>终于编辑完毕了,笔者真的感动到连鼻涕也流下来.所谓驱动就是认识硬件,还有前期建模.虽然<驱动篇I>的硬件都是我们熟悉的老友记,例如UART,VGA等,但是<驱动篇I>贵就贵在建模技巧的升华,亦即低级建模II. 话说低级建模II,读过<建模篇>的朋友多少也会面熟几分,因为它是低级建模的进化形态.许久以前,笔者就有点燃低级建模II的念头,但是懒惰的性格让笔者别扭许久.某天,老大忽然说道:"让咱们大干一场吧

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验六:数码管模块

实验六:数码管模块 有关数码管的驱动,想必读者已经学烂了 ... 不过,作为学习的新仪式,再烂的东西也要温故知新,不然学习就会不健全.黑金开发板上的数码管资源,由始至终都没有改变过,笔者因此由身怀念.为了点亮多位数码管从而显示数字,一般都会采用动态扫描,然而有关动态扫描的信息请怒笔者不再重复.在此,同样也是动态扫描,但我们却用不同的思路去理解. 图6.1 6位数码管. 如图6.1所示,哪里有一排6位数码管,其中包好8位DIG信号还有6位SEL信号.DIG为digit,即俗称的数码管码,如果数码管

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验三:按键模块② — 点击与长点击

实验三:按键模块② - 点击与长点击 实验二我们学过按键功能模块的基础内容,其中我们知道按键功能模块有如下操作: l 电平变化检测: l 过滤抖动: l 产生有效按键. 实验三我们也会z执行同样的事情,不过却是产生不一样的有效按键: l 按下有效(点击): l 长按下有效(长点击). 图3.1 按下有效,时序示意图. 图3.2 长按下有效,时序示意图. 如图3.1所示,按下有效既是"点击",当按键被按下并且消抖完毕以后,isSClick信号就有被拉高一个时钟(Short Click).

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验二:按键模块① - 消抖

实验二:按键模块① - 消抖 按键消抖实验可谓是经典中的经典,按键消抖实验虽曾在<建模篇>出现过,而且还惹来一堆麻烦.事实上,笔者这是在刁难各位同学,好让对方的惯性思维短路一下,但是惨遭口水攻击 ... 面对它,笔者宛如被甩的男人,对它又爱又恨.不管怎么样,如今 I'll be back,笔者再也不会重复一样的悲剧. 按键消抖说傻不傻说难不难.所谓傻,它因为原理不仅简单(就是延迟几下下而已),而且顺序语言(C语言)也有无数不尽的例子.所谓难,那是因为人们很难从单片机的思维跳出来 ... 此外,

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验四:按键模块③ — 单击与双击

实验四:按键模块③ — 单击与双击 实验三我们创建了“点击”还有“长点击”等有效按键的多功能按键模块.在此,实验四同样也是创建多功能按键模块,不过却有不同的有效按键.实验四的按键功能模块有以下两项有效按键: l 单击(按下有效): l 双击(连续按下两下有效). 图4.1 单击有效按键,时序示意图. 实验四的“单击”基本上与实验三的“点击”一模一样,既按键被按下,经过消抖以后isSClick信号被拉高一个时钟,结果如图4.1所示,过程非常单调.反之,“双击”实现起来,会比较麻烦一些,因为我们还要

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验五:按键模块④ — 点击,长点击,双击

实验五:按键模块④ — 点击,长点击,双击 实验二至实验四,我们一共完成如下有效按键: l 点击(按下有效) l 点击(释放有效) l 长击(长按下有效) l 双击(连续按下有效) 然而,不管哪个实验都是只有两项“功能”的按键模块而已,如今我们要创建三项“功能”的按键模块,亦即点击(按下有效),长击,还有双击.实验继续之前,让我们先来复习一下各种有效按键. 图5.1 点击(按下有效). 如图5.1所示,所谓点击(按下有效)就是按键按下以后,isSClick信号(Single Click) 产生一

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验九:PS/2模块③ — 键盘与多组合键

实验九:PS/2模块③ — 键盘与多组合键 笔者曾经说过,通码除了单字节以外,也有双字节通码,而且双字节通码都是 8’hE0开头,别名又是 E0按键.常见的的E0按键有,<↑>,<↓>,<←>,<→>,<HOME>,<PRTSC> 等编辑键.除此之外,一些组合键也是E0按键,例如 <RCtrl> 或者 <RAlt> .所以说,当我们设计组合键的时候,除了考虑“左边”的组合键以外,我们也要考虑“右边”的组合键.&

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验七:PS/2模块① — 键盘

实验七:PS/2模块① — 键盘 实验七依然也是熟烂的PS/2键盘.相较<建模篇>的PS/2键盘实验,实验七实除了实现基本的驱动以外,我们还要深入解PS/2时序,还有PS/2键盘的行为.不过,为了节省珍贵的页数,怒笔者不再重复有关PS/2的基础内容,那些不晓得的读者请复习<建模篇>或者自行谷歌一下. 市场上常见的键盘都是应用第二套扫描码,各种扫描码如图7.2所示.<建模篇>之际,笔者也只是擦边一下PS/2键盘,简单读取单字节通码与断码而已.所谓单字节通码,就是有效的按下

【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验八:PS/2模块② — 键盘与组合键

实验八:PS/2模块② — 键盘与组合键 实验七之际,我们学习如何读取PS/2键盘发送过来的通码与断码,不过实验内容也是一键按下然后释放,简单按键行为而已.然而,实验八的实验内容却是学习组合键的按键行为. 不知读者是否有类似的经历?当我们使用键盘的时候,如果5~6按键同时按下,电脑随之便会发出“哔哔”的警报声,键盘立即失效.这是键盘限制设计,不同产品也有不同限制的按键数量.默认下,最大按键数量是5~7个.所谓组合键就是两个以上的按键所产生的有效按键.举例而言,按下按键 <A> 输出“字符a”,

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

实验十八:SDRAM模块① — 单字读写 笔者与SDRAM有段不短的孽缘,它作为冤魂日夜不断纠缠笔者.笔者尝试过许多方法将其退散,不过屡试屡败的笔者,最终心情像橘子一样橙.<整合篇>之际,笔者曾经大战几回儿,不过内容都是点到即止.最近它破蛊而出,日夜不停:“好~痛苦!好~痛苦!”地呻吟着,吓得笔者不敢半夜如厕.疯狂之下,誓要歪它不可 ... 可恶的东西,笔者要它血债血还! 图18.1 数据读取(理想时序左,物理时序右). 首先,让我们来了解一下,什么才是数据读取的最佳状态?如图18.1所示,红