1
0
mirror of https://github.com/Gehstock/Mist_FPGA.git synced 2026-02-26 08:33:31 +00:00
Files
Gehstock.Mist_FPGA/Console_MiST/Gamate_MiST/rtl/sdram.sv
2022-06-19 19:13:03 +02:00

288 lines
8.1 KiB
Systemverilog

//
// sdram.v
// This version issues refresh only when 8bit channel reads the same 16bit word 2 times
//
// sdram controller implementation
// Copyright (c) 2018 Sorgelig
//
// 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, // byte mask
output reg SDRAM_DQMH, // byte mask
output reg [1:0] SDRAM_BA, // two banks
output reg SDRAM_nCS, // a single chip select
output reg SDRAM_nWE, // write enable
output reg SDRAM_nRAS, // row address select
output reg SDRAM_nCAS, // columns address select
output SDRAM_CLK,
output SDRAM_CKE,
// cpu/chipset interface
input init, // init signal after FPGA config to initialize RAM
input clk, // sdram is accessed at up to 128MHz
input [24:0] ch0_addr,
input ch0_rd,
input ch0_wr,
input [7:0] ch0_din,
output reg [7:0] ch0_dout,
output reg ch0_busy,
input [24:0] ch1_addr,
input ch1_rd,
input ch1_wr,
input [7:0] ch1_din,
output reg [7:0] ch1_dout,
output reg ch1_busy,
input [24:0] ch2_addr,
input ch2_rd,
input ch2_wr,
input [7:0] ch2_din,
output reg [7:0] ch2_dout,
output reg ch2_busy
);
assign SDRAM_nCS = 0;
assign SDRAM_CKE = 1;
assign {SDRAM_DQMH,SDRAM_DQML} = SDRAM_A[12:11];
localparam RASCAS_DELAY = 3'd1; // tRCD=20ns -> 2 cycles@85MHz
localparam BURST_LENGTH = 3'd0; // 0=1, 1=2, 2=4, 3=8, 7=full page
localparam ACCESS_TYPE = 1'd0; // 0=sequential, 1=interleaved
localparam CAS_LATENCY = 3'd2; // 2/3 allowed
localparam OP_MODE = 2'd0; // only 0 (standard operation) allowed
localparam NO_WRITE_BURST = 1'd1; // 0=write burst enabled, 1=only single access write
localparam MODE = { 3'b000, NO_WRITE_BURST, OP_MODE, CAS_LATENCY, ACCESS_TYPE, BURST_LENGTH};
localparam STATE_IDLE = 3'd0; // state to check the requests
localparam STATE_START = STATE_IDLE+1'd1; // state in which a new command is started
localparam STATE_NEXT = STATE_START+1'd1; // state in which a new command is started
localparam STATE_CONT = STATE_START+RASCAS_DELAY;
localparam STATE_READY = STATE_CONT+CAS_LATENCY+2'd2;
localparam STATE_LAST = STATE_READY; // last state in cycle
reg [2:0] state;
reg [22:0] a;
reg [1:0] bank;
reg [15:0] data;
reg we;
reg ram_req=0;
wire [2:0] rd,wr;
assign rd = {ch2_rd, ch1_rd, ch0_rd};
assign wr = {ch2_wr, ch1_wr, ch0_wr};
// access manager
always @(posedge clk) begin
reg old_ref;
reg [2:0] old_rd,old_wr;//,rd,wr;
reg [24:1] last_a[3] = '{'1,'1,'1};
old_rd <= old_rd & rd;
old_wr <= old_wr & wr;
if(state == STATE_IDLE && mode == MODE_NORMAL) begin
ram_req <= 0;
we <= 0;
ch0_busy <= 0;
ch1_busy <= 0;
ch2_busy <= 0;
if((~old_rd[0] & rd[0]) | (~old_wr[0] & wr[0])) begin
old_rd[0] <= rd[0];
old_wr[0] <= wr[0];
we <= wr[0];
{bank,a} <= ch0_addr;
data <= {ch0_din,ch0_din};
ram_req <= wr[0] || (last_a[0] != ch0_addr[24:1]);
last_a[0] <= wr[0] ? '1 : ch0_addr[24:1];
ch0_busy <= 1;
state <= STATE_START;
end
else if((~old_rd[1] & rd[1]) | (~old_wr[1] & wr[1])) begin
old_rd[1] <= rd[1];
old_wr[1] <= wr[1];
we <= wr[1];
{bank,a} <= ch1_addr;
data <= {ch1_din,ch1_din};
ram_req <= wr[1] || (last_a[1] != ch1_addr[24:1]);
last_a[1] <= wr[1] ? '1 : ch1_addr[24:1];
ch1_busy <= 1;
state <= STATE_START;
end
else if((~old_rd[2] & rd[2]) | (~old_wr[2] & wr[2])) begin
old_rd[2] <= rd[2];
old_wr[2] <= wr[2];
we <= wr[2];
{bank,a} <= ch2_addr;
data <= {ch2_din,ch2_din};
ram_req <= wr[2] || (last_a[2] != ch2_addr[24:1]);
last_a[2] <= wr[2] ? '1 : ch2_addr[24:1];
ch2_busy <= 1;
state <= STATE_START;
end
end
if (state == STATE_READY) begin
ch0_busy <= 0;
ch1_busy <= 0;
ch2_busy <= 0;
end
if(mode != MODE_NORMAL || state != STATE_IDLE || reset) begin
state <= state + 1'd1;
if(state == STATE_LAST) state <= STATE_IDLE;
end
end
localparam MODE_NORMAL = 2'b00;
localparam MODE_RESET = 2'b01;
localparam MODE_LDM = 2'b10;
localparam MODE_PRE = 2'b11;
// initialization
reg [1:0] mode;
reg [4:0] reset=5'h1f;
always @(posedge clk) begin
reg init_old=0;
init_old <= init;
if(init_old & ~init) reset <= 5'h1f;
else if(state == STATE_LAST) begin
if(reset != 0) begin
reset <= reset - 5'd1;
if(reset == 14) mode <= MODE_PRE;
else if(reset == 3) mode <= MODE_LDM;
else mode <= MODE_RESET;
end
else mode <= MODE_NORMAL;
end
end
localparam CMD_NOP = 3'b111;
localparam CMD_ACTIVE = 3'b011;
localparam CMD_READ = 3'b101;
localparam CMD_WRITE = 3'b100;
localparam CMD_BURST_TERMINATE = 3'b110;
localparam CMD_PRECHARGE = 3'b010;
localparam CMD_AUTO_REFRESH = 3'b001;
localparam CMD_LOAD_MODE = 3'b000;
wire [1:0] dqm = {we & ~a[0], we & a[0]};
// SDRAM state machines
always @(posedge clk) begin
reg [15:0] last_data[3];
reg [15:0] data_reg;
if(state == STATE_START) SDRAM_BA <= (mode == MODE_NORMAL) ? bank : 2'b00;
SDRAM_DQ <= 'Z;
casex({ram_req,we,mode,state})
{2'b1X, MODE_NORMAL, STATE_START}: {SDRAM_nRAS, SDRAM_nCAS, SDRAM_nWE} <= CMD_ACTIVE;
{2'b11, MODE_NORMAL, STATE_CONT }: {SDRAM_nRAS, SDRAM_nCAS, SDRAM_nWE, SDRAM_DQ} <= {CMD_WRITE, data};
{2'b10, MODE_NORMAL, STATE_CONT }: {SDRAM_nRAS, SDRAM_nCAS, SDRAM_nWE} <= CMD_READ;
{2'b0X, MODE_NORMAL, STATE_START}: {SDRAM_nRAS, SDRAM_nCAS, SDRAM_nWE} <= CMD_AUTO_REFRESH;
// init
{2'bXX, MODE_LDM, STATE_START}: {SDRAM_nRAS, SDRAM_nCAS, SDRAM_nWE} <= CMD_LOAD_MODE;
{2'bXX, MODE_PRE, STATE_START}: {SDRAM_nRAS, SDRAM_nCAS, SDRAM_nWE} <= CMD_PRECHARGE;
default: {SDRAM_nRAS, SDRAM_nCAS, SDRAM_nWE} <= CMD_NOP;
endcase
casex({ram_req,mode,state})
{1'b1, MODE_NORMAL, STATE_START}: SDRAM_A <= a[13:1];
{1'b1, MODE_NORMAL, STATE_CONT }: SDRAM_A <= {dqm, 2'b10, a[22:14]};
// init
{1'bX, MODE_LDM, STATE_START}: SDRAM_A <= MODE;
{1'bX, MODE_PRE, STATE_START}: SDRAM_A <= 13'b0010000000000;
default: SDRAM_A <= 13'b0000000000000;
endcase
data_reg <= SDRAM_DQ;
if(state == STATE_READY) begin
if(ch0_busy) begin
if(ram_req) begin
if(we) ch0_dout <= data[7:0];
else begin
ch0_dout <= a[0] ? data_reg[15:8] : data_reg[7:0];
last_data[0] <= data_reg;
end
end
else ch0_dout <= a[0] ? last_data[0][15:8] : last_data[0][7:0];
end
if(ch1_busy) begin
if(ram_req) begin
if(we) ch1_dout <= data[7:0];
else begin
ch1_dout <= a[0] ? data_reg[15:8] : data_reg[7:0];
last_data[1] <= data_reg;
end
end
else ch1_dout <= a[0] ? last_data[1][15:8] : last_data[1][7:0];
end
if(ch2_busy) begin
if(ram_req) begin
if(we) ch2_dout <= data[7:0];
else begin
ch2_dout <= a[0] ? data_reg[15:8] : data_reg[7:0];
last_data[2] <= data_reg;
end
end
else ch2_dout <= a[0] ? last_data[2][15:8] : last_data[2][7:0];
end
end
end
altddio_out
#(
.extend_oe_disable("OFF"),
.intended_device_family("Cyclone V"),
.invert_output("OFF"),
.lpm_hint("UNUSED"),
.lpm_type("altddio_out"),
.oe_reg("UNREGISTERED"),
.power_up_high("OFF"),
.width(1)
)
sdramclk_ddr
(
.datain_h(1'b0),
.datain_l(1'b1),
.outclock(clk),
.dataout(SDRAM_CLK),
.aclr(1'b0),
.aset(1'b0),
.oe(1'b1),
.outclocken(1'b1),
.sclr(1'b0),
.sset(1'b0)
);
endmodule