126 lines
4.0 KiB
Verilog
126 lines
4.0 KiB
Verilog
///////////////////////////////////////////////
|
|
// Demo SDRAM controller for MT48LC1M16A1 legacy SDRAM
|
|
// (C) fpga4fun.com & KNJN LLC 2014
|
|
|
|
// The MT48LC1M16A1 is a 16Mb SDRAM arranged in 1M x 16bits (using 2 banks)
|
|
|
|
// This controller feature set has been reduced to make it easy to understand
|
|
// It is based on a more complete controller targeted for Xylo-EM and Xylo-LM boards
|
|
|
|
// Assumptions:
|
|
// 1. the SDRAM has been initialized with CAS latency=2, and any valid burst mode
|
|
// 2. the read agent is active enough to refresh the RAM (if not, add a refresh timer)
|
|
|
|
// For more info, check
|
|
// http://www.fpga4fun.com/SDRAM.html
|
|
|
|
///////////////////////////////////////////////
|
|
module SDRAM_ctrl(
|
|
input clk,
|
|
|
|
// read agent
|
|
input RdReq,
|
|
output RdGnt,
|
|
input [19:0] RdAddr,
|
|
output reg [15:0] RdData,
|
|
output RdDataValid,
|
|
|
|
// write agent
|
|
input WrReq,
|
|
output WrGnt,
|
|
input [19:0] WrAddr,
|
|
input [15:0] WrData,
|
|
|
|
// SDRAM
|
|
output SDRAM_CKE, SDRAM_WEn, SDRAM_CASn, SDRAM_RASn,
|
|
output reg [10:0] SDRAM_A,
|
|
output reg [0:0] SDRAM_BA,
|
|
output reg [1:0] SDRAM_DQM = 2'b11,
|
|
inout [15:0] SDRAM_DQ
|
|
);
|
|
|
|
assign SDRAM_CKE = 1'b1;
|
|
|
|
localparam [2:0] SDRAM_CMD_LOADMODE = 3'b000;
|
|
localparam [2:0] SDRAM_CMD_REFRESH = 3'b001;
|
|
localparam [2:0] SDRAM_CMD_PRECHARGE = 3'b010;
|
|
localparam [2:0] SDRAM_CMD_ACTIVE = 3'b011;
|
|
localparam [2:0] SDRAM_CMD_WRITE = 3'b100;
|
|
localparam [2:0] SDRAM_CMD_READ = 3'b101;
|
|
localparam [2:0] SDRAM_CMD_NOP = 3'b111;
|
|
|
|
reg [2:0] SDRAM_CMD = SDRAM_CMD_NOP;
|
|
assign {SDRAM_RASn, SDRAM_CASn, SDRAM_WEn} = SDRAM_CMD;
|
|
|
|
// here we decide which of reads or writes have priority
|
|
wire read_now = RdReq; // give priority to read requests
|
|
wire write_now = ~RdReq & WrReq; // and if a read is not requested, give writes a chance...
|
|
|
|
reg [1:0] state=0;
|
|
reg ReadSelected=0; always @(posedge clk) if(state==2'h0) ReadSelected <= read_now;
|
|
wire WriteSelected = ~ReadSelected;
|
|
|
|
wire ReadCycle = (state==2'h0) ? read_now : ReadSelected;
|
|
wire [19:0] Addr = ReadCycle ? RdAddr : WrAddr;
|
|
reg [19:0] AddrR=0; always @(posedge clk) AddrR <= Addr;
|
|
|
|
wire SameRowAndBank = (Addr[19:8]==AddrR[19:8]);
|
|
assign RdGnt = (state==2'h0 & read_now) | (state==2'h1 & ReadSelected & RdReq & SameRowAndBank);
|
|
assign WrGnt = (state==2'h0 & write_now) | (state==2'h1 & WriteSelected & WrReq & SameRowAndBank);
|
|
|
|
always @(posedge clk)
|
|
case(state)
|
|
2'h0: begin
|
|
if(RdReq | WrReq) begin // is there a read or write request?
|
|
SDRAM_CMD <= SDRAM_CMD_ACTIVE; // if so activate
|
|
SDRAM_BA <= Addr[19]; // this bank
|
|
SDRAM_A <= Addr[18:8]; // this row
|
|
SDRAM_DQM <= 2'b11;
|
|
state <= 2'h1;
|
|
end
|
|
else
|
|
begin
|
|
SDRAM_CMD <= SDRAM_CMD_NOP; // otherwise stay idle
|
|
SDRAM_BA <= 0;
|
|
SDRAM_A <= 0;
|
|
SDRAM_DQM <= 2'b11;
|
|
state <= 2'h0;
|
|
end
|
|
end
|
|
2'h1: begin
|
|
SDRAM_CMD <= ReadSelected ? SDRAM_CMD_READ : SDRAM_CMD_WRITE;
|
|
SDRAM_BA <= AddrR[19];
|
|
SDRAM_A[9:0] <= {2'b00, AddrR[7:0]}; // column
|
|
SDRAM_A[10] <= 1'b0; // no auto-precharge
|
|
SDRAM_DQM <= 2'b00;
|
|
state <= (ReadSelected ? RdReq : WrReq) & SameRowAndBank ? 2'h1 : 2'h2;
|
|
end
|
|
2'h2: begin
|
|
SDRAM_CMD <= SDRAM_CMD_PRECHARGE; // close the row when we're done with it
|
|
SDRAM_BA <= 0;
|
|
SDRAM_A <= 11'b100_0000_0000; // all banks precharge
|
|
SDRAM_DQM <= 2'b11;
|
|
state <= 2'h0;
|
|
end
|
|
2'h3: begin
|
|
SDRAM_CMD <= SDRAM_CMD_NOP;
|
|
SDRAM_BA <= 0;
|
|
SDRAM_A <= 0;
|
|
SDRAM_DQM <= 2'b11;
|
|
state <= 2'h0;
|
|
end
|
|
endcase
|
|
|
|
localparam trl = 4; // total read latency is the SDRAM CAS-latency (two) plus the SDRAM controller induced latency (two)
|
|
reg [trl-1:0] RdDataValidPipe; always @(posedge clk) RdDataValidPipe <= {RdDataValidPipe[trl-2:0], state==2'h1 & ReadSelected};
|
|
assign RdDataValid = RdDataValidPipe[trl-1];
|
|
always @(posedge clk) RdData <= SDRAM_DQ;
|
|
|
|
reg SDRAM_DQ_OE = 1'b0; always @(posedge clk) SDRAM_DQ_OE <= (state==2'h1) & WriteSelected;
|
|
reg [15:0] WrData1=0; always @(posedge clk) WrData1 <= WrData;
|
|
reg [15:0] WrData2=0; always @(posedge clk) WrData2 <= WrData1;
|
|
|
|
assign SDRAM_DQ = SDRAM_DQ_OE ? WrData2 : 16'hZZZZ;
|
|
endmodule
|
|
///////////////////////////////////////////////
|