zoukankan      html  css  js  c++  java
  • 【重新发布,代码开源】FPGA设计千兆以太网MAC(1)——通过MDIO接口配置与检测PHY芯片

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

    https://www.cnblogs.com/moluoqishi/p/9118283.html

    一、前言  

      本文设计思想采用明德扬至简设计法。以太网这一高效实用的数据传输方式应用于各个领域,如网络交换设备,高速网络相机等。虽然各FPGA厂商都提供MAC IP核,但大多收费,有时无法破解。不同厂家之间无法移植,而且为了通用性考虑牺牲了效率,因此自己动手写一个以太网MAC是个不错的选择。

      本博文讨论通过MDIO接口管理PHY芯片来验证其正确工作,为在此基础上设计MAC逻辑开个头。PHY芯片采用RTL8211EGVB,选用GMII接口与MAC连接。下面我们来开始第一步,在此之前明确设计目的:检测PHY芯片是否完成自动协商 链路速率是否达到1000M。所以要从datasheet中了解到芯片引脚 寄存器地址 接口时序。

    二、设计分析

      管理帧格式如下:

      读写操作时序:

       MDC为MAC驱动时钟信号,MDIO是串行数据总线,需要连接上拉电阻保证idle状态下高电平。其中前导码包含32个比特“1”,PHY地址根据芯片引脚连接而定,此处为01.turn around域是为了防止读操作时数据冲突,在读操作过程中MAC和PHY均在第1比特处进入高阻态,PHY在第2比特处驱动MDIO接口为低电平以占据总线控制权。注意两点:第一如果时钟信号在读写操作后停止,时钟必须保证至少7个时钟周期持续翻转且MDIO高电平从而保证之前的操作完成。故在设计中可以等待一段时间后再拉低时钟使能信号。第二两个操作之间至少一个idle比特。

       正确驱动接口时序需要关注AC characterisics.

               

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

      接下来关注要访问的内部寄存器地址,首先读取PHY寄存器数据以检测其工作状态,若发现异常再考虑写入数据。这里读取基本模式状态寄存器0X01的bit5,若为1说明自动协商完成。第二个寄存器是PHY特定状态寄存器0X11中的[15:14]和13,分别是当前速率和全/半双工通信模式。若检测到自动协商完成,且工作在1000M全双工模式下,说明工作正确。

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

      所有准备工作完成,现在开始设计。按照“自顶向下”设计原则,规划好整体结构和模块间接口,再设计内部状态机一步步实现逻辑功能。

       Mdio_ctrl模块负责完成PHY芯片的配置与检测逻辑,Mdio接口模块完成读写操作时序。此处仅通过读操作简单检测PHY状态,暂不进行配置,故两模块工作状态跳转如图所示:

       剩下的工作就是把两个状态机实现出来,非常简单。有需要的朋友可以参考一下,关于芯片的具体参数详见:Realtek RTL8211E(G)-VB(VL)-CG Datasheet 1.8.上代码!

    四、代码编写

    MDIO控制模块:

      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(state_c)  
     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 && (en);
     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 && (link_up);
    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_duplex
    105 
    106 
    107 //计数器
    108 always @(posedge clk or negedge rst_n) begin 
    109     if (rst_n==0) begin
    110         ms_cnt <= 0; 
    111     end
    112     else if(add_ms_cnt) begin
    113         if(end_ms_cnt)
    114             ms_cnt <= 0; 
    115         else
    116             ms_cnt <= ms_cnt+1 ;
    117    end
    118 end
    119 assign add_ms_cnt = (state_c == WAIT);
    120 assign end_ms_cnt = add_ms_cnt  && ms_cnt == (MS_CYC)-1 ;//100MHZ时钟100_000
    121 
    122 always @(posedge clk or negedge rst_n) begin 
    123     if (rst_n==0) begin
    124         wait_cnt <= 0; 
    125     end
    126     else if(add_wait_cnt) begin
    127         if(end_wait_cnt)
    128             wait_cnt <= 0; 
    129         else
    130             wait_cnt <= wait_cnt+1 ;
    131    end
    132 end
    133 assign add_wait_cnt = (end_ms_cnt);
    134 assign end_wait_cnt = add_wait_cnt  && wait_cnt == (WAIT_MS)-1 ;
    135 
    136 always @(posedge clk or negedge rst_n) begin 
    137     if (rst_n==0) begin
    138         rd_cnt <= 0; 
    139     end
    140     else if(add_rd_cnt) begin
    141         if(end_rd_cnt)
    142             rd_cnt <= 0; 
    143         else
    144             rd_cnt <= rd_cnt+1 ;
    145    end
    146 end
    147 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)begin
    151     if(rst_n==1'b0)begin
    152         rd_finish <= 0;
    153     end
    154     else if(end_rd_cnt)begin
    155         rd_finish <= 1'b1;
    156     end
    157     else if(state_c == CHECK)
    158         rd_finish <= 0;
    159 end
    160 
    161 
    162 always @(posedge clk or negedge rst_n) begin 
    163     if (rst_n==0) begin
    164         rdata_cnt <= 0; 
    165     end
    166     else if(add_rdata_cnt) begin
    167         if(end_rdata_cnt)
    168             rdata_cnt <= 0; 
    169         else
    170             rdata_cnt <= rdata_cnt+1 ;
    171    end
    172 end
    173 assign add_rdata_cnt = (rd_vld);
    174 assign end_rdata_cnt = add_rdata_cnt  && rdata_cnt == (2)-1 ;
    175 
    176 //接口信号逻辑
    177 always  @(posedge clk or negedge rst_n)begin
    178     if(rst_n==1'b0)begin
    179         rd_en <= 0;
    180         phy_addr <= 0;
    181         reg_addr <= 0;
    182     end
    183     else if(add_rd_cnt)begin
    184         rd_en <= 1'b1;
    185         phy_addr <= 5'b00001;
    186         reg_addr <= registers[10-5*rd_cnt-1 -:5];
    187     end
    188     else begin
    189         rd_en <= 0;
    190         phy_addr <= 0;
    191         reg_addr <= 0;
    192     end
    193 end
    194 
    195 assign registers = {BMSR,PHYSR};//5'h01,5'h11
    196 
    197 always  @(posedge clk or negedge rst_n)begin
    198     if(rst_n==1'b0)begin
    199         rd_memory[0] <= 0;
    200         rd_memory[1] <= 0;
    201     end
    202     else if(add_rdata_cnt)begin
    203         rd_memory[rdata_cnt] <= rd_data;
    204     end
    205 end
    206 
    207 //用户侧输出检测结果
    208 always  @(posedge clk or negedge rst_n)begin
    209     if(rst_n==1'b0)begin
    210         chk_vld <= 0;
    211     end
    212     else if(state_c == CHECK)begin
    213         chk_vld <= 1'b1;
    214     end
    215     else
    216         chk_vld <= 0;
    217 end
    218 
    219 always  @(posedge clk or negedge rst_n)begin
    220     if(rst_n==1'b0)begin
    221         chk_result <= 0;
    222     end
    223     else if(check2idle)begin
    224         chk_result <= 1'b1;
    225     end
    226     else if(check2wait)
    227         chk_result <= 0;
    228 end
    229 
    230 endmodule
    mdio_ctrl

    MDIO时序接口模块:

      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 if(rd_en)begin
     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(state_c)  
     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 end
    100 
    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) begin
    109         rd_flag <= (0)  ;
    110     end
    111     else if(state_c == IDLE && rd_en)begin
    112         rd_flag <= (1'b1)  ;
    113     end 
    114     else if(state_c == WRI_COM)
    115         rd_flag <= 0;
    116 end
    117 
    118 
    119 //分频计数器
    120 always @(posedge clk or negedge rst_n) begin 
    121     if (rst_n==0) begin
    122         div_cnt <= 0; 
    123     end
    124     else if(add_div_cnt) begin
    125         if(end_div_cnt)
    126             div_cnt <= 0; 
    127         else
    128             div_cnt <= div_cnt+1 ;
    129    end
    130 end
    131 assign add_div_cnt = (1);
    132 assign end_div_cnt = add_div_cnt  && div_cnt == (N)-1 ;
    133 
    134 //比特计数器
    135 always @(posedge clk or negedge rst_n) begin 
    136     if (rst_n==0) begin
    137         bit_cnt <= 0; 
    138     end
    139     else if(add_bit_cnt) begin
    140         if(end_bit_cnt)
    141             bit_cnt <= 0; 
    142         else
    143             bit_cnt <= bit_cnt+1 ;
    144    end
    145 end
    146 assign add_bit_cnt = (end_div_cnt && state_c != IDLE);
    147 assign end_bit_cnt = add_bit_cnt  && bit_cnt == (M)-1 ;
    148 
    149 always@(*)begin
    150     case(state_c)
    151         WRI_COM:M = 48;
    152         RD_DATA:M = 16;
    153         default:M = 10;
    154     endcase
    155 end
    156 
    157 //mdc时钟
    158 always @(posedge clk or negedge rst_n )begin 
    159     if(rst_n==0) begin
    160         mdc <= (1'b1)  ;
    161     end
    162     else if(add_div_cnt && div_cnt == (N>>1) - 1)begin
    163         mdc <= (1'b1)  ;
    164     end 
    165     else if(end_div_cnt)
    166         mdc <= 0;
    167 end
    168 
    169 
    170 //mdio输出
    171 always @(posedge clk or negedge rst_n )begin 
    172     if(rst_n==0) begin
    173         mdo <= (1'b1)  ;
    174     end
    175     else if(add_bit_cnt && state_c == WRI_COM)begin
    176         mdo <= command[48-1-bit_cnt]  ;
    177     end 
    178     else if(state_c != WRI_COM)
    179         mdo <= 1'b1;
    180 end
    181 
    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) begin
    186         mdo_en <= (0)  ;
    187     end
    188     else if(state_c == WRI_COM && add_bit_cnt)
    189         case(bit_cnt)
    190             0: mdo_en <= 1'b1;
    191             46:mdo_en <= 0;
    192             default:;
    193         endcase
    194 end
    195 
    196 //mdio输入
    197 always @(posedge clk or negedge rst_n )begin 
    198     if(rst_n==0) begin
    199         rd_data <= (0)  ;
    200     end
    201     else if(add_bit_cnt && state_c == RD_DATA)begin
    202         rd_data[16-1-bit_cnt] <= (mdi)  ;
    203     end 
    204 end
    205 
    206 always @(posedge clk or negedge rst_n )begin 
    207     if(rst_n==0) begin
    208         rd_vld <= (0)  ;
    209     end
    210     else if(rd_data2idle)begin
    211         rd_vld <= (1'b1)  ;
    212     end 
    213     else
    214         rd_vld <= 0;
    215 end
    216 
    217 
    218 endmodule
    mdio_interface

    顶层封装:

     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   mdio
    13     );
    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       (clk) ,//100M
    26     .rst_n     (rst_n) ,
    27 
    28     .en        (mdio_en) ,
    29     .chk_result(link_up) ,
    30     .chk_vld   (chk_done) ,
    31 
    32     .rdy       (rdy) ,    
    33     .rd_en     (rd_en) ,
    34     .phy_addr  (phy_addr) ,
    35     .reg_addr  (reg_addr) ,
    36     .rd_data   (rd_data) ,
    37     .rd_vld    (rd_vld) 
    38 );
    39 
    40 mdio_interface#(.MDC_CYC(800))//ns
    41 mdio_interface
    42 (
    43     .clk      (clk) ,//100M时钟
    44     .rst_n    (rst_n) ,
    45 
    46     .rd_en    (rd_en) ,
    47     .phy_addr (phy_addr) ,
    48     .reg_addr (reg_addr) ,
    49     .rd_data  (rd_data) ,
    50     .rd_vld   (rd_vld) ,
    51     .rdy      (rdy) ,
    52 
    53     .mdo      (mdo) ,
    54     .mdo_en   (mdo_en) ,
    55     .mdi      (mdi) ,
    56     .mdc      (mdc)
    57     );
    58 
    59     //三态门
    60     assign mdio = mdo_en ? mdo : 1'bz;
    61     assign mdi = mdio; 
    62 
    63 endmodule
    phy_manage

    五、功能仿真

    之后编写testbench进行行为仿真:

      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       (clk) ,
     32        .rst_n     (rst_n) ,
     33 
     34        .mdio_en   (mdio_en) ,
     35        .link_up   (link_up) ,
     36        .chk_done  (chk_done) ,
     37 
     38        .mdc       (mdc) ,
     39        .mdio      (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 begin
    100         forever begin
    101             @(posedge mdc);
    102             if(uut.mdio_interface.state_c == 2)begin
    103                 #10;
    104                 i = i+1;
    105             end
    106             else 
    107                 i = 0;
    108         end
    109     end
    110 
    111 
    112  endmodule
    phy_manage_tb

      testbench中利用force强迫更新mdio双向端口方式模拟PHY芯片响应。仿真波形上半部分为MDIO控制模块信号,下半部分则是MDIO时序接口模块信号。可见当读取寄存器数值满足PHY工作需求时,link_up信号拉高,证明此时MAC可以传输数据给PHY。

    六、板级调试

      完整的设计,板级调试是必不可少的。真正地将接口调通,PHY芯片正确响应才能说明达到设计目的。顶层封装测试工程,内部例化:差分时钟缓冲原语、PLL、PHY管理顶层封装以及VIO ILA调试IP。我们来看下原理图顶层:

    测试工程顶层:

     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_clk
    30 (
    31 .I (sys_clk_p), //差分时钟的正端输入,需要和顶层模块的端口直接连接
    32 .IB (sys_clk_n), // 差分时钟的负端输入,需要和顶层模块的端口直接连接
    33 .O (sys_clk_ibufg) //时钟缓冲输出
    34 );
    35 
    36  clk_wiz_0 u_clk
    37    (
    38     // Clock out ports
    39     .clk_out1(clk),     // output clk_out1 100Mhz
    40    // Clock in ports
    41     .clk_in1(sys_clk_ibufg));      // input clk_in1
    42 
    43  vio_0 u_vio (
    44   .clk(clk),                // input wire clk
    45   .probe_out0(en)  // output wire [0 : 0] probe_out0
    46 );
    47 
    48 phy_manage phy_manage(
    49     .clk      (clk)  ,
    50     .rst_n    (rst_n)  ,
    51 
    52     .mdio_en  (en)  ,
    53     .link_up  (link_up)  ,
    54     .chk_done (chk_done)  ,
    55 
    56     .mdc      (mdc)  ,
    57     .mdio     (mdio)  
    58     );
    59 
    60 
    61 endmodule
    mdio_test

    时钟引脚约束文件:

     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探针相关约束命令单独放入一个约束文件便于调试IP的管理和修改,debug约束文件就不贴出来了。

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

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

      最终RJ45接口绿色指示灯常亮,表明自动协商完成,网络连接正确。到此简易的PHY芯片检测管理模块设计完成。

  • 相关阅读:
    OS__信号量(semaphore)PV操作
    c++ _宏与内联函数
    ubuntu_ root change to user
    联想Y450在Ubuntu下调节屏幕亮度
    AI—家庭组机器人平台环境配置,高级人工智能实验总结
    如何在ubuntu下使用windows下的程序(eg: .exe)
    Python_XML的三种解析方法
    Python学习资源汇总
    转:Emmet 学习之路
    sql入门
  • 原文地址:https://www.cnblogs.com/moluoqishi/p/9118283.html
Copyright © 2011-2022 走看看