blob: d566f6f96afd9ce315f9130e4ad6fc36eb60a953 [file] [log] [blame]
//////////////////////////////////////////////////////////////////////////////
// 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 Bank Controller
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:
This module takes requests from sdrc_req_gen, checks for page hit/miss and
issues precharge/activate commands and then passes the request to sdrc_xfr_ctl.
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_bank_fsm (clk,
reset_n,
/* Req from req_gen */
r2b_req, // request
r2b_req_id, // ID
r2b_start, // First chunk of burst
r2b_last, // Last chunk of burst
r2b_wrap,
r2b_raddr, // row address
r2b_caddr, // col address
r2b_len, // length
r2b_write, // write request
b2r_ack,
sdr_dma_last,
/* Transfer request to xfr_ctl */
b2x_req, // Request to xfr_ctl
b2x_start, // first chunk of transfer
b2x_last, // last chunk of transfer
b2x_wrap,
b2x_id, // Transfer ID
b2x_addr, // row/col address
b2x_len, // transfer length
b2x_cmd, // transfer command
x2b_ack, // command accepted
/* Status to/from xfr_ctl */
tras_ok, // TRAS OK for this bank
xfr_ok,
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
/* current xfr row address of the bank */
bank_row,
/* SDRAM Timing */
tras_delay, // Active to precharge delay
trp_delay, // Precharge to active delay
trcd_delay); // Active to R/W delay
parameter SDR_DW = 16; // SDR Data Width
parameter SDR_BW = 2; // SDR Byte Width
input clk, reset_n;
/* Req from bank_ctl */
input r2b_req, r2b_start, r2b_last,
r2b_write, r2b_wrap;
input [`SDR_REQ_ID_W-1:0] r2b_req_id;
input [12:0] r2b_raddr;
input [12:0] r2b_caddr;
input [`REQ_BW-1:0] r2b_len;
output b2r_ack;
input sdr_dma_last;
/* Req to xfr_ctl */
output b2x_req, b2x_start, b2x_last,
tras_ok, b2x_wrap;
output [`SDR_REQ_ID_W-1:0] b2x_id;
output [12:0] b2x_addr;
output [`REQ_BW-1:0] b2x_len;
output [1:0] b2x_cmd;
input x2b_ack;
/* Status from xfr_ctl */
input x2b_refresh, x2b_act_ok, x2b_rdok,
x2b_wrok, x2b_pre_ok, xfr_ok;
input [3:0] tras_delay, trp_delay, trcd_delay;
output [12:0] bank_row;
/****************************************************************************/
// Internal Nets
`define BANK_IDLE 3'b000
`define BANK_PRE 3'b001
`define BANK_ACT 3'b010
`define BANK_XFR 3'b011
`define BANK_DMA_LAST_PRE 3'b100
reg [2:0] bank_st, next_bank_st;
wire b2x_start, b2x_last;
reg l_start, l_last;
reg b2x_req, b2r_ack;
wire [`SDR_REQ_ID_W-1:0] b2x_id;
reg [`SDR_REQ_ID_W-1:0] l_id;
reg [12:0] b2x_addr;
reg [`REQ_BW-1:0] l_len;
wire [`REQ_BW-1:0] b2x_len;
reg [1:0] b2x_cmd_t;
reg bank_valid;
reg [12:0] bank_row;
reg [3:0] tras_cntr, timer0;
reg l_wrap, l_write;
wire b2x_wrap;
reg [12:0] l_raddr;
reg [12:0] l_caddr;
reg l_sdr_dma_last;
reg bank_prech_page_closed;
wire tras_ok_internal, tras_ok, activate_bank;
wire page_hit, timer0_tc_t, ld_trp, ld_trcd;
/*** Timing Break Logic Added for FPGA - Start ****/
reg x2b_wrok_r, xfr_ok_r , x2b_rdok_r;
reg [1:0] b2x_cmd_r,timer0_tc_r,tras_ok_r,x2b_pre_ok_r,x2b_act_ok_r;
always @ (posedge clk)
if (~reset_n) begin
x2b_wrok_r <= 1'b0;
xfr_ok_r <= 1'b0;
x2b_rdok_r <= 1'b0;
b2x_cmd_r <= 2'b0;
timer0_tc_r <= 1'b0;
tras_ok_r <= 1'b0;
x2b_pre_ok_r <= 1'b0;
x2b_act_ok_r <= 1'b0;
end
else begin
x2b_wrok_r <= x2b_wrok;
xfr_ok_r <= xfr_ok;
x2b_rdok_r <= x2b_rdok;
b2x_cmd_r <= b2x_cmd_t;
timer0_tc_r <= (ld_trp | ld_trcd) ? 1'b0 : timer0_tc_t;
tras_ok_r <= tras_ok_internal;
x2b_pre_ok_r <= x2b_pre_ok;
x2b_act_ok_r <= x2b_act_ok;
end
wire x2b_wrok_t = (`TARGET_DESIGN == `FPGA) ? x2b_wrok_r : x2b_wrok;
wire xfr_ok_t = (`TARGET_DESIGN == `FPGA) ? xfr_ok_r : xfr_ok;
wire x2b_rdok_t = (`TARGET_DESIGN == `FPGA) ? x2b_rdok_r : x2b_rdok;
wire [1:0] b2x_cmd = (`TARGET_DESIGN == `FPGA) ? b2x_cmd_r : b2x_cmd_t;
wire timer0_tc = (`TARGET_DESIGN == `FPGA) ? timer0_tc_r : timer0_tc_t;
assign tras_ok = (`TARGET_DESIGN == `FPGA) ? tras_ok_r : tras_ok_internal;
wire x2b_pre_ok_t = (`TARGET_DESIGN == `FPGA) ? x2b_pre_ok_r : x2b_pre_ok;
wire x2b_act_ok_t = (`TARGET_DESIGN == `FPGA) ? x2b_act_ok_r : x2b_act_ok;
/*** Timing Break Logic Added for FPGA - End****/
always @ (posedge clk)
if (~reset_n) begin
bank_valid <= 1'b0;
tras_cntr <= 4'b0;
timer0 <= 4'b0;
bank_st <= `BANK_IDLE;
end // if (~reset_n)
else begin
bank_valid <= (x2b_refresh || bank_prech_page_closed) ? 1'b0 : // force the bank status to be invalid
(activate_bank) ? 1'b1 : bank_valid;
tras_cntr <= (activate_bank) ? tras_delay :
(~tras_ok_internal) ? tras_cntr - 4'b1 : 4'b0;
timer0 <= (ld_trp) ? trp_delay :
(ld_trcd) ? trcd_delay :
(timer0 != 'h0) ? timer0 - 4'b1 : timer0;
bank_st <= next_bank_st;
end // else: !if(~reset_n)
always @ (posedge clk) begin
bank_row <= (bank_st == `BANK_ACT) ? b2x_addr : bank_row;
if (~reset_n) begin
l_start <= 1'b0;
l_last <= 1'b0;
l_id <= 1'b0;
l_len <= 1'b0;
l_wrap <= 1'b0;
l_write <= 1'b0;
l_raddr <= 1'b0;
l_caddr <= 1'b0;
l_sdr_dma_last <= 1'b0;
end
else begin
if (b2r_ack) begin
l_start <= r2b_start;
l_last <= r2b_last;
l_id <= r2b_req_id;
l_len <= r2b_len;
l_wrap <= r2b_wrap;
l_write <= r2b_write;
l_raddr <= r2b_raddr;
l_caddr <= r2b_caddr;
l_sdr_dma_last <= sdr_dma_last;
end // if (b2r_ack)
end
end // always @ (posedge clk)
assign tras_ok_internal = ~|tras_cntr;
assign activate_bank = (b2x_cmd == `OP_ACT) & x2b_ack;
assign page_hit = (r2b_raddr == bank_row) ? bank_valid : 1'b0; // its a hit only if bank is valid
assign timer0_tc_t = ~|timer0;
assign ld_trp = (b2x_cmd == `OP_PRE) ? x2b_ack : 1'b0;
assign ld_trcd = (b2x_cmd == `OP_ACT) ? x2b_ack : 1'b0;
always @ (*) begin
bank_prech_page_closed = 1'b0;
b2x_req = 1'b0;
b2x_cmd_t = 2'bx;
b2r_ack = 1'b0;
b2x_addr = 13'bx;
next_bank_st = bank_st;
case (bank_st)
`BANK_IDLE : begin
if(`TARGET_DESIGN == `FPGA) begin // To break the timing, b2x request are generated delayed
if (~r2b_req) begin
next_bank_st = `BANK_IDLE;
end // if (~r2b_req)
else if (page_hit) begin
b2r_ack = 1'b1;
b2x_cmd_t = (r2b_write) ? `OP_WR : `OP_RD;
next_bank_st = `BANK_XFR;
end // if (page_hit)
else begin // page_miss
b2r_ack = 1'b1;
b2x_cmd_t = `OP_PRE;
next_bank_st = `BANK_PRE; // bank was precharged on l_sdr_dma_last
end // else: !if(page_hit)
end else begin // ASIC
if (~r2b_req) begin
bank_prech_page_closed = 1'b0;
b2x_req = 1'b0;
b2x_cmd_t = 2'bx;
b2r_ack = 1'b0;
b2x_addr = 13'bx;
next_bank_st = `BANK_IDLE;
end // if (~r2b_req)
else if (page_hit) begin
b2x_req = (r2b_write) ? x2b_wrok_t & xfr_ok_t :
x2b_rdok_t & xfr_ok_t;
b2x_cmd_t = (r2b_write) ? `OP_WR : `OP_RD;
b2r_ack = 1'b1;
b2x_addr = r2b_caddr;
next_bank_st = (x2b_ack) ? `BANK_IDLE : `BANK_XFR; // in case of hit, stay here till xfr sm acks
end // if (page_hit)
else begin // page_miss
b2x_req = tras_ok & x2b_pre_ok_t;
b2x_cmd_t = `OP_PRE;
b2r_ack = 1'b1;
b2x_addr = r2b_raddr & 13'hBFF; // Dont want to pre all banks!
next_bank_st = (l_sdr_dma_last) ? `BANK_PRE : (x2b_ack) ? `BANK_ACT : `BANK_PRE; // bank was precharged on l_sdr_dma_last
end // else: !if(page_hit)
end
end // case: `BANK_IDLE
`BANK_PRE : begin
b2x_req = tras_ok & x2b_pre_ok_t;
b2x_cmd_t = `OP_PRE;
b2r_ack = 1'b0;
b2x_addr = l_raddr & 13'hBFF; // Dont want to pre all banks!
bank_prech_page_closed = 1'b0;
next_bank_st = (x2b_ack) ? `BANK_ACT : `BANK_PRE;
end // case: `BANK_PRE
`BANK_ACT : begin
b2x_req = timer0_tc & x2b_act_ok_t;
b2x_cmd_t = `OP_ACT;
b2r_ack = 1'b0;
b2x_addr = l_raddr;
bank_prech_page_closed = 1'b0;
next_bank_st = (x2b_ack) ? `BANK_XFR : `BANK_ACT;
end // case: `BANK_ACT
`BANK_XFR : begin
b2x_req = (l_write) ? timer0_tc & x2b_wrok_t & xfr_ok_t :
timer0_tc & x2b_rdok_t & xfr_ok_t;
b2x_cmd_t = (l_write) ? `OP_WR : `OP_RD;
b2r_ack = 1'b0;
b2x_addr = l_caddr;
bank_prech_page_closed = 1'b0;
next_bank_st = (x2b_refresh) ? `BANK_ACT :
(x2b_ack & l_sdr_dma_last) ? `BANK_DMA_LAST_PRE :
(x2b_ack) ? `BANK_IDLE : `BANK_XFR;
end // case: `BANK_XFR
`BANK_DMA_LAST_PRE : begin
b2x_req = tras_ok & x2b_pre_ok_t;
b2x_cmd_t = `OP_PRE;
b2r_ack = 1'b0;
b2x_addr = l_raddr & 13'hBFF; // Dont want to pre all banks!
bank_prech_page_closed = 1'b1;
next_bank_st = (x2b_ack) ? `BANK_IDLE : `BANK_DMA_LAST_PRE;
end // case: `BANK_DMA_LAST_PRE
endcase // case(bank_st)
end // always @ (bank_st or ...)
assign b2x_start = (bank_st == `BANK_IDLE) ? r2b_start : l_start;
assign b2x_last = (bank_st == `BANK_IDLE) ? r2b_last : l_last;
assign b2x_id = (bank_st == `BANK_IDLE) ? r2b_req_id : l_id;
assign b2x_len = (bank_st == `BANK_IDLE) ? r2b_len : l_len;
assign b2x_wrap = (bank_st == `BANK_IDLE) ? r2b_wrap : l_wrap;
endmodule // sdr_bank_fsm