ぱたへね

はてなダイアリーはrustの色分けができないのでこっちに来た

MIIレジスタの読み書きを行う。

OpenCoresで公開されているEthmacを使って、ボード上に載っているPHYのレジスタを読み出す方法をまとめました。

配布ファイルのテストベンチethmac/trunk/bench/verilog/tb_ethernet.vから、MIIレジスタの読み書き部分だけ動作させています。アクセスに使用するレジスタの説明もまとめました。

MIIレジスタへのアクセス方法

MDCの出力

MIIレジスタにアクセスするためには、MDCに正しいクロックを出力する必要があります。
クロックの周波数を決めるために、MIIMODERレジスタにMDC生成用の分周率を設定します。規格上は最大2.5MHzなので、分周後の周波数が2.5MHz以下になるように設定します。実際には、読み書きできれば適当な値で良いと思います。

Bit# Access Description
31-9 Reserved
8 RW MIINOPRE – No Preamble: 0 = 32-bit preamble sent1 = No preamble send
7-0 RW CLKDIV – Clock Divider: The field is a host clock divider factor. The host clock can be divided by an even number, greater then 1. The default value is 0x64 (100).

Table 15: MIIMODER Register

ステータスを見る

PHYのレジスタへのアクセスは、MIIが動作中で無いことを確認してから行います。MIISTATUSレジスタを読み出し、読み出した値の1bit目が1の時はMIIは動作中です。

Bit# Access Description
31-3 Reserved
2 R NVALID: Invalid 0 = The data in the MSTATUS register is valid. 1 = The data in the MSTATUS register is invalid. This bit is only valid when the scan status operation is active.
1 R BUSY: 0 = The MII is ready. 1 = The MII is busy (operation in progress).
0 R LINKFAIL: 0 = The link is OK. 1 = The link failed.The Link fail condition occurred (now the link might be OK). Another status read gets a new status.

Table 20: MIISTATUS Register

レジスタライト

MIIADDRESS レジスタに、PHYのアドレスと、書き込むレジスタのアドレスを書き込みます。

Bit# Access Description
31-13 Reserved
12-8 RW RGAD – Register Address (within the PHY selected by the FIAD[4:0])
7-5 Reserved
4-0 RW FIAD – PHY Address

Table 17: MIIADDRESS Register

MIITX_DATAレジスタに、PHYのレジスタに書き込む値を書き込みます。

Bit# Access Description
31-16  Reserved
15-0 RW CTRLDATA – Control Data (data to be written to the PHY)

Table 18: MIITX_DATA Register

MIICOMMANDレジスタに、ライトコマンド(32'h00000004) を書き込むと、レジスタのライトが実行されます。

Bit# Access Description
31-3 Reserved
2 RW WCTRLDATA – Write Control Data
1 RW RSTAT – Read Status
0 RW SCANSTAT – Scan Status

Table 16: MIICOMMAND Register

レジスタリード

MIIADDRESS レジスタに、PHYのアドレスと、読み出すレジスタのアドレスを書き込みます。
MIICOMMANDレジスタに、リードコマンド(32'h00000002)を書き込むと、MIISTATUSレジスタのBUSYビットが1になり、レジスターのリードが始まります。
MIISTATUSレジスタのBUSYビットが0になるのを待ちます。
MIIRX_DATAレジスタから、PHYから読み出された値を読み出します。

Bit# Access Description
31-16 Reserved
15-0 R PRSD – Received Data (data read from the PHY)

Table 19: MIIRX_DATA Register

topファイル

シミュレーションのトップファイルです。

// OpenCoresで公開されているEthermac用のテストベンチ
// 
// Eathermacプロジェクト  
// http://opencores.org/project,ethmac


// timescaleの設定
//`timescale 1ns/1ps

//`include "wb_model_defines.v"
`include "ethmac_defines.v"
`include "timescale.v"

module ethmac_miim_tb ();

   //DUTの入力に接続する信号
   wire           wb_clk_i;     // WISHBONE clock
   wire           wb_rst_i;     // WISHBONE reset
   wire [31:0]    wb_dat_i;     // WISHBONE data input
   wire [31:0]   wb_dat_o;     // WISHBONE data output
   wire          wb_err_o;     // WISHBONE error output
   
   // WISHBONE slave
   wire [31:0]    wb_adr_i;     // WISHBONE address reg
   wire [3:0] 	   wb_sel_i;     // WISHBONE byte select input
   wire           wb_we_i;      // WISHBONE write enable input
   wire           wb_cyc_i;     // WISHBONE cycle input
   wire           wb_stb_i;     // WISHBONE strobe input
   wire          wb_ack_o;     // WISHBONE acknowledge output
   
   // WISHBONE master
   wire [31:0]   m_wb_adr_o;
   wire [3:0]    m_wb_sel_o;
   wire          m_wb_we_o;
   reg [31:0]    m_wb_dat_i;
   wire [31:0]   m_wb_dat_o;
   wire          m_wb_cyc_o;
   wire          m_wb_stb_o;
   reg           m_wb_ack_i;
   reg           m_wb_err_i;

   wire [2:0]  m_wb_cti_o;   // Cycle Type Identifier
   wire [1:0]  m_wb_bte_o;   // Burst Type Extension
   
   // Tx
   reg 	 mtx_clk_pad_i; // Transmit clock (from PHY)
   wire [3:0]  mtxd_pad_o;    // Transmit nibble (to PHY)
   wire 	 mtxen_pad_o;   // Transmit enable (to PHY)
   wire 	 mtxerr_pad_o;  // Transmit error (to PHY)
   
   // Rx
   reg 	 mrx_clk_pad_i; // Receive clock (from PHY)
   reg [3:0] 	 mrxd_pad_i;    // Receive nibble (from PHY)
   reg 	 mrxdv_pad_i;   // Receive data valid (from PHY)
   reg 	 mrxerr_pad_i;  // Receive data error (from PHY)
   
   // Common Tx and Rx
   reg 	 mcoll_pad_i;   // Collision (from PHY)
   reg 	 mcrs_pad_i;    // Carrier sense (from PHY)
   
   // MII Management interface
   reg 	 md_pad_i;      // MII data input (from I/O cell)
   wire 	 mdc_pad_o;     // MII Management data clock (to PHY)
   wire 	 md_pad_o;      // MII data output (to I/O cell)
   wire 	 md_padoe_o;    // MII data output enable (to I/O cell)
  
   wire 	 int_o;         // Interrupt output

  wire [3:0] 	 MTxD;
  wire 		 MTxEn;
  wire 		 MTxErr;
  
  wire [3:0] 	 MRxD;     // This goes to PHY
  wire 		 MRxDV;    // This goes to PHY
  wire 		 MRxErr;   // This goes to PHY
  wire 		 MColl;    // This goes to PHY
  wire 		 MCrs;     // This goes to PHY
  
  wire 	      Mdi_I;
  wire 	      Mdo_O;
  wire 	      Mdo_OE;
  tri 	      Mdio_IO;
  wire 	      Mdc_O;
  
  
   wire  [4:0] wbm_tag;
   assign wbm_tag = 0;

  // CLKの周期
  parameter WB_PERIOD = 10.0; 
  parameter TX_PERIOD =  8.0;
  parameter RX_PERIOD =  8.0;

  
  //DUT
  ethmac u0
    (
     .wb_clk_i(wb_clk_i), .wb_rst_i(wb_rst_i), .wb_dat_i(wb_dat_i), .wb_dat_o(wb_dat_o), 
     .wb_adr_i(wb_adr_i[11:2]), .wb_sel_i(wb_sel_i), .wb_we_i(wb_we_i), .wb_cyc_i(wb_cyc_i), 
     .wb_stb_i(wb_stb_i), .wb_ack_o(wb_ack_o), .wb_err_o(wb_err_o), 
     .m_wb_adr_o(m_wb_adr_o), .m_wb_sel_o(m_wb_sel_o), .m_wb_we_o(m_wb_we_o), 
     .m_wb_dat_o(m_wb_dat_o), .m_wb_dat_i(m_wb_dat_i), .m_wb_cyc_o(m_wb_cyc_o), 
     .m_wb_stb_o(m_wb_stb_o), .m_wb_ack_i(m_wb_ack_i), .m_wb_err_i(m_wb_err_i), 
     .m_wb_cti_o(m_wb_cti_o), .m_wb_bte_o(m_wb_bte_o),

     .mtx_clk_pad_i(mtx_clk), .mtxd_pad_o(MTxD), .mtxen_pad_o(MTxEn), .mtxerr_pad_o(MTxErr),
     .mrx_clk_pad_i(mrx_clk), .mrxd_pad_i(MRxD), .mrxdv_pad_i(MRxDV), .mrxerr_pad_i(MRxErr), 
     .mcoll_pad_i(MColl),    .mcrs_pad_i(MCrs), 
     .mdc_pad_o(Mdc_O), .md_pad_i(Mdi_I), .md_pad_o(Mdo_O), .md_padoe_o(Mdo_OE),
     
     .int_o(int_o)
     );

  syscon #(.clk_period(WB_PERIOD)) sc 
    (
     .RST_sys(RST_sys),
     .CLK_stop(CLK_stop),
     .RST_o(wb_rst_i),
     .CLK_o(wb_clk_i)
     );
  
  wb_master_mii_test #(.clk_period(WB_PERIOD)) master 
    (
     .RST_sys(RST_sys),
     .CLK_stop(CLK_stop),
     
     .RST_I(wb_rst_i),
     .CLK_I(wb_clk_i),
     
     .ADR_O(wb_adr_i),
     .DAT_I(wb_dat_o),
     .DAT_O(wb_dat_i),
     .WE_O(wb_we_i),
     
     .STB_O(wb_stb_i),
     .CYC_O(wb_cyc_i),
     .ACK_I(wb_stb_o),
     .ERR_I(wb_err_o),
     .RTY_I(),
     
     .LOCK_O(),
     .SEL_O(wb_sel_i),
     
     .CYCLE_IS()
     );

// Connecting Ethernet PHY Module
assign Mdio_IO = Mdo_OE ? Mdo_O : 1'bz ;
assign Mdi_I   = Mdio_IO;
integer phy_log_file_desc;

  eth_phy eth_phy
    (
     // WISHBONE reset
     .m_rst_n_i(!wb_rst_i),
     
     // MAC TX
     .mtx_clk_o(mtx_clk),    .mtxd_i(MTxD),    .mtxen_i(MTxEn),    .mtxerr_i(MTxErr),
     
     // MAC RX
     .mrx_clk_o(mrx_clk),    .mrxd_o(MRxD),    .mrxdv_o(MRxDV),    .mrxerr_o(MRxErr),
     .mcoll_o(MColl),        .mcrs_o(MCrs),
     
     // MIIM
     .mdc_i(Mdc_O),          .md_io(Mdio_IO),
     
     // SYSTEM
     .phy_log(phy_log_file_desc)
     );
  
  
  // clkの生成   
   always # (TX_PERIOD/2) mtx_clk_pad_i = ~mtx_clk_pad_i;
   always # (RX_PERIOD/2) mrx_clk_pad_i = ~mrx_clk_pad_i;

  initial begin
     phy_log_file_desc = $fopen("../log/eth_tb_phy.log");
     if (phy_log_file_desc < 2)
       begin
	 $display("*E Could not open/create eth_tb_phy.log file in ../log/ directory!");
	 $finish;
       end

     #1 	
       mtx_clk_pad_i = 1; mrx_clk_pad_i = 0;
  end
   
endmodule // ethmac_miim_tb

テストベンチ

実際にテストを行っているWishboneのバスマスターです。

// -------------------------------------------------------------------------------
// ----                                                                       ----
// ---- WISHBONE Wishbone_BFM IP Core                                         ----
// ----                                                                       ----
// ---- This file is part of the Wishbone_BFM project                         ----
// ---- http://www.opencores.org/cores/Wishbone_BFM/                          ----
// ----                                                                       ----
// ---- Description                                                           ----
// ---- Implementation of Wishbone_BFM IP core according to                   ----
// ---- Wishbone_BFM IP core specification document.                          ----
// ----                                                                       ----
// ---- To Do:                                                                ----
// ----	NA                                                                 ----
// ----                                                                       ----
// ---- Author(s):                                                            ----
// ----   Andrew Mulcock, amulcock@opencores.org                              ----
// ----                                                                       ----
// -------------------------------------------------------------------------------
// ----                                                                       ----
// ---- Copyright (C) 2008 Authors and OPENCORES.ORG                          ----
// ----                                                                       ----
// ---- This source file may be used and distributed without                  ----
// ---- restriction provided that this copyright statement is not             ----
// ---- removed from the file and that any derivative work contains           ----
// ---- the original copyright notice and the associated disclaimer.          ----
// ----                                                                       ----
// ---- This source file is free software; you can redistribute it            ----
// ---- and/or modify it under the terms of the GNU Lesser General            ----
// ---- Public License as published by the Free Software Foundation           ----
// ---- either version 2.1 of the License, or (at your option) any            ----
// ---- later version.                                                        ----
// ----                                                                       ----
// ---- This source is distributed in the hope that it will be                ----
// ---- useful, but WITHOUT ANY WARRANTY; without even the implied            ----
// ---- warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR               ----
// ---- PURPOSE. See the GNU Lesser General Public License for more           ----
// ---- details.                                                              ----
// ----                                                                       ----
// ---- You should have received a copy of the GNU Lesser General             ----
// ---- Public License along with this source; if not, download it            ----
// ---- from http://www.opencores.org/lgpl.shtml                              ----
// ----                                                                       ----
// -------------------------------------------------------------------------------
// -- file to 'exercise' the Wishbone bus.
// --
// --  Idea is to look like a wishbone master, 
// --   and provide procedures to exercise the bus.
// --
// --  syscon is an external module that provides the reset and clocks 
// --   to all the other modules in the design.
// --
// --  to enable the test script in this master to control
// --   the syscon reset and clock stop,
// --    this master provides tow 'extra' outputs
// --   rst_i and clk_stop
// --
// --    when rst_sys is high, then syscon will issue a reset
// --    when clk_stop is high, then syscon will stop the clock
// --     on the next low transition. i.e. stopped clock is low.

module wb_master_mii_test #(parameter write32_time_out = 6, read32_time_out = 6, clk_period = 10, max_block_size = 128)
    (
    output RST_sys,
    output reg CLK_stop,
    
    // WISHBONE master interface:
    input RST_I,
    input CLK_I,

    output reg [31:0] ADR_O,
    input  [31:0] DAT_I,
    output reg [31:0] DAT_O,
    output reg WE_O,

    output reg STB_O,
    output reg CYC_O,
    input ACK_I,
    input ERR_I,
    input RTY_I,
    
    output reg LOCK_O,
    output reg [3:0] SEL_O,
    
    output reg [5:0] CYCLE_IS
    );

  reg 	   reset_int;
  reg 	   [31:0] read_data;

  // for test
  parameter Tp = 1;
  reg     [8:0]  clk_div; // only 8 bits are valid!
  reg     [4:0]  phy_addr;
  reg     [4:0]  reg_addr;
  reg     [15:0] phy_data;
  reg     [15:0] tmp_data;
  integer        fail;
  integer 	 tests_successfull;
  integer 	 tests_failed;
  reg [799:0] 	 test_name; // used for tb_log_file
  integer 	 tb_log_file;  
  
  assign RST_sys = reset_int;
  
`include "io_package.v"
//`include "ethmac_defines.v"
`include "tb_eth_defines.v"
`include "eth_phy_defines.v"
  
//`define ETH_MIIMODER_CLKDIV   32'h000000FF /* Clock Divider */

  task check_mii_busy; // MII - check if BUSY
  reg [31:0] tmp;
    begin
      @(posedge CLK_I);                                                                  
      // MII read status register
      rd_32(`ETH_MIISTATUS, tmp);
      while(tmp[`ETH_MIISTATUS_BUSY] !== 1'b0) //`ETH_MIISTATUS_BUSY
	begin
	  @(posedge CLK_I);
	  rd_32(`ETH_MIISTATUS, tmp);
	end
    end
  endtask // check_mii_busy
  
  task mii_write_req; // requests write to MII
    input [4:0]  phy_addr;
    input [4:0]  reg_addr;
    input [15:0] data_in;
    begin
      // MII address, PHY address = 1, command register address = 0
      wr_32(`ETH_MIIADDRESS, (`ETH_MIIADDRESS_FIAD & phy_addr) | (`ETH_MIIADDRESS_RGAD & (reg_addr << 8)));
      // MII TX data
      wr_32(`ETH_MIITX_DATA, {16'h0000, data_in});
      // MII command
      wr_32(`ETH_MIICOMMAND, `ETH_MIICOMMAND_WCTRLDATA);
      @(posedge CLK_I);                                                                  
    end
  endtask // mii_write_req

  task mii_read_req; // requests read from MII
    input [4:0]  phy_addr;
    input [4:0]  reg_addr;
    begin
      // MII address, PHY address = 1, command register address = 0
      wr_32(`ETH_MIIADDRESS, (`ETH_MIIADDRESS_FIAD & phy_addr) | (`ETH_MIIADDRESS_RGAD & (reg_addr << 8)));
      // MII command
      wr_32(`ETH_MIICOMMAND, `ETH_MIICOMMAND_RSTAT);
      @(posedge CLK_I);
    end
  endtask // mii_read_req

  task test_ok ;
    reg [799:0] display_test ;
    integer 	outp;
    begin
      tests_successfull = tests_successfull + 1 ;
      outp =  1 | tb_log_file;
      
      display_test = test_name ;
      while ( display_test[799:792] == 0 )
	display_test = display_test << 8 ;
      
      $fdisplay( outp , "    *************************************************************************************" ) ;
      $fdisplay( outp , "    At time: %t ", $time ) ;
      $fdisplay( outp , "    Test: %s", display_test ) ;
      $fdisplay( outp , "    reported *SUCCESSFULL*! ") ;
      $fdisplay( outp , "    *************************************************************************************" ) ;
      $fdisplay( outp , " " ) ;
    end
  endtask // test_ok

  
  task test_fail ;
    input [7999:0] failure_reason ;
    //  reg   [8007:0] display_failure ;
    reg [7999:0]   display_failure ;
    reg [799:0]    display_test ;
    integer 	   outp;
    begin
      outp =  1 | tb_log_file;
      tests_failed = tests_failed + 1 ;
      
      display_failure = failure_reason; // {failure_reason, "!"} ;
      while ( display_failure[7999:7992] == 0 )
	display_failure = display_failure << 8 ;
      
      display_test = test_name ;
      while ( display_test[799:792] == 0 )
	display_test = display_test << 8 ;
      
      $fdisplay( outp , "    *************************************************************************************" ) ;
      $fdisplay( outp , "    At time: %t ", $time ) ;
      $fdisplay( outp , "    Test: %s", display_test ) ;
      $fdisplay( outp , "    *FAILED* because") ;
      $fdisplay( outp , "    %s", display_failure ) ;
      $fdisplay( outp , "    *************************************************************************************" ) ;
      $fdisplay( outp , " " ) ;
      
`ifdef STOP_ON_FAILURE
      #20 $stop ;
`endif
    end
  endtask // test_fail
  
  
  initial begin
    fail = 0;
    tests_failed = 0;
    tests_successfull = 0;
    tb_log_file = $fopen("../log/eth_tb.log");
    if (tb_log_file < 2)
      begin
	$display("*E Could not open/create testbench log file in ../log/ directory!");
	$finish;
      end
    $fdisplay(tb_log_file, "========================== ETHERNET IP Core Testbench results ===========================");
    $fdisplay(tb_log_file, " ");
    
    #1 CLK_stop = 0;
    reset_int = 0;
    wb_init();
    
    wb_rst(2);    
    clock_wait(2); //  Wait 100 ns for global reset to finish

    $display("  TEST 1: VARIOUS READINGS FROM 'REAL' PHY REGISTERS");
    clk_div = 0;
    //mii_set_clk_div(clk_div[7:0]);
    wr_32(`ETH_MIIMODER, (`ETH_MIIMODER_CLKDIV & clk_div));
    clock_wait(100);

    reg_addr = 5'h1F;
    phy_addr = 5'h1;
    while(reg_addr >= 5'h4)
    begin
      // read request
      #Tp mii_read_req(phy_addr, reg_addr);
      check_mii_busy; // wait for read to finish
      // read data
      rd_32(`ETH_MIIRX_DATA, phy_data);
      if (phy_data !== 16'hDEAD)
      begin
        test_fail("Wrong data was read from PHY from 'not used' address space");
        fail = fail + 1;
      end
      if (reg_addr == 5'h4) // go out of for loop
        reg_addr = 5'h3;
      else
        reg_addr = reg_addr - 5'h9;
    end

    reg_addr = 5'h3;
    // read request
    #Tp mii_read_req(phy_addr, reg_addr);
    check_mii_busy; // wait for read to finish
    // read data
    rd_32(`ETH_MIIRX_DATA, phy_data);
    if (phy_data !== {`PHY_ID2, `MAN_MODEL_NUM, `MAN_REVISION_NUM})
      begin
	test_fail("Wrong data was read from PHY from ID register 2");
	fail = fail + 1;
      end
    if(fail == 0)
      test_ok;
    else
      fail = 0;

    // TEST 2: VARIOUS WRITINGS TO 'REAL' PHY REGISTERS ( CONTROL AND NON WRITABLE REGISTERS )
    test_name   = "TEST 2: VARIOUS WRITINGS TO 'REAL' PHY REGISTERS ( CONTROL AND NON WRITABLE REGISTERS )";
    `TIME; $display("  TEST 2: VARIOUS WRITINGS TO 'REAL' PHY REGISTERS ( CONTROL AND NON WRITABLE REGISTERS )");
  
    // negate data and try to write into unwritable register
    tmp_data = ~phy_data;
    // write request
    #Tp mii_write_req(phy_addr, reg_addr, tmp_data);
    check_mii_busy; // wait for write to finish
    // read request
    #Tp mii_read_req(phy_addr, reg_addr);
    check_mii_busy; // wait for read to finish
    // read data
    rd_32(`ETH_MIIRX_DATA, tmp_data);
    if (tmp_data !== phy_data)
    begin
      test_fail("Data was written into unwritable PHY register - ID register 2");
      fail = fail + 1;
    end
  
    // set address
    reg_addr = 5'h0; // control register
    // read request
    #Tp mii_read_req(phy_addr, reg_addr);
    check_mii_busy; // wait for read to finish
    // read data
    rd_32(`ETH_MIIRX_DATA, tmp_data);
    // write request
    phy_data = 16'h7DFF; // bit 15 (RESET bit) and bit 9 are self clearing bits
    #Tp mii_write_req(phy_addr, reg_addr, phy_data);
    check_mii_busy; // wait for write to finish
    // read request
    #Tp mii_read_req(phy_addr, reg_addr);
    check_mii_busy; // wait for read to finish
    // read data
    rd_32(`ETH_MIIRX_DATA, phy_data);
    if (phy_data !== 16'h7DFF)
    begin
      test_fail("Data was not correctly written into OR read from writable PHY register - control register");
      fail = fail + 1;
    end
    // write request
    #Tp mii_write_req(phy_addr, reg_addr, tmp_data);
    check_mii_busy; // wait for write to finish
    // read request
    #Tp mii_read_req(phy_addr, reg_addr);
    check_mii_busy; // wait for read to finish
    // read data
    rd_32(`ETH_MIIRX_DATA, phy_data);
    if (phy_data !== tmp_data)
    begin
      test_fail("Data was not correctly written into OR read from writable PHY register - control register");
      fail = fail + 1;
    end
    if(fail == 0)
      test_ok;
    else
      fail = 0;

    
    # 100 CLK_stop = 1;
    # 100 $finish();
  end
endmodule 

実行結果

Veritakでの実行結果です。

C:\home\Dropbox\patahene\ethmac\trunk\veritak\mii_test\ethmac_miim_tb.v(14)::ethmac_miim_tb
Verilogのシミュレーションの準備が完了しました。スタートは,Goボタンを押してください。
------------- シミュレーションを開始します。--------------------

  TEST 1: VARIOUS READINGS FROM 'REAL' PHY REGISTERS
    *************************************************************************************
    At time:                 8356 
    Test:                                                                                                     
    reported *SUCCESSFULL*! 
    *************************************************************************************
 
  Time: 8356
  TEST 2: VARIOUS WRITINGS TO 'REAL' PHY REGISTERS ( CONTROL AND NON WRITABLE REGISTERS )
    *************************************************************************************
    At time:                18636 
    Test: TEST 2: VARIOUS WRITINGS TO 'REAL' PHY REGISTERS ( CONTROL AND NON WRITABLE REGISTERS )             
    reported *SUCCESSFULL*! 
    *************************************************************************************
 
Info: $finishコマンドを実行します。time=18836
---------- シミュレーションを終了します。time=18836----------