FPGA 学习小组
直播中

alexdos

6年用户 804经验值
擅长:可编程逻辑 电源/新能源 嵌入式技术 模拟技术
私信 关注

【FPGA】I²C程序的仿真与测试----仿真主程序

仿真主程序完成主节点数据到从节点的控制,代码如下:

  1. `include "timescale.v"
  2. //模块定义
  3. module tst_bench_top();
  4. //连线和寄存器
  5. reg clk;
  6. reg rstn;
  7. wire [31:0] adr;
  8. wire [ 7:0] dat_i, dat_o;
  9. wire we;
  10. wire stb;
  11. wire cyc;
  12. wire ack;
  13. wire inta;
  14. //q 保存状态寄存器内容
  15. reg [7:0] q, qq;
  16. wire scl, scl_o, scl_oen;
  17. wire sda, sda_o, sda_oen;
  18. //寄存器地址
  19. parameter PRER_LO = 3'b000; //分频寄存器低位地址
  20. parameter PRER_HI = 3'b001; //高位地址
  21. parameter CTR = 3'b010; //控制寄存器地址,(7)使能位|6 中断使能位|5-0其余保留位
  22. parameter RXR = 3'b011; //接收寄存器地址,(7)接收到的最后一个字节的数据
  23. parameter TXR = 3'b011; //传输寄存器地址,(7)传输地址时最后一位为读写位,1 为读
  24. parameter CR = 3'b100; //命令寄存器地址,
  25. //(7)开始|6 结束|5 读|4 写|3 应答(作为接收方时,发送应答信号,“0”为应答,“1”为不应答)|2 保留位|1 保留位|0 中断应答位,这八位自动清除
  26. parameter SR = 3'b100; //状态寄存器地址,(7)接收应答位(“0”为接收到应答)|6 忙位(产生开始信号后变为 1,结束信号后变为 0)|5 仲裁位|4-2 保留位|1 传输中位(1 表示正在传输数据,0 表示传输结束)|中断标志位
  27. parameter TXR_R = 3'b101;
  28. parameter CR_R = 3'b110;
  29. // 产生时钟信号,一个时间单位为 1ns,周期为 10ns,频率为 100MHz。
  30. always #5 clk = ~clk;
  31. //连接 master 模拟模块
  32. wb_master_model #(8, 32) u0 (
  33. .clk(clk), //时钟
  34. .rst(rstn), //重起
  35. .adr(adr), //地址
  36. .din(dat_i), //输入的数据
  37. .dout(dat_o), //输出的数据
  38. .cyc(cyc),
  39. .stb(stb),
  40. .we(we),
  41. .sel(),
  42. .ack(ack), //应答
  43. .err(1'b0),
  44. .rty(1'b0)
  45. );
  46. //连接 i2c 接口
  47. i2c_master_top i2c_top (
  48. //连接到 master 模拟模块部分
  49. .wb_clk_i(clk), //时钟
  50. .wb_rst_i(1'b0), //同步重起位
  51. .arst_i(rstn), //异步重起
  52. .wb_adr_i(adr[2:0]), //地址输入
  53. .wb_dat_i(dat_o), //数据输入接口
  54. .wb_dat_o(dat_i), //数据从接口输出
  55. .wb_we_i(we), //写使能信号
  56. .wb_stb_i(stb), //片选信号,应该一直为高
  57. .wb_cyc_i(cyc),
  58. .wb_ack_o(ack), //应答信号输出到 master 模拟模块
  59. .wb_inta_o(inta), //中断信号输出到 master 模拟模块

  60. 第 4 章 模拟 I
  61. 2
  62. C 协议
  63. ·121·
  64. //输出的 i2c 信号,连接到 slave 模拟模块
  65. .scl_pad_i(scl),
  66. .scl_pad_o(scl_o),
  67. .scl_padoen_o(scl_oen),
  68. .sda_pad_i(sda),
  69. .sda_pad_o(sda_o),
  70. .sda_padoen_o(sda_oen)
  71. );
  72. //连接到 slave 模拟模块
  73. i2c_slave_model #(7'b1010_000) i2c_slave (
  74. .scl(scl),
  75. .sda(sda)
  76. );
  77. //为 master 模拟模块产生 scl 和 sda 的三态缓冲
  78. assign scl = scl_oen ? 1'bz : scl_o; // create tri-state buffer for i2c_master scl line
  79. assign sda = sda_oen ? 1'bz : sda_o; // create tri-state buffer for i2c_master sda line
  80. //上拉
  81. pullup p1(scl); // pullup scl line
  82. pullup p2(sda); // pullup sda line
  83. //初始化
  84. initial
  85. begin
  86. $display("n 状态: %t I2C 接口测试开始!nn", $time);
  87. // 初始值
  88. clk = 0;
  89. //重起系统
  90. rstn = 1'b1; // negate reset
  91. #2;
  92. rstn = 1'b0; // assert reset
  93. repeat(20) @(posedge clk);
  94. rstn = 1'b1; // negate reset
  95. $display("状态: %t 完成系统重起!", $time);
  96. @(posedge clk);
  97. // 对接口编程
  98. // 写内部寄存器
  99. // 分频 100M/100K*5=O'200=h'C8
  100. u0.wb_write(1, PRER_LO, 8'hc7);
  101. u0.wb_write(1, PRER_HI, 8'h00);
  102. $display("状态: %t 完成分频寄存器操作!", $time);
  103. //读分频寄存器内容
  104. u0.wb_cmp(0, PRER_LO, 8'hc8);
  105. u0.wb_cmp(0, PRER_HI, 8'h00);
  106. $display("状态: %t 完成分频寄存器确认操作!", $time);
  107. //接口使能
  108. u0.wb_write(1, CTR, 8'h80);
  109. $display("状态: %t 完成接口使能!", $time);
  110. // 驱动 slave 地址
  111. // h'a0=b'1010_0000,地址+写状态,写入的地址为 h'50
  112. u0.wb_write(1, TXR, 8'ha0);
  113. //命令内容为 b'1001_0000,产生开始位,并设置写状态
  114. u0.wb_write(0, CR, 8'h90);
  115. $display("状态: %t 产生开始位, 然后写命令 a0(地址+写),命令开始!", $time);
  116. // 检查状态位信息
  117. // 检查传输是否结束
  118. u0.wb_read(1, SR, q);
  119. while(q[1])
  120. u0.wb_read(0, SR, q);
  121. $display("状态: %t 地址驱动写操作完成!", $time);
  122. // 待写的地址为 h'01
  123. u0.wb_write(1, TXR, 8'h01);
  124. // 产生写命令 b'0001_0000
  125. u0.wb_write(0, CR, 8'h10);
  126. $display("状态: %t 待写地址为 01,命令开始!", $time);
  127. // 检查状态位
  128. u0.wb_read(1, SR, q);
  129. while(q[1])
  130. u0.wb_read(0, SR, q);
  131. $display("状态: %t 写操作完成!", $time);
  132. // 写入内容
  133. u0.wb_write(1, TXR, 8'ha5);
  134. u0.wb_write(0, CR, 8'h10);
  135. $display("状态: %t 写入内容为 a5,开始写入过程!", $time);
  136. u0.wb_read(1, SR, q);
  137. while(q[1])
  138. u0.wb_read(1, SR, q);
  139. $display("状态: %t 写 a5 到地址 h'01 中完成!", $time);
  140. // 写入下一个地址 5a
  141. u0.wb_write(1, TXR, 8'h5a); // present data
  142. // 写入并停止
  143. u0.wb_write(0, CR, 8'h50); // set command (stop, write)
  144. $display("状态: %t 写 5a 到下一个地址,产生停止位!", $time);
  145. u0.wb_read(1, SR, q);
  146. while(q[1])
  147. u0.wb_read(1, SR, q); // poll it until it is zero
  148. $display("状态: %t 写第二个地址结束!", $time);
  149. // 读
  150. // 驱动 slave 地址
  151. u0.wb_write(1, TXR, 8'ha0);
  152. u0.wb_write(0, CR, 8'h90);
  153. $display("状态: %t 产生开始位,写命令 a0 (slave 地址+write)", $time);
  154. u0.wb_read(1, SR, q);
  155. while(q[1])
  156. u0.wb_read(1, SR, q); // poll it until it is zero
  157. $display("状态: %t slave 地址驱动完成!", $time);
  158. // 发送地址
  159. u0.wb_write(1, TXR, 8'h01);
  160. u0.wb_write(0, CR, 8'h10);
  161. $display("状态: %t 发送地址 01!", $time);
  162. u0.wb_read(1, SR, q);
  163. while(q[1])
  164. u0.wb_read(1, SR, q);
  165. $display("状态: %t 地址发送完成!", $time);
  166. // 驱动 slave 地址,1010_0001,h'50+read
  167. u0.wb_write(1, TXR, 8'ha1);
  168. u0.wb_write(0, CR, 8'h90);
  169. $display("状态: %t 产生重复开始位, 读地址+开始位", $time);
  170. u0.wb_read(1, SR, q);
  171. while(q[1])
  172. u0.wb_read(1, SR, q);
  173. $display("状态: %t 命令结束!", $time);
  174. // 读数据
  175. u0.wb_write(1, CR, 8'h20);
  176. $display("状态: %t 读+应答命令", $time);
  177. u0.wb_read(1, SR, q);
  178. while(q[1])
  179. u0.wb_read(1, SR, q);
  180. $display("状态: %t 读结束!", $time);
  181. // 检查读的内容
  182. u0.wb_read(1, RXR, qq);
  183. if(qq !== 8'ha5)
  184. $display("n 错误: 需要的是 a5, received %x at time %t", qq, $time);
  185. // 读下一个地址内容
  186. u0.wb_write(1, CR, 8'h20);
  187. $display("状态: %t 读+ 应答", $time);
  188. u0.wb_read(1, SR, q);
  189. while(q[1])
  190. u0.wb_read(1, SR, q);
  191. $display("状态: %t 第二个地址读结束!", $time);
  192. u0.wb_read(1, RXR, qq);
  193. if(qq !== 8'h5a)
  194. $display("n 错误: 需要的是 5a, received %x at time %t", qq, $time);
  195. // 读
  196. u0.wb_write(1, CR, 8'h20);
  197. $display("状态: %t 读 + 应答", $time);
  198. u0.wb_read(1, SR, q);
  199. while(q[1])
  200. u0.wb_read(1, SR, q);
  201. $display("状态: %t 第三个地址读完成!", $time);
  202. u0.wb_read(1, RXR, qq);
  203. $display("状态: %t 第三个地址内容是 %x !", $time, qq);
  204. // 读
  205. u0.wb_write(1, CR, 8'h28);
  206. $display("状态: %t 读 + 不应答!", $time);
  207. u0.wb_read(1, SR, q);
  208. while(q[1])
  209. u0.wb_read(1, SR, q);
  210. $display("状态: %t 第四个地址读完成!", $time);
  211. u0.wb_read(1, RXR, qq);
  212. $display("状态: %t 第四个地址内容为 %x !", $time, qq);
  213. // 检查不存在的 slave 地址
  214. // drive slave address
  215. u0.wb_write(1, TXR, 8'ha0);
  216. u0.wb_write(0, CR, 8'h90);
  217. $display("状态: %t 产生开始位, 发送命令 a0 (slave 地址+写). 检查非法地址!",$time);
  218. u0.wb_read(1, SR, q);
  219. while(q[1])
  220. u0.wb_read(1, SR, q); // poll it until it is zero
  221. $display("状态: %t 命令结束!", $time);
  222. // 发送内存地址
  223. u0.wb_write(1, TXR, 8'h10);
  224. u0.wb_write(0, CR, 8'h10);
  225. $display("状态: %t 发送 slave 内存地址 10!", $time);
  226. u0.wb_read(1, SR, q);
  227. while(q[1])
  228. u0.wb_read(1, SR, q);
  229. $display("状态: %t 地址发送完毕!", $time);
  230. // slave 发送不应答
  231. $display("状态: %t 检查不应答位!", $time);
  232. if(!q[7])
  233. $display("n 错误: 需要 NACK, 接收到 ACKn");
  234. // 从 slave 读数据
  235. u0.wb_write(1, CR, 8'h40);
  236. $display("状态: %t 产生'stop'位", $time);
  237. u0.wb_read(1, SR, q);
  238. while(q[1])
  239. u0.wb_read(1, SR, q); // poll it until it is zero
  240. $display("状态: %t 结束!", $time);
  241. #25000; // wait 25us
  242. $display("nn 状态: %t 测试结束!", $time);
  243. $finish;
  244. end
  245. endmodule

更多回帖

发帖
×
20
完善资料,
赚取积分