FPGA 学习小组
直播中

alexdos

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

以太网控制器外部PHY芯片模拟程序代码实现

模拟程序模拟了简化的 LXT971A 芯片(Inter 公司的外部 PHY 芯片)。PHY 芯片通过 MIIM(媒体无关接口管理模块)来连接以太网控制器,因此:

• 当以太网控制器向 PHY 芯片模拟程序发送数据时,PHY 芯片模拟程序控制数据按照协议的进行传输;

• 当 PHY 芯片向以太网控制器发送数据时,外部 PHY 芯片模拟程序首先按照协议要求产生需要传输的数据,然后发送到以太网控制器。

外部 PHY 芯片模拟程序的主要代码如下:

  1. `include "timescale.v"
  2. `include "eth_phy_defines.v"
  3. `include "tb_eth_defines.v"
  4. module eth_phy (m_rst_n_i, mtx_clk_o, mtxd_i, mtxen_i, mtxerr_i, mrx_clk_o, mrxd_o, mrxdv_o,
  5. mrxerr_o,
  6. mcoll_o, mcrs_o,mdc_i, md_io, phy_log);
  7. //输入输出信号
  8. input m_rst_n_i;
  9. ……
  10. //寄存器和连线
  11. reg control_bit15; // self clearing bit
  12. ……
  13. // PHY 芯片模拟程序的 MIIM 部分
  14. ……
  15. //初始化
  16. initial
  17. begin
  18. md_io_enable = 1'b0;
  19. respond_to_all_phy_addr = 1'b0;
  20. no_preamble = 1'b0;
  21. end
  22. // 使输出处于三态
  23. assign #1 md_io = (m_rst_n_i && md_io_enable) ? md_io_output : 1'bz ;
  24. //寄存器输入
  25. always@(posedge mdc_i or negedge m_rst_n_i)
  26. begin
  27. if (!m_rst_n_i)
  28. md_io_reg <= #1 0;
  29. else
  30. md_io_reg <= #1 md_io;
  31. end
  32. // 获得 PHY 地址、寄存器地址和数据输入,把需要输出的数据移位输出
  33. // putting Data out and shifting
  34. always@(posedge mdc_i or negedge m_rst_n_i)
  35. begin
  36. if (!m_rst_n_i)
  37. begin
  38. phy_address <= 0;
  39. reg_address <= 0;
  40. reg_data_in <= 0;
  41. reg_data_out <= 0;
  42. md_io_output <= 0;
  43. end
  44. else
  45. begin
  46. if (md_get_phy_address)
  47. begin
  48. phy_address[4:1] <= phy_address[3:0]; // correct address is `ETH_PHY_ADDR
  49. phy_address[0] <= md_io;
  50. end
  51. if (md_get_reg_address)
  52. begin
  53. reg_address[4:1] <= reg_address[3:0];
  54. reg_address[0] <= md_io;
  55. end
  56. if (md_get_reg_data_in)
  57. begin
  58. reg_data_in[15:1] <= reg_data_in[14:0];
  59. reg_data_in[0] <= md_io;
  60. end
  61. if (md_put_reg_data_out)
  62. begin
  63. reg_data_out <= register_bus_out;
  64. end
  65. if (md_io_enable)
  66. begin
  67. md_io_output <= reg_data_out[15];
  68. reg_data_out[15:1] <= reg_data_out[14:0];
  69. reg_data_out[0] <= 1'b0;
  70. end
  71. end
  72. end
  73. assign #1 register_bus_in = reg_data_in; // md_put_reg_data_in - allows writing to a selected
  74. register
  75. // 统计通过 MIIM(媒体无关接口管理模块)传输的数据
  76. always@(posedge mdc_i or negedge m_rst_n_i)
  77. begin
  78. if (!m_rst_n_i)
  79. begin
  80. if (no_preamble)
  81. md_transfer_cnt <= 33;
  82. else
  83. md_transfer_cnt <= 1;
  84. end
  85. else
  86. begin
  87. if (md_transfer_cnt_reset)
  88. begin
  89. if (no_preamble)
  90. md_transfer_cnt <= 33;
  91. else
  92. md_transfer_cnt <= 1;
  93. end
  94. else if (md_transfer_cnt < 64)
  95. begin
  96. md_transfer_cnt <= md_transfer_cnt + 1'b1;
  97. end
  98. else
  99. begin
  100. if (no_preamble)
  101. md_transfer_cnt <= 33;
  102. else
  103. md_transfer_cnt <= 1;
  104. end
  105. end
  106. end
  107. // MIIM 的传输控制
  108. always@(m_rst_n_i or md_transfer_cnt or md_io_reg or md_io_rd_wr or
  109. phy_address or respond_to_all_phy_addr or no_preamble)
  110. begin
  111. #1;
  112. while ((m_rst_n_i) && (md_transfer_cnt <= 64))
  113. begin
  114. // 复位信号
  115. // 检查报头
  116. if (md_transfer_cnt < 33)
  117. begin
  118. #4 md_put_reg_data_in = 1'b0;
  119. if (md_io_reg !== 1'b1)
  120. begin
  121. #1 md_transfer_cnt_reset = 1'b1;
  122. end
  123. else
  124. begin
  125. #1 md_transfer_cnt_reset = 1'b0;
  126. end
  127. end
  128. //检查开始位
  129. else if (md_transfer_cnt == 33)
  130. begin
  131. if (no_preamble)
  132. begin
  133. #4 md_put_reg_data_in = 1'b0;
  134. if (md_io_reg === 1'b0)
  135. begin
  136. #1 md_transfer_cnt_reset = 1'b0;
  137. end
  138. else
  139. begin
  140. #1 md_transfer_cnt_reset = 1'b1;
  141. //if ((md_io_reg !== 1'bz) && (md_io_reg !== 1'b1))
  142. if (md_io_reg !== 1'bz)
  143. begin
  144. //错误
  145. `ifdef VERBOSE
  146. $fdisplay(phy_log, "*E (%0t)(%m)MIIM - wrong first start bit (without preamble)",
  147. $time);
  148. `endif
  149. #10 $stop;
  150. end
  151. end
  152. end
  153. else // with preamble
  154. begin
  155. #4 ;
  156. `ifdef VERBOSE
  157. $fdisplay(phy_log, " (%0t)(%m)MIIM - 32-bit preamble received", $time);
  158. `endif
  159. // check start bit only if md_transfer_cnt_reset is inactive, because if
  160. // preamble suppression was changed start bit should not be checked
  161. if ((md_io_reg !== 1'b0) && (md_transfer_cnt_reset == 1'b0))
  162. begin
  163. // 错误
  164. `ifdef VERBOSE
  165. $fdisplay(phy_log, "*E (%0t)(%m)MIIM - wrong first start bit", $time);
  166. `endif
  167. #10 $stop;
  168. end
  169. end
  170. end
  171. else if (md_transfer_cnt == 34)
  172. begin
  173. #4;
  174. if (md_io_reg !== 1'b1)
  175. begin
  176. // 错误
  177. #1;
  178. `ifdef VERBOSE
  179. if (no_preamble)
  180. $fdisplay(phy_log, "*E (%0t)(%m)MIIM - wrong second start bit (without preamble)",
  181. $time);
  182. else
  183. $fdisplay(phy_log, "*E (%0t)(%m)MIIM - wrong second start bit", $time);
  184. `endif
  185. #10 $stop;
  186. end
  187. else
  188. begin
  189. `ifdef VERBOSE
  190. if (no_preamble)
  191. #1 $fdisplay(phy_log, " (%0t)(%m)MIIM - 2 start bits received (without preamble)",
  192. $time);
  193. else
  194. #1 $fdisplay(phy_log, " (%0t)(%m)MIIM - 2 start bits received", $time);
  195. `endif
  196. end
  197. end
  198. // 寄存器 op-code
  199. else if (md_transfer_cnt == 35)
  200. begin
  201. #4;
  202. if (md_io_reg === 1'b1)
  203. begin
  204. #1 md_io_rd_wr = 1'b1;
  205. end
  206. else
  207. begin
  208. #1 md_io_rd_wr = 1'b0;
  209. end
  210. end
  211. else if (md_transfer_cnt == 36)
  212. begin
  213. #4;
  214. if ((md_io_reg === 1'b0) && (md_io_rd_wr == 1'b1))
  215. begin
  216. #1 md_io_rd_wr = 1'b1; // reading from PHY registers
  217. `ifdef VERBOSE
  218. $fdisplay(phy_log, " (%0t)(%m)MIIM - op-code for READING from registers", $time);
  219. `endif
  220. end
  221. else if ((md_io_reg === 1'b1) && (md_io_rd_wr == 1'b0))
  222. begin
  223. #1 md_io_rd_wr = 1'b0; // writing to PHY registers
  224. `ifdef VERBOSE
  225. $fdisplay(phy_log, " (%0t)(%m)MIIM - op-code for WRITING to registers", $time);
  226. `endif
  227. end
  228. else
  229. begin
  230. // 操作码错误
  231. `ifdef VERBOSE
  232. #1 $fdisplay(phy_log, "*E (%0t)(%m)MIIM - wrong OP-CODE", $time);
  233. `endif
  234. #10 $stop;
  235. end
  236. // 获得 PHY 地址
  237. begin
  238. #1 md_get_phy_address = 1'b1;
  239. end
  240. end
  241. else if (md_transfer_cnt == 41)
  242. begin
  243. #4 md_get_phy_address = 1'b0;
  244. // set the signal - get register address
  245. #1 md_get_reg_address = 1'b1;
  246. end
  247. // 获得寄存器地址
  248. else if (md_transfer_cnt == 46)
  249. begin
  250. #4 md_get_reg_address = 1'b0;
  251. #1 md_put_reg_data_out = 1'b1;
  252. end
  253. ……
  254. // PHY 芯片与以太网控制器之间数据传输的控制
  255. // 寄存器
  256. reg mcoll_o;
  257. ……
  258. //初始化所有寄存器
  259. initial
  260. begin
  261. mcrs_rx = 0;
  262. mcrs_tx = 0;
  263. task_mcoll = 0;
  264. task_mcrs = 0;
  265. task_mcrs_lost = 0;
  266. no_collision_in_half_duplex = 0;
  267. collision_in_full_duplex = 0;
  268. no_carrier_sense_in_tx_half_duplex = 0;
  269. no_carrier_sense_in_rx_half_duplex = 0;
  270. carrier_sense_in_tx_full_duplex = 0;
  271. no_carrier_sense_in_rx_full_duplex = 0;
  272. real_carrier_sense = 0;
  273. end
  274. // 数据冲突
  275. always@(m_rst_n_i or control_bit8_0 or collision_in_full_duplex or
  276. mcrs_rx or mcrs_tx or task_mcoll or no_collision_in_half_duplex
  277. )
  278. begin
  279. if (!m_rst_n_i)
  280. mcoll_o = 0;
  281. else
  282. begin
  283. if (control_bit8_0[8]) // full duplex
  284. begin
  285. if (collision_in_full_duplex) // collision is usually not asserted in full duplex
  286. begin
  287. mcoll_o = ((mcrs_rx && mcrs_tx) || task_mcoll);
  288. `ifdef VERBOSE
  289. if (mcrs_rx && mcrs_tx)
  290. $fdisplay(phy_log, " (%0t)(%m) Collision set in FullDuplex!", $time);
  291. if (task_mcoll)
  292. $fdisplay(phy_log, " (%0t)(%m) Collision set in FullDuplex from TASK!", $time);
  293. `endif
  294. end
  295. else
  296. begin
  297. mcoll_o = task_mcoll;
  298. `ifdef VERBOSE
  299. if (task_mcoll)
  300. $fdisplay(phy_log, " (%0t)(%m) Collision set in FullDuplex from TASK!", $time);
  301. `endif
  302. end
  303. end
  304. else // half duplex
  305. begin
  306. mcoll_o = ((mcrs_rx && mcrs_tx && !no_collision_in_half_duplex) ||
  307. task_mcoll);
  308. `ifdef VERBOSE
  309. if (mcrs_rx && mcrs_tx)
  310. $fdisplay(phy_log, " (%0t)(%m) Collision set in HalfDuplex!", $time);
  311. if (task_mcoll)
  312. $fdisplay(phy_log, " (%0t)(%m) Collision set in HalfDuplex from TASK!", $time);
  313. `endif
  314. end
  315. end
  316. end
  317. //载波监听多路访问
  318. always@(m_rst_n_i or control_bit8_0 or carrier_sense_in_tx_full_duplex or
  319. no_carrier_sense_in_rx_full_duplex or
  320. no_carrier_sense_in_tx_half_duplex or
  321. no_carrier_sense_in_rx_half_duplex or
  322. mcrs_rx or mcrs_tx or task_mcrs or task_mcrs_lost
  323. )
  324. begin
  325. if (!m_rst_n_i)
  326. mcrs_o = 0;
  327. else
  328. begin
  329. if (control_bit8_0[8]) // full duplex
  330. begin
  331. if (carrier_sense_in_tx_full_duplex) // carrier sense is usually not asserted during
  332. TX in full duplex
  333. mcrs_o = ((mcrs_rx && !no_carrier_sense_in_rx_full_duplex) ||
  334. mcrs_tx || task_mcrs) && !task_mcrs_lost;
  335. else
  336. mcrs_o = ((mcrs_rx && !no_carrier_sense_in_rx_full_duplex) ||
  337. task_mcrs) && !task_mcrs_lost;
  338. end
  339. else // half duplex
  340. begin
  341. mcrs_o = ((mcrs_rx && !no_carrier_sense_in_rx_half_duplex) ||
  342. (mcrs_tx && !no_carrier_sense_in_tx_half_duplex) ||
  343. task_mcrs) && !task_mcrs_lost;
  344. end
  345. end
  346. end
  347. // 以太网控制器发送数据控制,PHY 芯片接收数据
  348. //寄存器
  349. reg [7:0] tx_mem [0:4194303]; // 4194304 是 22 位地址线所能提供的所有地址,每个地址是 8

  350. ……
  351. //发送数据控制
  352. always@(posedge mtx_clk_o)
  353. begin
  354. // 保存数据并进行基本的帧数据检查
  355. if (!m_rst_n_i)
  356. begin
  357. tx_cnt <= 0;
  358. tx_preamble_ok <= 0;
  359. tx_sfd_ok <= 0;
  360. tx_len <= 0;
  361. tx_len_err <= 0;
  362. end
  363. else
  364. begin
  365. if (!mtxen_i)
  366. begin
  367. tx_cnt <= 0;
  368. end
  369. else
  370. begin
  371. //发送四位字节数据的计数器
  372. tx_cnt <= tx_cnt + 1;
  373. //设置初始化值,检查第一个四位字节数据的报头
  374. if (tx_cnt == 0)
  375. begin
  376. `ifdef VERBOSE
  377. $fdisplay(phy_log, " (%0t)(%m) TX frame started with tx_en set!", $time);
  378. `endif
  379. if (mtxd_i == 4'h5)
  380. tx_preamble_ok <= 1;
  381. else
  382. tx_preamble_ok <= 0;
  383. tx_sfd_ok <= 0;
  384. tx_byte_aligned_ok <= 0;
  385. tx_len <= 0;
  386. tx_len_err <= 0;
  387. end
  388. // 检查报头
  389. if ((tx_cnt > 0) && (tx_cnt <= 13))
  390. begin
  391. if ((tx_preamble_ok != 1) || (mtxd_i != 4'h5))
  392. tx_preamble_ok <= 0;
  393. end
  394. // 检查 SFD
  395. if (tx_cnt == 14)
  396. begin
  397. `ifdef VERBOSE
  398. if (tx_preamble_ok == 1)
  399. $fdisplay(phy_log, " (%0t)(%m) TX frame preamble OK!", $time);
  400. else
  401. $fdisplay(phy_log, "*E (%0t)(%m) TX frame preamble NOT OK!", $time);
  402. `endif
  403. if (mtxd_i == 4'h5)
  404. tx_sfd_ok <= 1;
  405. else
  406. tx_sfd_ok <= 0;
  407. end
  408. if (tx_cnt == 15)
  409. begin
  410. if ((tx_sfd_ok != 1) || (mtxd_i != 4'hD))
  411. tx_sfd_ok <= 0;
  412. end
  413. // 控制存储地址数据、类型/长度、数据内容和 FCS 到发送数据缓冲区
  414. if (tx_cnt > 15)
  415. begin
  416. if (tx_cnt == 16)
  417. begin
  418. `ifdef VERBOSE
  419. if (tx_sfd_ok == 1)
  420. $fdisplay(phy_log, " (%0t)(%m) TX frame SFD OK!", $time);
  421. else
  422. $fdisplay(phy_log, "*E (%0t)(%m) TX frame SFD NOT OK!", $time);
  423. `endif
  424. end
  425. if (tx_cnt[0] == 0)
  426. begin
  427. tx_mem_data_in[3:0] <= mtxd_i; // storing LSB nibble
  428. tx_byte_aligned_ok <= 0; // if transfer will stop after this, then there was drible
  429. nibble
  430. end
  431. else
  432. begin
  433. tx_mem[tx_mem_addr_in[21:0]] <= {mtxd_i, tx_mem_data_in[3:0]}; // storing data into
  434. tx memory
  435. tx_len <= tx_len + 1; // enlarge byte length counter
  436. tx_byte_aligned_ok <= 1; // if transfer will stop after this, then transfer is byte
  437. alligned
  438. tx_mem_addr_in <= tx_mem_addr_in + 1'b1;
  439. end
  440. if (mtxerr_i)
  441. tx_len_err <= tx_len;
  442. end
  443. end
  444. end
  445. //为发送数据产生载波信号
  446. if (!m_rst_n_i)
  447. begin
  448. mcrs_tx <= 0;
  449. mtxen_d1 <= 0;
  450. mtxen_d2 <= 0;
  451. mtxen_d3 <= 0;
  452. mtxen_d4 <= 0;
  453. mtxen_d5 <= 0;
  454. mtxen_d6 <= 0;
  455. end
  456. else
  457. begin
  458. mtxen_d1 <= mtxen_i;
  459. mtxen_d2 <= mtxen_d1;
  460. mtxen_d3 <= mtxen_d2;
  461. mtxen_d4 <= mtxen_d3;
  462. mtxen_d5 <= mtxen_d4;
  463. mtxen_d6 <= mtxen_d5;
  464. if (real_carrier_sense)
  465. mcrs_tx <= mtxen_d6;
  466. else
  467. mcrs_tx <= mtxen_i;
  468. end
  469. end
  470. `ifdef VERBOSE
  471. reg frame_started;
  472. initial
  473. begin
  474. frame_started = 0;
  475. end
  476. always@(posedge mtxen_i)
  477. begin
  478. frame_started <= 1;
  479. end
  480. always@(negedge mtxen_i)
  481. begin
  482. if (frame_started)
  483. begin
  484. $fdisplay(phy_log, " (%0t)(%m) TX frame ended with tx_en reset!", $time);
  485. frame_started <= 0;
  486. end
  487. end
  488. always@(posedge mrxerr_o)
  489. begin
  490. $fdisplay(phy_log, " (%0t)(%m) RX frame ERROR signal was set!", $time);
  491. end
  492. `endif
  493. ……
  494. endmodule

回帖(1)

花柚子

2019-1-21 09:41:55
谢谢楼主的分享
举报

更多回帖

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