| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646 | // -----------------------------------------------------------------------////   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;   tri   [24:0] addr;   logic	addr0;		// addr[0] latched at transaction start   logic [15:0] rd;   logic	req;   logic  [1:0]	rstrb;		// Data read strobe   tri   [31:0] wd;   tri    [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 DRAMmodule 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.wrack;	    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 + 15) >> 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   endgenerateendmodule // dram_portmodule 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] grant;   assign grant[0] = 1'b0;	// Dummy to make the below logic simpler   generate      genvar i;      for (i = 1; i <= port_count; i++)	begin : u	   assign ustr[i].rst_n = dstr.rst_n;	   assign ustr[i].clk   = dstr.clk;	   assign ustr[i].addr0 = dstr.addr0;	   assign ustr[i].rd    = dstr.rd;	   assign ustr[i].rstrb = dstr.rstrb;	   assign ustr[i].wrack = dstr.wrack;	   assign grant[i] = ~|grant[i-1:0] & ustr[i].req &			     (ustr[i].prio >= rfsh_prio);	   assign ustr[i].start = grant[i] & dstr.start;	   assign dstr.addr     = grant[i] ? ustr[i].addr  : 'bz;	   assign dstr.wd       = grant[i] ? ustr[i].wd    : 'bz;	   assign dstr.wstrb    = grant[i] ? ustr[i].wstrb : 'bz;	end // block: u   endgenerate   assign dstr.req =  |grant;   assign do_rfsh  = ~|grant & |rfsh_prio;endmodule // dram_arbitermodule 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	    init_tmr, // tRP timer	      input	    rfsh_tmr, // tREFI/2 timer	      // SDRAM hardware interface	      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];   // 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;   // Refresh timer logic   reg			    rfsh_tmr_q;   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 [3:0] {	st_reset,		// Reset until init timer expires	st_init_rfsh,		// Refresh cycles during initialization	st_init_mrd,		// MRD register write during initialization	st_ready,		// Ready to issue command in the next cycle	st_rfsh,		// Refresh cycle        st_rd_wr_act,		// Port 1 ACT command	st_rd_wr,		// Port 1 transaction	st_wr2_act,		// Port 2 write ACT command	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_tmr_q <= 1'b0;	  rfsh_prio  <= 2'b00;       end     else       begin	  rfsh_tmr_q         <= rfsh_tmr; // Edge detect	  // Refresh priority management: saturating 2-bit counter	  if (is_rfsh)	    rfsh_prio <= 2'b00; // This is a refresh cycle	  else if (rfsh_tmr & ~rfsh_tmr_q)	    rfsh_prio <= rfsh_prio + (~&rfsh_prio);       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 [24:0] addr;   reg	      wrq2_more;   wire [13:0] row_addr  = addr[24:12];   wire  [1:0] bank_addr = addr[11:10];   wire  [8:0] col_addr  = addr[9:1];   assign p1.addr0       = 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.start      <= 1'b0;	  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;	  addr          <= 25'bx;       end     else       begin	  // Default values	  dram_a        <= 13'b0;	  dram_ba       <= bank_addr;	  dram_dqm      <= 2'b00;	  dram_d        <= { 8'hAA, 3'b000, dram_cmd };	  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;	  op_ctr  <= op_ctr + 1'b1;	  op_zero <= &op_cycle; // About to wrap around	  p1.start      <= 1'b0;	  case (state)	    st_reset:	      begin		 op_ctr  <= 6'b0;		 op_zero <= 1'b0;		 dram_a[10] <= 1'b1; // Precharge all banks		 dram_cmd  <= cmd_nop;		 if (init_tmr)		   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_ready;		   else		     dram_cmd <= cmd_mrd;	      end	    st_ready:	      begin		 op_ctr  <= 6'b0;		 op_zero <= 1'b0;		 dram_cmd <= cmd_desl;		 p1.wrack <= 1'bx;		 be_q     <= 4'bxxxx;		 wdata_q  <= 32'hxxxx_xxxx;		 addr     <= 25'bx;		 dram_a  <= 13'h1bb;		 dram_d  <= 16'hbbbb;		 // Port 1 and refresh have priority over port 2;		 // the various port 1 instances and refresh have		 // priorities set by the arbiter block.		 if (do_rfsh)		   begin		      state        <= st_rfsh;		   end		 else if (p1.req)		   begin		      addr         <= p1.addr;		      p1.wrack     <= |p1.wstrb;		      wdata_q      <= p1.wd;		      be_q         <= p1.wstrb;		      state        <= st_rd_wr_act;		      p1.start     <= 1'b1;		   end // if (p1.req)		 else if (wrq2[0])		   begin		      // Begin port 2 write		      addr         <= { a2, 1'b0 };		      state        <= st_wr2_act;		   end	      end // case: st_ready	    st_rfsh: begin		 if (op_cycle == 0)		   dram_cmd    <= cmd_ref;		 else if (op_cycle == t_rfc-2)		   state <= st_ready;	      end	    st_rd_wr_act: begin	       op_ctr  <= 6'b0;	       op_zero <= 1'b0;	       dram_cmd <= cmd_act;	       dram_a   <= row_addr;	       dram_ba  <= bank_addr;	       state    <= st_rd_wr;	    end	    st_rd_wr:	      begin		 dram_d_en <= p1.wrack;		 dram_dqm  <= {2{p1.wrack}};		 dram_d    <= 16'hcccc ^ {16{p1.wrack}};		 // 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;		      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;		      state <= st_ready;		   end		 endcase // case (op_cycle)	      end // case: st_rd_wr	    st_wr2_act:	      begin		 op_ctr   <= 6'b0;		 op_zero  <= 1'b0;		 dram_a   <= row_addr;		 dram_ba  <= bank_addr;		 dram_cmd <= cmd_act;		 state    <= st_wr2;	      end	    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, 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		      // tRP will be complete before the next ACT		      dram_dqm    <= 2'b11;		      state <= st_ready;		   end		 endcase // case (op_cycle)	      end // case: st_wr2	  endcase // case(state)       end // else: !if(~rst_n)endmodule // dram
 |