123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466 |
- // -----------------------------------------------------------------------
- //
- // 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 0 is single byte per transaction,
- // and has highest priority; it is intended for transactions from the
- // ABC-bus. Port 1 does 8-strobe burst transactions. If additional ports
- // are needed, the intent is to add an arbiter to port 1.
- //
- // All signals are in the sdram clock domain.
- //
- module sdram (
- // Reset and clock
- input rst_n,
- input clk,
- // 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 0: single byte, high priority
- input [24:0] a0, // Address, must be stable until ack
- output [7:0] rd0, // Data from SDRAM
- input rrq0, // Read request
- output rack0, // Read ack
- input [7:0] wd0, // Data to SDRAM
- input wrq0, // Write request
- output wack0, // Write ack
- // Port 1
- input [24:1] a1,
- output [15:0] rd1,
- input rrq1,
- output rack1,
- input [15:0] wd1,
- input [1:0] wbe1, // Write byte enable
- input wrq1,
- output wack1
- );
- // 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
- // 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:
- // tREF 64/8192 ms 781 1302 Refresh time per row (max)
- // tP 100 us 10000 16667 Time until first command (min)
- parameter t_cl = 3;
- parameter t_rcd = 3;
- parameter t_rfc = 10;
- parameter t_rp = 3;
- parameter t_ras = 7;
- parameter t_rc = 10;
- parameter t_wr = 2;
- parameter t_mrd = 2;
- parameter t_ref = 9; // 512 cycles (extra conservative)
- parameter t_p = 15; // 32768 cycles
- parameter burst = 3; // 8-cycle bursts
- // Mode register data
- wire mrd_wburst = 1'b1; // Write bursts enabled
- wire [2:0] mrd_cl = t_cl;
- wire [2:0] mrd_burst = burst;
- 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
- // Handy functions for timing calculations
- function int max(input int a, b);
- if (a > b)
- max = a;
- else
- max = b;
- endfunction // max
- function int nops(input int need, elapsed);
- if (need > elapsed + 1)
- nops = need - (elapsed+1);
- else
- nops = 0;
- endfunction // nops
- // Command opcodes (CS#, RAS#, CAS#, WE#)
- parameter cmd_desl = 4'b1111; // Deselect (= NOP)
- parameter cmd_nop = 4'b0111; // NO OPERATION
- parameter cmd_bst = 4'b0110; // BURST TERMINATE
- parameter cmd_rd = 4'b0101; // READ
- parameter cmd_wr = 4'b0100; // WRITE
- parameter cmd_act = 4'b0011; // ACTIVE
- parameter cmd_pre = 4'b0010; // PRECHARGE
- parameter cmd_ref = 4'b0001; // AUTO REFRESH
- parameter cmd_mrd = 4'b0000; // LOAD MODE REGISTER
- // 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.
- ddio_out sr_clk_out (
- .aclr ( 1'b0 ),
- .datain_h ( 1'b0 ),
- .datain_l ( 1'b1 ),
- .outclock ( clk ),
- .dataout ( sr_clk )
- );
-
- // SDRAM output signal registers
- reg dram_cke;
- assign sr_cke = dram_cke;
- reg [3:0] dram_cmd;
- 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];
- reg [11: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 dram_d_en; // Drive data out
- assign sr_dq = dram_d_en ? dram_d : 16'hzzzz;
- // Input register for SDRAM data; a single register to make it
- // possible to put it in the I/O register
- reg [15:0] dram_q;
- // Port 0 output signals
- assign rd0 = a0[0] ? dram_q[15:8] : dram_q[7:0];
- reg rack0_q;
- assign rack0 = rack0_q;
- reg wack0_q;
- assign wack0 = wack0_q;
- // Port 1 output signals
- assign rd1 = dram_q;
- reg rack1_q;
- assign rack1 = rack1_q;
- reg wack1_q;
- assign wack1 = wack1_q;
- // State machine and counters
- reg [t_ref:0] rfsh_ctr; // Refresh timer
- reg [t_p:t_ref] init_ctr; // Initialization counter
- reg [3:0] nop_ctr; // Insert NOPs into a cycle
- reg [burst-1:0] burst_ctr; // Position in the current burst
- reg [3:0] state;
- parameter st_reset = 4'h00; // Reset until init timer expires
- parameter st_init_rfsh1 = 4'h01; // 1st refresh during initialization
- parameter st_init_rfsh2 = 4'h02; // 2st refresh during initialization
- parameter st_init_mrd = 4'h03;
- parameter st_idle = 4'h04; // Idle state: all banks precharged
- parameter st_p0_rd_cmd = 4'h06;
- parameter st_p0_rd_pre = 4'h07;
- parameter st_p0_rd_data = 4'h08;
- parameter st_p0_wr_cmd = 4'h09;
- parameter st_p0_wr_pre = 4'h0a;
- parameter st_p1_rd_cmd = 4'h0b;
- parameter st_p1_rd_pre = 4'h0c;
- parameter st_p1_rd_data = 4'h0d;
- parameter st_p1_wr_cmd = 4'h0e;
- parameter st_p1_wr_data = 4'h0f;
- reg is_rfsh; // A refresh cycle, clear rfsh_ctr
- always @(posedge clk or negedge rst_n)
- if (~rst_n)
- begin
- rfsh_ctr <= 1'b0;
- init_ctr <= 1'b0;
- end
- else
- begin
- if (is_rfsh)
- rfsh_ctr <= 1'b0;
- else
- rfsh_ctr <= rfsh_ctr + 1'b1;
- // The refresh counter is also used as a prescaler
- // for the initialization counter.
- if (init_ctr[t_ref] ^ rfsh_ctr[t_ref])
- begin
- init_ctr[t_p:t_ref+1] <= init_ctr[t_p:t_ref+1] + 1'b1;
- init_ctr[t_ref] <= rfsh_ctr[t_ref];
- end
- end // else: !if(~rst_n)
- //
- // 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.
- //
- always @(posedge clk or negedge rst_n)
- if (~rst_n)
- begin
- dram_cke <= 1'b0;
- dram_cmd <= cmd_desl;
- dram_a <= 13'h0000;
- dram_ba <= 2'b00;
- dram_dqm <= 2'b00;
- dram_d <= 16'h0000;
- dram_d_en <= 1'b1; // Don't float except during read
- nop_ctr <= 4'd0;
- burst_ctr <= 1'b0;
- state <= st_reset;
- is_rfsh <= 1'b0;
- rack0_q <= 1'b0;
- wack0_q <= 1'b0;
- rack1_q <= 1'b0;
- wack1_q <= 1'b0;
- end
- else
- begin
- dram_cke <= 1'b1; // Always true once out of reset
- // Default values
- dram_a <= 13'h0000;
- dram_ba <= 2'b00;
- dram_dqm <= (state == st_p0_wr_pre) ? 2'b11 : 2'b00;
- dram_d <= 16'h0000;
- is_rfsh <= 1'b0;
- rack0_q <= 1'b0;
- wack0_q <= 1'b0;
- rack1_q <= 1'b0;
- wack1_q <= 1'b0;
- if (|nop_ctr)
- begin
- dram_cmd <= cmd_nop;
- nop_ctr <= nop_ctr - 1'b1;
- end
- else
- begin
- nop_ctr <= 1'b0; // Redundant, but might help compiler
- dram_d_en <= 1'b1;
- burst_ctr <= 1'b0;
- case (state)
- st_reset:
- begin
- dram_cmd <= cmd_desl;
- if (init_ctr[t_p])
- begin
- dram_cmd <= cmd_pre;
- dram_a[10] <= 1'b1; // Precharge All Banks
- state <= st_init_rfsh1;
- nop_ctr <= nops(t_rp, 0);
- end
- end
- st_init_rfsh1:
- begin
- dram_cmd <= cmd_ref;
- state <= st_init_rfsh2;
- nop_ctr <= nops(t_rfc, 0);
- end
- st_init_rfsh2:
- begin
- dram_cmd <= cmd_ref;
- state <= st_init_mrd;
- nop_ctr <= nops(t_rfc, 0);
- end
- st_init_mrd:
- begin
- dram_cmd <= cmd_mrd;
- dram_a <= mrd_val;
- state <= st_idle;
- nop_ctr <= nops(t_mrd, 0);
- end
- st_idle:
- begin
- // 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.
- casez ( {rrq0|wrq0, rrq1|wrq1, rfsh_ctr[t_ref:t_ref-1]} )
- 4'b1zzz:
- begin
- // Begin port 0 transaction
- dram_cmd <= cmd_act;
- dram_a <= a0[24:12];
- dram_ba <= a0[11:10];
- state <= wrq0 ? st_p0_wr_cmd : st_p0_rd_cmd;
- nop_ctr <= nops(t_rcd, 0);
- end
- 4'b010z:
- begin
- // Begin port 1 transaction
- dram_cmd <= cmd_act;
- dram_a <= a1[24:12];
- dram_ba <= a1[11:10];
- state <= wrq1 ? st_p1_wr_cmd : st_p1_rd_cmd;
- nop_ctr <= nops(t_rcd, 0);
- end
- 4'b0z1z, 4'b0001:
- begin
- // Begin refresh transaction
- dram_cmd <= cmd_ref;
- state <= st_idle;
- nop_ctr <= nops(t_rfc, 0);
- is_rfsh <= 1'b1;
- end
- 4'b0000:
- begin
- dram_cmd <= cmd_desl;
- state <= st_idle;
- end
- endcase // casez ( {rrq0|wrq0, rrq1|wrq1, rfsh_ctr[t_ref:t_ref-1]} )
- end // case: st_idle
- st_p0_rd_cmd:
- begin
- dram_cmd <= cmd_rd;
- dram_d_en <= 1'b0; // Tristate our output
- dram_a[8:0] <= a0[9:1];
- dram_ba <= a0[11:10];
- state <= st_p0_rd_pre;
- end
- st_p0_rd_pre:
- begin
- // The PRE command issued in the cycle immediately
- // after the read will terminate the burst after
- // exactly one data strobe. Note that the nop_ctr
- // below is CL+1, not CL, due to bus turnaround.
- dram_cmd <= cmd_pre;
- dram_d_en <= 1'b0; // Tristate our output
- dram_ba <= a0[11:10];
- state <= st_p0_rd_data;
- nop_ctr <= nops(t_cl + 1, 1);
- end
- st_p0_rd_data:
- begin
- // The nop_ctr elapsed calculation has:
- // +1 for the bus turnaround cycle added previously
- dram_cmd <= cmd_nop;
- dram_d_en <= 1'b0; // Tristate one more cycle
- dram_q <= sr_dq;
- rack0_q <= 1'b1;
- state <= st_idle;
- nop_ctr <= max(nops(t_rp, t_cl + 1),
- nops(t_rc, t_rcd + t_cl + 1));
- end
- st_p0_wr_cmd:
- begin
- dram_cmd <= cmd_wr;
- dram_a[8:0] <= a0[9:1];
- dram_ba <= a0[11:10];
- dram_dqm <= { ~a0[0], a0[0] };
- dram_d <= { wd0, wd0 };
- wack0_q <= 1'b1;
- state <= st_p0_wr_pre;
- nop_ctr <= nops(t_wr, 0);
- end
- st_p0_wr_pre:
- begin
- dram_cmd <= cmd_pre;
- nop_ctr <= max(nops(t_rp, 0),
- nops(t_rc, t_rcd + t_wr));
- state <= st_idle;
- end
- st_p1_rd_cmd:
- begin
- dram_cmd <= cmd_rd;
- dram_d_en <= 1'b0; // Tristate our output
- dram_a[8:0] <= a1[9:1];
- dram_ba <= a1[11:10];
- dram_a[10] <= 1'b1; // Auto precharge
- nop_ctr <= nops(t_cl + 1, 0);
- state <= st_p1_rd_data;
- end
- st_p1_rd_data:
- begin
- dram_cmd <= cmd_nop;
- dram_d_en <= 1'b0; // Tristate our output
- dram_q <= sr_dq;
- rack1_q <= 1'b1;
- burst_ctr <= burst_ctr + 1'b1;
- if (&burst_ctr)
- begin
- state <= st_idle;
- nop_ctr <= max(nops(t_rp, t_cl + (1 << burst)),
- nops(t_rc, t_rcd + t_cl + (1 << burst)));
- end
- end
- st_p1_wr_cmd:
- begin
- dram_cmd <= cmd_wr;
- dram_a[8:0] <= a1[9:1];
- dram_ba <= a1[11:10];
- dram_a[10] <= 1'b1; // Auto precharge
- dram_d <= wd1;
- dram_dqm <= ~wbe1;
- wack1_q <= 1'b1;
- burst_ctr <= burst_ctr + 1'b1;
- state <= st_p1_wr_data;
- end
- st_p1_wr_data:
- begin
- dram_cmd <= cmd_nop;
- dram_d <= wd1;
- dram_dqm <= ~wbe1;
- wack1_q <= 1'b1;
- burst_ctr <= burst_ctr + 1'b1;
- if (&burst_ctr)
- begin
- state <= st_idle;
- nop_ctr <= max(nops(t_wr + t_rp, 0),
- nops(t_rc, t_rcd + (1 << burst)));
- end
- end
- endcase // case(state)
- end // else: !if(|nop_ctr)
- end // else: !if(~rst_n)
- endmodule // dram
|