// -----------------------------------------------------------------------
//
//   Copyright 2010-2021 H. Peter Anvin - All Rights Reserved
//
//   This program is free software; you can redistribute it and/or modify
//   it under the terms of the GNU General Public License as published by
//   the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
//   Boston MA 02110-1301, USA; either version 2 of the License, or
//   (at your option) any later version; incorporated herein by reference.
//
// -----------------------------------------------------------------------

//
// Simple SDRAM controller
//
//  Very simple non-parallelizing SDRAM controller.
//
//
//  Two ports are provided:
//  Port 1 does aligned 4-byte accesses with byte enables.
//  Port 2 does aligned 8-byte accesses, write only, with no byte
//  enables; it supports streaming from a FIFO.
//
//  Port 1 is multiplexed via an arbiter, which receives a bus
//  defined by the sdram_bus interface.
//
//  All signals are in the sdram clock domain.
//
//  [rw]ack is asserted at the beginning of a read- or write cycle and
//  deasserted afterwards; rready is asserted once all data is read and
//  the read data (rdX port) is valid; it remains asserted after the
//  transaction is complete and rack is deasserted.
//

//
// The interface to the port modules. The read data is 16 bits
// at a time, and is only valid in the cycle rstrb[x] is asserted.
//
// The only output signal that is unique to this port
// is "start". All other signals are broadcast.
//
interface dram_bus;
   logic [1:0]	prio;		// Priority vs refresh
   logic        rst_n;
   logic        clk;
   logic [24:0] addr;
   logic	addr0;		// addr[0] latched at transaction start
   logic [15:0] rd;
   logic	req;
   logic  [1:0]	rstrb;		// Data read strobe
   logic [31:0] wd;
   logic  [3:0] wstrb;
   logic	start;		// Transaction start
   logic	wrack;		// Transaction is a write

   // Upstream direction
   modport ustr (
		input  prio,
		output rst_n,
		output clk,
		input  addr,
		output addr0,
		output rd,
		input  req,
		output rstrb,
		input  wd,
		input  wstrb,
		output start,
		output wrack
		);

   // Downstream direction
   modport dstr (
		output prio,
		input  rst_n,
		input  clk,
		output addr,
		input  addr0,
		input  rd,
		output req,
		input  rstrb,
		output wd,
		output wstrb,
		input  start,
		input  wrack
		 );
endinterface // dram_bus

// Port into the DRAM
module dram_port
  #(parameter width = 32)
  (
   dram_bus.dstr            bus,
   input             [1:0]  prio,

   input             [24:0] addr,
   output reg   [width-1:0] rd,
   input	            valid,
   output reg               ready,
   input        [width-1:0] wd,
   input [(width >> 3)-1:0] wstrb
   );

   reg started;

   assign bus.prio  = prio;
   assign bus.addr  = addr;
   assign bus.req   = valid & ~started;

   always_comb
     begin
	bus.wd    = 32'hxxxx_xxxx;
	bus.wstrb = 4'b0000;

	if (width == 8)
	  begin
	     bus.wd[15:0]   = { wd, wd };
	     bus.wstrb[1:0] = { wstrb[0] & addr[0], wstrb[0] & ~addr[0] };
	  end
	else
	  begin
	     bus.wd[width-1:0]           = wd;
	     bus.wstrb[(width >> 3)-1:0] = wstrb;
	  end
     end

   always @(negedge bus.rst_n or posedge bus.clk)
     if (~bus.rst_n)
       begin
	  ready   <= 1'b0;
	  started <= 1'b0;
       end
     else
       begin
	  if (~valid)
	    begin
	       ready   <= 1'b0;
	       started <= 1'b0;
	    end
	  else if (bus.start)
	    begin
	       started <= 1'b1;
	       ready   <= |bus.wstrb; // All write data latched
	    end
	  else if (started & ~ready)
	    begin
	       ready <= bus.rstrb[(width - 1) >> 4];
	    end
       end // else: !if(~bus.rst_n)

   genvar i;
   generate
      for (i = 0; i < (width >> 4); i++)
	begin : w
	   always @(posedge bus.clk)
	     if (started & ~ready & bus.rstrb[i])
	       begin
		  if (width == 8)
		    rd <= bus.addr0 ? bus.rd[15:8] : bus.rd[7:0];
		  else
		    rd[i*16+15:i*16] <= bus.rd;
	       end
	end
   endgenerate
endmodule // dram_port

module dram_arbiter
  #(parameter port_count = 1)
   (
    dram_bus.ustr ustr [1:port_count],
    dram_bus.dstr dstr,
    input [1:0] rfsh_prio,
    output logic do_rfsh
    );

   logic [port_count:0] requesting;
   logic [24:0] addr  [1:port_count];
   logic [31:0] wd    [1:port_count];
   logic [3:0]  wstrb [1:port_count];
   logic [1:0]	prio  [1:port_count];

   always_comb
     requesting[0] = 1'b0;

   genvar  i;
   generate
      for (i = 1; i <= port_count; i++)
	begin : u
	   always_comb
	     begin
		ustr[i].rst_n = dstr.rst_n;
		ustr[i].clk   = dstr.clk;
		ustr[i].addr0 = dstr.addr0;
		ustr[i].rd    = dstr.rd;
		ustr[i].rstrb = dstr.rstrb;
		ustr[i].wrack = dstr.wrack;
		ustr[i].start = 1'b0;

		addr[i]       = ustr[i].addr;
		wd[i]         = ustr[i].wd;
		wstrb[i]      = ustr[i].wstrb;
		prio[i]       = ustr[i].prio;

		if (~|requesting[i-1:0] & ustr[i].req)
		  begin
		     requesting[i] = 1'b1;
		     ustr[i].start = dstr.start & (ustr[i].prio >= rfsh_prio);
		  end
		else
		  begin
		     requesting[i] = 1'b0;
		     ustr[i].start = 1'b0;
		  end
	     end // always_comb
	end // for (i = 1; i <= port_count; i++)
   endgenerate

   always_comb
     begin
	dstr.req   = 1'b0;
	dstr.addr  = 25'bx;
	dstr.wd    = 32'bx;
	dstr.wstrb = 4'bx;
	do_rfsh    = |rfsh_prio;

	for (int j = 1; j <= port_count; j++)
	  begin
	     if (requesting[j])
	       begin
		  dstr.req   = 1'b1;
		  dstr.addr  = addr[j];
		  dstr.wd    = wd[j];
		  dstr.wstrb = wstrb[j];
		  do_rfsh    = prio[j] < rfsh_prio;
	       end
	  end // for (int j = 1; j <= port_count; j++)
     end // always_comb
endmodule // dram_arbiter

module sdram
#( parameter
   port1_count     =  1,

   //  Timing parameters
   //  The parameters are hardcoded for Micron MT48LC16M16A2-6A,
   //  per datasheet:
   //			  100 MHz  167 MHz
   //    ----------------------------------------------------------
   //    CL                     2        3    READ to data out
   //    tRCD	    18 ns       2        3    ACTIVE to READ/WRITE
   //    tRFC	    60 ns       6       10    REFRESH to ACTIVE
   //    tRP        18 ns       2        3    PRECHARGE to ACTIVE/REFRESH
   //    tRAS	    42 ns       5        7    ACTIVE to PRECHARGE
   //    tRC        60 ns       6       10    ACTIVE to ACTIVE (same bank)
   //    tRRD       12 ns       2        2    ACTICE to ACTIVE (different bank)
   //    tWR        12 ns       2        2    Last write data to PRECHARGE
   //    tMRD                   2	 2    MODE REGISTER to ACTIVE/REFRESH
   //
   //    These parameters are set by power of 2:
   //    tREFi 64/8192 ms     781     1302    Refresh time per row (max)
   //    tP        100 us   10000    16667    Time until first command (min)

   t_cl		=  3,
   t_rcd	=  3,
   t_rfc	= 10,
   t_rp		=  3,
   t_ras	=  7,
   t_rc		= 10,
   t_rrd        =  2,
   t_wr		=  2,
   t_mrd	=  2,

   t_refi_lg2	= 10,	// 1024 cycles
   t_p_lg2	= 15,	// 32768 cycles

   burst_lg2	=  1	// log2(burst length)
)
(
	      // Reset and clock
	      input		rst_n,
	      input		clk,
	      input		out_clk, // Phase shifted external clock

	      // SDRAM hardware interface
	      output		sr_clk, // SDRAM clock output buffer
	      output		sr_cke, // SDRAM clock enable
	      output		sr_cs_n, // SDRAM CS#
	      output		sr_ras_n, // SDRAM RAS#
	      output		sr_cas_n, // SDRAM CAS#
	      output		sr_we_n, // SDRAM WE#
	      output [1:0]	sr_dqm, // SDRAM DQM (per byte)
	      output [1:0]	sr_ba, // SDRAM bank selects
	      output [12:0]	sr_a, // SDRAM address bus
	      inout [15:0]	sr_dq, // SDRAM data bus

	      // Port 1
              dram_bus.ustr     port1 [1:port1_count],

	      // Port 2
	      input [24:1]	a2,
	      input [15:0]	wd2,
	      input [1:0]	wrq2,
	      output reg	wacc2 // Data accepted, advance data & addr
	      );

`include "functions.sv"		// For modelsim

   // Mode register data
   wire			    mrd_wburst = 1'b1;     // Write bursts enabled
   wire [2:0]		    mrd_cl     = t_cl;
   wire [2:0]		    mrd_burst  = burst_lg2;
   wire			    mrd_interleave = 1'b0; // Interleaved bursts
   wire [12:0] mrd_val  = { 3'b000,		// Reserved
			    ~mrd_wburst,	// Write burst disable
			    2'b00,		// Normal operation
			    mrd_cl,		// CAS latency
			    mrd_interleave,	// Interleaved bursts
			    mrd_burst };	// Burst length

   // Where to issue a PRECHARGE when we only want to read one word
   // (terminate the burst as soon as possible, but no sooner...)
   localparam t_pre_rd_when = max(t_ras, t_rcd + 1);

   // Where to issue a PRECHARGE when we only want to write one word
   // (terminate the burst as soon as possible, but no sooner...)
   localparam t_pre_wr_when = max(t_ras, t_rcd + t_wr);

   // Actual burst length (2^burst_lg2)
   localparam burst_n = 1 << burst_lg2;

   // Command opcodes and attributes (is_rfsh, CS#, RAS#, CAS#, WE#)
   localparam	cmd_desl = 5'b0_1111; // Deselect (= NOP)
   localparam	cmd_nop  = 5'b0_0111;  // NO OPERATION
   localparam	cmd_bst  = 5'b0_0110;  // BURST TERMINATE
   localparam	cmd_rd   = 5'b0_0101;  // READ
   localparam	cmd_wr   = 5'b0_0100;  // WRITE
   localparam	cmd_act  = 5'b0_0011;  // ACTIVE
   localparam	cmd_pre  = 5'b0_0010;  // PRECHARGE
   localparam	cmd_ref  = 5'b1_0001;  // AUTO REFRESH
   localparam	cmd_mrd  = 5'b0_0000;  // LOAD MODE REGISTER
   reg [4:0]	dram_cmd;
   wire		is_rfsh  = dram_cmd[4];
   assign	sr_cs_n  = dram_cmd[3];
   assign	sr_ras_n = dram_cmd[2];
   assign	sr_cas_n = dram_cmd[1];
   assign	sr_we_n  = dram_cmd[0];

   assign       sr_cke   = 1'b1;

`ifdef SD_CLK_USE_DDIO
   // SDRAM output clock buffer. The SDRAM output clock is
   // inverted with respect to our internal clock, so that
   // the SDRAM sees the positive clock edge in the middle of
   // our clocks.
   //
   // Use a DDIO buffer for best performance
   // For EP4CE15 only could use a secondary PLL here, but it
   // isn't clear it buys us a whole lot.
   //
   // This buffer is driven by a separate PLL output, so that
   // the phase shift between the clock and the outputs/inputs
   // can be tuned.
   ddio_out sr_clk_out (
			.aclr ( 1'b0 ),
			.datain_h ( 1'b1 ),
			.datain_l ( 1'b0 ),
			.outclock ( out_clk ),
			.dataout ( sr_clk )
			);
`else // !`ifdef SD_CLK_USE_DDIO
   // Dedicated clock pin
   assign sr_clk = out_clk;
`endif

   // SDRAM output signal registers
   reg [12:0]		    dram_a;
   assign		    sr_a = dram_a;
   reg [1:0]		    dram_ba;
   assign		    sr_ba = dram_ba;
   reg [1:0]		    dram_dqm;
   assign		    sr_dqm = dram_dqm;
   reg [15:0]		    dram_d; // Data to DRAM
   reg [15:0]		    dram_q; // Data from DRAM (I/O buffers)
   reg			    dram_d_en; // Drive data out
   assign		    sr_dq = dram_d_en ? dram_d : 16'hzzzz;

   // State machine and counters
   reg [t_refi_lg2-2:0]	      rfsh_ctr;  // Refresh timer
   wire			      rfsh_ctr_msb = rfsh_ctr[t_refi_lg2-2];
   reg			      rfsh_ctr_last_msb;
   wire			      rfsh_tick = rfsh_ctr_last_msb & ~rfsh_ctr_msb;
   reg [t_p_lg2:t_refi_lg2-1] init_ctr;  // Reset to init counter
   reg [1:0]		      rfsh_prio; // Refresh priority (0-3)

   // Port1 and refresh arbiter
   dram_bus                   p1 ();
   wire			      do_rfsh;

   assign p1.rst_n = rst_n;
   assign p1.clk   = clk;

   dram_arbiter #(.port_count(port1_count))
   arbiter (
	    .ustr      ( port1 ),
	    .dstr      ( p1.dstr ),
	    .rfsh_prio ( rfsh_prio ),
	    .do_rfsh   ( do_rfsh )
	    );

   // The actual values are unimportant; the compiler will optimize
   // the state machine implementation.
   typedef enum logic [2:0] {
	 st_reset,		// Reset until init timer expires
	 st_init_rfsh,		// Refresh cycles during initialization
	 st_init_mrd,		// MRD register write during initialization
	 st_idle,		// Idle state: all banks precharged
	 st_rfsh,
	 st_rd_wr,		// Port 0/1 transaction
	 st_wr2			// Port 2 write (burstable)
   } state_t;
   state_t state = st_reset;

   always @(posedge clk or negedge rst_n)
     if (~rst_n)
       begin
	  rfsh_ctr  <= 1'b0;
	  rfsh_prio <= 2'b00;
	  init_ctr  <= 1'b0;
       end
     else
       begin
	  rfsh_ctr           <= rfsh_ctr + 1'b1;
	  rfsh_ctr_last_msb  <= rfsh_ctr_msb;

	  // Refresh priority management: saturating 2-bit counter
	  if (is_rfsh)
	    rfsh_prio <= 2'b00; // This is a refresh cycle
	  else
	    rfsh_prio <= rfsh_prio + (rfsh_tick & ~&rfsh_prio);

	  // The refresh counter is also used as a prescaler
	  // for the initialization counter.
	  // Note that means init_ctr is two cycles "behind"
	  // rfsh_ctr; this is totally fine.
	  init_ctr <= init_ctr + rfsh_tick;
       end // else: !if(~rst_n)

   reg  [5:0] op_ctr;		// Cycle into the current state
   wire [3:0] op_cycle    = op_ctr[3:0]; // Cycle into the current command
   wire [1:0] init_op_ctr = op_ctr[5:4]; // Init operation counter
   reg	      op_zero;		// op_cycle wrap around (init_op_ctr changed)

   reg [31:0] wdata_q;
   reg [ 3:0] be_q;
   reg [ 9:0] col_addr;
   reg	      wrq2_more;

   assign p1.start = (state == st_idle);
   assign p1.addr0 = col_addr[0];
   assign p1.rd = dram_q;

   //
   // Careful with the timing here... there is one cycle between
   // registers and wires, and the DRAM observes the clock 1/2
   // cycle from the internal logic. This affects read timing.
   //
   // Note that rready starts out as 1. This allows a 0->1 detection
   // on the rready line to be used as cycle termination signal.
   //
   always @(posedge clk or negedge rst_n)
     if (~rst_n)
       begin
	  dram_cmd      <= cmd_desl;
	  dram_a        <= 13'hxxxx;
	  dram_ba       <= 2'bxx;
	  dram_dqm      <= 2'b00;
	  dram_d        <= 16'hxxxx;
	  dram_q        <= 16'hxxxx;
	  dram_d_en     <= 1'b1; // Don't float except during read

	  op_ctr        <= 6'h0;
	  op_zero       <= 1'b0;
	  state         <= st_reset;
	  p1.wrack      <= 1'bx;
	  p1.rd         <= 16'hxxxx;
	  p1.rstrb      <= 2'b00;

	  wacc2         <= 1'b0;
	  wrq2_more     <= 1'bx;

	  wdata_q       <= 32'hxxxx_xxxx;
	  be_q          <= 4'bxxxx;
	  col_addr      <= 10'hxxx;
       end
     else
       begin
	  // Default values
	  // Note: dram_ba are preserved
	  dram_a        <= 13'b0;
	  dram_dqm      <= 2'b00;
	  dram_d        <= 16'haaaa;
	  dram_cmd      <= cmd_nop;

	  dram_d_en     <= 1'b1; // Don't float except during read

	  dram_q        <= sr_dq;
	  p1.rstrb      <= 2'b00;
	  wacc2         <= 1'b0;

	  if (state == st_reset || state == st_idle)
	    begin
	       op_ctr    <= 6'b0;
	       op_zero   <= 1'b0;
	    end
	  else
	    begin
	       op_ctr <= op_ctr + 1'b1;
	       op_zero <= &op_cycle; // About to wrap around
	    end // else: !if(state == st_reset || state == st_idle)

	  case (state)
	    st_reset:
	      begin
		 dram_a[10] <= 1'b1; // Precharge all banks

		 dram_cmd  <= cmd_nop;
		 if (init_ctr[t_p_lg2])
		   begin
		      dram_cmd <= cmd_pre;
		      state    <= st_init_rfsh;
		   end
	      end

	    st_init_rfsh:
	      begin
		 if (op_zero)
		   begin
		      dram_cmd <= cmd_ref;

		      if (init_op_ctr == 2'b11)
			state <= st_init_mrd;
		   end
	      end

	    st_init_mrd:
	      begin
		 dram_a <= mrd_val;
		 dram_ba <= 2'b00;

		 if (op_zero)
		   if (init_op_ctr[0])
		     state <= st_idle;
		   else
		     dram_cmd <= cmd_mrd;
	      end

	    st_idle:
	      begin
		 p1.wrack <= 1'bx;
		 be_q     <= 4'bxxxx;
		 wdata_q  <= 32'hxxxx_xxxx;

		 // A data transaction starts with ACTIVE command;
		 // a refresh transaction starts with REFRESH.
		 // Port 0 has the highest priority, then
		 // refresh, then port 1; a refresh transaction
		 // is started opportunistically if nothing is
		 // pending and the refresh counter is no less than
		 // half expired.
		 dram_a  <= 13'h1bb;
		 dram_ba <= 2'bxx;
		 dram_d  <= 16'hbbbb;

		 if (do_rfsh)
		   begin
		      dram_cmd    <= cmd_ref;
		      state       <= st_rfsh;
		   end
		 else if (p1.req)
		   begin
		      dram_cmd     <= cmd_act;
		      dram_a       <= p1.addr[24:12];
		      dram_ba      <= p1.addr[11:10];
		      col_addr     <= p1.addr[9:0];
		      p1.wrack     <= |p1.wstrb;
		      wdata_q      <= p1.wd;
		      be_q         <= p1.wstrb;
		      state        <= st_rd_wr;
		   end // if (p1.req)
		 else if (wrq2[0])
		   begin
		      // Begin port 2 write
		      dram_cmd    <= cmd_act;
		      dram_a      <= a2[24:12];
		      dram_ba     <= a2[11:10];
		      state       <= st_wr2;
		   end
		 else
		   begin
		      dram_cmd    <= cmd_desl;
		      state       <= st_idle;
		   end
	      end // case: st_idle

	    st_rfsh:
	      begin
		 if (op_cycle == t_rfc-2)
		   state <= st_idle;
	      end

	    st_rd_wr:
	      begin
		 dram_d_en <= p1.wrack;
		 dram_dqm  <= {2{p1.wrack}};
		 dram_d    <= 16'hcccc ^ {16{p1.wrack}};

		 if (op_cycle < 7)
		   dram_q <= op_cycle * 16'h1111;

		 // Commands
		 //
		 // This assumes:
		 // tRCD =  3
		 // rRRD =  2
		 // CL   =  3
		 // tRC  = 10
		 // tRAS =  7
		 // tWR  =  2
		 // tRP  =  3
		 //
		 case (op_cycle)
		   2: begin
		      dram_a[10]   <= 1'b0; // No auto precharge
		      dram_a[8:0]  <= col_addr[9:1];
		      dram_cmd     <= p1.wrack ? cmd_wr : cmd_rd;
		      dram_d       <= wdata_q[15:0];
		      dram_dqm     <= {2{p1.wrack}} & ~be_q[1:0];
		   end
		   3: begin
		      dram_d       <= wdata_q[31:16];
		      dram_dqm     <= {2{p1.wrack}} & ~be_q[3:2];
		   end
		   6: begin
		      // Earliest legal cycle to precharge
		      // It seems auto precharge violates tRAS(?)
		      // so do it explicitly.
		      dram_a[10]   <= 1'b1; // One bank
		      dram_cmd     <= cmd_pre;
		   end
		   // CL+2 cycles after the read command
		   // The +2 accounts for internal and I/O delays
		   7: begin
		      p1.rstrb[0] <= ~p1.wrack;
		   end
		   8: begin
		      p1.rstrb[1] <= ~p1.wrack;
		   end
		   9: begin
		      state <= st_idle;
		   end
		 endcase // case (op_cycle)
	      end // case: st_rd_wr

	    st_wr2:
	      begin
		 // Streamable write from flash ROM
		 dram_d      <= wd2;
		 dram_a[10]  <= 1'b0;  // No auto precharge/precharge one bank
		 dram_a[8:0] <= a2[9:1];

		 case (op_cycle)
		   0: begin
		      wacc2 <= 1'b1;
		   end
		   1: begin
		      wacc2 <= 1'b1;
		   end
		   2: begin
		      dram_cmd  <= cmd_wr;
		      wacc2     <= 1'b1;
		      wrq2_more <= wrq2[1];
		   end
		   3: begin
		      wacc2    <= 1'b1;
		   end
		   4: begin
		      dram_cmd  <= cmd_wr;
		      if (wrq2_more & ~(p1.req | do_rfsh))
			begin
			   // Burst can continue
			   wacc2       <= 1'b1;
			   op_ctr[3:0] <= 4'd1;
			end
		   end // case: 4
		   6: begin
		      dram_dqm    <= 2'b11; // This shouldn't be necessary?!
		   end
		   7: begin
		      // tWR completed
		      dram_cmd    <= cmd_pre;
		      dram_dqm    <= 2'b11;
		   end
		   8: begin
		      dram_dqm    <= 2'b11;
		   end
		   9: begin
		      // tRP will be complete in the next cycle
		      dram_dqm    <= 2'b11;
		      state <= st_idle;
		   end
		 endcase // case (op_cycle)
	      end // case: st_wr2
	  endcase // case(state)
       end // else: !if(~rst_n)
endmodule // dram
