// Copyright (C) 2006  Free Software Initiative of Japan (FSIJ)
//
// Author: NIIBE Yutaka
//

module usb_rx_sof(clk, 
		  rx_dp_in,
		  rx_dn_in,
		  sof_blink, ack, err, syn);
   input clk;
   input rx_dp_in;
   input rx_dn_in;
   output ack;
   output reg err;
   output     sof_blink;

   reg [9:0] sof_count;
   wire      rx_clk, k, j, se0, rx_active, syn_err, sof;

   output    syn;

   rx_phy rx_phy(clk, rx_dp_in, rx_dn_in, rx_clk, k, j, se0);
   syn_detect syn_detect(rx_clk, k, j, se0, rx_active, syn_err, syn);
   sof_ack_detect sof_ack_detect(rx_clk, j, rx_active, sof, ack);

   always @ (posedge clk)
     if (sof_count == 10'b0)
       err <= 1'b0;
     else if (syn_err)
       err <= 1'b1;

   always @ (negedge sof)
     if (sof_count >= 999)
       sof_count <= 0;
     else
       sof_count <= sof_count + 1;

   assign     sof_blink = sof_count[9];
endmodule

//
// ~~~~____~~~~____~~~~____~~~~__ rx_dp_in
//
// ~~~~~____~~~~____~~~~____~~~~_ rx_dp_1
//
//        
// ~~~~~~____~~~~____~~~~____~~~~ rx_dp
//
// _____~___~___~___~___~___~___~ rx_edge
//
//      3012301230123012301230123
//      0012301230123012301230123
//      1301201230123012301230123
//      2012301230123012301230123
//
// _______~___~___~___~___~___~___~___ rx_clk
//

module rx_phy(clk, rx_dp_in, rx_dn_in, rx_clk, k, j, se0);
   input      clk, rx_dp_in, rx_dn_in;
   output     rx_clk, k, j, se0;

   reg [1:0]  state;
   reg 	      rx_dp_1, rx_dn_1;
   reg 	      rx_dp, rx_dn;

   always @(posedge clk) rx_dp_1 <= rx_dp_in;
   always @(posedge clk) rx_dp <= rx_dp_1;

   always @(posedge clk) rx_dn_1 <= rx_dn_in;
   always @(posedge clk) rx_dn <= rx_dn_1;

   wire       rx_edge;
   assign     rx_edge = (rx_dp_1 != rx_dp);
   assign     rx_clk = (state == 2'h1);

   assign     k =   !rx_dp &  rx_dn;
   assign     j =    rx_dp & !rx_dn;
   assign     se0 = !rx_dp & !rx_dn;

   always @(posedge clk)
     if (rx_edge)
       case (state)      
	 2'h0: state <= 2'h0;
	 2'h1: state <= 2'h3;
	 2'h2: state <= 2'h0;
	 2'h3: state <= 2'h0;
       endcase
     else
       case (state)
	 2'h0: state <= 2'h1;
	 2'h1: state <= 2'h2;
	 2'h2: state <= 2'h3;
	 2'h3: state <= 2'h0;
       endcase
endmodule

module syn_detect(rx_clk, k, j, se0, rx_active, syn_err, syn);
   input      rx_clk, k, j, se0;
   output reg rx_active, syn_err;
   reg [2:0]  syn_state;
   reg syn;
   output syn;

   reg 	  dup;

   parameter IDLE	= 3'h0,
	       K1	= 3'h1,
	       J1	= 3'h2,
	       K2	= 3'h3,
	       J2	= 3'h4,
	       K3	= 3'h5,
	       J3	= 3'h6,
	       K4	= 3'h7;

   always @(posedge rx_clk)
     if (syn|syn_err)	rx_active <= 1'b1;
     else if (se0)	rx_active <= 1'b0;

   always @(negedge rx_clk)
     begin
	syn <= 1'b0;
	syn_err <= 1'b0;
	syn_state <= IDLE;
	if (!rx_active)
	  case (syn_state)
	    IDLE:
	      begin
		 dup <= 1'b0;
		 if (k)	syn_state <= K1;
	      end
	    K1:
	      if (j)	syn_state <= J1;
	      else if (k && !dup) begin dup <= 1'b1; syn_state <= K1; end
	      else	syn_err <= 1'b1;
	    J1:
	      if (k)	syn_state <= K2;
	      else if (j && !dup) begin dup <= 1'b1; syn_state <= J1; end
	      else	syn_err <= 1'b1;
	    K2:
	      if (j)	syn_state <= J2;
	      else if (k && !dup) begin dup <= 1'b1; syn_state <= K2; end
	      else	syn_err <= 1'b1;
	    J2:
	      if (k)	syn_state <= K3;
	      else	syn_err <= 1'b1;
	    K3:
	      if (j)	syn_state <= J3;
	      else	syn_err <= 1'b1;
	    J3:
	      if (k)	syn_state <= K4;
	      else	syn_err <= 1'b1;
	    K4:
	      if (k)	syn <= 1'b1;
	      else	syn_err <= 1'b1;
	  endcase
     end
endmodule

module sof_ack_detect(rx_clk, rx, rx_active, sof, ack);
   input      rx_clk, rx, rx_active;
   output reg sof, ack;

   reg 	      rx_r;
   wire       nrzi;
   wire       drop_bit;
   reg [7:0]  pid;
   reg [2:0]  one_cnt;
   reg [2:0]  byte_clk;
   reg 	      stop;

   always @(negedge rx_clk)
     rx_r <= rx;

   assign     drop_bit = (one_cnt == 3'h6);
   assign     nrzi = (rx_r == rx);

   always @(negedge rx_clk)
     if (nrzi && !drop_bit) one_cnt <= one_cnt + 3'h1;
     else one_cnt <= 3'h0;

   always @(negedge rx_clk)
     if (!rx_active) begin
	byte_clk <= 3'h0;
	sof <= 1'b0;
	stop <= 1'b0;
     end
     else if (!stop && !drop_bit) begin
	pid = {nrzi, pid[7:1]};
	byte_clk = byte_clk + 3'h1;
	if (byte_clk == 3'h0) begin
	   stop <= 1'b1;
	   case (pid)
	     8'ha5:
	       sof <= 1'b1;
	     8'hd2:
	       ack <= !ack;
	   endcase
	end
     end
endmodule