mirror of
https://github.com/MiSTer-devel/Archie_MiSTer.git
synced 2026-04-19 03:04:04 +00:00
297 lines
8.9 KiB
Verilog
297 lines
8.9 KiB
Verilog
/*
|
|
Copyright (c) 2013-2014, Stephen J. Leary
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
* Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
* Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
* Neither the name of the Stephen J. Leary nor the
|
|
names of its contributors may be used to endorse or promote products
|
|
derived from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL STEPHEN J. LEARY BE LIABLE FOR ANY
|
|
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
module sdram
|
|
(
|
|
// interface to the MT48LC16M16 chip
|
|
input sd_clk, // sdram is accessed at 128MHz
|
|
input sd_rst, // reset the sdram controller.
|
|
output sd_cke, // clock enable.
|
|
inout reg[15:0] sd_dq, // 16 bit bidirectional data bus
|
|
output reg[12:0] sd_addr, // 13 bit multiplexed address bus
|
|
output [1:0] sd_dqm, // two byte masks
|
|
output reg[1:0] sd_ba, // two banks
|
|
output sd_cs_n, // a single chip select
|
|
output sd_we_n, // write enable
|
|
output sd_ras_n, // row address select
|
|
output sd_cas_n, // columns address select
|
|
output reg sd_ready, // sd ready.
|
|
output sd_clk_out,
|
|
|
|
// cpu/chipset interface
|
|
|
|
input wb_clk, // 32MHz chipset clock to which sdram state machine is synchonized
|
|
input [31:0] wb_dat_i, // data input from chipset/cpu
|
|
output reg[31:0] wb_dat_o = 0, // data output to chipset/cpu
|
|
output reg wb_ack = 0,
|
|
input [23:2] wb_adr,
|
|
input [3:0] wb_sel, //
|
|
input [2:0] wb_cti, // cycle type.
|
|
input wb_stb, //
|
|
input wb_cyc, // cpu/chipset requests cycle
|
|
input wb_we // cpu/chipset requests write
|
|
);
|
|
|
|
localparam RASCAS_DELAY = 3'd3; // tRCD=20ns -> 3 cycles@128MHz
|
|
localparam BURST_LENGTH = 3'b011; // 000=1, 001=2, 010=4, 011=8, 111 = continuous.
|
|
localparam ACCESS_TYPE = 1'b0; // 0=sequential, 1=interleaved
|
|
localparam CAS_LATENCY = 3'd3; // 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 RFC_DELAY = 4'd7; // tRFC=66ns -> 9 cycles@128MHz
|
|
|
|
// all possible commands
|
|
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;
|
|
|
|
localparam MODE = { 3'b000, NO_WRITE_BURST, OP_MODE, CAS_LATENCY, ACCESS_TYPE, BURST_LENGTH};
|
|
|
|
reg [8:0] reset = 0;
|
|
reg [15:0] sd_dat[8]; // data output to chipset/cpu
|
|
|
|
reg sd_done = 1'b0;
|
|
reg [3:0] sd_cmd; // current command sent to sd ram
|
|
|
|
reg [9:0] sd_refresh = 10'd0;
|
|
reg sd_auto_refresh = 1'b0;
|
|
wire sd_req = wb_stb & wb_cyc & ~wb_ack;
|
|
wire sd_reading = wb_stb & wb_cyc & ~wb_we;
|
|
wire sd_writing = wb_stb & wb_cyc & wb_we;
|
|
|
|
localparam CYCLE_IDLE = 4'd0;
|
|
localparam CYCLE_RAS_START = CYCLE_IDLE;
|
|
localparam CYCLE_RFSH_START = CYCLE_RAS_START;
|
|
localparam CYCLE_CAS0 = CYCLE_RAS_START + RASCAS_DELAY;
|
|
localparam CYCLE_CAS1 = CYCLE_CAS0 + 4'd1;
|
|
localparam CYCLE_CAS2 = CYCLE_CAS1 + 4'd1;
|
|
localparam CYCLE_CAS3 = CYCLE_CAS2 + 4'd1;
|
|
localparam CYCLE_READ0 = CYCLE_CAS0 + CAS_LATENCY + 4'd2;
|
|
localparam CYCLE_READ1 = CYCLE_READ0+ 1'd1;
|
|
localparam CYCLE_READ2 = CYCLE_READ1+ 1'd1;
|
|
localparam CYCLE_READ3 = CYCLE_READ2+ 1'd1;
|
|
localparam CYCLE_READ4 = CYCLE_READ3+ 1'd1;
|
|
localparam CYCLE_READ5 = CYCLE_READ4+ 1'd1;
|
|
localparam CYCLE_READ6 = CYCLE_READ5+ 1'd1;
|
|
localparam CYCLE_READ7 = CYCLE_READ6+ 1'd1;
|
|
localparam CYCLE_RFSH_END = CYCLE_RFSH_START + RFC_DELAY;
|
|
|
|
localparam RAM_CLK = 126000000;
|
|
localparam REFRESH_PERIOD = (RAM_CLK / (16 * 8192));
|
|
|
|
always @(posedge sd_clk) begin
|
|
reg sd_reqD, sd_reqD2;
|
|
reg sd_newreq;
|
|
reg [3:0] sd_cycle = CYCLE_IDLE;
|
|
reg [2:0] word;
|
|
reg [15:0] sd_dq_reg;
|
|
|
|
sd_dq <= 16'bZZZZZZZZZZZZZZZZ;
|
|
sd_cmd <= CMD_NOP;
|
|
|
|
sd_dq_reg <= sd_dq;
|
|
|
|
sd_reqD <= sd_req;
|
|
if(~sd_reqD & sd_req) sd_newreq <= 1;
|
|
|
|
// count while the cycle is active
|
|
if(sd_cycle != CYCLE_IDLE) sd_cycle <= sd_cycle + 3'd1;
|
|
sd_refresh <= sd_refresh + 9'd1;
|
|
|
|
if (sd_rst) reset <= 0;
|
|
else begin
|
|
if (~&reset) begin
|
|
sd_ready <= 0;
|
|
sd_ba <= 0;
|
|
word <= 0;
|
|
sd_cycle <= CYCLE_IDLE;
|
|
sd_auto_refresh <= 0;
|
|
sd_refresh <= 0;
|
|
|
|
reset <= reset + 1'd1;
|
|
|
|
if(reset == 32 || reset == 96) begin
|
|
sd_cmd <= CMD_PRECHARGE;
|
|
sd_addr <= 13'b0010000000000; // precharge all banks
|
|
end
|
|
|
|
if(reset == 208) begin
|
|
sd_cmd <= CMD_LOAD_MODE;
|
|
sd_addr <= MODE;
|
|
end
|
|
end
|
|
else begin
|
|
sd_ready <= 1;
|
|
|
|
if(word) begin
|
|
word <= word + 1'd1;
|
|
sd_dat[word] <= sd_dq_reg;
|
|
end
|
|
|
|
if (sd_auto_refresh) begin
|
|
if(sd_cycle >= CYCLE_RFSH_END) begin
|
|
sd_auto_refresh <= 0;
|
|
sd_cycle <= CYCLE_IDLE;
|
|
end
|
|
end
|
|
else begin
|
|
case(sd_cycle)
|
|
CYCLE_IDLE: begin
|
|
if (sd_refresh > REFRESH_PERIOD) begin
|
|
// this is the auto refresh code.
|
|
// it kicks in so that 8192 auto refreshes are
|
|
// issued in a 64ms period. Other bus operations
|
|
// are stalled during this period.
|
|
sd_auto_refresh<= 1;
|
|
sd_refresh <= 0;
|
|
sd_cmd <= CMD_AUTO_REFRESH;
|
|
sd_cycle <= sd_cycle + 3'd1;
|
|
end
|
|
else if(sd_newreq) begin
|
|
sd_cmd <= CMD_ACTIVE;
|
|
sd_addr <= wb_adr[21:10];
|
|
sd_ba <= wb_adr[23:22];
|
|
sd_cycle <= sd_cycle + 3'd1;
|
|
end
|
|
end
|
|
|
|
// this is the first CAS cycle
|
|
CYCLE_CAS0: begin
|
|
// always, always read on a 32bit boundary and completely ignore the lsb of wb_adr.
|
|
sd_addr <= { 4'b0000, wb_adr[9:2], 1'b0 }; // no auto precharge
|
|
|
|
if (sd_reading) begin
|
|
sd_cmd <= CMD_READ;
|
|
sd_addr[10] <= 1; // auto precharge
|
|
end else if (sd_writing) begin
|
|
sd_cmd <= CMD_WRITE;
|
|
sd_addr[12:11] <= ~wb_sel[1:0];
|
|
sd_dq <= wb_dat_i[15:0];
|
|
end
|
|
end
|
|
|
|
CYCLE_CAS1: begin
|
|
// now we access the second part of the 32 bit location.
|
|
if (sd_writing) begin
|
|
sd_addr[10] <= 1; // auto precharge
|
|
sd_addr[0] <= 1;
|
|
sd_cmd <= CMD_WRITE;
|
|
sd_addr[12:11] <= ~wb_sel[3:2];
|
|
sd_done <= ~sd_done;
|
|
sd_newreq <= 0;
|
|
sd_dq <= wb_dat_i[31:16];
|
|
end
|
|
end
|
|
|
|
CYCLE_READ0: begin
|
|
if (sd_reading) begin
|
|
sd_dat[0] <= sd_dq_reg;
|
|
word <= 1;
|
|
end else begin
|
|
if (sd_writing) sd_cycle <= CYCLE_IDLE;
|
|
end
|
|
end
|
|
CYCLE_READ1: begin
|
|
sd_done <= ~sd_done;
|
|
sd_newreq <= 0;
|
|
end
|
|
|
|
CYCLE_READ5: begin
|
|
sd_cycle <= CYCLE_IDLE;
|
|
end
|
|
endcase
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
always @(posedge wb_clk) begin
|
|
reg sd_doneD;
|
|
reg [1:0] word;
|
|
|
|
sd_doneD <= sd_done;
|
|
wb_ack <= 0;
|
|
|
|
if(word) word <= word + 1'd1;
|
|
|
|
if (wb_stb & wb_cyc) begin
|
|
if ((sd_done ^ sd_doneD) & ~wb_ack) begin
|
|
wb_dat_o <= {sd_dat[1],sd_dat[0]};
|
|
word <= ~wb_cti[2] & (wb_cti[1] ^ wb_cti[0]); // burst constant/incremental
|
|
wb_ack <= 1;
|
|
end
|
|
|
|
if (word) begin
|
|
wb_dat_o <= {sd_dat[{word,1'b1}],sd_dat[{word,1'b0}]};
|
|
wb_ack <= 1;
|
|
end
|
|
end
|
|
else begin
|
|
word <= 0;
|
|
end
|
|
end
|
|
|
|
// drive control signals according to current command
|
|
assign sd_cke = 1;
|
|
assign sd_cs_n = 0;
|
|
assign sd_ras_n = sd_cmd[2];
|
|
assign sd_cas_n = sd_cmd[1];
|
|
assign sd_we_n = sd_cmd[0];
|
|
assign sd_dqm = sd_addr[12:11];
|
|
|
|
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(sd_clk),
|
|
.dataout(sd_clk_out),
|
|
.aclr(1'b0),
|
|
.aset(1'b0),
|
|
.oe(1'b1),
|
|
.outclocken(1'b1),
|
|
.sclr(1'b0),
|
|
.sset(1'b0)
|
|
);
|
|
|
|
endmodule
|