module oh_clockdiv
  #(parameter N             = 2,         // data width
    parameter SYN           = "TRUE",    // synthesizable (or not)
    parameter LATCH_TYPE    = "DEFAULT", // implementation type
    parameter CLOCKMUX_TYPE = "DEFAULT"  // implementation type
    )
   (
    //inputs
    input 	 clk, // main clock
    input 	 nreset, // async active low reset (from oh_rsync)
    input 	 clkchange, // indicates a parameter change
    input 	 clken, // clock enable
    input [7:0]  clkdiv, // [7:0]=period (0==bypass, 1=div/2, 2=div/3, etc)
    input [15:0] clkphase0, // [7:0]=rising,[15:8]=falling
    input [15:0] clkphase1, // [7:0]=rising,[15:8]=falling
    //outputs
    output 	 clkout0, // primary output clock
    output 	 clkrise0, // rising edge match
    output 	 clkfall0, // falling edge match
    output 	 clkout1, // secondary output clock
    output 	 clkrise1, // rising edge match
    output 	 clkfall1, // falling edge match
    output 	 clkstable    // clock is guaranteed to be stable
    );

   //regs
   reg [7:0] counter;
   reg 	     clkout0_reg;
   reg 	     clkout1_reg;
   reg 	     clkout1_shift;
   reg [2:0] period;
   wire      period_match;
   wire [3:0] clk1_sel;
   wire [3:0] clk1_sel_sh;
   wire [1:0] clk0_sel;
   wire [1:0] clk0_sel_sh;

   //###########################################
   //# CHANGE DETECT (count 8 periods)
   //###########################################

   always @ (posedge clk or negedge nreset)
     if(!nreset)
       period[2:0] <= 'b0;
     else if (clkchange)
       period[2:0] <='b0;
     else if(period_match & ~clkstable)
       period[2:0] <= period[2:0] +1'b1;

   assign clkstable = (period[2:0]==3'b111);

   //###########################################
   //# CYCLE COUNTER
   //###########################################

   always @ (posedge clk or negedge nreset)
     if (!nreset)
       counter[7:0]   <= 'b0;
     else if(clken)
       if(period_match)
	 counter[7:0] <= 'b0;
       else
	 counter[7:0] <= counter[7:0] + 1'b1;

   assign period_match = (counter[7:0] == clkdiv[7:0]);

   //###########################################
   //# RISING/FALLING EDGE SELECTORS
   //###########################################

   assign clkrise0     = (counter[7:0] == clkphase0[7:0]);
   assign clkfall0     = (counter[7:0] == clkphase0[15:8]);
   assign clkrise1     = (counter[7:0] == clkphase1[7:0]);
   assign clkfall1     = (counter[7:0] == clkphase1[15:8]);

   //###########################################
   //# CLKOUT0
   //###########################################

   always @ (posedge clk or negedge nreset)
     if(!nreset)
       clkout0_reg <= 1'b0;
     else if(clkrise0)
       clkout0_reg <= 1'b1;
     else if(clkfall0)
       clkout0_reg <= 1'b0;

   // clock mux
   assign clk0_sel[1] =  (clkdiv[7:0]==8'd0);   // not implemented
   assign clk0_sel[0] = ~(clkdiv[7:0]==8'd0);


   // clock select needs to be stable high
   oh_lat0 #(.N(2),
	     .SYN(SYN),
	     .TYPE(LATCH_TYPE))
   oh_lat0 (.out (clk0_sel_sh[1:0]),
	    .clk (clk),
	    .in  (clk0_sel[1:0]));

   oh_clockmux2 #(.N(1),
		  .SYN(SYN),
		  .TYPE(CLOCKMUX_TYPE))
   oh_clockmux2 (.clkout(clkout0),
		 .en0(clk0_sel[0]),
		 .en1(clk0_sel[1]),
		 .clkin0(clkout0_reg),
		 .clkin1(clk));

   //###########################################
   //# CLKOUT1
   //###########################################

   always @ (posedge clk or negedge nreset)
     if(!nreset)
       clkout1_reg <= 1'b0;
     else if(clkrise1)
       clkout1_reg <= 1'b1;
     else if(clkfall1)
       clkout1_reg <= 1'b0;

   // creating divide by 2 shifted clock with negedge
   always @ (negedge clk)
     clkout1_shift <= clkout1_reg;

   // clock mux
   assign clk1_sel[3] =  1'b0;               // not implemented
   assign clk1_sel[2] = (clkdiv[7:0]==8'd0); // div1 (bypass)
   assign clk1_sel[1] = (clkdiv[7:0]==8'd1); // div2 clock
   assign clk1_sel[0] = |clkdiv[7:1];        // all others

   // clock select needs to be stable high
   oh_lat0 #(.N(4),
	     .SYN(SYN),
	     .TYPE(LATCH_TYPE))
   latch_clk1 (.out (clk1_sel_sh[3:0]),
	       .clk (clk),
	       .in  (clk1_sel[3:0]));

   assign clk1_sel[3] =  1'b0;               // not implemented
   assign clk1_sel[2] = (clkdiv[7:0]==8'd0); // div1 (bypass)
   assign clk1_sel[1] = (clkdiv[7:0]==8'd1); // div2 clock
   assign clk1_sel[0] = |clkdiv[7:1];        // all others


   oh_clockmux4 #(.N(1),
		  .SYN(SYN),
		  .TYPE(CLOCKMUX_TYPE))
   mux_clk1 (.clkout(clkout1),
	     .en0   (clk1_sel[0]),
	     .en1   (clk1_sel[1]),
	     .en2   (clk1_sel[2]),
	     .en3   (clk1_sel[3]),
	     .clkin0(clkout1_reg),
	     .clkin1(clkout1_shift),
	     .clkin2(clk),
	     .clkin3(1'b0));

endmodule // oh_clockdiv