| ////////////////////////////////////////////////////////////////////////////// |
| // SPDX-FileCopyrightText: 2021 , Dinesh Annayya |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // SPDX-License-Identifier: Apache-2.0 |
| // SPDX-FileContributor: Created by Dinesh Annayya <dinesha@opencores.org> |
| // |
| /********************************************************************* |
| |
| SDRAM Controller Transfer control |
| |
| This file is part of the sdram controller project |
| https://github.com/dineshannayya/yifive_r0.git |
| http://www.opencores.org/cores/yifive/ |
| http://www.opencores.org/cores/sdr_ctrl/ |
| |
| Description: SDRAM Controller Transfer control |
| |
| This module takes requests from sdrc_bank_ctl and runs the |
| transfer. The input request is guaranteed to be in a bank that is |
| precharged and activated. This block runs the transfer until a |
| burst boundary is reached, then issues another read/write command |
| to sequentially step thru memory if wrap=0, until the transfer is |
| completed. |
| |
| if a read transfer finishes and the caddr is not at a burst boundary |
| a burst terminate command is issued unless another read/write or |
| precharge to the same bank is pending. |
| |
| if a write transfer finishes and the caddr is not at a burst boundary |
| a burst terminate command is issued unless a read/write is pending. |
| |
| If a refresh request is made, the bank_ctl will be held off until |
| the number of refreshes requested are completed. |
| |
| This block also handles SDRAM initialization. |
| |
| |
| To Do: |
| nothing |
| |
| Author(s): |
| - Dinesh Annayya, dinesha@opencores.org |
| Version : 1.0 - 8th Jan 2012 |
| |
| |
| |
| Copyright (C) 2000 Authors and OPENCORES.ORG |
| |
| This source file may be used and distributed without |
| restriction provided that this copyright statement is not |
| removed from the file and that any derivative work contains |
| the original copyright notice and the associated disclaimer. |
| |
| This source file is free software; you can redistribute it |
| and/or modify it under the terms of the GNU Lesser General |
| Public License as published by the Free Software Foundation; |
| either version 2.1 of the License, or (at your option) any |
| later version. |
| |
| This source is distributed in the hope that it will be |
| useful, but WITHOUT ANY WARRANTY; without even the implied |
| warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
| PURPOSE. See the GNU Lesser General Public License for more |
| details. |
| |
| You should have received a copy of the GNU Lesser General |
| Public License along with this source; if not, download it |
| from http://www.opencores.org/lgpl.shtml |
| |
| *******************************************************************/ |
| |
| `include "sdrc_define.v" |
| |
| module sdrc_xfr_ctl (clk, |
| reset_n, |
| |
| /* Transfer request from bank_ctl */ |
| r2x_idle, // Req is idle |
| b2x_idle, // All banks are idle |
| b2x_req, // Req from bank_ctl |
| b2x_start, // first chunk of transfer |
| b2x_last, // last chunk of transfer |
| b2x_id, // Transfer ID |
| b2x_ba, // bank address |
| b2x_addr, // row/col address |
| b2x_len, // transfer length |
| b2x_cmd, // transfer command |
| b2x_wrap, // Wrap mode transfer |
| x2b_ack, // command accepted |
| |
| /* Status to bank_ctl, req_gen */ |
| b2x_tras_ok, // Tras for all banks expired |
| x2b_refresh, // We did a refresh |
| x2b_pre_ok, // OK to do a precharge (per bank) |
| x2b_act_ok, // OK to do an activate |
| x2b_rdok, // OK to do a read |
| x2b_wrok, // OK to do a write |
| |
| /* SDRAM I/O */ |
| sdr_cs_n, |
| sdr_cke, |
| sdr_ras_n, |
| sdr_cas_n, |
| sdr_we_n, |
| sdr_dqm, |
| sdr_ba, |
| sdr_addr, |
| sdr_din, |
| sdr_dout, |
| sdr_den_n, |
| |
| /* Data Flow to the app */ |
| x2a_rdstart, |
| x2a_wrstart, |
| x2a_rdlast, |
| x2a_wrlast, |
| x2a_id, |
| a2x_wrdt, |
| a2x_wren_n, |
| x2a_wrnext, |
| x2a_rddt, |
| x2a_rdok, |
| sdr_init_done, |
| |
| /* SDRAM Parameters */ |
| sdram_enable, |
| sdram_mode_reg, |
| |
| /* output for generate row address of the transfer */ |
| xfr_bank_sel, |
| |
| /* SDRAM Timing */ |
| cas_latency, |
| trp_delay, // Precharge to refresh delay |
| trcar_delay, // Auto-refresh period |
| twr_delay, // Write recovery delay |
| rfsh_time, // time per row (31.25 or 15.6125 uS) |
| rfsh_rmax); // Number of rows to rfsh at a time (<120uS) |
| |
| parameter SDR_DW = 16; // SDR Data Width |
| parameter SDR_BW = 2; // SDR Byte Width |
| |
| |
| input clk, reset_n; |
| |
| /* Req from bank_ctl */ |
| input b2x_req, b2x_start, b2x_last, b2x_tras_ok, |
| b2x_wrap, r2x_idle, b2x_idle; |
| input [`SDR_REQ_ID_W-1:0] b2x_id; |
| input [1:0] b2x_ba; |
| input [12:0] b2x_addr; |
| input [`REQ_BW-1:0] b2x_len; |
| input [1:0] b2x_cmd; |
| output x2b_ack; |
| |
| /* Status to bank_ctl */ |
| output [3:0] x2b_pre_ok; |
| output x2b_refresh, x2b_act_ok, x2b_rdok, |
| x2b_wrok; |
| /* Data Flow to the app */ |
| output x2a_rdstart, x2a_wrstart, x2a_rdlast, x2a_wrlast; |
| output [`SDR_REQ_ID_W-1:0] x2a_id; |
| |
| input [SDR_DW-1:0] a2x_wrdt; |
| input [SDR_BW-1:0] a2x_wren_n; |
| output [SDR_DW-1:0] x2a_rddt; |
| output x2a_wrnext, x2a_rdok, sdr_init_done; |
| |
| /* Interface to SDRAMs */ |
| output sdr_cs_n, sdr_cke, sdr_ras_n, sdr_cas_n, |
| sdr_we_n; |
| output [SDR_BW-1:0] sdr_dqm; |
| output [1:0] sdr_ba; |
| output [12:0] sdr_addr; |
| input [SDR_DW-1:0] sdr_din; |
| output [SDR_DW-1:0] sdr_dout; |
| output [SDR_BW-1:0] sdr_den_n; |
| |
| output [1:0] xfr_bank_sel; |
| |
| input sdram_enable; |
| input [12:0] sdram_mode_reg; |
| input [2:0] cas_latency; |
| input [3:0] trp_delay, trcar_delay, twr_delay; |
| input [`SDR_RFSH_TIMER_W-1 : 0] rfsh_time; |
| input [`SDR_RFSH_ROW_CNT_W-1:0] rfsh_rmax; |
| |
| |
| /************************************************************************/ |
| // Internal Nets |
| |
| `define XFR_IDLE 2'b00 |
| `define XFR_WRITE 2'b01 |
| `define XFR_READ 2'b10 |
| `define XFR_RDWT 2'b11 |
| |
| reg [1:0] xfr_st, next_xfr_st; |
| reg [12:0] xfr_caddr; |
| wire last_burst; |
| wire x2a_rdstart, x2a_wrstart, x2a_rdlast, x2a_wrlast; |
| reg l_start, l_last, l_wrap; |
| wire [`SDR_REQ_ID_W-1:0] x2a_id; |
| reg [`SDR_REQ_ID_W-1:0] l_id; |
| wire [1:0] xfr_ba; |
| reg [1:0] l_ba; |
| wire [12:0] xfr_addr; |
| wire [`REQ_BW-1:0] xfr_len, next_xfr_len; |
| reg [`REQ_BW-1:0] l_len; |
| |
| reg mgmt_idle, mgmt_req; |
| reg [3:0] mgmt_cmd; |
| reg [12:0] mgmt_addr; |
| reg [1:0] mgmt_ba; |
| |
| reg sel_mgmt, sel_b2x; |
| reg cb_pre_ok, rdok, wrok, wr_next, |
| rd_next, sdr_init_done, act_cmd, d_act_cmd; |
| wire [3:0] b2x_sdr_cmd, xfr_cmd; |
| reg [3:0] i_xfr_cmd; |
| wire mgmt_ack, x2b_ack, b2x_read, b2x_write, |
| b2x_prechg, d_rd_next, dt_next, xfr_end, |
| rd_pipe_mt, ld_xfr, rd_last, d_rd_last, |
| wr_last, l_xfr_end, rd_start, d_rd_start, |
| wr_start, page_hit, burst_bdry, xfr_wrap, |
| b2x_prechg_hit; |
| reg [6:0] l_rd_next, l_rd_start, l_rd_last; |
| |
| assign b2x_read = (b2x_cmd == `OP_RD) ? 1'b1 : 1'b0; |
| |
| assign b2x_write = (b2x_cmd == `OP_WR) ? 1'b1 : 1'b0; |
| |
| assign b2x_prechg = (b2x_cmd == `OP_PRE) ? 1'b1 : 1'b0; |
| |
| assign b2x_sdr_cmd = (b2x_cmd == `OP_PRE) ? `SDR_PRECHARGE : |
| (b2x_cmd == `OP_ACT) ? `SDR_ACTIVATE : |
| (b2x_cmd == `OP_RD) ? `SDR_READ : |
| (b2x_cmd == `OP_WR) ? `SDR_WRITE : `SDR_DESEL; |
| |
| assign page_hit = (b2x_ba == l_ba) ? 1'b1 : 1'b0; |
| |
| assign b2x_prechg_hit = b2x_prechg & page_hit; |
| |
| assign xfr_cmd = (sel_mgmt) ? mgmt_cmd : |
| (sel_b2x) ? b2x_sdr_cmd : i_xfr_cmd; |
| |
| assign xfr_addr = (sel_mgmt) ? mgmt_addr : |
| (sel_b2x) ? b2x_addr : xfr_caddr+1; |
| |
| assign mgmt_ack = sel_mgmt; |
| |
| assign x2b_ack = sel_b2x; |
| |
| assign ld_xfr = sel_b2x & (b2x_read | b2x_write); |
| |
| assign xfr_len = (ld_xfr) ? b2x_len : l_len; |
| |
| //assign next_xfr_len = (l_xfr_end && !ld_xfr) ? l_len : xfr_len - 1; |
| assign next_xfr_len = (ld_xfr) ? b2x_len : |
| (l_xfr_end) ? l_len: l_len - 1; |
| |
| assign d_rd_next = (cas_latency == 3'b001) ? l_rd_next[2] : |
| (cas_latency == 3'b010) ? l_rd_next[3] : |
| (cas_latency == 3'b011) ? l_rd_next[4] : |
| (cas_latency == 3'b100) ? l_rd_next[5] : |
| l_rd_next[6]; |
| |
| assign d_rd_last = (cas_latency == 3'b001) ? l_rd_last[2] : |
| (cas_latency == 3'b010) ? l_rd_last[3] : |
| (cas_latency == 3'b011) ? l_rd_last[4] : |
| (cas_latency == 3'b100) ? l_rd_last[5] : |
| l_rd_last[6]; |
| |
| assign d_rd_start = (cas_latency == 3'b001) ? l_rd_start[2] : |
| (cas_latency == 3'b010) ? l_rd_start[3] : |
| (cas_latency == 3'b011) ? l_rd_start[4] : |
| (cas_latency == 3'b100) ? l_rd_start[5] : |
| l_rd_start[6]; |
| |
| assign rd_pipe_mt = (cas_latency == 3'b001) ? ~|l_rd_next[1:0] : |
| (cas_latency == 3'b010) ? ~|l_rd_next[2:0] : |
| (cas_latency == 3'b011) ? ~|l_rd_next[3:0] : |
| (cas_latency == 3'b100) ? ~|l_rd_next[4:0] : |
| ~|l_rd_next[5:0]; |
| |
| assign dt_next = wr_next | d_rd_next; |
| |
| assign xfr_end = ~|xfr_len; |
| |
| assign l_xfr_end = ~|(l_len-1); |
| |
| assign rd_start = ld_xfr & b2x_read & b2x_start; |
| |
| assign wr_start = ld_xfr & b2x_write & b2x_start; |
| |
| assign rd_last = rd_next & last_burst & ~|xfr_len[`REQ_BW-1:1]; |
| |
| //assign wr_last = wr_next & last_burst & ~|xfr_len[APP_RW-1:1]; |
| |
| assign wr_last = last_burst & ~|xfr_len[`REQ_BW-1:1]; |
| |
| //assign xfr_ba = (ld_xfr) ? b2x_ba : l_ba; |
| assign xfr_ba = (sel_mgmt) ? mgmt_ba : |
| (sel_b2x) ? b2x_ba : l_ba; |
| |
| assign xfr_wrap = (ld_xfr) ? b2x_wrap : l_wrap; |
| |
| // assign burst_bdry = ~|xfr_caddr[2:0]; |
| wire [1:0] xfr_caddr_lsb = (xfr_caddr[1:0]+1); |
| assign burst_bdry = ~|(xfr_caddr_lsb[1:0]); |
| |
| always @ (posedge clk) begin |
| if (~reset_n) begin |
| xfr_caddr <= 13'b0; |
| l_start <= 1'b0; |
| l_last <= 1'b0; |
| l_wrap <= 1'b0; |
| l_id <= 0; |
| l_ba <= 0; |
| l_len <= 0; |
| l_rd_next <= 7'b0; |
| l_rd_start <= 7'b0; |
| l_rd_last <= 7'b0; |
| act_cmd <= 1'b0; |
| d_act_cmd <= 1'b0; |
| xfr_st <= `XFR_IDLE; |
| end // if (~reset_n) |
| |
| else begin |
| xfr_caddr <= (ld_xfr) ? b2x_addr : |
| (rd_next | wr_next) ? xfr_caddr + 1 : xfr_caddr; |
| l_start <= (dt_next) ? 1'b0 : |
| (ld_xfr) ? b2x_start : l_start; |
| l_last <= (ld_xfr) ? b2x_last : l_last; |
| l_wrap <= (ld_xfr) ? b2x_wrap : l_wrap; |
| l_id <= (ld_xfr) ? b2x_id : l_id; |
| l_ba <= (ld_xfr) ? b2x_ba : l_ba; |
| l_len <= next_xfr_len; |
| l_rd_next <= {l_rd_next[5:0], rd_next}; |
| l_rd_start <= {l_rd_start[5:0], rd_start}; |
| l_rd_last <= {l_rd_last[5:0], rd_last}; |
| act_cmd <= (xfr_cmd == `SDR_ACTIVATE) ? 1'b1 : 1'b0; |
| d_act_cmd <= act_cmd; |
| xfr_st <= next_xfr_st; |
| end // else: !if(~reset_n) |
| |
| end // always @ (posedge clk) |
| |
| |
| always @ (*) begin |
| case (xfr_st) |
| |
| `XFR_IDLE : begin |
| |
| sel_mgmt = mgmt_req; |
| sel_b2x = ~mgmt_req & sdr_init_done & b2x_req; |
| i_xfr_cmd = `SDR_DESEL; |
| rd_next = ~mgmt_req & sdr_init_done & b2x_req & b2x_read; |
| wr_next = ~mgmt_req & sdr_init_done & b2x_req & b2x_write; |
| rdok = ~mgmt_req; |
| cb_pre_ok = 1'b1; |
| wrok = ~mgmt_req; |
| next_xfr_st = (mgmt_req | ~sdr_init_done) ? `XFR_IDLE : |
| (~b2x_req) ? `XFR_IDLE : |
| (b2x_read) ? `XFR_READ : |
| (b2x_write) ? `XFR_WRITE : `XFR_IDLE; |
| |
| end // case: `XFR_IDLE |
| |
| `XFR_READ : begin |
| rd_next = ~l_xfr_end | |
| l_xfr_end & ~mgmt_req & b2x_req & b2x_read; |
| wr_next = 1'b0; |
| rdok = l_xfr_end & ~mgmt_req; |
| // Break the timing path for FPGA Based Design |
| cb_pre_ok = (`TARGET_DESIGN == `FPGA) ? 1'b0 : l_xfr_end; |
| wrok = 1'b0; |
| sel_mgmt = 1'b0; |
| |
| if (l_xfr_end) begin // end of transfer |
| |
| if (~l_wrap) begin |
| // Current transfer was not wrap mode, may need BT |
| // If next cmd is a R or W or PRE to same bank allow |
| // it else issue BT |
| // This is a little pessimistic since BT is issued |
| // for non-wrap mode transfers even if the transfer |
| // ends on a burst boundary, but is felt to be of |
| // minimal performance impact. |
| |
| i_xfr_cmd = `SDR_BT; |
| sel_b2x = b2x_req & ~mgmt_req & (b2x_read | b2x_prechg_hit); |
| |
| end // if (~l_wrap) |
| |
| else begin |
| // Wrap mode transfer, by definition is end of burst |
| // boundary |
| |
| i_xfr_cmd = `SDR_DESEL; |
| sel_b2x = b2x_req & ~mgmt_req & ~b2x_write; |
| |
| end // else: !if(~l_wrap) |
| |
| next_xfr_st = (sdr_init_done) ? ((b2x_req & ~mgmt_req & b2x_read) ? `XFR_READ : `XFR_RDWT) : `XFR_IDLE; |
| |
| end // if (l_xfr_end) |
| |
| else begin |
| // Not end of transfer |
| // If current transfer was not wrap mode and we are at |
| // the start of a burst boundary issue another R cmd to |
| // step sequemtially thru memory, ELSE, |
| // issue precharge/activate commands from the bank control |
| |
| i_xfr_cmd = (burst_bdry & ~l_wrap) ? `SDR_READ : `SDR_DESEL; |
| sel_b2x = ~(burst_bdry & ~l_wrap) & b2x_req; |
| next_xfr_st = `XFR_READ; |
| |
| end // else: !if(l_xfr_end) |
| |
| end // case: `XFR_READ |
| |
| `XFR_RDWT : begin |
| rd_next = ~mgmt_req & b2x_req & b2x_read; |
| wr_next = rd_pipe_mt & ~mgmt_req & b2x_req & b2x_write; |
| rdok = ~mgmt_req; |
| cb_pre_ok = 1'b1; |
| wrok = rd_pipe_mt & ~mgmt_req; |
| |
| sel_mgmt = mgmt_req; |
| |
| sel_b2x = ~mgmt_req & b2x_req; |
| |
| i_xfr_cmd = `SDR_DESEL; |
| |
| next_xfr_st = (~mgmt_req & b2x_req & b2x_read) ? `XFR_READ : |
| (~rd_pipe_mt) ? `XFR_RDWT : |
| (~mgmt_req & b2x_req & b2x_write) ? `XFR_WRITE : |
| `XFR_IDLE; |
| |
| end // case: `XFR_RDWT |
| |
| `XFR_WRITE : begin |
| rd_next = l_xfr_end & ~mgmt_req & b2x_req & b2x_read; |
| wr_next = ~l_xfr_end | |
| l_xfr_end & ~mgmt_req & b2x_req & b2x_write; |
| rdok = l_xfr_end & ~mgmt_req; |
| cb_pre_ok = 1'b0; |
| wrok = l_xfr_end & ~mgmt_req; |
| sel_mgmt = 1'b0; |
| |
| if (l_xfr_end) begin // End of transfer |
| |
| if (~l_wrap) begin |
| // Current transfer was not wrap mode, may need BT |
| // If next cmd is a R or W allow it else issue BT |
| // This is a little pessimistic since BT is issued |
| // for non-wrap mode transfers even if the transfer |
| // ends on a burst boundary, but is felt to be of |
| // minimal performance impact. |
| |
| |
| sel_b2x = b2x_req & ~mgmt_req & (b2x_read | b2x_write); |
| i_xfr_cmd = `SDR_BT; |
| end // if (~l_wrap) |
| |
| else begin |
| // Wrap mode transfer, by definition is end of burst |
| // boundary |
| |
| sel_b2x = b2x_req & ~mgmt_req & ~b2x_prechg_hit; |
| i_xfr_cmd = `SDR_DESEL; |
| end // else: !if(~l_wrap) |
| |
| next_xfr_st = (~mgmt_req & b2x_req & b2x_read) ? `XFR_READ : |
| (~mgmt_req & b2x_req & b2x_write) ? `XFR_WRITE : |
| `XFR_IDLE; |
| |
| end // if (l_xfr_end) |
| |
| else begin |
| // Not end of transfer |
| // If current transfer was not wrap mode and we are at |
| // the start of a burst boundary issue another R cmd to |
| // step sequemtially thru memory, ELSE, |
| // issue precharge/activate commands from the bank control |
| |
| if (burst_bdry & ~l_wrap) begin |
| sel_b2x = 1'b0; |
| i_xfr_cmd = `SDR_WRITE; |
| end // if (burst_bdry & ~l_wrap) |
| |
| else begin |
| sel_b2x = b2x_req & ~mgmt_req; |
| i_xfr_cmd = `SDR_DESEL; |
| end // else: !if(burst_bdry & ~l_wrap) |
| |
| next_xfr_st = `XFR_WRITE; |
| end // else: !if(l_xfr_end) |
| |
| end // case: `XFR_WRITE |
| |
| endcase // case(xfr_st) |
| |
| end // always @ (xfr_st or ...) |
| |
| // signals to bank_ctl (x2b_refresh, x2b_act_ok, x2b_rdok, x2b_wrok, |
| // x2b_pre_ok[3:0] |
| |
| assign x2b_refresh = (xfr_cmd == `SDR_REFRESH) ? 1'b1 : 1'b0; |
| |
| assign x2b_act_ok = ~act_cmd & ~d_act_cmd; |
| |
| assign x2b_rdok = rdok; |
| |
| assign x2b_wrok = wrok; |
| |
| //assign x2b_pre_ok[0] = (l_ba == 2'b00) ? cb_pre_ok : 1'b1; |
| //assign x2b_pre_ok[1] = (l_ba == 2'b01) ? cb_pre_ok : 1'b1; |
| //assign x2b_pre_ok[2] = (l_ba == 2'b10) ? cb_pre_ok : 1'b1; |
| //assign x2b_pre_ok[3] = (l_ba == 2'b11) ? cb_pre_ok : 1'b1; |
| |
| assign x2b_pre_ok[0] = cb_pre_ok; |
| assign x2b_pre_ok[1] = cb_pre_ok; |
| assign x2b_pre_ok[2] = cb_pre_ok; |
| assign x2b_pre_ok[3] = cb_pre_ok; |
| assign last_burst = (ld_xfr) ? b2x_last : l_last; |
| |
| /************************************************************************/ |
| // APP Data I/F |
| |
| wire [SDR_DW-1:0] x2a_rddt; |
| |
| //assign x2a_start = (ld_xfr) ? b2x_start : l_start; |
| assign x2a_rdstart = d_rd_start; |
| assign x2a_wrstart = wr_start; |
| |
| assign x2a_rdlast = d_rd_last; |
| assign x2a_wrlast = wr_last; |
| |
| assign x2a_id = (ld_xfr) ? b2x_id : l_id; |
| |
| assign x2a_rddt = sdr_din; |
| |
| assign x2a_wrnext = wr_next; |
| |
| assign x2a_rdok = d_rd_next; |
| |
| /************************************************************************/ |
| // SDRAM I/F |
| |
| reg sdr_cs_n, sdr_cke, sdr_ras_n, sdr_cas_n, |
| sdr_we_n; |
| reg [SDR_BW-1:0] sdr_dqm; |
| reg [1:0] sdr_ba; |
| reg [12:0] sdr_addr; |
| reg [SDR_DW-1:0] sdr_dout; |
| reg [SDR_BW-1:0] sdr_den_n; |
| |
| always @ (posedge clk) |
| if (~reset_n) begin |
| sdr_cs_n <= 1'b1; |
| sdr_cke <= 1'b1; |
| sdr_ras_n <= 1'b1; |
| sdr_cas_n <= 1'b1; |
| sdr_we_n <= 1'b1; |
| sdr_dqm <= {SDR_BW{1'b1}}; |
| sdr_den_n <= {SDR_BW{1'b1}}; |
| end // if (~reset_n) |
| else begin |
| sdr_cs_n <= xfr_cmd[3]; |
| sdr_ras_n <= xfr_cmd[2]; |
| sdr_cas_n <= xfr_cmd[1]; |
| sdr_we_n <= xfr_cmd[0]; |
| sdr_cke <= (xfr_st != `XFR_IDLE) ? 1'b1 : |
| ~(mgmt_idle & b2x_idle & r2x_idle); |
| sdr_dqm <= (wr_next) ? a2x_wren_n : {SDR_BW{1'b0}}; |
| sdr_den_n <= (wr_next) ? {SDR_BW{1'b0}} : {SDR_BW{1'b1}}; |
| end // else: !if(~reset_n) |
| |
| always @ (posedge clk) begin |
| |
| if (~xfr_cmd[3]) begin |
| sdr_addr <= xfr_addr; |
| sdr_ba <= xfr_ba; |
| end // if (~xfr_cmd[3]) |
| |
| sdr_dout <= (wr_next) ? a2x_wrdt : sdr_dout; |
| |
| end // always @ (posedge clk) |
| |
| /************************************************************************/ |
| // Refresh and Initialization |
| |
| `define MGM_POWERUP 3'b000 |
| `define MGM_PRECHARGE 3'b001 |
| `define MGM_PCHWT 3'b010 |
| `define MGM_REFRESH 3'b011 |
| `define MGM_REFWT 3'b100 |
| `define MGM_MODE_REG 3'b101 |
| `define MGM_MODE_WT 3'b110 |
| `define MGM_ACTIVE 3'b111 |
| |
| reg [2:0] mgmt_st, next_mgmt_st; |
| reg [3:0] tmr0, tmr0_d; |
| reg [3:0] cntr1, cntr1_d; |
| wire tmr0_tc, cntr1_tc, rfsh_timer_tc, ref_req, precharge_ok; |
| reg ld_tmr0, ld_cntr1, dec_cntr1, set_sdr_init_done; |
| reg [`SDR_RFSH_TIMER_W-1 : 0] rfsh_timer; |
| reg [`SDR_RFSH_ROW_CNT_W-1:0] rfsh_row_cnt; |
| |
| always @ (posedge clk) |
| if (~reset_n) begin |
| mgmt_st <= `MGM_POWERUP; |
| tmr0 <= 4'b0; |
| cntr1 <= 4'h7; |
| rfsh_timer <= 0; |
| rfsh_row_cnt <= 0; |
| sdr_init_done <= 1'b0; |
| end // if (~reset_n) |
| else begin |
| mgmt_st <= next_mgmt_st; |
| tmr0 <= (ld_tmr0) ? tmr0_d : |
| (~tmr0_tc) ? tmr0 - 1 : tmr0; |
| cntr1 <= (ld_cntr1) ? cntr1_d : |
| (dec_cntr1) ? cntr1 - 1 : cntr1; |
| sdr_init_done <= (set_sdr_init_done | sdr_init_done) & sdram_enable; |
| rfsh_timer <= (rfsh_timer_tc) ? 0 : rfsh_timer + 1; |
| rfsh_row_cnt <= (~set_sdr_init_done) ? 0 : |
| (rfsh_timer_tc) ? rfsh_row_cnt + 1 : rfsh_row_cnt; |
| end // else: !if(~reset_n) |
| |
| assign tmr0_tc = ~|tmr0; |
| |
| assign cntr1_tc = ~|cntr1; |
| |
| assign rfsh_timer_tc = (rfsh_timer == rfsh_time) ? 1'b1 : 1'b0; |
| |
| assign ref_req = (rfsh_row_cnt >= rfsh_rmax) ? 1'b1 : 1'b0; |
| |
| assign precharge_ok = cb_pre_ok & b2x_tras_ok; |
| |
| assign xfr_bank_sel = l_ba; |
| |
| always @ (mgmt_st or sdram_enable or mgmt_ack or trp_delay or tmr0_tc or |
| cntr1_tc or trcar_delay or rfsh_row_cnt or ref_req or sdr_init_done |
| or precharge_ok or sdram_mode_reg) begin |
| |
| case (mgmt_st) |
| |
| `MGM_POWERUP : begin |
| mgmt_idle = 1'b0; |
| mgmt_req = 1'b0; |
| mgmt_cmd = `SDR_DESEL; |
| mgmt_ba = 2'b0; |
| mgmt_addr = 13'h400; // A10 = 1 => all banks |
| ld_tmr0 = 1'b0; |
| tmr0_d = 4'b0; |
| dec_cntr1 = 1'b0; |
| ld_cntr1 = 1'b1; |
| cntr1_d = 4'hf; // changed for sdrams with higher refresh cycles during initialization |
| set_sdr_init_done = 1'b0; |
| next_mgmt_st = (sdram_enable) ? `MGM_PRECHARGE : `MGM_POWERUP; |
| end // case: `MGM_POWERUP |
| |
| `MGM_PRECHARGE : begin // Precharge all banks |
| mgmt_idle = 1'b0; |
| mgmt_req = 1'b1; |
| mgmt_cmd = (precharge_ok) ? `SDR_PRECHARGE : `SDR_DESEL; |
| mgmt_ba = 2'b0; |
| mgmt_addr = 13'h400; // A10 = 1 => all banks |
| ld_tmr0 = mgmt_ack; |
| tmr0_d = trp_delay; |
| ld_cntr1 = 1'b0; |
| cntr1_d = 4'h7; |
| dec_cntr1 = 1'b0; |
| set_sdr_init_done = 1'b0; |
| next_mgmt_st = (precharge_ok & mgmt_ack) ? `MGM_PCHWT : `MGM_PRECHARGE; |
| end // case: `MGM_PRECHARGE |
| |
| `MGM_PCHWT : begin // Wait for Trp |
| mgmt_idle = 1'b0; |
| mgmt_req = 1'b1; |
| mgmt_cmd = `SDR_DESEL; |
| mgmt_ba = 2'b0; |
| mgmt_addr = 13'h400; // A10 = 1 => all banks |
| ld_tmr0 = 1'b0; |
| tmr0_d = trp_delay; |
| ld_cntr1 = 1'b0; |
| cntr1_d = 4'b0; |
| dec_cntr1 = 1'b0; |
| set_sdr_init_done = 1'b0; |
| next_mgmt_st = (tmr0_tc) ? `MGM_REFRESH : `MGM_PCHWT; |
| end // case: `MGM_PRECHARGE |
| |
| `MGM_REFRESH : begin // Refresh |
| mgmt_idle = 1'b0; |
| mgmt_req = 1'b1; |
| mgmt_cmd = `SDR_REFRESH; |
| mgmt_ba = 2'b0; |
| mgmt_addr = 13'h400; // A10 = 1 => all banks |
| ld_tmr0 = mgmt_ack; |
| tmr0_d = trcar_delay; |
| dec_cntr1 = mgmt_ack; |
| ld_cntr1 = 1'b0; |
| cntr1_d = 4'h7; |
| set_sdr_init_done = 1'b0; |
| next_mgmt_st = (mgmt_ack) ? `MGM_REFWT : `MGM_REFRESH; |
| end // case: `MGM_REFRESH |
| |
| `MGM_REFWT : begin // Wait for trcar |
| mgmt_idle = 1'b0; |
| mgmt_req = 1'b1; |
| mgmt_cmd = `SDR_DESEL; |
| mgmt_ba = 2'b0; |
| mgmt_addr = 13'h400; // A10 = 1 => all banks |
| ld_tmr0 = 1'b0; |
| tmr0_d = trcar_delay; |
| dec_cntr1 = 1'b0; |
| ld_cntr1 = 1'b0; |
| cntr1_d = 4'h7; |
| set_sdr_init_done = 1'b0; |
| next_mgmt_st = (~tmr0_tc) ? `MGM_REFWT : |
| (~cntr1_tc) ? `MGM_REFRESH : |
| (sdr_init_done) ? `MGM_ACTIVE : `MGM_MODE_REG; |
| end // case: `MGM_REFWT |
| |
| `MGM_MODE_REG : begin // Program mode Register & wait for |
| mgmt_idle = 1'b0; |
| mgmt_req = 1'b1; |
| mgmt_cmd = `SDR_MODE; |
| mgmt_ba = {1'b0, sdram_mode_reg[11]}; |
| mgmt_addr = sdram_mode_reg; |
| ld_tmr0 = mgmt_ack; |
| tmr0_d = 4'h7; |
| dec_cntr1 = 1'b0; |
| ld_cntr1 = 1'b0; |
| cntr1_d = 4'h7; |
| set_sdr_init_done = 1'b0; |
| next_mgmt_st = (mgmt_ack) ? `MGM_MODE_WT : `MGM_MODE_REG; |
| end // case: `MGM_MODE_REG |
| |
| `MGM_MODE_WT : begin // Wait for tMRD |
| mgmt_idle = 1'b0; |
| mgmt_req = 1'b1; |
| mgmt_cmd = `SDR_DESEL; |
| mgmt_ba = 2'bx; |
| mgmt_addr = 13'bx; |
| ld_tmr0 = 1'b0; |
| tmr0_d = 4'h7; |
| dec_cntr1 = 1'b0; |
| ld_cntr1 = 1'b0; |
| cntr1_d = 4'h7; |
| set_sdr_init_done = 1'b0; |
| next_mgmt_st = (~tmr0_tc) ? `MGM_MODE_WT : `MGM_ACTIVE; |
| end // case: `MGM_MODE_WT |
| |
| `MGM_ACTIVE : begin // Wait for ref_req |
| mgmt_idle = ~ref_req; |
| mgmt_req = 1'b0; |
| mgmt_cmd = `SDR_DESEL; |
| mgmt_ba = 2'bx; |
| mgmt_addr = 13'bx; |
| ld_tmr0 = 1'b0; |
| tmr0_d = 4'h7; |
| dec_cntr1 = 1'b0; |
| ld_cntr1 = ref_req; |
| cntr1_d = rfsh_row_cnt; |
| set_sdr_init_done = 1'b1; |
| next_mgmt_st = (~sdram_enable) ? `MGM_POWERUP : |
| (ref_req) ? `MGM_PRECHARGE : `MGM_ACTIVE; |
| end // case: `MGM_MODE_WT |
| |
| endcase // case(mgmt_st) |
| |
| end // always @ (mgmt_st or ....) |
| |
| |
| |
| endmodule // sdr_xfr_ctl |