1
0
mirror of https://github.com/Gehstock/Mist_FPGA.git synced 2026-02-24 07:52:14 +00:00
Files
Gehstock.Mist_FPGA/Console_MiST/Supervision_MiST/rtl/sdram.sv
2021-05-28 15:53:05 +02:00

243 lines
7.0 KiB
Systemverilog

//
// sdram.v
//
// sdram controller implementation for the MiST board
// https://github.com/mist-devel/mist-board
//
// Copyright (c) 2013 Till Harbaum <till@harbaum.org>
// Copyright (c) 2019 Gyorgy Szombathelyi
//
// This source file 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, either version 3 of the License, or
// (at your option) any later version.
//
// This source file 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
module sdram (
// interface to the MT48LC16M16 chip
inout reg [15:0] SDRAM_DQ, // 16 bit bidirectional data bus
output reg [12:0] SDRAM_A, // 13 bit multiplexed address bus
output reg SDRAM_DQML, // two byte masks
output reg SDRAM_DQMH, // two byte masks
output reg [1:0] SDRAM_BA, // two banks
output SDRAM_nCS, // a single chip select
output SDRAM_nWE, // write enable
output SDRAM_nRAS, // row address select
output SDRAM_nCAS, // columns address select
// cpu/chipset interface
input init_n, // init signal after FPGA config to initialize RAM
input clk, // sdram clock
input port1_req,
output reg port1_ack,
input port1_we,
input [23:1] port1_a,
input [1:0] port1_ds,
input [15:0] port1_d,
output [15:0] port1_q,
input [17:1] cpu1_addr,
output reg [15:0] cpu1_q
);
parameter MHZ = 80; // 80 MHz default clock, adjust to calculate the refresh rate correctly
localparam RASCAS_DELAY = 3'd2; // tRCD=20ns -> 2 cycles@<100MHz
localparam BURST_LENGTH = 3'b000; // 000=1, 001=2, 010=4, 011=8
localparam ACCESS_TYPE = 1'b0; // 0=sequential, 1=interleaved
localparam CAS_LATENCY = 3'd2; // 2/3 allowed
localparam OP_MODE = 2'b00; // only 00 (standard operation) allowed
localparam NO_WRITE_BURST = 1'b1; // 0= write burst enabled, 1=only single access write
localparam MODE = { 3'b000, NO_WRITE_BURST, OP_MODE, CAS_LATENCY, ACCESS_TYPE, BURST_LENGTH};
// 64ms/8192 rows = 7.8us -> 842 cycles@108MHz
localparam RFRSH_CYCLES = 16'd78*MHZ/10;
// ---------------------------------------------------------------------
// ------------------------ cycle state machine ------------------------
// ---------------------------------------------------------------------
/*
SDRAM state machine
1 word burst, CL2
cmd issued registered
0 RAS0 data ready
1
2 CAS0
3
4
5 DATA0
*/
localparam STATE_RAS0 = 3'd0; // first state in cycle
localparam STATE_CAS0 = STATE_RAS0 + RASCAS_DELAY; // CAS phase - 2
localparam STATE_READ0 = 3'd0; //STATE_CAS0 + CAS_LATENCY + 2'd2; // 6
localparam STATE_LAST = 3'd5;
reg [2:0] t;
always @(posedge clk) begin
t <= t + 1'd1;
if (t == STATE_LAST) t <= STATE_RAS0;
end
// ---------------------------------------------------------------------
// --------------------------- startup/reset ---------------------------
// ---------------------------------------------------------------------
// wait 1ms (32 8Mhz cycles) after FPGA config is done before going
// into normal operation. Initialize the ram in the last 16 reset cycles (cycles 15-0)
reg [4:0] reset;
reg init = 1'b1;
always @(posedge clk, negedge init_n) begin
if(!init_n) begin
reset <= 5'h1f;
init <= 1'b1;
end else begin
if((t == STATE_LAST) && (reset != 0)) reset <= reset - 5'd1;
init <= !(reset == 0);
end
end
// ---------------------------------------------------------------------
// ------------------ generate ram control signals ---------------------
// ---------------------------------------------------------------------
// all possible commands
localparam CMD_INHIBIT = 4'b1111;
localparam CMD_NOP = 4'b0111;
localparam CMD_ACTIVE = 4'b0011;
localparam CMD_READ = 4'b0101;
localparam CMD_WRITE = 4'b0100;
localparam CMD_BURST_TERMINATE = 4'b0110;
localparam CMD_PRECHARGE = 4'b0010;
localparam CMD_AUTO_REFRESH = 4'b0001;
localparam CMD_LOAD_MODE = 4'b0000;
reg [3:0] sd_cmd; // current command sent to sd ram
reg [15:0] sd_din;
// drive control signals according to current command
assign SDRAM_nCS = sd_cmd[3];
assign SDRAM_nRAS = sd_cmd[2];
assign SDRAM_nCAS = sd_cmd[1];
assign SDRAM_nWE = sd_cmd[0];
reg [24:1] addr_latch;
reg [24:1] addr_latch_next;
reg [17:1] addr_last;
reg [15:0] din_latch;
reg oe_latch;
reg we_latch;
reg [1:0] ds;
localparam PORT_NONE = 2'd0;
localparam PORT_CPU1 = 2'd1;
localparam PORT_REQ = 2'd2;
reg [2:0] next_port;
reg [2:0] port;
reg port1_state;
// PORT1
always @(*) begin
if (port1_req ^ port1_state) begin
next_port = PORT_REQ;
addr_latch_next = { 1'b0, port1_a };
end else if (cpu1_addr != addr_last) begin
next_port = PORT_CPU1;
addr_latch_next = { 7'd0, cpu1_addr };
end else begin
next_port = PORT_NONE;
addr_latch_next = addr_latch;
end
end
always @(posedge clk) begin
// permanently latch ram data to reduce delays
sd_din <= SDRAM_DQ;
SDRAM_DQ <= 16'bZZZZZZZZZZZZZZZZ;
{ SDRAM_DQMH, SDRAM_DQML } <= 2'b11;
sd_cmd <= CMD_NOP; // default: idle
if(init) begin
// initialization takes place at the end of the reset phase
if(t == STATE_RAS0) begin
if(reset == 15) begin
sd_cmd <= CMD_PRECHARGE;
SDRAM_A[10] <= 1'b1; // precharge all banks
end
if(reset == 10 || reset == 8) begin
sd_cmd <= CMD_AUTO_REFRESH;
end
if(reset == 2) begin
sd_cmd <= CMD_LOAD_MODE;
SDRAM_A <= MODE;
SDRAM_BA <= 2'b00;
end
end
end else begin
// RAS phase
if(t == STATE_RAS0) begin
addr_latch <= addr_latch_next;
port <= next_port;
{ oe_latch, we_latch } <= 2'b00;
if (next_port != PORT_NONE) begin
sd_cmd <= CMD_ACTIVE;
SDRAM_A <= addr_latch_next[22:10];
SDRAM_BA <= addr_latch_next[24:23];
if (next_port == PORT_REQ) begin
{ oe_latch, we_latch } <= { ~port1_we, port1_we };
ds <= port1_ds;
din_latch <= port1_d;
port1_state <= port1_req;
end else begin
{ oe_latch, we_latch } <= 2'b10;
ds <= 2'b11;
addr_last <= cpu1_addr;
end
end else begin
sd_cmd <= CMD_AUTO_REFRESH;
end
end
// CAS phase
if(t == STATE_CAS0 && (we_latch || oe_latch)) begin
sd_cmd <= we_latch?CMD_WRITE:CMD_READ;
{ SDRAM_DQMH, SDRAM_DQML } <= ~ds;
if (we_latch) begin
SDRAM_DQ <= din_latch;
port1_ack <= port1_req;
end
SDRAM_A <= { 4'b0010, addr_latch[9:1] }; // auto precharge
SDRAM_BA <= addr_latch[24:23];
end
// Data returned
if(t == STATE_READ0 && oe_latch) begin
case(port)
PORT_REQ: begin port1_q <= sd_din; port1_ack <= port1_req; end
PORT_CPU1: begin cpu1_q <= sd_din; end
default: ;
endcase;
end
end
end
endmodule