//
// Simple hardware random number generator
//

module rng
#(parameter
  nclocks = 1,	// Asynchronous clocks
  width = 32	// Output bus width
  )
   (
    // System reset and clock
    input		   rst_n,
    input		   sys_clk,

    input		   read_stb,
    input		   latch_stb,
    output		   ready,
    output reg [width-1:0] q,

     // Randomness inputs
    input [nclocks-1:0]    clocks, // Random input synchronized to sys_clk
    (* keep = 1 *) inout [2:0] rngio	// Unconnected pins with pullups
    );

   // Number of internal oscillators
   localparam n_osc = 2;
   wire [n_osc:1]	   osc;
   wire [n_osc:1]	   osc_s;

   //
   // Latch control and enable logic
   //
   reg [n_osc:1]	   osc_q;
   reg [n_osc:1]	   osc_e; // Edge detected

   typedef enum logic [2:0] {
	st_starting,		// Powering up oscillators
	st_started,		// Oscillators running
	st_strobed,		// Starting strobe received
	st_latch,		// Ending strobe received, latch output data
	st_ready,		// Output data available
	st_stopped		// Data available, powered down due to idle
   } state_t;
   state_t state = st_starting;

   // Number of ticks before shutting down
   localparam shutdown_ticks_lg2 = 1;
   reg [shutdown_ticks_lg2-1:0] shutdown_tmr;

   assign ready   = (state == st_ready) | (state == st_stopped);
   wire   running = (state != st_stopped);

   wire [width-1:0] random_data;

   always @(posedge sys_clk or negedge rst_n)
     if (~rst_n)
       begin
	  state <= st_starting;
	  osc_q <= 'b0;
	  osc_e <= 'b0;
       end
     else
       begin
	  osc_q        <= ~osc_s;
	  osc_e        <= 'b0;
	  shutdown_tmr <= 'b0;

	  case (state)
	    st_starting: begin
	       osc_e <= osc_e | (osc_s & osc_q);
	       if (&osc_e)
		 state <= st_started;
	    end
	    st_started: begin
	       if (latch_stb)
		 state <= st_strobed;
	    end
	    st_strobed: begin
	       if (latch_stb)
		 state <= st_latch;
	    end
	    st_latch: begin
	       // Wait here if read_stb is still active from previous read
	       if (~read_stb)
		 begin
		    q <= random_data;
		    state <= st_ready;
		 end
	    end
	    st_ready: begin
	       shutdown_tmr <= shutdown_tmr + latch_stb;

	       // If shutdown_tmr is nonzero, then we have had at least
	       // one latch_stb already while waiting here, and so we
	       // can use that data immediately.
	       if (read_stb)
		 state <= |shutdown_tmr ? st_latch :
			  latch_stb ? st_strobed : st_started;
	       else if (latch_stb & &shutdown_tmr)
		 state <= st_stopped;
	    end
	    st_stopped: begin
	       // Data is available, but haven't been consumed for
	       // a long time, so we have shut down the ring oscillators.
	       // When a read happens, start them up again.
	       if (read_stb)
		 state <= st_starting;
	    end
	  endcase // case (state)
       end // else: !if(~rst_n)

   //
   // Internal oscillators: allow them to be stopped to save power
   // if we have had multiple ticks with no data accesses.
   //

   // Internal on-chip oscillator
   int_osc int_osc
     (
      .clkout ( osc[1] ),
      .oscena ( running )
      );

   // Ring oscillator using the rngio pins
   assign rngio[0] = ~rngio[2] & running;
   assign rngio[1] = ~rngio[0] & running;
   assign rngio[2] = ~rngio[1] & running;

   assign osc[2] = rngio[0];

   synchronizer #(.width(n_osc)) synchro
     (
      .rst_n ( 1'b1 ),
      .clk ( sys_clk ),
      .d ( osc ),
      .q ( osc_s )
      );

   // LFSR randomness accumulator

   // See http://users.ece.cmu.edu/~koopman/crc/crc36.html for
   // choice of polynomial. The x^poly_width term in the polynomial
   // is implicit.
   localparam poly_width = 33;
   localparam [poly_width-1:0] poly = 33'h0_0000_009d;

   localparam lsfr_max = width > poly_width ? width-1 : poly_width-1;

   wire			lsfr_input = ^{osc_s, clocks};

   reg [lsfr_max:0]	lsfr;

   always @(posedge sys_clk)
     lsfr <= ({lsfr[lsfr_max-1:0], lsfr_input} ^
	      {{(lsfr_max+1){lsfr[poly_width-1]}} & poly});

   assign random_data = lsfr[width-1:0];

endmodule // rng
