/* * Simplifications: * * 1) There is no burst access. Currently there is 7 cycles from CE detect * to RDATA available and ACK wait, one additional cycle max to detect * CE, and one cycle for the enables to stabilize at the top layer. * This is 9 cycles total. Even at 8.33 MHz max ISA clock, we still * should be able to service memory accesses in a single ISA clock * period with a 80 MHz local clock reference. * 2) Loose limits of 64MB here, though easily changed * 3) Pages are always closed (w/ auto pre-charge) following every access * 4) Fixed 16-bit SDRAM width with two byte enables * 5) Synchronous scaler operation with system clock * 6) No buffering to SDRAM * - Depricated need based on host size = SDRAM size * - Writes hold the SDRAM in write state until bus cycle ends */ `define SDR_ADR_WIDTH 13 `define SDR_ROW_WIDTH 13 `define SDR_COL_WIDTH 10 `define SDR_BNK_WIDTH 2 // 40 MHz = 25 ns // 80 MHz = 12.5 ns // 100 MHz = 10ns // 140 MHz = 7.142857 ns // Additional state delays: `define SDR_tMRD 1 // 14 ns - Mode register program time `define SDR_tRC 5 // 70 ns - Command Period (REF to REF / ACT to ACT) `define SDR_tRP 1 // 20 ns - Command Period (PRE to ACT) `define SDR_tRCD 1 // 20 ns - Active command to read / write delay `define SDR_tDAL 2 // 35 ns - Input data to active / refresh (Command delay time) `define SDR_TREFI 625 // 7812.5 ns - Refresh cycle time `define T200US 16000 // Mode register setting /* * Burst Length: * 000 = 1 * 001 = 2 * 010 = 4 * 011 = 8 * 100 = Reserved * 101 = Reserved * 110 = Reserved * 111 = Full Page (sequential only) */ `define MR_Burst_Length 3'b000 /* * Burst Type: * 0 = Sequential * 1 = Interleaved */ `define MR_Burst_Type 1'b0 /* * CAS Latency: * 000 = Reserved * 001 = Reserved * 010 = 2 * 011 = 3 * 100 = Reserved * 101 = Reserved * 110 = Reserved * 111 = Reserved */ `define MR_CAS_Latency 3'b010 /* * Operating Mode: * 00 = Standard Operation * 01 = Reserved * 10 = Reserved * 11 = Reserved */ `define MR_Operation_Mode 2'b00 /* * Write Burst Mode: * 0 = Programmed Burst Length * 1 = Single Location Access */ `define MR_Write_Burst_Mode 1'b0 // Initialization FSM states `define iState_NOP 3'b000 `define iState_PRE 3'b001 `define iState_tRP 3'b010 `define iState_AR 3'b011 `define iState_tRC 3'b100 `define iState_MRS 3'b101 `define iState_tMRD 3'b110 `define iState_READY 3'b111 // Command FSM states `define cState_IDLE 4'b0000 `define cState_tRCD 4'b0001 `define cState_CL 4'b0010 `define cState_RDATA 4'b0011 `define cState_tRC 4'b0100 `define cState_tDAL 4'b0101 `define cState_ACTIVE 4'b0110 `define cState_READ 4'b0111 `define cState_WRITE 4'b1000 `define cState_AR 4'b1001 `define cState_tRP 4'b1011 `define cState_ACK 4'b1100 // SDRAM commands { pad_CSn, pad_RASn, pad_CASn, pad_WEn } `define cmd_INHIBIT 4'b1111 `define cmd_NOP 4'b0111 `define cmd_ACTIVE 4'b0011 `define cmd_READ 4'b0101 `define cmd_WRITE 4'b0100 `define cmd_BURST_STOP 4'b0110 `define cmd_PRECHARGE 4'b0010 `define cmd_AUTO_REFRESH 4'b0001 `define cmd_MODE_SET 4'b0000 module sdram ( input sdr_CLK, input sdr_RST, input [25:1] sdr_A, input [1:0] sdr_SEL, input [15:0] sdr_DAT_i, output [15:0] sdr_DAT_o, input sdr_WE, output sdr_ACK, input sdr_CE, inout [15:0] pad_DQ, output [12:0] pad_A, output [1:0] pad_BA, output pad_CKE, output pad_CSn, output pad_RASn, output pad_CASn, output pad_WEn, output [1:0] pad_DQM, output pad_CLK ); // FSM variables reg [2:0] iState; reg [3:0] cState; // Pad control reg sdram_CKE; reg [3:0] sdram_CMD; reg [1:0] sdram_BA; reg [12:0] sdram_A; reg [1:0] sdram_DQM; reg sdram_Den; assign pad_CKE = sdram_CKE; assign pad_CSn = sdram_CMD[3]; assign pad_RASn = sdram_CMD[2]; assign pad_CASn = sdram_CMD[1]; assign pad_WEn = sdram_CMD[0]; assign pad_BA = sdram_BA; assign pad_A = sdram_A; assign pad_DQM = sdram_DQM; assign pad_DQ = sdram_Den ? sdr_DAT_i : 16'bzzzz; // Inverting low delay path to pad using DDR driver ODDRXE sdr_clk_inst_inv ( .D0 (1'b0), .D1 (1'b1), .SCLK (sdr_CLK), .RST (sdr_RST), .Q (pad_CLK) ); /* * Relative to the outgoing clock, we present data on the * falling edge and the SDRAM part latches in and presents * on the rising edge. Thus we need to latch in the read * data on our (relative) falling edge in the middle of the * RDATA state exactly CAS Latency cycles after our READ * command assertion. */ reg [15:0] sdram_DATA; always @(negedge sdr_CLK) if (cState == `cState_RDATA) sdram_DATA <= pad_DQ; assign sdr_DAT_o = sdram_DATA; /* * General purpose counter used for counting off clock * tick delay necessary for the propagation from one * state in the FSM to the next. These delays are used * to satisfy SDRAM part timings. */ reg clk_RESET; reg [3:0] clk_COUNT; always @(posedge sdr_CLK) begin if (sdr_RST) clk_COUNT <= 0; else begin if (clk_RESET) clk_COUNT <= 0; else clk_COUNT <= clk_COUNT + 1; end end always @( * ) begin case (iState) `iState_tRP: clk_RESET = (clk_COUNT == `SDR_tRP - 1) ? 1 : 0; `iState_tMRD: clk_RESET = (clk_COUNT == `SDR_tMRD - 1) ? 1 : 0; `iState_tRC: clk_RESET = (clk_COUNT == `SDR_tRC - 1) ? 1 : 0; `iState_READY: begin case (cState) `cState_ACTIVE: clk_RESET = (clk_COUNT == `SDR_tRCD - 1) ? 1 : 0; `cState_tRCD: clk_RESET = (clk_COUNT == `SDR_tRCD - 1) ? 1 : 0; `cState_tRC: clk_RESET = (clk_COUNT == `SDR_tRC - 1) ? 1 : 0; `cState_CL: clk_RESET = (clk_COUNT == `MR_CAS_Latency - 1) ? 1 : 0; `cState_tRP: clk_RESET = (clk_COUNT == `SDR_tRP - 1) ? 1 : 0; `cState_tDAL: clk_RESET = (clk_COUNT == `SDR_tDAL - 1) ? 1 : 0; default: clk_RESET = 1; endcase end default: clk_RESET = 1; endcase end // Mandatory SDR startup state-machine delay reg sys_DLY_200US; reg [15:0] sys_dly_cnt; always @(posedge sdr_CLK) begin if (sdr_RST) begin sys_DLY_200US <= 1'b0; sys_dly_cnt <= `T200US; end else begin if (sys_dly_cnt == 0) begin sys_DLY_200US <= 1'b1; sys_dly_cnt <= sys_dly_cnt; end else begin sys_DLY_200US <= 1'b0; sys_dly_cnt <= sys_dly_cnt - 1; end end end /* * Initialization FSM * * Least common denominator of spec and vendor data sheets * say 200us of NOPs after clock and PSU stable, then PRECHARGE * followed by at least 8 auto refresh cycles before setting * the mode register */ reg [3:0] iAR; always @(posedge sdr_CLK) begin if (sdr_RST) begin iState <= `iState_NOP; iAR <= 4'b1000; end else begin case (iState) `iState_NOP: // wait for 200 us if (sys_DLY_200US) iState <= `iState_PRE; `iState_PRE: // precharge all iState <= `SDR_tRP ? `iState_tRP : `iState_AR; `iState_tRP: // wait until tRP satisfied if (clk_COUNT == `SDR_tRP - 1) iState <= `iState_AR; `iState_AR: // auto referesh if (iAR) begin iAR <= iAR - 1; iState <= `SDR_tRC ? `iState_tRC : `iState_AR; end else iState <= `SDR_tRC ? `iState_tRC : `iState_MRS; `iState_tRC: // wait until tRC satisfied if (clk_COUNT == `SDR_tRC - 1) iState <= (iAR) ? `iState_AR : `iState_MRS; `iState_MRS: // load mode register iState <= `SDR_tMRD ? `iState_tMRD : `iState_READY; `iState_tMRD: // wait until tMRD satisfied if (clk_COUNT == `SDR_tMRD - 1) iState <= `iState_READY; `iState_READY: // stay at this state for normal operation iState <= `iState_READY; default: iState <= `iState_NOP; endcase end end wire sys_INIT_DONE = (iState == `iState_READY) ? 1'b1 : 1'b0; // Auto refresh request reg refresh_REQ; reg [11:0] refresh_CNT; always @(posedge sdr_CLK) begin if (sdr_RST) begin refresh_CNT <= 0; refresh_REQ <= 0; end else begin if (cState == `cState_AR) begin refresh_CNT <= 0; refresh_REQ <= 0; end else if (refresh_CNT == `SDR_TREFI) refresh_REQ <= 1; else refresh_CNT <= refresh_CNT + 1; end end always @(posedge sdr_CLK) begin if (sdr_RST) cState <= `cState_IDLE; else begin case (cState) `cState_IDLE: // wait until refresh request or WB cycle if (sys_INIT_DONE & refresh_REQ) cState <= `cState_AR; else if (sys_INIT_DONE & sdr_CE) cState <= `cState_ACTIVE; `cState_ACTIVE: // assert row/bank addr if (`SDR_tRCD) cState <= `cState_tRCD; else cState <= sdr_WE ? `cState_WRITE : `cState_READ; `cState_tRCD: // wait until tRCD satisfied if (clk_COUNT == `SDR_tRCD - 1) cState <= sdr_WE ? `cState_WRITE : `cState_READ; `cState_READ: // assert col/bank addr for read with auto-precharge cState <= `cState_CL; `cState_CL: // CASn latency if (clk_COUNT == `MR_CAS_Latency - 1) cState <= `cState_RDATA; `cState_RDATA: // read cycle data phase cState <= `SDR_tRP ? `cState_tRP : `cState_ACK; `cState_tRP: // tRP latency if (clk_COUNT == `SDR_tRP - 1) cState <= `cState_ACK; `cState_WRITE: // assert col/bank addr for write with auto-precharge cState <= `SDR_tDAL ? `cState_tDAL : `cState_ACK; `cState_tDAL: // wait until (tWR + tRP) satisfied before issuing next if (clk_COUNT == `SDR_tDAL - 1) // tDAL cState <= `cState_ACK; `cState_ACK: cState <= sdr_CE ? `cState_ACK : `cState_IDLE; `cState_AR: // auto-refresh cState <= `SDR_tRC ? `cState_tRC : `cState_IDLE; `cState_tRC: // wait until tRC satisfied if (clk_COUNT == `SDR_tRC - 1) cState <= `cState_IDLE; default: cState <= `cState_IDLE; endcase end end assign sdr_ACK = (cState == `cState_ACK) ? 1'b1 : 1'b0; // SDRAM control signal generation parameter CA_LSB = 1; parameter CA_MSB = `SDR_COL_WIDTH + CA_LSB - 1; parameter BA_LSB = CA_MSB + 1; parameter BA_MSB = `SDR_BNK_WIDTH + BA_LSB - 1; parameter RA_LSB = BA_MSB + 1; parameter RA_MSB = `SDR_ROW_WIDTH + RA_LSB - 1; wire [`SDR_BNK_WIDTH-1:0] bnk_A = sdr_A[BA_MSB:BA_LSB]; wire [`SDR_ROW_WIDTH-1:0] row_A = sdr_A[RA_MSB:RA_LSB]; wire [`SDR_COL_WIDTH-1:0] col_A = sdr_A[CA_MSB:CA_LSB]; wire auto_PRE = 1'b1; // Auto-precharge after READ/WRITE burst always @(posedge sdr_CLK) begin if (sdr_RST) begin sdram_CMD <= `cmd_INHIBIT; sdram_CKE <= 0; sdram_BA <= {`SDR_BNK_WIDTH{1'b1}}; sdram_A <= {`SDR_ADR_WIDTH{1'b1}}; sdram_DQM <= 2'b11; sdram_Den <= 0; end else begin case (iState) `iState_tRP, `iState_tRC, `iState_tMRD, `iState_NOP: begin sdram_CMD <= `cmd_NOP; sdram_CKE <= 1; sdram_BA <= {`SDR_BNK_WIDTH{1'b1}}; sdram_A <= {`SDR_ADR_WIDTH{1'b1}}; sdram_DQM <= 2'b11; sdram_Den <= 0; end `iState_PRE: begin sdram_CMD <= `cmd_PRECHARGE; sdram_CKE <= 1; sdram_BA <= {`SDR_BNK_WIDTH{1'b1}}; sdram_A <= {`SDR_ADR_WIDTH{1'b1}}; sdram_DQM <= 2'b11; sdram_Den <= 0; end `iState_AR: begin sdram_CMD <= `cmd_AUTO_REFRESH; sdram_CKE <= 1; sdram_BA <= {`SDR_BNK_WIDTH{1'b1}}; sdram_A <= {`SDR_ADR_WIDTH{1'b1}}; sdram_DQM <= 2'b11; sdram_Den <= 0; end `iState_MRS: begin sdram_CMD <= `cmd_MODE_SET; sdram_CKE <= 1; sdram_BA <= {`SDR_BNK_WIDTH{1'b0}}; sdram_A <= { 3'b000, `MR_Write_Burst_Mode, `MR_Operation_Mode, `MR_CAS_Latency, `MR_Burst_Type, `MR_Burst_Length }; sdram_DQM <= 2'b11; sdram_Den <= 0; end `iState_READY: begin case (cState) `cState_IDLE, `cState_tRCD, `cState_tRC, `cState_CL, `cState_RDATA, `cState_tRP, `cState_ACK: begin sdram_CMD <= `cmd_NOP; sdram_CKE <= 1; sdram_BA <= {`SDR_BNK_WIDTH{1'b1}}; sdram_A <= {`SDR_ADR_WIDTH{1'b1}}; sdram_DQM <= 2'b11; sdram_Den <= 0; end `cState_ACTIVE: begin sdram_CMD <= `cmd_ACTIVE; sdram_CKE <= 1; sdram_BA <= bnk_A; sdram_A <= row_A; sdram_DQM <= 2'b11; sdram_Den <= 0; end `cState_READ: begin sdram_CMD <= `cmd_READ; sdram_CKE <= 1; sdram_BA <= bnk_A; sdram_A <= {2'b00, auto_PRE, col_A[9:0]}; sdram_DQM <= ~sdr_SEL; sdram_Den <= 0; end `cState_WRITE: begin sdram_CMD <= `cmd_WRITE; sdram_CKE <= 1; sdram_BA <= bnk_A; sdram_A <= {2'b00, auto_PRE, col_A[9:0]}; sdram_DQM <= ~sdr_SEL; sdram_Den <= 1; end `cState_AR: begin sdram_CMD <= `cmd_AUTO_REFRESH; sdram_CKE <= 1; sdram_BA <= {`SDR_BNK_WIDTH{1'b1}}; sdram_A <= {`SDR_ADR_WIDTH{1'b1}}; sdram_DQM <= 2'b11; sdram_Den <= 0; end default: begin sdram_CMD <= `cmd_NOP; sdram_CKE <= 1; sdram_BA <= {`SDR_BNK_WIDTH{1'b1}}; sdram_A <= {`SDR_ADR_WIDTH{1'b1}}; sdram_DQM <= 2'b11; sdram_Den <= 0; end endcase end default: begin sdram_CMD <= `cmd_NOP; sdram_CKE <= 1; sdram_BA <= {`SDR_BNK_WIDTH{1'b1}}; sdram_A <= {`SDR_ADR_WIDTH{1'b1}}; sdram_DQM <= 2'b11; sdram_Den <= 0; end endcase end end endmodule