/** * simple controller for ISSI IS42S16160G-7 SDRAM found in De0 Nano * 16Mbit x 16 data bit bus (32 megabytes) * Default options * 133Mhz * CAS 3 * * Very simple host interface * * No burst support * * haddr - address for reading and wriging 16 bits of data * * data_input - data for writing, latched in when wr_enable is highz0 * * data_output - data for reading, comes available sometime * *few clocks* after rd_enable and address is presented on bus * * rst_n - start init ram process * * rd_enable - read enable, on clk posedge haddr will be latched in, * after *few clocks* data will be available on the data_output port * * wr_enable - write enable, on clk posedge haddr and data_input will * be latched in, after *few clocks* data will be written to sdram * * Theory * This simple host interface has a busy signal to tell you when you are * not able to issue commands. */ module sdram_controller_new ( /* HOST INTERFACE */ wr_addr, wr_data, wr_enable, rd_addr, rd_data, rd_ready, rd_enable, busy, rst_n, clk, /* SDRAM SIDE */ addr, bank_addr, data, clock_enable, cs_n, ras_n, cas_n, we_n, data_mask_low, data_mask_high ); /* Internal Parameters */ parameter ROW_WIDTH = 13; parameter COL_WIDTH = 9; parameter BANK_WIDTH = 2; parameter SDRADDR_WIDTH = ROW_WIDTH > COL_WIDTH ? ROW_WIDTH : COL_WIDTH; parameter HADDR_WIDTH = BANK_WIDTH + ROW_WIDTH + COL_WIDTH; parameter CLK_FREQUENCY = 133; // Mhz parameter REFRESH_TIME = 32; // ms (how often we need to refresh) parameter REFRESH_COUNT = 8192; // cycles (how many refreshes required per refresh time) // clk / refresh = clk / sec // , sec / refbatch // , ref / refbatch localparam CYCLES_BETWEEN_REFRESH = ( CLK_FREQUENCY * 1_000 * REFRESH_TIME ) / REFRESH_COUNT; // STATES - State localparam IDLE = 5'b00000; localparam INIT_NOP1 = 5'b01000, INIT_PRE1 = 5'b01001, INIT_NOP1_1=5'b00101, INIT_REF1 = 5'b01010, INIT_NOP2 = 5'b01011, INIT_REF2 = 5'b01100, INIT_NOP3 = 5'b01101, INIT_LOAD = 5'b01110, INIT_NOP4 = 5'b01111; localparam REF_PRE = 5'b00001, REF_NOP1 = 5'b00010, REF_REF = 5'b00011, REF_NOP2 = 5'b00100; localparam READ_ACT = 5'b10000, READ_NOP1 = 5'b10001, READ_CAS = 5'b10010, READ_NOP2 = 5'b10011, READ_READ = 5'b10100; localparam WRIT_ACT = 5'b11000, WRIT_NOP1 = 5'b11001, WRIT_CAS = 5'b11010, WRIT_NOP2 = 5'b11011; // Commands CCRCWBBA // ESSSE100 localparam CMD_PALL = 8'b10010001, CMD_REF = 8'b10001000, CMD_NOP = 8'b10111000, CMD_MRS = 8'b1000000x, CMD_BACT = 8'b10011xxx, CMD_READ = 8'b10101xx1, CMD_WRIT = 8'b10100xx1; /* Interface Definition */ /* HOST INTERFACE */ input [HADDR_WIDTH-1:0] wr_addr; input [15:0] wr_data; input wr_enable; input [HADDR_WIDTH-1:0] rd_addr; output [15:0] rd_data; input rd_enable; output rd_ready; output busy; input rst_n; input clk; /* SDRAM SIDE */ output [SDRADDR_WIDTH-1:0] addr; output [BANK_WIDTH-1:0] bank_addr; inout [15:0] data; output clock_enable; output cs_n; output ras_n; output cas_n; output we_n; output data_mask_low; output data_mask_high; /* I/O Registers */ reg [HADDR_WIDTH-1:0] haddr_r; reg [15:0] wr_data_r; reg [15:0] rd_data_r; reg busy; reg data_mask_low_r; reg data_mask_high_r; reg [SDRADDR_WIDTH-1:0] addr_r; reg [BANK_WIDTH-1:0] bank_addr_r; reg rd_ready_r; wire [15:0] data_output; wire data_mask_low, data_mask_high; assign data_mask_high = data_mask_high_r; assign data_mask_low = data_mask_low_r; assign rd_data = rd_data_r; /* Internal Wiring */ reg [3:0] state_cnt; reg [9:0] refresh_cnt; reg [7:0] command; reg [4:0] state; // TODO output addr[6:4] when programming mode register reg [7:0] command_nxt; reg [3:0] state_cnt_nxt; reg [4:0] next; assign {clock_enable, cs_n, ras_n, cas_n, we_n} = command[7:3]; // state[4] will be set if mode is read/write assign bank_addr = (state[4]) ? bank_addr_r : command[2:1]; assign addr = (state[4] | state == INIT_LOAD) ? addr_r : { {SDRADDR_WIDTH-11{1'b0}}, command[0], 10'd0 }; assign data = (state == WRIT_CAS) ? wr_data_r : 16'bz; assign rd_ready = rd_ready_r; // HOST INTERFACE // all registered on posedge always @ (posedge clk) if (~rst_n) begin state <= INIT_NOP1; command <= CMD_NOP; state_cnt <= 4'hf; haddr_r <= {HADDR_WIDTH{1'b0}}; wr_data_r <= 16'b0; rd_data_r <= 16'b0; busy <= 1'b0; end else begin state <= next; command <= command_nxt; if (!state_cnt) state_cnt <= state_cnt_nxt; else state_cnt <= state_cnt - 1'b1; if (wr_enable) wr_data_r <= wr_data; if (state == READ_READ) begin rd_data_r <= data; rd_ready_r <= 1'b1; end else rd_ready_r <= 1'b0; busy <= state[4]; if (rd_enable) haddr_r <= rd_addr; else if (wr_enable) haddr_r <= wr_addr; end // Handle refresh counter always @ (posedge clk) if (~rst_n) refresh_cnt <= 10'b0; else if (state == REF_NOP2) refresh_cnt <= 10'b0; else refresh_cnt <= refresh_cnt + 1'b1; /* Handle logic for sending addresses to SDRAM based on current state*/ always @* begin if (state[4]) {data_mask_low_r, data_mask_high_r} = 2'b00; else {data_mask_low_r, data_mask_high_r} = 2'b11; bank_addr_r = 2'b00; addr_r = {SDRADDR_WIDTH{1'b0}}; if (state == READ_ACT | state == WRIT_ACT) begin bank_addr_r = haddr_r[HADDR_WIDTH-1:HADDR_WIDTH-(BANK_WIDTH)]; addr_r = haddr_r[HADDR_WIDTH-(BANK_WIDTH+1):HADDR_WIDTH-(BANK_WIDTH+ROW_WIDTH)]; end else if (state == READ_CAS | state == WRIT_CAS) begin // Send Column Address // Set bank to bank to precharge bank_addr_r = haddr_r[HADDR_WIDTH-1:HADDR_WIDTH-(BANK_WIDTH)]; // Examples for math // BANK ROW COL // HADDR_WIDTH 2 + 13 + 9 = 24 // SDRADDR_WIDTH 13 // Set CAS address to: // 0s, // 1 (A10 is always for auto precharge), // 0s, // column address addr_r = { {SDRADDR_WIDTH-(11){1'b0}}, 1'b1, /* A10 */ {10-COL_WIDTH{1'b0}}, haddr_r[COL_WIDTH-1:0] }; end else if (state == INIT_LOAD) begin // Program mode register during load cycle // B C SB // R A EUR // S S-3Q ST // T 654L210 addr_r = {{SDRADDR_WIDTH-10{1'b0}}, 10'b1000110000}; end end // Next state logic always @* begin state_cnt_nxt = 4'd0; command_nxt = CMD_NOP; if (state == IDLE) // Monitor for refresh or hold if (refresh_cnt >= CYCLES_BETWEEN_REFRESH) begin next = REF_PRE; command_nxt = CMD_PALL; end else if (rd_enable) begin next = READ_ACT; command_nxt = CMD_BACT; end else if (wr_enable) begin next = WRIT_ACT; command_nxt = CMD_BACT; end else begin // HOLD next = IDLE; end else if (!state_cnt) case (state) // INIT ENGINE INIT_NOP1: begin next = INIT_PRE1; command_nxt = CMD_PALL; end INIT_PRE1: begin next = INIT_NOP1_1; end INIT_NOP1_1: begin next = INIT_REF1; command_nxt = CMD_REF; end INIT_REF1: begin next = INIT_NOP2; state_cnt_nxt = 4'd7; end INIT_NOP2: begin next = INIT_REF2; command_nxt = CMD_REF; end INIT_REF2: begin next = INIT_NOP3; state_cnt_nxt = 4'd7; end INIT_NOP3: begin next = INIT_LOAD; command_nxt = CMD_MRS; end INIT_LOAD: begin next = INIT_NOP4; state_cnt_nxt = 4'd1; end // INIT_NOP4: default - IDLE // REFRESH REF_PRE: begin next = REF_NOP1; end REF_NOP1: begin next = REF_REF; command_nxt = CMD_REF; end REF_REF: begin next = REF_NOP2; state_cnt_nxt = 4'd7; end // REF_NOP2: default - IDLE // WRITE WRIT_ACT: begin next = WRIT_NOP1; state_cnt_nxt = 4'd1; end WRIT_NOP1: begin next = WRIT_CAS; command_nxt = CMD_WRIT; end WRIT_CAS: begin next = WRIT_NOP2; state_cnt_nxt = 4'd1; end // WRIT_NOP2: default - IDLE // READ READ_ACT: begin next = READ_NOP1; state_cnt_nxt = 4'd1; end READ_NOP1: begin next = READ_CAS; command_nxt = CMD_READ; end READ_CAS: begin next = READ_NOP2; state_cnt_nxt = 4'd1; end READ_NOP2: begin next = READ_READ; end // READ_READ: default - IDLE default: begin next = IDLE; end endcase else begin // Counter Not Reached - HOLD next = state; command_nxt = command; end end endmodule