FPGA 学习小组
直播中

alexdos

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

【FPGA】I²C程序的仿真与测试----从节点的仿真

从节点仿真程序需要模拟从主节点接收数据,并发出应答信号,代码如下:

  1. `include "timescale.v"
  2. //模块定义
  3. module i2c_slave_model (scl, sda);
  4. // 参数
  5. // 地址
  6. parameter I2C_ADR = 7'b001_0000;
  7. // 输入、输出
  8. input scl;
  9. inout sda;
  10. // 变量申明
  11. wire debug = 1'b1;
  12. reg [7:0] mem [3:0]; // 初始化内存
  13. reg [7:0] mem_adr; // 内存地址
  14. reg [7:0] mem_do; // 内存数据输出
  15. reg sta, d_sta;
  16. reg sto, d_sto;
  17. reg [7:0] sr; // 8 位移位寄存器
  18. reg rw; // 读写方向
  19. wire my_adr; // 地址
  20. wire i2c_reset; // RESET 信号
  21. reg [2:0] bit_cnt;
  22. wire acc_done; // 传输完成
  23. reg ld;
  24. reg sda_o;
  25. wire sda_dly;
  26. // 状态机的状态定义
  27. parameter idle = 3'b000;
  28. parameter slave_ack = 3'b001;
  29. parameter get_mem_adr = 3'b010;
  30. parameter gma_ack = 3'b011;
  31. parameter data = 3'b100;
  32. parameter data_ack = 3'b101;
  33. reg [2:0] state;
  34. // 模块主体
  35. //初始化
  36. initial
  37. begin
  38. sda_o = 1'b1;
  39. state = idle;
  40. end
  41. // 产生移位寄存器
  42. always @(posedge scl)
  43. sr <= #1 {sr[6:0],sda};
  44. //检测到访问地址与从节点一致
  45. assign my_adr = (sr[7:1] == I2C_ADR);
  46. //产生位寄存器
  47. always @(posedge scl)
  48. if(ld)
  49. bit_cnt <= #1 3'b111;
  50. else
  51. bit_cnt <= #1 bit_cnt - 3'h1;
  52. //产生访问结束标志
  53. assign acc_done = !(|bit_cnt);
  54. // sda 延迟
  55. assign #1 sda_dly = sda;
  56. //检测到开始状态
  57. always @(negedge sda)
  58. if(scl)
  59. begin
  60. sta <= #1 1'b1;
  61. if(debug)
  62. $display("DEBUG i2c_slave; start condition detected at %t", $time);
  63. end
  64. else
  65. sta <= #1 1'b0;
  66. always @(posedge scl)
  67. d_sta <= #1 sta;
  68. // 检测到停止状态信号
  69. always @(posedge sda)
  70. if(scl)
  71. begin
  72. sto <= #1 1'b1;
  73. if(debug)
  74. $display("DEBUG i2c_slave; stop condition detected at %t", $time);
  75. end
  76. else
  77. sto <= #1 1'b0;
  78. //产生 I2C 的 RESET 信号
  79. assign i2c_reset = sta || sto;
  80. // 状态机
  81. always @(negedge scl or posedge sto)
  82. if (sto || (sta && !d_sta) )
  83. begin
  84. state <= #1 idle; // reset 状态机
  85. sda_o <= #1 1'b1;
  86. ld <= #1 1'b1;
  87. end
  88. else
  89. begin
  90. // 初始化
  91. sda_o <= #1 1'b1;
  92. ld <= #1 1'b0;
  93. case(state)
  94. idle: // idle 状态
  95. if (acc_done && my_adr)
  96. begin
  97. state <= #1 slave_ack;
  98. rw <= #1 sr[0];
  99. sda_o <= #1 1'b0; // 产生应答信号
  100. #2;
  101. if(debug && rw)
  102. $display("DEBUG i2c_slave; command byte received (read) at %t",
  103. $time);
  104. if(debug && !rw)
  105. $display("DEBUG i2c_slave; command byte received (write) at %t",
  106. $time);
  107. if(rw)
  108. begin
  109. mem_do <= #1 mem[mem_adr];
  110. if(debug)
  111. begin
  112. #2 $display("DEBUG i2c_slave; data block read %x from
  113. address %x (1)", mem_do, mem_adr);
  114. #2 $display("DEBUG i2c_slave; memcheck [0]=%x, [1]=%x,
  115. [2]=%x", mem[4'h0], mem[4'h1], mem[4'h2]);
  116. end
  117. end
  118. end
  119. slave_ack:
  120. begin
  121. if(rw)
  122. begin
  123. state <= #1 data;
  124. sda_o <= #1 mem_do[7];
  125. end
  126. else
  127. state <= #1 get_mem_adr;
  128. ld <= #1 1'b1;
  129. end
  130. get_mem_adr: // 等待内存地址
  131. if(acc_done)
  132. begin
  133. state <= #1 gma_ack;
  134. mem_adr <= #1 sr; // 保存内存地址
  135. sda_o <= #1 !(sr <= 15); // 收到合法地址信号后发出应答信号
  136. if(debug)
  137. #1 $display("DEBUG i2c_slave; address received. adr=%x, ack=%b",
  138. sr, sda_o);
  139. end
  140. gma_ack:
  141. begin
  142. state <= #1 data;
  143. ld <= #1 1'b1;
  144. end
  145. data: // 接收数据
  146. begin
  147. if(rw)
  148. sda_o <= #1 mem_do[7];
  149. if(acc_done)
  150. begin
  151. state <= #1 data_ack;
  152. mem_adr <= #2 mem_adr + 8'h1;
  153. sda_o <= #1 (rw && (mem_adr <= 15) );
  154. if(rw)
  155. begin
  156. #3 mem_do <= mem[mem_adr];
  157. if(debug)
  158. #5 $display("DEBUG i2c_slave; data block read %x from
  159. address %x (2)", mem_do, mem_adr);
  160. end
  161. if(!rw)
  162. begin
  163. mem[ mem_adr[3:0] ] <= #1 sr; // store data in memory
  164. if(debug)
  165. #2 $display("DEBUG i2c_slave; data block write %x to
  166. address %x", sr, mem_adr);
  167. end
  168. end
  169. end
  170. data_ack:
  171. begin
  172. ld <= #1 1'b1;
  173. if(rw)
  174. if(sda) //
  175. begin
  176. state <= #1 idle;
  177. sda_o <= #1 1'b1;
  178. end
  179. else
  180. begin
  181. state <= #1 data;
  182. sda_o <= #1 mem_do[7];
  183. end
  184. else
  185. begin
  186. state <= #1 data;
  187. sda_o <= #1 1'b1;
  188. end
  189. end
  190. endcase
  191. end
  192. // 从内存读数据
  193. always @(posedge scl)
  194. if(!acc_done && rw)
  195. mem_do <= #1 {mem_do[6:0], 1'b1};
  196. // 产生三态
  197. assign sda = sda_o ? 1'bz : 1'b0;
  198. // 检查时序
  199. wire tst_sto = sto;
  200. wire tst_sta = sta;
  201. wire tst_scl = scl;
  202. //指定各个信号的上升沿和下降沿
  203. specify
  204. specparam normal_scl_low = 4700,
  205. normal_scl_high = 4000,
  206. normal_tsu_sta = 4700,
  207. normal_tsu_sto = 4000,
  208. normal_sta_sto = 4700,
  209. fast_scl_low = 1300,
  210. fast_scl_high = 600,
  211. fast_tsu_sta = 1300,
  212. fast_tsu_sto = 600,
  213. fast_sta_sto = 1300;
  214. $width(negedge scl, normal_scl_low);
  215. $width(posedge scl, normal_scl_high);
  216. $setup(negedge sda &&& scl, negedge scl, normal_tsu_sta); // 开始状态信号
  217. $setup(posedge scl, posedge sda &&& scl, normal_tsu_sto); // 停止状态信号
  218. $setup(posedge tst_sta, posedge tst_scl, normal_sta_sto);
  219. endspecify
  220. endmodule

更多回帖

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