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----------