URL
https://opencores.org/ocsvn/openrisc_me/openrisc_me/trunk
Subversion Repositories openrisc_me
[/] [openrisc/] [trunk/] [orpsocv2/] [bench/] [verilog/] [smii_phy.v] - Rev 403
Compare with Previous | Blame | View Log
////////////////////////////////////////////////////////////////////// //// //// //// SMII Receiver/Decoder (usually at PHY end) //// //// //// //// Description //// //// Low pin count serial MII ethernet interface //// //// //// //// To Do: //// //// - //// //// //// //// Author(s): //// //// - Michael Unneback, unneback@opencores.org //// //// ORSoC AB michael.unneback@orsoc.se //// //// - Julius Baxter, jb@orsoc.se //// //// //// ////////////////////////////////////////////////////////////////////// //// //// //// Copyright (C) 2009 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 //// //// //// ////////////////////////////////////////////////////////////////////// module smii_phy ( // SMII input smii_tx, input smii_sync, output reg smii_rx, // MII // TX /* ALL I/Os swapped compared to SMII on MAC end MAC - jb */ output reg [3:0] ethphy_mii_tx_d, output reg ethphy_mii_tx_en, output reg ethphy_mii_tx_err, input ethphy_mii_tx_clk, // RX input [3:0] ethphy_mii_rx_d, input ethphy_mii_rx_dv, input ethphy_mii_rx_err, input ethphy_mii_rx_clk, input ethphy_mii_mcoll, input ethphy_mii_crs, input fast_ethernet, input duplex, input link, // internal //input [10:1] state, // clock and reset input clk, /* Global reference clock for both SMII modules */ input rst_n ); reg [3:0] rx_tmp; reg jabber = 0; reg mtx_clk_tmp, mrx_clk_tmp; reg [3:0] tx_cnt; reg [3:0] rx_cnt; /**************************************************************************/ /* Counters */ /**************************************************************************/ /* Generate the state counter, based on incoming sync signal */ /* 10-bit shift register, indicating where we are */ reg [10:1] state_shiftreg; /* A wire hooked up from bit 0 with the last byte of the state counter/shiftreg */ wire [7:0] state_shiftreg_top_byte; assign state_shiftreg_top_byte[7:0] = state_shiftreg[10:3]; always @(posedge clk) begin if (smii_sync) /* sync signal from MAC */ state_shiftreg <= 10'b0000000010; else if (state_shiftreg[10]) state_shiftreg <= 10'b0000000001; else state_shiftreg[10:2] <= state_shiftreg[9:1]; end /* counter from 0 to 9, counting the 10-bit segments we'll transmit via SMII*/ reg [3:0] segment_ctr; always @(posedge clk) begin if(!rst_n) segment_ctr <= 4'h0; else begin if(fast_ethernet) /* If using 100Mbs, then each segment is different, we don't count the repeats */ segment_ctr <= 4'h0; else if (state_shiftreg[10]) if (segment_ctr == 4'h9) /* Wrap */ segment_ctr <= 4'h0; else /* Increment */ segment_ctr <= segment_ctr + 1'b1; end end // always @ (posedge clk) /**************************************************************************/ /* RX path logic PHY->(MII->SMII)->MAC */ /**************************************************************************/ reg [7:0] rx_data_byte_rx_clk; reg [4:0] rx_dv_nib_0; reg rx_nib_first,rx_nib_first_r; // if high, nib_0 contains the "first" of the pair of nibs reg [3:0] rx_segment_begin_num; // Allow us to check if RX DV has been low for a while reg [3:0] rx_dv_long_low_sr; wire dv_long_low; always @(posedge ethphy_mii_rx_clk) rx_dv_long_low_sr[3:0] <= {rx_dv_long_low_sr[2:0], ethphy_mii_rx_dv}; assign rx_dv_long_low = ~(|rx_dv_long_low_sr); wire [9:0] rx_fifo_out; wire rx_fifo_empty,rx_fifo_almost_empty; always @(posedge ethphy_mii_rx_clk or negedge rst_n) begin if(!rst_n) begin rx_dv_nib_0 <= 0; rx_nib_first <= 0; rx_nib_first_r <= 0; end else begin if (!rx_nib_first) rx_dv_nib_0 <= {ethphy_mii_rx_dv,ethphy_mii_rx_d}; if(ethphy_mii_rx_dv) rx_nib_first <= ~rx_nib_first; rx_nib_first_r <= rx_nib_first; end end // always @ (posedge ethphy_mii_rx_clk or negedge rst_n) wire rx_fifo_pop; reg ethphy_mii_rx_dv_r; wire ethphy_mii_rx_dv_re; wire ethphy_mii_rx_dv_fe; always @(posedge ethphy_mii_rx_clk) ethphy_mii_rx_dv_r <= ethphy_mii_rx_dv; assign ethphy_mii_rx_dv_re = ethphy_mii_rx_dv & !ethphy_mii_rx_dv_r; assign ethphy_mii_rx_dv_fe = !ethphy_mii_rx_dv & ethphy_mii_rx_dv_r; reg rx_fifo_final_pop; always @(posedge clk) if (!rst_n) rx_fifo_final_pop <= 0; else if (rx_fifo_final_pop & state_shiftreg[1] & (((rx_segment_begin_num == segment_ctr) & !fast_ethernet) | fast_ethernet)) rx_fifo_final_pop <= 0; else if (rx_fifo_pop & rx_fifo_empty) rx_fifo_final_pop <= 1; always @(posedge ethphy_mii_rx_clk) if (ethphy_mii_rx_dv_re) rx_segment_begin_num <= segment_ctr; reg do_fifo_pop; always @(posedge clk) if (!rst_n) do_fifo_pop <= 0; else if (rx_fifo_empty) do_fifo_pop <= 0; else if (!rx_fifo_almost_empty) do_fifo_pop <= 1; assign rx_fifo_pop = (state_shiftreg[9] & do_fifo_pop) & (((rx_segment_begin_num == segment_ctr) & !fast_ethernet) | fast_ethernet); reg rx_dv_r; wire rx_dv; // Error where rx_dv goes high one cycle too late, so is low for very first data frame - FIXME! //reg rx_dv; always @(posedge clk) if (!rst_n) rx_dv_r <= 0; else if (rx_fifo_final_pop & state_shiftreg[1] & (((rx_segment_begin_num == segment_ctr) & !fast_ethernet) | fast_ethernet)) rx_dv_r <= 0; else if (rx_fifo_pop) rx_dv_r <= rx_fifo_out[9]; assign rx_dv = rx_dv_r | rx_fifo_pop; reg sending_segment; always @(posedge clk) if (!rst_n) sending_segment <= 0; else if (rx_fifo_final_pop & state_shiftreg[1] & (((rx_segment_begin_num == segment_ctr) & !fast_ethernet) | fast_ethernet)) sending_segment <= 0; else if ((state_shiftreg[9] & do_fifo_pop) & (((rx_segment_begin_num == segment_ctr) & !fast_ethernet) | fast_ethernet)) sending_segment <= !rx_fifo_empty; /* A fifo, storing RX bytes coming from the PHY interface */ generic_fifo #(10, 1024, 10) rx_fifo ( // Outputs .psh_full (), .pop_q (rx_fifo_out), .pop_empty (rx_fifo_empty), .almost_empty (rx_fifo_almost_empty), // Inputs .async_rst_n (rst_n), .psh_clk (ethphy_mii_rx_clk), .psh_we (rx_nib_first_r), .psh_d ({ethphy_mii_rx_dv,ethphy_mii_rx_err,ethphy_mii_rx_d,rx_dv_nib_0[3:0]}), .pop_clk (clk), .pop_re (rx_fifo_pop) ); reg [9:0] smii_rx_frame_next; always @(posedge clk) if (state_shiftreg[10]) smii_rx_frame_next <= rx_dv ? {rx_fifo_out[7:0],1'b1,ethphy_mii_crs} : {3'b101,jabber,link,duplex,fast_ethernet, ethphy_mii_rx_err,1'b0,ethphy_mii_crs}; always @(posedge clk) smii_rx <= state_shiftreg[10] & smii_rx_frame_next[0] | state_shiftreg[1] & smii_rx_frame_next[1] | state_shiftreg[2] & smii_rx_frame_next[2] | state_shiftreg[3] & smii_rx_frame_next[3] | state_shiftreg[4] & smii_rx_frame_next[4] | state_shiftreg[5] & smii_rx_frame_next[5] | state_shiftreg[6] & smii_rx_frame_next[6] | state_shiftreg[7] & smii_rx_frame_next[7] | state_shiftreg[8] & smii_rx_frame_next[8] | state_shiftreg[9] & smii_rx_frame_next[9]; reg [79:0] rx_statename; always @* begin case (1) state_shiftreg[1] : rx_statename = "CRS"; state_shiftreg[2] : rx_statename = "RX_DV"; state_shiftreg[3]: rx_statename = "RXD0/RXERR"; state_shiftreg[4]: rx_statename = "RXD1/Fast"; state_shiftreg[5]: rx_statename = "RXD2/Dupl"; state_shiftreg[6]: rx_statename = "RXD3/Link"; state_shiftreg[7]: rx_statename = "RXD4/Jabb"; state_shiftreg[8]: rx_statename = "RXD5/UNV"; state_shiftreg[9]: rx_statename = "RXD6/FCD"; state_shiftreg[10] : rx_statename = "RXD7/AS1"; default: rx_statename = "XXXXXXX"; endcase // case (1) end // always @ * /* Status seq.: CRS, DV, ER, Speed, Duplex, Link, Jabber, UPV, FCD, 1 */ // {1'b1,1'b0,1'b1,jabber,link,duplex,,ethphy_mii_rx_err}); /**************************************************************************/ /* TX path logic MAC->(SMII->MII)->PHY */ /**************************************************************************/ /* We ignore the data when TX_EN bit is not high - it's only used in MAC to MAC comms*/ /* Register the sequence appropriately as it comes in */ reg tx_er_seqbit_scratch; reg tx_en_seqbit_scratch; reg [7:0] tx_data_byte_scratch; reg [2:0] tx_byte_to_phy; /* PHY sourced TX_CLK domain */ wire tx_fifo_empty, tx_fifo_almost_empty; wire tx_fifo_full; wire [7:0] tx_fifo_q_dat; wire tx_fifo_q_err; reg tx_fifo_pop; reg [3:0] tx_segment_begin_num; wire [3:0] tx_segment_load_num; assign tx_segment_load_num = (tx_segment_begin_num == 0) ? 4'h9 : tx_segment_begin_num - 1; /* Signal to tell us an appropriate time to copy the values out of the temp regs we put the incoming TX line into when we've received a sequence off the SMII TX line that has TX_EN high */ wire tx_seqbits_copy; assign tx_seqbits_copy = ( ( ((!fast_ethernet) & (segment_ctr == tx_segment_load_num)) | fast_ethernet ) & tx_en_seqbit_scratch & state_shiftreg[1] ); always @(posedge clk) begin /* remember which counter value we were at when tx enable/valid went high. This is only useful when not doing fast ethernet*/ /* tx en has gone high - remember the sequence number we're in */ if ((tx_segment_begin_num == 4'hf) & (tx_en_seqbit_scratch)) tx_segment_begin_num <= segment_ctr; /* If tx enable goes low again, reset the segment number */ if (!tx_en_seqbit_scratch) /* reset to 0xf */ tx_segment_begin_num <= 4'hf; end always @(posedge clk) begin if (!rst_n) begin tx_er_seqbit_scratch <= 0; tx_en_seqbit_scratch <= 0; tx_data_byte_scratch <= 0; end else begin if(state_shiftreg[1]) tx_er_seqbit_scratch <= smii_tx; if(state_shiftreg[2]) tx_en_seqbit_scratch <= smii_tx; /* Preserve all but current bit of interest, as indicated by state vector bit (reversed, becuase we get MSbit first) and OR in the current smii_tx line value at this position*/ if((|state_shiftreg[10:3]) & tx_en_seqbit_scratch) tx_data_byte_scratch <= (tx_data_byte_scratch & ~state_shiftreg_top_byte) | ({8{smii_tx}} & state_shiftreg_top_byte); end end // always @ (posedge clk) reg [3:0] nib; /* In the event we have a valid byte frame then get it to the PHY as quickly as possible - this is TX_CLK domain */ always @(posedge ethphy_mii_tx_clk or negedge rst_n) begin if(!rst_n) begin tx_byte_to_phy <= 0; tx_fifo_pop <= 1'b0; /* Output MII registers to the PHY */ ethphy_mii_tx_d <= 0; ethphy_mii_tx_en <= 0; ethphy_mii_tx_err <= 0; end else begin /* If fast_ethernet/100mbs we wait until the FIFO is full otherwise, we push out the byte each time we get one */ //if((!tx_fifo_empty && !fast_ethernet) || //(tx_fifo_full && fast_ethernet)) if (!tx_fifo_almost_empty) begin if(tx_byte_to_phy == 0) begin tx_byte_to_phy <= 1; tx_fifo_pop <= 1; end end /* FIFO control loop */ if (tx_byte_to_phy == 1)/* Output bits 3-0 (bottom nibble ) */ begin ethphy_mii_tx_en <= 1; tx_fifo_pop <= 0; tx_byte_to_phy <= 2; ethphy_mii_tx_d <= tx_fifo_q_dat[3:0]; nib <= tx_fifo_q_dat[7:4]; ethphy_mii_tx_err <= tx_fifo_q_err; end else if (tx_byte_to_phy == 2) /* Output bits 7-4 (top nibble) */ begin //ethphy_mii_tx_d <= tx_fifo_q_dat[7:4]; ethphy_mii_tx_d <= nib; if(!tx_fifo_empty) /* Check if more in FIFO */ begin tx_fifo_pop <= 1; tx_byte_to_phy <= 1; end else /* Finish up */ begin tx_byte_to_phy <= 3; end end else if (tx_byte_to_phy == 3) /* De-assert TX_EN */ begin tx_byte_to_phy <= 2'b00; ethphy_mii_tx_en <= 0; end end // else: !if(!rst_n) end // always @ (posedge ethphy_mii_tx_clk or negedge rst_n) /* A fifo, storing TX bytes coming from the SMII interface */ generic_fifo #(9, 64, 6) tx_fifo ( // Outputs .psh_full (tx_fifo_full), .pop_q ({tx_fifo_q_err,tx_fifo_q_dat}), .pop_empty (tx_fifo_empty), .almost_empty (tx_fifo_almost_empty), // Inputs .async_rst_n (rst_n), .psh_clk (clk), .psh_we (tx_seqbits_copy), .psh_d ({tx_er_seqbit_scratch,tx_data_byte_scratch}), .pop_clk (ethphy_mii_tx_clk), .pop_re (tx_fifo_pop)); //assign mcoll = mcrs & mtxen; endmodule // smii_top /* Generic fifo - this is bad, should probably be done some other way */ module generic_fifo (async_rst_n, psh_clk, psh_we, psh_d, psh_full, pop_clk, pop_re, pop_q, pop_empty, almost_empty); parameter dw = 8; parameter size = 16; parameter size_log_2 = 4; /* Asynch. reset, active low */ input async_rst_n; /* Push side signals */ input psh_clk; input psh_we; input [dw-1:0] psh_d; output psh_full; /* Pop side signals */ input pop_clk; input pop_re; //output reg [dw-1:0] pop_q; output reg [dw-1:0] pop_q; output pop_empty; output wire almost_empty; integer i; /* Actual FIFO memory */ reg [dw-1:0] fifo_mem [0:size-1]; initial begin for (i=0;i<size;i=i+1) begin fifo_mem[i] = {dw{1'b0}}; end end /* FIFO position ptr regs */ reg [size_log_2 - 1 : 0 ] wr_ptr, rd_ptr, ctr; /* FIFO full signal for push side */ //assign psh_full = (ptr == size-1) ? 1 : 0; /* This full logic means we all but one slot in the FIFO */ assign psh_full = ctr == size; /* FIFO empty signal for pop side */ //assign pop_empty = (ptr == 0) ? 1 : 0; //assign pop_empty = ctr==0; assign pop_empty = rd_ptr == wr_ptr; assign almost_empty = ctr < 16; always @(posedge pop_re or negedge async_rst_n) begin if (!async_rst_n) begin rd_ptr <= 0; pop_q <= 0; end else begin if (!pop_empty) begin pop_q <= fifo_mem[rd_ptr]; ctr <= ctr - 1; rd_ptr <= rd_ptr + 1; end end end always @(posedge psh_we or negedge async_rst_n) begin if (!async_rst_n) begin for (i=0;i<size;i=i+1) fifo_mem[i] <= 0; wr_ptr <= 0; ctr <= 0; end else begin fifo_mem[wr_ptr] <= psh_d; wr_ptr <= #1 wr_ptr + 1; ctr <= ctr + 1; end end endmodule // generic_fifo