/////////////////////////////////////////////// // 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 ///////////////////////////////////////////////