/* * Implements a DDR-SDRAM interface with the following characteristics: * - 2 GBit Available Memory * - 16 Bit Physical Data Width * - 128 Bit Data Burst (8x Words) * * Concepts and initial source from: * https://numato.com/kb/learning-fpga-verilog-beginners-guide-part-6-ddr-sdram-a7/ */ module ddr_sdram_interface(input clk_100mhz, input clk_166mhz, input clk_200mhz, output clk_sdram, // Physical IO inout [15:0] ddr3_dq, inout [ 1:0] ddr3_dqs_n, inout [ 1:0] ddr3_dqs_p, output [13:0] ddr3_addr, output [ 2:0] ddr3_ba, output ddr3_ras_n, output ddr3_cas_n, output ddr3_we_n, output ddr3_reset_n, output [ 0:0] ddr3_ck_p, output [ 0:0] ddr3_ck_n, output [ 0:0] ddr3_cke, output [ 0:0] ddr3_cs_n, output [ 1:0] ddr3_dm, output [ 0:0] ddr3_odt, // Read Data and Control input readEn, input [ 24:0] readAddr, output reg [127:0] readData = 0, output readReady, // Write Data and Control input writeEn, input [ 24:0] writeAddr, input [127:0] writeData, input [ 15:0] writeMask, output writeReady); /* * Command Definitions */ localparam CMD_WRITE = 3'b000; localparam CMD_READ = 3'b001; /* * Generate Power-on-Reset */ reg [9:0] por_counter = 1023; always @ (posedge clk_100mhz) begin if (por_counter) begin por_counter <= por_counter - 1 ; end end wire resetn = (por_counter == 0); /* * Internal DDR3 Signals */ wire calib_done; reg [27:0] app_addr = 0; reg [ 2:0] app_cmd = 0; reg app_en = 0; wire app_rdy; reg [127:0] app_wdf_data = 0; reg [ 15:0] app_wdf_mask = 0; reg app_wdf_wren = 0; wire app_wdf_rdy; wire [127:0] app_rd_data; wire app_rd_data_valid; wire ui_clk; wire ui_clk_sync_rst; assign clk_sdram = ui_clk; /* * Instantiate DDR3 Interface */ ddr_sdram_mig ddr_sdram_mig ( // DDR3 Physical IO .ddr3_addr (ddr3_addr), .ddr3_ba (ddr3_ba), .ddr3_cas_n (ddr3_cas_n), .ddr3_ck_n (ddr3_ck_n), .ddr3_ck_p (ddr3_ck_p), .ddr3_cke (ddr3_cke), .ddr3_ras_n (ddr3_ras_n), .ddr3_reset_n (ddr3_reset_n), .ddr3_we_n (ddr3_we_n), .ddr3_dq (ddr3_dq), .ddr3_dqs_n (ddr3_dqs_n), .ddr3_dqs_p (ddr3_dqs_p), .ddr3_cs_n (ddr3_cs_n), .ddr3_dm (ddr3_dm), .ddr3_odt (ddr3_odt), // Calibration state flag .init_calib_complete (calib_done), // User interface data .app_addr (app_addr), .app_cmd (app_cmd), .app_en (app_en), .app_wdf_data (app_wdf_data), .app_wdf_end ( 1'b1), .app_wdf_mask (app_wdf_mask), .app_wdf_wren (app_wdf_wren), .app_rd_data (app_rd_data), .app_rd_data_end (), .app_rd_data_valid (app_rd_data_valid), .app_rdy (app_rdy), .app_wdf_rdy (app_wdf_rdy), .app_sr_req (1'b0), .app_ref_req (1'b0), .app_zq_req (1'b0), .app_sr_active (), .app_ref_ack (), .app_zq_ack (), // Feedback data .device_temp (), .ui_clk (ui_clk), .ui_clk_sync_rst (ui_clk_sync_rst), // Clock and reset .sys_rst (resetn), .sys_clk_i (clk_166mhz), .clk_ref_i (clk_200mhz) ); /* * State Definitions */ localparam IDLE = 3'd0; localparam WRITE = 3'd1; localparam WRITE_DONE = 3'd2; localparam READ = 3'd3; localparam READ_DONE = 3'd4; reg [2:0] state = IDLE; /* * Cross 'readEn' to 'doRead' Domain */ reg doRead = 0; always @(posedge ui_clk) begin if (state == READ) begin doRead <= 0; end else if (readEn) begin doRead <= 1; end end assign readReady = (calib_done & ~doRead); /* * Cross 'writeEn' to 'doWrite' Domain */ reg doWrite = 0; always @(posedge ui_clk) begin if (state == WRITE) begin doWrite <= 0; end else if (writeEn) begin doWrite <= 1; end end assign writeReady = (calib_done & ~doWrite); /* * State Machine * Process reset, wait for calibration, then oscillate between read and write. */ always @(posedge ui_clk) begin if (ui_clk_sync_rst) begin state <= IDLE; app_en <= 0; app_wdf_wren <= 0; end else begin case (state) IDLE: begin // Is calibrated? if (calib_done) begin // Pending read? if (doRead) begin state <= READ; // Pending write? end else if (doWrite) begin state <= WRITE; end end end WRITE: begin // Command write if (app_rdy & app_wdf_rdy) begin state <= WRITE_DONE; app_en <= 1; app_wdf_wren <= 1; app_addr <= 'd8 * writeAddr; app_cmd <= CMD_WRITE; app_wdf_data <= writeData; app_wdf_mask <= writeMask; end end WRITE_DONE: begin // Wait for write to finish if (app_rdy & app_en) begin app_en <= 0; end if (app_wdf_rdy & app_wdf_wren) begin app_wdf_wren <= 0; end if (~app_en & ~app_wdf_wren) begin // Next state if (doRead & calib_done) begin state <= READ; end else if (doWrite & calib_done) begin state <= WRITE; end else begin state <= IDLE; end end end READ: begin // Command read if (app_rdy) begin app_en <= 1; app_addr <= 'd8 * readAddr; app_cmd <= CMD_READ; state <= READ_DONE; end end READ_DONE: begin // Wait for read to finish if (app_rdy & app_en) begin app_en <= 0; end if (app_rd_data_valid) begin readData <= app_rd_data; // Next state if (doWrite & calib_done) begin state <= WRITE; end else if (doRead & calib_done) begin state <= READ; end else begin state <= IDLE; end end end default: state <= IDLE; endcase end end endmodule