代码开源,IIC协议建立模型

  案例采取明德扬设计思想形成。IIC协议是丰盛常用的接口协议,在电子类岗位招聘须要中日常出现它的人影。关于IIC协议那里只做简单介绍,详细消息请自行百度或查六柱预测关Datasheet,网上资料十三分多。该篇博文首要讲什么样选拔verilog来叙述IIC协议,以读写EEPROM为例引导我们探听下明德扬四段式状态机规范和优势,其余还有部分要幸亏陈设进度中总计的经历技术。

原创博客,转载请注解出处:【重新发布,代码开源】FPGA设计千兆以太网MAC——通过MDIO接口配置与检查和测试PHY芯片-没落骑士-和讯

  IIC协议时序格式以Datasheet中时序图的款型供我们参考。IIC协议有一条时钟线SCL和一条双线数据总线SDA。SDA在SCL高电平日保险平稳,不然视为初阶或终止条件。

图片 1

图片 2

  读写操作时序:

图片 3

图片 4

  发送端发送1byte多少后,接收端在下3个SCL高电平时期拉低总线表示应答,即接收数据成功。

图片 5

图片 6

  MDC为MAC驱动时钟信号,MDIO是串行数据总线,需求连接上拉电阻保证idle状态下高电平。在这之中前导码包罗三十一个比特“1”,PHY地址遵照芯片引脚连接而定,此处为01.turn
around域是为了预防读操作时数据抵触,在读操作进度中MAC和PHY均在第一比特处进入高阻态,PHY在第一比特处驱动MDIO接口为低电平以占据总线控制权。注意两点:第②假如时钟信号在读写操作后截止,时钟必须保险至少八个时钟周期持续翻转且MDIO高电平从而确认保证在此之前的操作实现。故在陈设中得以等待一段时间后再拉低时钟使能信号。第三多少个操作之间起码一个idle比特。

  以下分别是器件地址为1字节的EEPROM的单字节写和读操作,需求专注的是DEVICE
ADDRESS段中前2个人稳定是4’b1010,后三人遵照EEPROM地址信号电平决定(本次实验地点信号引脚均接地,因而后二人为000),最终1人是读写标志位,低电平写。

  正确驱动接口时序须求关切AC
characterisics.

图片 7

图片 8图片 9

图片 10

  很鲜明MAC驱动总线时,在MDC下降沿更新数据。而PHY驱动总线时,MDC上涨沿后更新数据。遵照datasheet中的timing参数设定MDC时钟周期是800ns,MAC接收PHY数据时降低沿采集样品。

  好了,有了以上五张时序图大家便领悟要怎么了,便是落到实处那几个时序嘛!对于那种串行时序,时间有先后且操作差距较大的要用状态机完成。每类别型操作定义在叁个状态中,状态之中须求七个操作则同盟计数器完毕。全体设计思路如下:先构造时钟信号SCL,这里频率定义为200KHz,而系统时钟有频率为200MHz差分晶振提供,显然要求用到分频计数器。由于SCL高电平时期数据要保全平稳,所以大家在分频计数器计数到四分一处拉高SCL,四分三处拉低SCL,那样做的好处是在完工计数时刚刚处在SCL低电平中间点,此处作为数据变化的时刻再伏贴可是。

  接下去关心要访问的里边寄存器地址,首先读取PHY寄存器数据以检查和测试其行事情景,若发现分外再考虑写入数据。那里读取基本情势状态寄存器0X01的bit5,若为1证实机关心下一代组织商完结。第3个寄存器是PHY特定状态寄存器0X1第11中学的[15:14]和13,分别是现阶段速率和全/半双工通讯形式。若检测到自动协商达成,且工作在一千M全双工形式下,表达工作不利。

图片 11

③ 、硬件架构与状态机设计

  有了时钟信号,下一步正是通过分裂的图景达成SDA信号线满意上述时序须求。大家先来划分状态(实际上时序图中都给我们标识好了),很明白综合考虑写和读三种时序,状态应定义为:开始状态、起先、写控制、响应1、写地址、响应二 、写多少、响应三 、重新开头、读控制、响应肆 、读数据、不响应、甘休。那里写控制和读控制就是DEVICE
ADDRESS阶段,唯一的区分在于读写标志位不一样。能来看以上景况划分包含写流程分支和读流程分支,可以根据指令用三个注明位加以分歧。

  全数准备工作完结,现在开班规划。依据“自顶向下”设计原则,规划好全部结构和模块直接口,再规划之中情状机一步步完成逻辑作用。

  定义状态参数并使用独热码实行编码:

图片 12

图片 13

  Mdio_ctrl模块负责完结PHY芯片的配备与检查和测试逻辑,Mdio接口模块形成读写操作时序。此处仅透过读操作简易检测PHY状态,暂不进行铺排,故两模块工作状态跳转如图所示:

  IIC商业事务中年老年是SCL高电平时期正是贰遍操作,由此为了让各种情状都有整数个SCL周期(完整分频计数周期),对每种情状进行比特计数,写控制、地址、写多少、读控制、读数据阶段计数周期是8,别的为1。其它为保证代码的“健壮性”,也正是便是发送1byte数额后没有响应也未必挂死在等候响应阶段,设定在每一趟等待响应阶段若响应才进去下一操作,不然回到开头状态。因此取得气象转移图(只包罗主要流程,转移条件及未响应回到IDLE状态未画出):

图片 14

图片 15

  剩下的行事正是把多少个状态机完成出来,卓殊简单。有亟待的心上人可以参见一下,关于芯片的现实参数详见:Realtek
卡宴TL8211E-CG Datasheet 1.8.上代码!

  至此全部的统一筹划工作都曾经到位,接下去就是根据上述分析编写代码。在编排代码在此以前大概介绍明德扬四段式状态机的布署思想和代码规范:四段式状态机实质上是在三段式状态机基础上独立建议情状转移条件定义的组织。目标是让设计者多少个小时段只注意于一件工作,约等于说当设计状态机的时候先把境况转移流程规定,而规范用不一致的信号名代替,等情景转移流程规定后再定义转移条件。那样做的另一个功利是作为条件的信号名能够很有益的在一连时序逻辑中使用。其中用于代替条件的信号名要服从类似如下格式:<state_c>2<state_n>。<>处用状态名代替。

四 、代码编写

完整代码如下:

MDIO控制模块:

  1 `timescale 1ns / 1ps
  2 
  3 module iic_interface#(parameter SCL_CYC = 1000)//200KHz
  4 (
  5     input clk,
  6     input rst_n,
  7     
  8     //用户侧接口
  9     input write_en,//写指令
 10     input read_en, //读指令
 11     input [7:0]share_addr,    //读写复用地址
 12     input [7:0] wri_data,//代写入数据
 13     input wri_data_vld,
 14     
 15     output reg busy,//总线忙信号
 16     output reg [7:0] rd_data,//读回数据
 17     output reg rd_data_vld,
 18     
 19     //仿真用接口
 20     output reg [13:0] state_c,
 21     
 22     //eeprom侧接口
 23     output reg scl, //时钟
 24     input sda_in,
 25     output reg sda_en,
 26     output reg sda_reg
 27     
 28     );
 29     
 30     reg [11:0] div_cnt;
 31     reg high_middle,low_middle;
 32     reg [3:0] bit_cnt;
 33     reg [3:0] N;
 34     //(*keep = "true"*)reg [13:0] state_c;
 35     reg [13:0] state_n;
 36     reg [7:0] wri_byte;
 37     reg rd_flag;
 38     reg [7:0] rd_buf;
 39     reg [13:0] state_c_tmp;
 40     reg [7:0] device_addr_wr_shift;
 41     
 42     wire add_bit_cnt,end_bit_cnt;
 43     wire add_div_cnt,end_div_cnt;
 44     wire idle2start,start2wri_ctrl,wri_ctrl2ack1,ack12addr,addr2ack2,ack22wri_data;
 45     wire wri_data2ack3,ack32stop,ack22re_start,re_start2rd_ctrl,rd_ctrl2ack4;
 46     wire ack42rd_data,rd_data2nack,nack2stop,stop2idle,ack2idle;
 47     reg ack_valid,ack_invalid;
 48     wire [2:0] cs;
 49     wire wri_vld;
 50     wire [7:0] device_addr_rd,device_addr_wr;
 51     wire [7:0] word_addr;
 52     
 53     //状态编码
 54     localparam IDLE     = 14'b00_0000_0000_0001,//1
 55                START    = 14'b00_0000_0000_0010,//2
 56                WRI_CTRL = 14'b00_0000_0000_0100,//4
 57                ACK1     = 14'b00_0000_0000_1000,//8
 58                ADDR     = 14'b00_0000_0001_0000,//10
 59                ACK2     = 14'b00_0000_0010_0000,//20
 60                WRI_DATA = 14'b00_0000_0100_0000,//40
 61                ACK3     = 14'b00_0000_1000_0000,//80
 62                RE_START = 14'b00_0001_0000_0000,//100
 63                RD_CTRL  = 14'b00_0010_0000_0000,//200
 64                ACK4     = 14'b00_0100_0000_0000,//400
 65                RD_DATA  = 14'b00_1000_0000_0000,//800
 66                NACK     = 14'b01_0000_0000_0000,//1000
 67                STOP     = 14'b10_0000_0000_0000;//2000
 68     
 69     //分频计数器 在响应操作直到完成或退出到IDLE中间都计数
 70     always@(posedge clk or negedge rst_n)begin
 71         if(!rst_n)
 72             div_cnt <= 0;
 73         else if(add_div_cnt)begin
 74             if(end_div_cnt)
 75                 div_cnt <= 0;
 76             else 
 77                 div_cnt <= div_cnt + 1'b1;
 78         end
 79         else 
 80             div_cnt <= 0;
 81     end
 82     
 83     assign add_div_cnt = busy == 1;
 84     assign end_div_cnt = add_div_cnt && div_cnt == SCL_CYC - 1;
 85     
 86     //比特计数器
 87     always@(posedge clk or negedge rst_n)begin
 88         if(!rst_n)
 89             bit_cnt <= 0;
 90         else if(add_bit_cnt)begin
 91             if(end_bit_cnt)
 92                 bit_cnt <= 0;
 93             else 
 94                 bit_cnt <= bit_cnt + 1'b1;
 95         end
 96     end
 97     
 98     assign add_bit_cnt = end_div_cnt;
 99     assign end_bit_cnt = add_bit_cnt && bit_cnt == N - 1;
100     
101     always@(*)begin
102         case(state_c)
103             WRI_CTRL:N <= 8;
104             ADDR:N <= 8;
105             WRI_DATA:N <= 8;
106             RD_CTRL:N <= 8;
107             RD_DATA:N <= 8;
108             default:N <= 1;
109         endcase
110     end
111     
112     //---------------------iic时序四段式状态机部分-------------------------
113     
114     //时序逻辑描述状态转移
115     always@(posedge clk or negedge rst_n)begin
116         if(!rst_n)
117             state_c <= IDLE;
118         else 
119             state_c <= state_n;
120     end
121     
122     //组合逻辑描述状态转移条件
123     always@(*)begin
124         case(state_c)
125             IDLE:begin       //空闲状态
126                 if(idle2start)
127                     state_n <= START;
128                 else 
129                     state_n <= state_c;
130             end
131             
132             START:begin    //产生开始条件 即SCL高电平期间SDA拉低
133                 if(start2wri_ctrl)
134                     state_n <= WRI_CTRL;
135                 else 
136                     state_n <= state_c;
137             end
138             
139             WRI_CTRL:begin  //写器件地址和写标志位
140                 if(wri_ctrl2ack1)
141                     state_n <= ACK1;
142                 else 
143                     state_n <= state_c;
144             end
145             
146             ACK1:begin   //等待响应
147                 if(ack12addr)
148                     state_n <= ADDR;
149                 else if(ack2idle)
150                     state_n <= IDLE;
151                 else 
152                     state_n <= state_c;
153             end
154             
155             ADDR:begin  //写存储单元地址
156                 if(addr2ack2)
157                     state_n <= ACK2;
158                 else 
159                     state_n <= state_c;
160             end
161             
162             ACK2:begin   //等待响应2
163                 if(ack22wri_data)   //写操作
164                     state_n <= WRI_DATA;
165                 else if(ack22re_start)//读操作
166                     state_n <= RE_START;
167                 else if(ack2idle)
168                     state_n <= IDLE;
169                 else 
170                     state_n <= state_c;
171             end
172             
173             WRI_DATA:begin   //写数据 8bit
174                 if(wri_data2ack3)
175                     state_n <= ACK3;
176                 else 
177                     state_n <= state_c;
178             end
179             
180             ACK3:begin   //等待响应3
181                 if(ack32stop)
182                     state_n <= STOP;
183                 else if(ack2idle)
184                     state_n <= IDLE;
185                 else 
186                     state_n <= state_c;
187             end
188             
189             RE_START:begin  //若为读操作在响应2后再次构造开始条件
190                 if(re_start2rd_ctrl)
191                     state_n <= RD_CTRL;
192                 else 
193                     state_n <= state_c;
194             end
195             
196             RD_CTRL:begin   //写入存储单元地址和读标志位
197                 if(rd_ctrl2ack4)
198                     state_n <= ACK4;
199                 else 
200                     state_n <= state_c;
201             end
202             
203             ACK4:begin  //等待响应4
204                 if(ack42rd_data)
205                     state_n <= RD_DATA;
206                 else if(ack2idle)
207                     state_n <= IDLE;
208                 else 
209                     state_n <= state_c;
210             end
211             
212             RD_DATA:begin  //读数据 8bit
213                 if(rd_data2nack)
214                     state_n <= NACK;
215                 else 
216                     state_n <= state_c;
217             end
218             
219             NACK:begin  //不响应 无操作即可
220                 if(nack2stop)
221                     state_n <= STOP;
222                 else 
223                     state_n <= state_c;
224             end
225             
226             STOP:begin  //构造停止条件
227                 if(stop2idle)
228                     state_n <= IDLE;
229                 else 
230                     state_n <= state_c;
231             end
232             
233             default:
234                 state_n <= IDLE;
235         endcase
236     end
237     
238     //连续赋值语句定义状态转移条件
239     assign idle2start       = state_c  == IDLE     && (write_en || read_en);
240     assign start2wri_ctrl   = state_c  == START    && end_bit_cnt;  
241     assign wri_ctrl2ack1    = state_c  == WRI_CTRL && end_bit_cnt;
242     assign ack12addr        = state_c  == ACK1     && ack_valid && end_bit_cnt;
243     assign addr2ack2        = state_c  == ADDR     && end_bit_cnt;
244     assign ack22wri_data    = state_c  == ACK2     && ack_valid && !rd_flag && end_bit_cnt;
245     assign wri_data2ack3    = state_c  == WRI_DATA && end_bit_cnt;
246     assign ack32stop        = state_c  == ACK3     && ack_valid && end_bit_cnt;
247     assign ack22re_start    = state_c  == ACK2     && ack_valid && rd_flag && end_bit_cnt;
248     assign re_start2rd_ctrl = state_c  == RE_START && end_bit_cnt;
249     assign rd_ctrl2ack4     = state_c  == RD_CTRL  && end_bit_cnt;
250     assign ack42rd_data     = state_c  == ACK4     && ack_valid && end_bit_cnt;
251     assign rd_data2nack     = state_c  == RD_DATA  && end_bit_cnt;
252     assign nack2stop        = state_c  == NACK     && ack_invalid && end_bit_cnt;
253     assign stop2idle        = state_c  == STOP     && end_bit_cnt;
254     assign ack2idle         = ack_state && ack_invalid;
255     
256 
257     
258     always@(posedge clk or negedge rst_n)begin
259         if(!rst_n)
260             ack_valid <= 0;
261         else if(ack12addr || ack22wri_data || ack32stop || ack22re_start || ack42rd_data || ack2idle)
262             ack_valid <= 0;
263         else if(ack_state && high_middle && !sda_en && !sda_in)
264             ack_valid <= 1;
265     end
266     
267     assign ack_state = state_c == ACK1 || state_c == ACK2 || state_c == ACK3 || state_c == ACK4;
268     
269     always@(posedge clk or negedge rst_n)begin
270         if(!rst_n)
271             ack_invalid <= 0;
272         else if(state_c == NACK && high_middle && !sda_en && sda_in)
273             ack_invalid <= 1;
274         else if(end_bit_cnt)
275             ack_invalid <= 0;
276     end
277     
278     //时序逻辑描述状态输出
279     
280     //scl时钟信号
281     always@(posedge clk or negedge rst_n)begin
282         if(!rst_n)
283             scl <= 0;
284         else if(add_div_cnt && div_cnt == SCL_CYC/4 - 1)
285             scl <= 1;
286         else if(add_div_cnt && div_cnt == SCL_CYC/4 + SCL_CYC/2 - 1)
287             scl <= 0;
288     end
289     
290     //找到scl高低电平中间点
291     always@(posedge clk or negedge rst_n)begin
292         if(!rst_n)
293             high_middle <= 0;
294         else if(add_div_cnt && div_cnt == SCL_CYC/2 - 1)
295             high_middle <= 1;
296         else 
297             high_middle <= 0;
298     end
299     
300     //三态门输出使能
301     always@(posedge clk or negedge rst_n)begin
302         if(!rst_n)
303             sda_en <= 1;
304         else if(idle2start || ack12addr || ack22wri_data || ack32stop || ack22re_start || nack2stop)
305             sda_en <= 1;
306         else if(wri_ctrl2ack1 || addr2ack2 || wri_data2ack3 || rd_ctrl2ack4 || rd_data2nack || ack2idle || stop2idle)
307             sda_en <= 0;
308     end
309     
310     //数据总线输出寄存器
311     always@(posedge clk or negedge rst_n)begin
312         if(!rst_n)
313             sda_reg <= 1;
314         else if(idle2start)
315             sda_reg <= 1;
316         else if((state_c == START || state_c == RE_START) && high_middle)
317             sda_reg <= 0;
318         else if(state_c == WRI_CTRL)
319             sda_reg <= device_addr_wr[7-bit_cnt];
320         else if(state_c == ADDR)
321             sda_reg <= word_addr[7 - bit_cnt];
322         else if(state_c == WRI_DATA)
323             sda_reg <= wri_data[7 - bit_cnt];
324         else if(state_c == STOP && high_middle)
325             sda_reg <= 1;
326         else if(ack22re_start)
327             sda_reg <= 1;
328         else if(state_c == RE_START && high_middle)
329             sda_reg <= 0;
330         else if(state_c == RD_CTRL)
331             sda_reg <= device_addr_rd[7- bit_cnt];
332         else if(ack_state)
333             sda_reg <= 0;
334         else if(nack2stop)
335             sda_reg <= 0;
336     end
337     
338     assign device_addr_wr = {4'b1010,cs,1'b0};
339     assign cs             = 3'b000;
340     assign word_addr      = share_addr;
341     assign device_addr_rd = {4'b1010,cs,1'b1};
342     
343     //读取数据缓存
344     always@(posedge clk or negedge rst_n)begin
345         if(!rst_n)
346             rd_buf <= 0;
347         else if(state_c == RD_DATA && high_middle)
348             rd_buf <= {rd_buf[6:0],sda_in};
349     end
350     
351     //读数据有效指示
352     always@(posedge clk or negedge rst_n)begin
353         if(!rst_n)
354             rd_data_vld <= 0;
355         else if(rd_data2nack)
356             rd_data_vld <= 1;
357         else 
358             rd_data_vld <= 0;
359     end
360     
361     //读数据输出
362     always@(posedge clk or negedge rst_n)begin
363         if(!rst_n)
364             rd_data <= 0;
365         else 
366             rd_data <= rd_buf;
367     end
368     
369     //读标志位
370     always@(posedge clk or negedge rst_n)begin
371         if(!rst_n)
372             rd_flag <= 0;
373         else if(read_en)
374             rd_flag <= 1;
375         else if(rd_flag && (stop2idle || state_c == IDLE))
376             rd_flag <= 0;
377     end
378     
379     //总线忙信号
380     always@(posedge clk or negedge rst_n)begin
381         if(!rst_n)
382             busy <= 0;
383         else if(write_en || read_en)
384             busy <= 1;
385         else if(busy == 1 &&(stop2idle || state_c == IDLE))
386             busy <= 0;
387     end
388     
389 endmodule

图片 16图片 17

   能够看出状态机部分每个分为:时序逻辑描述状态转移,组合逻辑描述状态转移条件,延续赋值定义状态转移条件以及时序逻辑描述状态相关输出。并且至始至终使用state_c和state_n多个信号表示现态和次态,使逻辑更是鲜明。接口部分为了有利于仿真和调节,参与状态信号state_c。那里提到到多个双向端口sda,用多个信号:输出使能sda_en,输出寄存器sda_reg和输入缓存sda_in表示。在顶层模块中动用那三个信号通过三态门的样式提交,关于三态门的采纳细节和虚伪艺术稍后讲述。

  1 `timescale 1ns / 1ps  2   3 module mdio_ctrl(  4     input           clk,//100M  5     input           rst_n,  6   7     input           en,  8     output reg          chk_result =0,  9     output reg          chk_vld =0, 10  11     input           rdy,     12     output reg          rd_en =0, 13     output reg [5-1:0]  phy_addr =0, 14     output reg [5-1:0]  reg_addr =0, 15     input  [16-1:0] rd_data, 16     input           rd_vld 17 ); 18  19 parameter MS_CYC = 100_000; 20  21  22 localparam  IDLE = 0 ; 23 localparam  WAIT = 1 ; 24 localparam  RD_PHY = 2 ; 25 localparam  CHECK = 3 ; 26  27 localparam WAIT_MS = 10;    28         29 localparam BMSR = 5'h01, 30            PHYSR = 5'h11; 31  32 reg [4-1:0] state_c = 0,state_n = 0; 33 wire idle2wait,wait2rd_phy,rd_phy2check,check2idle,check2wait; 34 wire link_up; 35 reg [16-1:0] rd_memory [0:1]; 36 reg [ (17-1):0]  ms_cnt =0    ; 37 wire        add_ms_cnt ; 38 wire        end_ms_cnt ; 39 reg [ (4-1):0]  wait_cnt  =0    ; 40 wire        add_wait_cnt ; 41 wire        end_wait_cnt ; 42 reg [ (2-1):0]  rd_cnt  =0    ; 43 wire        add_rd_cnt ; 44 wire        end_rd_cnt ; 45 reg [ (2-1):0]  rdata_cnt  =0    ; 46 wire        add_rdata_cnt ; 47 wire        end_rdata_cnt ;    48 wire [5*2-1:0] registers;      49 reg rd_finish = 0; 50  51 initial begin 52     rd_memory[0] = 0; 53     rd_memory[1] = 0; 54 end 55  56 always @(posedge clk or negedge rst_n) begin  57     if (rst_n==0) begin 58         state_c <= IDLE ; 59     end 60     else begin 61         state_c <= state_n; 62    end 63 end 64  65 always @ begin  66     case   67         IDLE :begin 68             if(idle2wait)  69                 state_n = WAIT ; 70             else  71                 state_n = state_c ; 72         end 73         WAIT :begin 74             if(wait2rd_phy)  75                 state_n = RD_PHY ; 76             else  77                 state_n = state_c ; 78         end 79         RD_PHY :begin 80             if(rd_phy2check)  81                 state_n = CHECK ; 82             else  83                 state_n = state_c ; 84         end 85         CHECK :begin 86             if(check2idle)  87                 state_n = IDLE ; 88             else if(check2wait)  89                 state_n = WAIT ; 90             else  91                 state_n = state_c ; 92         end 93         default : state_n = IDLE ; 94     endcase 95 end 96  97 assign idle2wait    = state_c==IDLE && ; 98 assign wait2rd_phy  = state_c==WAIT && (end_wait_cnt); 99 assign rd_phy2check = state_c==RD_PHY && (end_rdata_cnt);100 assign check2idle   = state_c==CHECK && ;101 assign check2wait   = state_c==CHECK && (!link_up);102 103 104 assign link_up = rd_memory[0][5] == 1'b1 && rd_memory[1][15:13] == 3'b10_1;//auto_nego && gigabit && full_duplex105 106 107 //计数器108 always @(posedge clk or negedge rst_n) begin 109     if (rst_n==0) begin110         ms_cnt <= 0; 111     end112     else if(add_ms_cnt) begin113         if(end_ms_cnt)114             ms_cnt <= 0; 115         else116             ms_cnt <= ms_cnt+1 ;117    end118 end119 assign add_ms_cnt = (state_c == WAIT);120 assign end_ms_cnt = add_ms_cnt  && ms_cnt == -1 ;//100MHZ时钟100_000121 122 always @(posedge clk or negedge rst_n) begin 123     if (rst_n==0) begin124         wait_cnt <= 0; 125     end126     else if(add_wait_cnt) begin127         if(end_wait_cnt)128             wait_cnt <= 0; 129         else130             wait_cnt <= wait_cnt+1 ;131    end132 end133 assign add_wait_cnt = (end_ms_cnt);134 assign end_wait_cnt = add_wait_cnt  && wait_cnt == -1 ;135 136 always @(posedge clk or negedge rst_n) begin 137     if (rst_n==0) begin138         rd_cnt <= 0; 139     end140     else if(add_rd_cnt) begin141         if(end_rd_cnt)142             rd_cnt <= 0; 143         else144             rd_cnt <= rd_cnt+1 ;145    end146 end147 assign add_rd_cnt = (state_c == RD_PHY && rdy && !rd_finish);148 assign end_rd_cnt = add_rd_cnt  && rd_cnt == (2)-1 ;149 150 always  @(posedge clk or negedge rst_n)begin151     if(rst_n==1'b0)begin152         rd_finish <= 0;153     end154     else if(end_rd_cnt)begin155         rd_finish <= 1'b1;156     end157     else if(state_c == CHECK)158         rd_finish <= 0;159 end160 161 162 always @(posedge clk or negedge rst_n) begin 163     if (rst_n==0) begin164         rdata_cnt <= 0; 165     end166     else if(add_rdata_cnt) begin167         if(end_rdata_cnt)168             rdata_cnt <= 0; 169         else170             rdata_cnt <= rdata_cnt+1 ;171    end172 end173 assign add_rdata_cnt = ;174 assign end_rdata_cnt = add_rdata_cnt  && rdata_cnt == (2)-1 ;175 176 //接口信号逻辑177 always  @(posedge clk or negedge rst_n)begin178     if(rst_n==1'b0)begin179         rd_en <= 0;180         phy_addr <= 0;181         reg_addr <= 0;182     end183     else if(add_rd_cnt)begin184         rd_en <= 1'b1;185         phy_addr <= 5'b00001;186         reg_addr <= registers[10-5*rd_cnt-1 -:5];187     end188     else begin189         rd_en <= 0;190         phy_addr <= 0;191         reg_addr <= 0;192     end193 end194 195 assign registers = {BMSR,PHYSR};//5'h01,5'h11196 197 always  @(posedge clk or negedge rst_n)begin198     if(rst_n==1'b0)begin199         rd_memory[0] <= 0;200         rd_memory[1] <= 0;201     end202     else if(add_rdata_cnt)begin203         rd_memory[rdata_cnt] <= rd_data;204     end205 end206 207 //用户侧输出检测结果208 always  @(posedge clk or negedge rst_n)begin209     if(rst_n==1'b0)begin210         chk_vld <= 0;211     end212     else if(state_c == CHECK)begin213         chk_vld <= 1'b1;214     end215     else216         chk_vld <= 0;217 end218 219 always  @(posedge clk or negedge rst_n)begin220     if(rst_n==1'b0)begin221         chk_result <= 0;222     end223     else if(check2idle)begin224         chk_result <= 1'b1;225     end226     else if(check2wait)227         chk_result <= 0;228 end229 230 endmodule

  先规划别的模块和顶层模块,之后对顶层模块进行虚伪测试,那时观察各类模块中国国投号数值分析排查难点。有了时序接口模块,在科学无误景况下,已经足以完毕对EEPROM的读写操作。现在明显规划目标,大家要落实EEPROM的一字节数据读写,由此能够透过按键发送指令向EEPROM中某地址中写入随便三个数量,之后用另二个按键发送读指令将刚写入地址中多少读出的方式注脚读写操作是还是不是正规干活。编写控制模块(控制模块仅实现IIC总线空闲时才响应操作,实际上用按键方式犹豫时间距离较长,不会冒出多少个指令抢占总线的场地,那里设计量控制制模块是为着适应其余场地或效益扩张用途)

mdio_ctrl

 1 `timescale 1ns / 1ps
 2 
 3 module iic_ctrl(
 4     input clk,
 5     input rst_n,
 6     input local_rd,
 7     input local_wr,
 8     
 9     input iic_busy,
10     output reg com_rd,
11     output reg com_wr
12     );
13     
14     wire ready;
15     
16     assign ready = !iic_busy;
17     
18     //写命令
19     always@(posedge clk or negedge rst_n)begin
20         if(!rst_n)
21             com_wr <= 0;
22         else if(local_wr && ready)//iic总线空闲时才响应操作
23             com_wr <= 1;
24         else 
25             com_wr <= 0;
26     end
27     
28     //读命令
29     always@(posedge clk or negedge rst_n)begin
30         if(!rst_n)
31             com_rd <= 0;
32         else if(local_rd && ready)
33             com_rd <= 1;
34         else 
35             com_rd <= 0;
36     end
37     
38     
39 endmodule

MDIO时序接口模块:

   剩下只需投入按键消抖模块,并把按键消抖模块,控制模块还有时序接口模块都例化在顶层文件中即可。按键消抖模块在事先的博文中有描述,那里运用计数器合作情状标志位的法子贯彻。须要证实的是七个按键使用二个按键消抖模块的宏图方法:只需将信号位宽定义为可变参数。

图片 18图片 19

 1 `timescale 1ns / 1ps
 2 
 3 module key_filter
 4 #(parameter DATA_W    = 24,
 5             KEY_W     = 2,
 6             TIME_20MS = 4_000_000)
 7 (
 8    input clk    ,
 9    input rst_n  ,
10    input [KEY_W-1 :0] key_in ,    //按键 按下为低电平
11    output reg [KEY_W-1 :0] key_vld 
12 );
13 
14     reg [DATA_W-1:0] cnt;
15     reg flag;
16     reg [KEY_W-1 :0] key_in_ff1;
17     reg [KEY_W-1 :0] key_in_ff0;
18 
19     wire add_cnt,end_cnt;
20     
21     //延时计数器
22     always  @(posedge clk or negedge rst_n)begin
23         if(rst_n==1'b0)
24             cnt <= 0;
25         else if(add_cnt)begin
26             if(end_cnt)
27                 cnt <= 0;
28             else
29                 cnt <= cnt + 1'b1;
30         end
31         else
32             cnt <= 0;
33     end
34     //按下状态才计数,松手清零
35     assign add_cnt = flag == 1'b0 && (key_in_ff1 != 2'b11); 
36     assign end_cnt = add_cnt && cnt == TIME_20MS - 1;
37     
38     //计数标志位,0有效 为了只计数一个周期
39     always  @(posedge clk or negedge rst_n)begin 
40         if(rst_n==1'b0)begin
41             flag <= 1'b0;
42         end
43         else if(end_cnt)begin
44             flag <= 1'b1;
45         end
46         else if(key_in_ff1 == 2'b11)begin//松手重新清零
47             flag <= 1'b0;
48         end
49     end
50     
51     //同步处理
52     always  @(posedge clk or negedge rst_n)begin 
53         if(rst_n==1'b0)begin
54             key_in_ff0 <= 0;
55             key_in_ff1 <= 0;
56         end
57         else begin
58             key_in_ff0 <= key_in    ;
59             key_in_ff1 <= key_in_ff0;
60         end
61     end
62 
63     //输出有效
64     always  @(posedge clk or negedge rst_n)begin 
65         if(rst_n==1'b0)begin
66             key_vld <= 0;
67         end
68         else if(end_cnt)begin
69             key_vld <= ~key_in_ff1;
70         end
71         else begin
72             key_vld <= 0;
73         end
74     end
75     
76 endmodule
  1 `timescale 1ns / 1ps  2   3 module mdio_interface#(parameter MDC_CYC = 800)//ns  4 (  5     input                   clk,//100M时钟  6     input                   rst_n,  7   8     input                   rd_en,  9     input       [5-1:0]     phy_addr, 10     input       [5-1:0]     reg_addr, 11     output reg  [16-1:0]    rd_data =0, 12     output reg              rd_vld =0, 13     output reg              rdy =0, 14  15     output reg              mdo =1, 16     output reg              mdo_en =0, 17     input                   mdi, 18     output reg              mdc =1 19     ); 20  21     localparam N = MDC_CYC/10; 22  23  24 localparam  IDLE = 0 ; 25 localparam  WRI_COM = 1 ; 26 localparam  RD_DATA = 2 ; 27  28 localparam PRE      = 32'hffff_ffff, 29            START    = 2'b01, 30            OP       = 2'b10, 31            TA       = 2'b11; 32  33 reg [3-1:0] state_c =0,state_n =0; 34 wire idle2wri_com,wri_com2rd_data,rd_data2idle; 35 reg [ (7-1):0]  div_cnt  =0    ; 36 wire        add_div_cnt ; 37 wire        end_div_cnt ; 38 reg [ (6-1):0]  bit_cnt  =0    ; 39 wire        add_bit_cnt ; 40 wire        end_bit_cnt ; 41 reg [6-1:0] M =0; 42 wire [48-1:0] command; 43 reg   rd_flag  =0    ; 44 reg [5-1:0] phy_addr_tmp = 0; 45 reg [5-1:0] reg_addr_tmp = 0; 46  47  48 //寄存地址 49 always  @(posedge clk or negedge rst_n)begin 50     if(rst_n==1'b0)begin 51         phy_addr_tmp <= 0; 52         reg_addr_tmp <= 0; 53     end 54     else ifbegin 55         phy_addr_tmp <= phy_addr; 56         reg_addr_tmp <= reg_addr; 57     end 58 end 59  60  61 always@begin 62     if(state_c == IDLE && !rd_en && !rd_flag) 63         rdy <= 1; 64     else 65         rdy <= 0; 66 end 67  68 always @(posedge clk or negedge rst_n) begin  69     if (rst_n==0) begin 70         state_c <= IDLE ; 71     end 72     else begin 73         state_c <= state_n; 74    end 75 end 76  77 always @ begin  78     case   79         IDLE :begin 80             if(idle2wri_com)  81                 state_n = WRI_COM ; 82             else  83                 state_n = state_c ; 84         end 85         WRI_COM :begin 86             if(wri_com2rd_data)  87                 state_n = RD_DATA ; 88             else  89                 state_n = state_c ; 90         end 91         RD_DATA :begin 92             if(rd_data2idle)  93                 state_n = IDLE ; 94             else  95                 state_n = state_c ; 96         end 97         default : state_n = IDLE ; 98     endcase 99 end100 101 102 assign idle2wri_com     = state_c==IDLE     && end_div_cnt && (rd_flag || rd_en);103 assign wri_com2rd_data  = state_c==WRI_COM  && end_bit_cnt;104 assign rd_data2idle     = state_c==RD_DATA  && end_bit_cnt;105 106 107 always @(posedge clk or negedge rst_n )begin 108     if(rst_n==0) begin109         rd_flag <= (0)  ;110     end111     else if(state_c == IDLE && rd_en)begin112         rd_flag <= (1'b1)  ;113     end 114     else if(state_c == WRI_COM)115         rd_flag <= 0;116 end117 118 119 //分频计数器120 always @(posedge clk or negedge rst_n) begin 121     if (rst_n==0) begin122         div_cnt <= 0; 123     end124     else if(add_div_cnt) begin125         if(end_div_cnt)126             div_cnt <= 0; 127         else128             div_cnt <= div_cnt+1 ;129    end130 end131 assign add_div_cnt = (1);132 assign end_div_cnt = add_div_cnt  && div_cnt == -1 ;133 134 //比特计数器135 always @(posedge clk or negedge rst_n) begin 136     if (rst_n==0) begin137         bit_cnt <= 0; 138     end139     else if(add_bit_cnt) begin140         if(end_bit_cnt)141             bit_cnt <= 0; 142         else143             bit_cnt <= bit_cnt+1 ;144    end145 end146 assign add_bit_cnt = (end_div_cnt && state_c != IDLE);147 assign end_bit_cnt = add_bit_cnt  && bit_cnt == -1 ;148 149 always@begin150     case151         WRI_COM:M = 48;152         RD_DATA:M = 16;153         default:M = 10;154     endcase155 end156 157 //mdc时钟158 always @(posedge clk or negedge rst_n )begin 159     if(rst_n==0) begin160         mdc <= (1'b1)  ;161     end162     else if(add_div_cnt && div_cnt == (N>>1) - 1)begin163         mdc <= (1'b1)  ;164     end 165     else if(end_div_cnt)166         mdc <= 0;167 end168 169 170 //mdio输出171 always @(posedge clk or negedge rst_n )begin 172     if(rst_n==0) begin173         mdo <= (1'b1)  ;174     end175     else if(add_bit_cnt && state_c == WRI_COM)begin176         mdo <= command[48-1-bit_cnt]  ;177     end 178     else if(state_c != WRI_COM)179         mdo <= 1'b1;180 end181 182 assign command = {PRE,START,OP,phy_addr_tmp,reg_addr_tmp,TA};183 184 always @(posedge clk or negedge rst_n )begin 185     if(rst_n==0) begin186         mdo_en <= (0)  ;187     end188     else if(state_c == WRI_COM && add_bit_cnt)189         case190             0: mdo_en <= 1'b1;191             46:mdo_en <= 0;192             default:;193         endcase194 end195 196 //mdio输入197 always @(posedge clk or negedge rst_n )begin 198     if(rst_n==0) begin199         rd_data <= (0)  ;200     end201     else if(add_bit_cnt && state_c == RD_DATA)begin202         rd_data[16-1-bit_cnt] <=   ;203     end 204 end205 206 always @(posedge clk or negedge rst_n )begin 207     if(rst_n==0) begin208         rd_vld <= (0)  ;209     end210     else if(rd_data2idle)begin211         rd_vld <= (1'b1)  ;212     end 213     else214         rd_vld <= 0;215 end216 217 218 endmodule

 顶层模块例化子模块:

mdio_interface

  1 `timescale 1ns / 1ps
  2 
  3 module eeprom_top(
  4     
  5     input sys_clk_p,
  6     input sys_clk_n,
  7     input rst_n,
  8     input [1:0] key,
  9     //仿真接口
 10     output sda_en,
 11     output [13:0] state_c,
 12     
 13     //EEPROM接口
 14     output scl,
 15     inout sda
 16     );
 17     
 18     wire sys_clk_ibufg;
 19     (*keep = "true"*)wire busy;
 20     (*keep = "true"*)wire read,write;
 21     wire [7:0] rd_data;
 22     wire rd_data_vld;
 23     (*keep = "true"*)wire sda_reg,sda_in;
 24     (*keep = "true"*)wire [1:0] key_vld;
 25     //(*keep = "true"*)wire sda_en;
 26     //(*keep = "true"*)wire [13:0] state_c;
 27     wire [39:0] probe0;
 28     
 29     IBUFGDS #
 30     (
 31     .DIFF_TERM ("FALSE"),
 32     .IBUF_LOW_PWR ("FALSE")
 33     )
 34     u_ibufg_sys_clk
 35     (
 36     .I (sys_clk_p),     //差分时钟的正端输入,需要和顶层模块的端口直接连接
 37     .IB (sys_clk_n),    // 差分时钟的负端输入,需要和顶层模块的端口直接连接
 38     .O (sys_clk_ibufg)  //时钟缓冲输出
 39     );
 40     
 41     
 42     key_filter
 43     #(.DATA_W(24),
 44       .KEY_W(2),
 45       .TIME_20MS(4_000_000))
 46     key_filter
 47     (
 48        .clk (sys_clk_ibufg)   ,
 49        .rst_n(rst_n)  ,
 50        .key_in (key),    //按键 按下为低电平
 51        .key_vld(key_vld) 
 52     );
 53     
 54     iic_ctrl iic_ctrl(
 55     .clk(sys_clk_ibufg),
 56     .rst_n(rst_n),
 57     .local_wr(key_vld[1]),
 58     .local_rd(key_vld[0]),
 59     
 60     .iic_busy(busy),
 61     .com_rd(read),
 62     .com_wr(write)
 63     );
 64     
 65     iic_interface
 66     #(.SCL_CYC(1000))
 67     iic_interface(
 68     .clk(sys_clk_ibufg),
 69     .rst_n(rst_n),
 70     
 71     //用户侧接口
 72     .write_en(write),  //写指令
 73     .read_en(read),    //读指令
 74     .share_addr(8'h15),//读写复用地址
 75     .wri_data(8'h32),  //待写入数据
 76     .wri_data_vld(1'b1),
 77     .busy(busy),       //总线忙信号
 78     .rd_data(rd_data), //读回数据
 79     .rd_data_vld(rd_data_vld),
 80     //仿真接口
 81     .state_c(state_c),
 82     //eeprom侧接口
 83     .scl(scl), //时钟
 84     .sda_in(sda_in),
 85     .sda_en(sda_en),
 86     .sda_reg(sda_reg)
 87     );
 88     
 89     //三态门
 90     assign sda    = sda_en ? sda_reg : 1'bz;
 91     assign sda_in = sda;
 92     
 93     ila_0 ila_0 (
 94     .clk(sys_clk_ibufg), // input wire clk
 95     .probe0(probe0) // input wire [39:0] probe0
 96 );
 97 
 98     assign probe0[13:0] = state_c; //14bit
 99     assign probe0[14] = busy;
100     assign probe0[15] = scl;
101     assign probe0[16] = sda_en;
102     assign probe0[17] = sda_reg;
103     assign probe0[18] = sda_in;
104     assign probe0[19] = write;
105     assign probe0[20] = read;
106     assign probe0[39:21] = 0;
107     
108 endmodule

顶层封装:

  看一下软件分析出的原理图结构(ILA
IP核是从此加上的):

图片 20图片 21

图片 22

 1 `timescale 1ns / 1ps 2  3 module phy_manage( 4     input   clk, 5     input   rst_n, 6  7     input   mdio_en, 8     output  link_up, 9     output  chk_done,10 11     output  mdc,12     inout   mdio13     );14 15 wire rdy;16 wire rd_en;17 wire [5-1:0] phy_addr;18 wire [5-1:0] reg_addr;19 (*DONT_TOUCH = "TRUE"*)wire [16-1:0] rd_data;20 wire rd_vld;21 wire mdo_en,mdo,mdi;22 23 24     mdio_ctrl mdio_ctrl(25     .clk        ,//100M26     .rst_n      ,27 28     .en         ,29     .chk_result ,30     .chk_vld    ,31 32     .rdy        ,    33     .rd_en      ,34     .phy_addr   ,35     .reg_addr   ,36     .rd_data    ,37     .rd_vld     38 );39 40 mdio_interface#(.MDC_CYC(800))//ns41 mdio_interface42 (43     .clk       ,//100M时钟44     .rst_n     ,45 46     .rd_en     ,47     .phy_addr  ,48     .reg_addr  ,49     .rd_data   ,50     .rd_vld    ,51     .rdy       ,52 53     .mdo       ,54     .mdo_en    ,55     .mdi       ,56     .mdc      57     );58 59     //三态门60     assign mdio = mdo_en ? mdo : 1'bz;61     assign mdi = mdio; 62 63 endmodule

  此处详细表达下双向端口使用:顶层模块中国建工业总会公司立三态门结构,在出口使能使得时作为出口端口,无效是表现高阻态,此时用作输入端口,由sda_in信号读取数值。那双向端口怎样仿真呢?很简短,在测试文件中也组织二个三态门结构,而输出使能信号为规划中输出使能信号的相反值,那样在规划中该端口显示高阻态时,正幸而测试文件中相应端口作为出口的等级。能够小心到本人在顶层模块中进入了多个虚假接口:state_c和sda_en,方便在测试文件中找到给出响应的职位。测试文件如下:

phy_manage

  1 `timescale 1ns / 1ps
  2 
  3 module eeprom_top_tb;
  4     
  5     reg sys_clk_p,sys_clk_n;
  6     reg rst_n;
  7     reg [1:0] key;
  8     
  9     wire scl;
 10     wire sda;
 11     wire sda_en;//高电平时待测试文件为输出
 12     
 13     reg [15:0] myrand;
 14     reg sda_tb_out;
 15     wire [13:0] state_c;
 16     
 17     eeprom_top eeprom_top(
 18     .sys_clk_p(sys_clk_p),
 19     .sys_clk_n(sys_clk_n),
 20     .rst_n(rst_n),
 21     .key(key),
 22     .sda_en(sda_en),
 23     .state_c(state_c),
 24     .scl(scl),
 25     .sda(sda)
 26     );
 27     
 28     assign sda = (!sda_en) ? sda_tb_out : 1'bz;
 29     
 30     parameter CYC = 5,
 31               RST_TIME = 2;
 32     
 33     defparam eeprom_top.key_filter.TIME_20MS = 200;
 34     
 35     initial begin
 36         sys_clk_p = 0;
 37         forever #(CYC/2) sys_clk_p = ~sys_clk_p;
 38     end
 39     
 40     initial begin
 41         sys_clk_n = 1;
 42         forever #(CYC/2) sys_clk_n = ~sys_clk_n;
 43     end
 44     
 45     localparam IDLE     = 14'b00_0000_0000_0001,
 46                START    = 14'b00_0000_0000_0010,
 47                WRI_CTRL = 14'b00_0000_0000_0100,
 48                ACK1     = 14'b00_0000_0000_1000,
 49                ADDR     = 14'b00_0000_0001_0000,
 50                ACK2     = 14'b00_0000_0010_0000,
 51                WRI_DATA = 14'b00_0000_0100_0000,
 52                ACK3     = 14'b00_0000_1000_0000,
 53                RE_START = 14'b00_0001_0000_0000,
 54                RD_CTRL  = 14'b00_0010_0000_0000,
 55                ACK4     = 14'b00_0100_0000_0000,
 56                RD_DATA  = 14'b00_1000_0000_0000,
 57                NACK     = 14'b01_0000_0000_0000,
 58                STOP     = 14'b10_0000_0000_0000;
 59     
 60     initial begin
 61         rst_n = 1;
 62         #1;
 63         rst_n = 0;
 64         #(CYC*RST_TIME);
 65         rst_n = 1;
 66     end
 67     
 68     initial begin
 69         #1;
 70         key = 2'b11;
 71         #(CYC*RST_TIME);
 72         #(CYC*10);
 73         
 74         press_key_wr;
 75         #120_000;
 76         press_key_rd;
 77         #80_000;
 78         $stop;
 79     end
 80     
 81     //构造响应条件
 82     always@(*)begin
 83         if(state_c == ACK1 || state_c == ACK2 || state_c == ACK3 || state_c == ACK4)
 84             sda_tb_out <= 0;
 85         else 
 86             sda_tb_out <= 1;
 87     end
 88     
 89     task press_key_wr;
 90     begin
 91         repeat(20)begin//模拟抖动过程
 92             myrand = {$random}%400;
 93             #myrand key[1] = ~key[1];
 94         end
 95         key[1] = 0;
 96         #3000;
 97         repeat(20)begin
 98             myrand = {$random}%400;
 99             #myrand key[1] = ~key[1];
100         end
101         key[1] = 1;
102         #3000;
103     end
104     endtask
105     
106     task press_key_rd;
107     begin
108         repeat(20)begin//模拟抖动过程
109             myrand = {$random}%400;
110             #myrand key[0] = ~key[0];
111         end
112         key[0] = 0;
113         #3000;
114         repeat(20)begin
115             myrand = {$random}%400;
116             #myrand key[0] = ~key[0];
117         end
118         key[0] = 1;
119         #3000;
120     end
121     endtask
122     
123 endmodule

五 、功能仿真

  小编的开发板使用差分晶振作为系统时钟,在测试文件中也要以差分信号的款式提交时钟。与单端时钟唯一的界别在于付出七个早先值分化周期相同的时钟信号。在那之中为了找到响应地方,引入状态编码,并在必要提交响应的时刻拉低总线。运转行为仿真:

自此编写testbench进行行为仿真:

完全布局:

图片 23图片 24

图片 25

  1 `timescale 1 ns/1 ps  2   3 `define BIT_CNT uut.mdio_interface.bit_cnt  4   5 module phy_manage_tb();  6   7 //时钟和复位  8 reg clk  ;  9 reg rst_n; 10  11 //uut的输入信号 12 reg mdio_en; 13  14 //uut的输出信号 15 wire link_up; 16 wire chk_done; 17 wire mdc; 18 wire mdio; 19 wire [16-1:0] back_data1,back_data2; 20  21         //时钟周期,单位为ns,可在此修改时钟周期。 22         parameter CYCLE    = 10; 23  24         //复位时间,此时表示复位3个时钟周期的时间。 25         parameter RST_TIME = 2 ; 26  27         defparam uut.mdio_ctrl.MS_CYC = 100; 28  29         //待测试的模块例化 30        phy_manage uut( 31        .clk        , 32        .rst_n      , 33  34        .mdio_en    , 35        .link_up    , 36        .chk_done   , 37  38        .mdc        , 39        .mdio        40     ); 41  42  43      //生成本地时钟50M 44      initial begin 45          clk = 1; 46          forever 47          #(CYCLE/2) 48          clk=~clk; 49      end 50  51      //产生复位信号 52      initial begin 53          rst_n = 1; 54          #1; 55          rst_n = 0; 56          #(CYCLE*RST_TIME); 57          rst_n = 1; 58      end 59  60      //输入信号din0赋值方式 61      initial begin 62          #1; 63          //赋初值 64          mdio_en = 0; 65          #(10*CYCLE); 66          mdio_en = 1; 67          #(1*CYCLE); 68          mdio_en = 0; 69          //开始赋值 70         #100_000; 71         $stop; 72      end 73      74      //模拟PHY响应 75  76     //data 77     assign back_data1 = {16'b0000_0000_0010_0000}; 78     assign back_data2 = {16'b1010_0000_0000_0000}; 79  80     integer i = 0,j = 0; 81     initial begin 82         forever begin 83             wait(uut.mdio_interface.state_c == 1 && `BIT_CNT == 47 ); 84             @(posedge mdc); 85             force mdio = 0; 86             @(posedge mdc); 87             j = j+1; 88             if(j == 1) 89                 force mdio = back_data1[16-1-i+1]; 90             else 91                 force mdio = back_data2[16-1-i+1]; 92  93             wait(uut.mdio_interface.state_c == 0); 94             @(posedge mdc); 95             release mdio; 96         end 97     end 98  99     initial begin100         forever begin101             @(posedge mdc);102             if(uut.mdio_interface.state_c == 2)begin103                 #10;104                 i = i+1;105             end106             else 107                 i = 0;108         end109     end110 111 112  endmodule

写操作:

phy_manage_tb

图片 26

  testbench中央银行使force强迫更新mdio双向端口格局模拟PHY芯片响应。仿真波形上半有个别为MDIO控制模块信号,下半部分则是MDIO时序接口模块信号。可知当读取寄存器数值知足PHY工作须要时,link_up信号拉高,注脚此时MAC能够传输数据给PHY。图片 27

读操作:

6、板级调节和测试

图片 28

  完整的规划,板级调节和测试是必备的。真正地将接口调通,PHY芯片正确响应才能证实达到设计指标。顶层封装测试工程,内部例化:差分时钟缓冲原语、PLL、PHY管理顶层封装以及VIO
ILA调节和测试IP。大家来看下原理图顶层:

  读写操作进程中状态转移、比特计数器、sda

图片 29

、scl那么些骨干信号数据经常,仿真通过。实际上那是设计进度中遇见些小难点,修改代码后的结果。下一步要在线调节和测试了,那里是本篇博文最终八个重中之重要注解的始末。以后本身会利用添加属性的方法(*mark_debug

“true”*)标志要考察的信号,再在综合后采纳debug设置引导引入调节和测试IP核。经过试验发现调节和测试核的引入是通过添加约束的法子贯彻的,而且当要观察别的信号时该约束部分必须改变不然报错,所以那边运用IP核例化调节和测试探测流程,直接在IP
catalog中生成ILA
IP核。那里有一个小技巧:生成IP核是只利用二个探针信号,并把位宽装置的较大,且使用OOC格局。在例化IP核后使用这么些信号的不一致位宽部分连接需求在线观望的信号。那样能够制止在频仍综合、布局布线的经过中重新编写翻译ILA
IP核部分,节约时间。

  打开硬件管理器,下载bit流后自动打开调节和测试界面。设置触发条件观望波形,那里可以很便利的行使状态信号的两样景色设置触发条件。

写操作:

图片 30

 读操作:图片 31

图片 32

  写入数据定义为8’h32,读取bit依次是0011_0010,即为32,表达正确将写入数据读出。大家能够在这一次实验基础上扩张,比如实现页写形式,或是使用串口来发送读写指令并读回数据等。经过这次博文,精通了IIC协议的四段式状态机完成格局,双向端口的三态门结构及假冒伪造低劣艺术,并能够灵活运用ILA
IP核实行在线调节和测试。希望我们和本人同样获得广大。欢迎交换~

测试工程顶层:

图片 33图片 34

 1 `timescale 1ns / 1ps 2  3  4 module mdio_test( 5     input sys_clk_p, 6     input sys_clk_n, 7     input rst_n, 8  9     output mdc,10     inout mdio,11 12     output phy_reset//PHY芯片复位信号 低有效13     );14 15 16 wire sys_clk_ibufg;17 wire clk;18 wire en;19 wire chk_done;20 wire link_up;21 22 assign phy_reset = 1'b1;//始终不复位23 24 IBUFGDS #25 (26 .DIFF_TERM ("FALSE"),27 .IBUF_LOW_PWR ("FALSE")28 )29 u_ibufg_sys_clk30 (31 .I (sys_clk_p), //差分时钟的正端输入,需要和顶层模块的端口直接连接32 .IB (sys_clk_n), // 差分时钟的负端输入,需要和顶层模块的端口直接连接33 .O (sys_clk_ibufg) //时钟缓冲输出34 );35 36  clk_wiz_0 u_clk37    (38     // Clock out ports39     .clk_out1,     // output clk_out1 100Mhz40    // Clock in ports41     .clk_in1(sys_clk_ibufg));      // input clk_in142 43  vio_0 u_vio (44   .clk,                // input wire clk45   .probe_out0  // output wire [0 : 0] probe_out046 );47 48 phy_manage phy_manage(49     .clk        ,50     .rst_n      ,51 52     .mdio_en    ,53     .link_up    ,54     .chk_done   ,55 56     .mdc        ,57     .mdio       58     );59 60 61 endmodule

mdio_test

钟表引脚约束文件:

图片 35图片 36

 1 create_clock -period 5.000 [get_ports sys_clk_p] 2 set_property PACKAGE_PIN R4 [get_ports sys_clk_p] 3 set_property IOSTANDARD DIFF_SSTL15 [get_ports sys_clk_p] 4  5 set_property PACKAGE_PIN T6 [get_ports rst_n] 6 set_property IOSTANDARD LVCMOS15 [get_ports rst_n] 7  8 set_property PACKAGE_PIN W10 [get_ports mdc] 9 set_property IOSTANDARD LVCMOS33 [get_ports mdc]10 11 set_property PACKAGE_PIN V10 [get_ports mdio]12 set_property IOSTANDARD LVCMOS33 [get_ports mdio]13 14 set_property PACKAGE_PIN L15 [get_ports phy_reset]15 set_property IOSTANDARD LVCMOS33 [get_ports phy_reset]

clk_pin 

  有少数相信调节和测试过以太网的人民代表大会多都跳过三个坑:没有驱动PHY的复位输入信号。自个儿也在此地栽过跟头,那里向来连接赋值拉高PHY芯片复位信号。关于板级调节和测试还有个小技巧,遵照高亚军先生的图书得知,将set
up
debug生成的ILA探针相关约束命令独立放入1个羁绊文件便于调节和测试IP的管理和改动,debug约束文件就不贴出来了。

  查看debug波形,MDIO时序接口模块在假释MDIO串行总线时,由于存在上拉电阻为高电平,下一个MDC时钟上涨沿时刻,PHY拉低MDIO信号响应并获得总线控制权,早先出口数据。

图片 37

  得到读取的八个寄存器数据,依照数值分析满意:PHY自动协商完成,且工作在全双工1000Mbps速率下。

图片 38

  最后本田UR-VJ45接口赤褐提醒灯常亮,评释自动协商完毕,网络连接正确。到此简易的PHY芯片检测管理模块设计成就。