123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579 |
- // -----------------------------------------------------------------------
- //
- // 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 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.
- //
- // 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.
- //
- module sdram
- #( parameter
- // 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 0: single byte, high priority
- input [24:0] a0, // Address, must be stable until ack
- output reg [7:0] rd0, // Data from SDRAM
- input rrq0, // Read request
- output reg rack0, // Read ack (transaction started)
- output reg rready0, // Read data valid
- input [7:0] wd0, // Data to SDRAM
- input wrq0, // Write request
- output reg wack0, // Write ack (data latched)
- // Port 1
- input [24:2] a1,
- output reg [31:0] rd1,
- input rrq1,
- output reg rack1,
- output reg rready1,
- input [31:0] wd1,
- input [3:0] wstrb1,
- output reg wack1,
- // 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
- wire wrq1 = |wstrb1;
-
- // 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 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
- // Bit 0 - refresh if opportune
- // Bit 1 - refresh urgent
- // 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_pre_idle,
- st_wr2 // Port 2 write (burstable)
- } state_t;
- state_t state = st_reset;
- reg is_write;
-
- 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
- if (is_rfsh)
- rfsh_prio <= 2'b00; // This is a refresh cycle
- else if (rfsh_tick)
- rfsh_prio <= { rfsh_prio[0], 1'b1 };
- // 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;
- //
- // 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_d_en <= 1'b1; // Don't float except during read
- op_ctr <= 6'h0;
- op_zero <= 1'b0;
- state <= st_reset;
- is_write <= 1'bx;
- rack0 <= 1'b0;
- rready0 <= 1'b1;
- wack0 <= 1'b0;
- rack1 <= 1'b0;
- rready1 <= 1'b1;
- wack1 <= 1'b0;
- 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'hxxxx;
- dram_dqm <= 2'b00;
- dram_d <= 16'haaaa;
- dram_cmd <= cmd_nop;
- dram_d_en <= 1'b1; // Don't float except during read
- if (state != st_rd_wr)
- begin
- rack0 <= 1'b0;
- wack0 <= 1'b0;
- rack1 <= 1'b0;
- wack1 <= 1'b0;
- end
- 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
- is_write <= 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;
-
- casez ( {rrq0|wrq0, rrq1|wrq1, wrq2[0], rfsh_prio} )
- 5'b1????:
- begin
- // Begin port 0 transaction
- dram_cmd <= cmd_act;
- dram_a <= a0[24:12];
- dram_ba <= a0[11:10];
- col_addr <= a0[9:0];
- if ( wrq0 )
- begin
- state <= st_rd_wr;
- wack0 <= 1'b1;
- wdata_q <= {16'hxxxx, wd0, wd0};
- be_q <= {2'b00, a0[0], ~a0[0]};
- is_write <= 1'b1;
- end
- else
- begin
- state <= st_rd_wr;
- rack0 <= 1'b1;
- rready0 <= 1'b0;
- is_write <= 1'b0;
- end
- end
- 5'b01?0?:
- begin
- // Begin port 1 transaction
- dram_cmd <= cmd_act;
- dram_a <= a1[24:12];
- dram_ba <= a1[11:10];
- col_addr <= { a1[9:2], 2'b00 };
- if ( wrq1 )
- begin
- state <= st_rd_wr;
- wack1 <= 1'b1;
- wdata_q <= wd1;
- be_q <= wstrb1;
- is_write <= 1'b1;
- end
- else
- begin
- state <= st_rd_wr;
- rack1 <= 1'b1;
- rready1 <= 1'b0;
- is_write <= 1'b0;
- end
- end
- 5'b0??1?, 5'b00?01:
- begin
- // Begin refresh transaction
- dram_cmd <= cmd_ref;
- state <= st_rfsh;
- end
- 5'b00100:
- begin
- // Begin port 2 write
- dram_cmd <= cmd_act;
- dram_a <= a2[24:12];
- dram_ba <= a2[11:10];
- state <= st_wr2;
- end
- default:
- begin
- dram_cmd <= cmd_desl;
- state <= st_idle;
- end
- endcase // casez ( {rrq0|wrq0, rrq1|wrq1, rfsh_prio} )
- end // case: st_idle
- st_rfsh:
- begin
- if (op_cycle == t_rfc-2)
- state <= st_idle;
- end
- st_rd_wr:
- begin
- dram_d_en <= is_write;
- dram_dqm <= {2{is_write}};
- dram_d <= 16'hcccc;
- // 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 <= is_write ? cmd_wr : cmd_rd;
- dram_d <= wdata_q[15:0];
- dram_dqm <= {2{is_write}} & ~be_q[1:0];
- wdata_q <= { 16'hdddd, wdata_q[31:16] };
- be_q <= { 2'hxx, be_q[3:2] };
- end
- 3: begin
- dram_d <= wdata_q[15:0];
- dram_dqm <= {2{is_write}} & ~be_q[1:0];
- wdata_q <= { 16'heeee, wdata_q[31:16] };
- be_q <= 4'bxxxx;
- 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
- if (rack0)
- rd0 <= col_addr[0] ? sr_dq[15:8] : sr_dq[7:0];
- rready0 <= rready0 | rack0;
- if (rack1)
- rd1[15:0] <= sr_dq;
- end
- 8: begin
- if (rack1)
- rd1[31:16] <= sr_dq;
- rready1 <= rready1 | rack1;
- state <= st_pre_idle;
- end
- endcase // case (op_cycle)
- end // case: st_rd_wr
- st_pre_idle:
- begin
- // Last cycle before tRC is a separate state
- // so that rack/wack will be cleared
- dram_d_en <= is_write;
- dram_dqm <= {2{is_write}};
- state <= st_idle;
- 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: 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 &
- ~(rrq0|wrq0|rrq1|wrq1|(|rfsh_prio)|(&dram_a[8:2])))
- 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
|