Files
de0-zx-spectrum/sdram.v
T

437 lines
14 KiB
Verilog
Raw Normal View History

/*
This SDRAM controller is for the Mojo's SDRAM shield which uses
a 48LC32M8A2-7E SDRAM chip. This module was designed under the
assumption that the click rate is 100MHz. Timing values would
need to be re-evaluated under different clock rates.
This controller features two baisc improvements over the most
basic of controllers. It does burst reads and writes of 4 bytes,
and it only closes a row when it has to.
*/
module sdram (
input clk,
input rst,
// these signals go directly to the IO pins
output sdram_clk,
output sdram_cle,
output sdram_cs,
output sdram_cas,
output sdram_ras,
output sdram_we,
output sdram_dqm,
output [1:0] sdram_ba,
output [12:0] sdram_a,
inout [7:0] sdram_dq,
// User interface
input [22:0] addr, // address to read/write
input rw, // 1 = write, 0 = read
input [31:0] data_in, // data from a read
output [31:0] data_out, // data for a write
output busy, // controller is busy when high
input in_valid, // pulse high to initiate a read/write
output out_valid // pulses high when data from read is valid
);
// Commands for the SDRAM
localparam CMD_UNSELECTED = 4'b1000;
localparam CMD_NOP = 4'b0111;
localparam CMD_ACTIVE = 4'b0011;
localparam CMD_READ = 4'b0101;
localparam CMD_WRITE = 4'b0100;
localparam CMD_TERMINATE = 4'b0110;
localparam CMD_PRECHARGE = 4'b0010;
localparam CMD_REFRESH = 4'b0001;
localparam CMD_LOAD_MODE_REG = 4'b0000;
localparam STATE_SIZE = 4;
localparam INIT = 0,
WAIT = 1,
PRECHARGE_INIT = 2,
REFRESH_INIT_1 = 3,
REFRESH_INIT_2 = 4,
LOAD_MODE_REG = 5,
IDLE = 6,
REFRESH = 7,
ACTIVATE = 8,
READ = 9,
READ_RES = 10,
WRITE = 11,
PRECHARGE = 12;
wire sdram_clk_ddr;
// This is used to drive the SDRAM clock
ODDR2 #(
.DDR_ALIGNMENT("NONE"),
.INIT(1'b0),
.SRTYPE("SYNC")
) ODDR2_inst (
.Q(sdram_clk_ddr), // 1-bit DDR output data
.C0(clk), // 1-bit clock input
.C1(~clk), // 1-bit clock input
.CE(1'b1), // 1-bit clock enable input
.D0(1'b0), // 1-bit data input (associated with C0)
.D1(1'b1), // 1-bit data input (associated with C1)
.R(1'b0), // 1-bit reset input
.S(1'b0) // 1-bit set input
);
IODELAY2 #(
.IDELAY_VALUE(0),
.IDELAY_MODE("NORMAL"),
.ODELAY_VALUE(100), // value of 100 seems to work at 100MHz
.IDELAY_TYPE("FIXED"),
.DELAY_SRC("ODATAIN"),
.DATA_RATE("SDR")
) IODELAY_inst (
.IDATAIN(1'b0),
.T(1'b0),
.ODATAIN(sdram_clk_ddr),
.CAL(1'b0),
.IOCLK0(1'b0),
.IOCLK1(1'b0),
.CLK(1'b0),
.INC(1'b0),
.CE(1'b0),
.RST(1'b0),
.BUSY(),
.DATAOUT(),
.DATAOUT2(),
.TOUT(),
.DOUT(sdram_clk)
);
// registers for SDRAM signals
reg cle_d, dqm_d;
reg [3:0] cmd_d;
reg [1:0] ba_d;
reg [12:0] a_d;
reg [7:0] dq_d;
reg [7:0] dqi_d;
// We want the output/input registers to be embedded in the
// IO buffers so we set IOB to "TRUE". This is to ensure all
// the signals are sent and received at the same time.
(* IOB = "TRUE" *)
reg cle_q, dqm_q;
(* IOB = "TRUE" *)
reg [3:0] cmd_q;
(* IOB = "TRUE" *)
reg [1:0] ba_q;
(* IOB = "TRUE" *)
reg [12:0] a_q;
(* IOB = "TRUE" *)
reg [7:0] dq_q;
(* IOB = "TRUE" *)
reg [7:0] dqi_q;
reg dq_en_d, dq_en_q;
// Output assignments
assign sdram_cle = cle_q;
assign sdram_cs = cmd_q[3];
assign sdram_ras = cmd_q[2];
assign sdram_cas = cmd_q[1];
assign sdram_we = cmd_q[0];
assign sdram_dqm = dqm_q;
assign sdram_ba = ba_q;
assign sdram_a = a_q;
assign sdram_dq = dq_en_q ? dq_q : 8'hZZ; // only drive when dq_en_q is 1
reg [STATE_SIZE-1:0] state_d, state_q = INIT;
reg [STATE_SIZE-1:0] next_state_d, next_state_q;
reg [22:0] addr_d, addr_q;
reg [31:0] data_d, data_q;
reg out_valid_d, out_valid_q;
assign data_out = data_q;
assign busy = !ready_q;
assign out_valid = out_valid_q;
reg [15:0] delay_ctr_d, delay_ctr_q;
reg [1:0] byte_ctr_d, byte_ctr_q;
reg [9:0] refresh_ctr_d, refresh_ctr_q;
reg refresh_flag_d, refresh_flag_q;
reg ready_d, ready_q;
reg saved_rw_d, saved_rw_q;
reg [22:0] saved_addr_d, saved_addr_q;
reg [31:0] saved_data_d, saved_data_q;
reg rw_op_d, rw_op_q;
reg [3:0] row_open_d, row_open_q;
reg [12:0] row_addr_d[3:0], row_addr_q[3:0];
reg [2:0] precharge_bank_d, precharge_bank_q;
integer i;
always @* begin
// Default values
dq_d = dq_q;
dqi_d = sdram_dq;
dq_en_d = 1'b0; // normally keep the bus in high-Z
cle_d = cle_q;
cmd_d = CMD_NOP; // default to NOP
dqm_d = 1'b0;
ba_d = 2'd0;
a_d = 25'd0;
state_d = state_q;
next_state_d = next_state_q;
delay_ctr_d = delay_ctr_q;
addr_d = addr_q;
data_d = data_q;
out_valid_d = 1'b0;
precharge_bank_d = precharge_bank_q;
rw_op_d = rw_op_q;
byte_ctr_d = 2'd0;
row_open_d = row_open_q;
// row_addr is a 2d array and must be coppied this way
for (i = 0; i < 4; i = i + 1)
row_addr_d[i] = row_addr_q[i];
// The data in the SDRAM must be refreshed periodically.
// This conter ensures that the data remains intact.
refresh_flag_d = refresh_flag_q;
refresh_ctr_d = refresh_ctr_q + 1'b1;
if (refresh_ctr_q > 10'd750) begin
refresh_ctr_d = 10'd0;
refresh_flag_d = 1'b1;
end
saved_rw_d = saved_rw_q;
saved_data_d = saved_data_q;
saved_addr_d = saved_addr_q;
ready_d = ready_q;
// This is a queue of 1 for read/write operations.
// When the queue is empty we aren't busy and can
// accept another request.
if (ready_q && in_valid) begin
saved_rw_d = rw;
saved_data_d = data_in;
saved_addr_d = addr;
ready_d = 1'b0;
end
case (state_q)
///// INITALIZATION /////
INIT: begin
ready_d = 1'b0;
row_open_d = 4'b0;
out_valid_d = 1'b0;
a_d = 13'b0;
ba_d = 2'b0;
cle_d = 1'b1;
state_d = WAIT;
delay_ctr_d = 16'd10100; // wait for 101us
next_state_d = PRECHARGE_INIT;
dq_en_d = 1'b0;
end
WAIT: begin
delay_ctr_d = delay_ctr_q - 1'b1;
if (delay_ctr_q == 13'd0) begin
state_d = next_state_q;
if (next_state_q == WRITE) begin
dq_en_d = 1'b1; // enable the bus early
dq_d = data_q[7:0];
end
end
end
PRECHARGE_INIT: begin
cmd_d = CMD_PRECHARGE;
a_d[10] = 1'b1; // all banks
ba_d = 2'd0;
state_d = WAIT;
next_state_d = REFRESH_INIT_1;
delay_ctr_d = 13'd0;
end
REFRESH_INIT_1: begin
cmd_d = CMD_REFRESH;
state_d = WAIT;
delay_ctr_d = 13'd7;
next_state_d = REFRESH_INIT_2;
end
REFRESH_INIT_2: begin
cmd_d = CMD_REFRESH;
state_d = WAIT;
delay_ctr_d = 13'd7;
next_state_d = LOAD_MODE_REG;
end
LOAD_MODE_REG: begin
cmd_d = CMD_LOAD_MODE_REG;
ba_d = 2'b0;
// Reserved, Burst Access, Standard Op, CAS = 2, Sequential, Burst = 4
a_d = {3'b000, 1'b0, 2'b00, 3'b010, 1'b0, 3'b010}; //010
state_d = WAIT;
delay_ctr_d = 13'd1;
next_state_d = IDLE;
refresh_flag_d = 1'b0;
refresh_ctr_d = 10'b1;
ready_d = 1'b1;
end
///// IDLE STATE /////
IDLE: begin
if (refresh_flag_q) begin // we need to do a refresh
state_d = PRECHARGE;
next_state_d = REFRESH;
precharge_bank_d = 3'b100; // all banks
refresh_flag_d = 1'b0; // clear the refresh flag
end else if (!ready_q) begin // operation waiting
ready_d = 1'b1; // clear the queue
rw_op_d = saved_rw_q; // save the values we'll need later
addr_d = saved_addr_q;
if (saved_rw_q) // Write
data_d = saved_data_q;
// if the row is open we don't have to activate it
if (row_open_q[saved_addr_q[9:8]]) begin
if (row_addr_q[saved_addr_q[9:8]] == saved_addr_q[22:10]) begin
// Row is already open
if (saved_rw_q)
state_d = WRITE;
else
state_d = READ;
end else begin
// A different row in the bank is open
state_d = PRECHARGE; // precharge open row
precharge_bank_d = {1'b0, saved_addr_q[9:8]};
next_state_d = ACTIVATE; // open current row
end
end else begin
// no rows open
state_d = ACTIVATE; // open the row
end
end
end
///// REFRESH /////
REFRESH: begin
cmd_d = CMD_REFRESH;
state_d = WAIT;
delay_ctr_d = 13'd6; // gotta wait 7 clocks (66ns)
next_state_d = IDLE;
end
///// ACTIVATE /////
ACTIVATE: begin
cmd_d = CMD_ACTIVE;
a_d = addr_q[22:10];
ba_d = addr_q[9:8];
delay_ctr_d = 13'd0;
state_d = WAIT;
if (rw_op_q)
next_state_d = WRITE;
else
next_state_d = READ;
row_open_d[addr_q[9:8]] = 1'b1; // row is now open
row_addr_d[addr_q[9:8]] = addr_q[22:10];
end
///// READ /////
READ: begin
cmd_d = CMD_READ;
a_d = {2'b0, 1'b0, addr_q[7:0], 2'b0};
ba_d = addr_q[9:8];
state_d = WAIT;
delay_ctr_d = 13'd2; // wait for the data to show up
next_state_d = READ_RES;
end
READ_RES: begin
byte_ctr_d = byte_ctr_q + 1'b1; // we need to read in 4 bytes
data_d = {dqi_q, data_q[31:8]}; // shift the data in
if (byte_ctr_q == 2'd3) begin
out_valid_d = 1'b1;
state_d = IDLE;
end
end
///// WRITE /////
WRITE: begin
byte_ctr_d = byte_ctr_q + 1'b1; // send out 4 bytes
if (byte_ctr_q == 2'd0) // first byte send write command
cmd_d = CMD_WRITE;
dq_d = data_q[7:0];
data_d = {8'h00, data_q[31:8]}; // shift the data out
dq_en_d = 1'b1; // enable out bus
a_d = {2'b0, 1'b0, addr_q[7:0], 2'b00};
ba_d = addr_q[9:8];
if (byte_ctr_q == 2'd3) begin
state_d = IDLE;
end
end
///// PRECHARGE /////
PRECHARGE: begin
cmd_d = CMD_PRECHARGE;
a_d[10] = precharge_bank_q[2]; // all banks
ba_d = precharge_bank_q[1:0];
state_d = WAIT;
delay_ctr_d = 13'd0;
if (precharge_bank_q[2]) begin
row_open_d = 4'b0000; // closed all rows
end else begin
row_open_d[precharge_bank_q[1:0]] = 1'b0; // closed one row
end
end
default: state_d = INIT;
endcase
end
always @(posedge clk) begin
if(rst) begin
cle_q <= 1'b0;
dq_en_q <= 1'b0;
state_q <= INIT;
ready_q <= 1'b0;
end else begin
cle_q <= cle_d;
dq_en_q <= dq_en_d;
state_q <= state_d;
ready_q <= ready_d;
end
saved_rw_q <= saved_rw_d;
saved_data_q <= saved_data_d;
saved_addr_q <= saved_addr_d;
cmd_q <= cmd_d;
dqm_q <= dqm_d;
ba_q <= ba_d;
a_q <= a_d;
dq_q <= dq_d;
dqi_q <= dqi_d;
next_state_q <= next_state_d;
refresh_flag_q <= refresh_flag_d;
refresh_ctr_q <= refresh_ctr_d;
data_q <= data_d;
addr_q <= addr_d;
out_valid_q <= out_valid_d;
row_open_q <= row_open_d;
for (i = 0; i < 4; i = i + 1)
row_addr_q[i] <= row_addr_d[i];
precharge_bank_q <= precharge_bank_d;
rw_op_q <= rw_op_d;
byte_ctr_q <= byte_ctr_d;
delay_ctr_q <= delay_ctr_d;
end
endmodule