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