URL
https://opencores.org/ocsvn/xulalx25soc/xulalx25soc/trunk
Subversion Repositories xulalx25soc
[/] [xulalx25soc/] [trunk/] [rtl/] [wbsdram.v] - Rev 21
Go to most recent revision | Compare with Previous | Blame | View Log
/////////////////////////////////////////////////////////////////////////// // // Filename: wbsdram.v // // Project: XuLA2 board // // Purpose: Provide 32-bit wishbone access to the SDRAM memory on a XuLA2 // LX-25 board. Specifically, on each access, the controller will // activate an appropriate bank of RAM (the SDRAM has four banks), and // then issue the read/write command. In the case of walking off the // bank, the controller will activate the next bank before you get to it. // Upon concluding any wishbone access, all banks will be precharged and // returned to idle. // // This particular implementation represents a second generation version // because my first version was too complex. To speed things up, this // version includes an extra wait state where the wishbone inputs are // clocked into a flip flop before any action is taken on them. // // Creator: Dan Gisselquist, Ph.D. // Gisselquist Technology, LLC // /////////////////////////////////////////////////////////////////////////// // // Copyright (C) 2015, Gisselquist Technology, LLC // // This program is free software (firmware): you can redistribute it and/or // modify it under the terms of the GNU General Public License as published // by the Free Software Foundation, either version 3 of the License, or (at // your option) any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License // for more details. // // You should have received a copy of the GNU General Public License along // with this program. (It's in the $(ROOT)/doc directory, run make with no // target there if the PDF file isn't present.) If not, see // <http://www.gnu.org/licenses/> for a copy. // // License: GPL, v3, as defined and found on www.gnu.org, // http://www.gnu.org/licenses/gpl.html // // ///////////////////////////////////////////////////////////////////////////// // `define DMOD_GETINPUT 1'b0 `define DMOD_PUTOUTPUT 1'b1 `define RAM_OPERATIONAL 2'b00 `define RAM_POWER_UP 2'b01 `define RAM_SET_MODE 2'b10 `define RAM_INITIAL_REFRESH 2'b11 module wbsdram(i_clk, i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, i_wb_data, o_wb_ack, o_wb_stall, o_wb_data, o_ram_cs_n, o_ram_cke, o_ram_ras_n, o_ram_cas_n, o_ram_we_n, o_ram_bs, o_ram_addr, o_ram_dmod, i_ram_data, o_ram_data, o_ram_dqm, o_debug); parameter RDLY = 6; input i_clk; // Wishbone // inputs input i_wb_cyc, i_wb_stb, i_wb_we; input [22:0] i_wb_addr; input [31:0] i_wb_data; // outputs output wire o_wb_ack; output reg o_wb_stall; output wire [31:0] o_wb_data; // SDRAM control output wire o_ram_cke; output reg o_ram_cs_n, o_ram_ras_n, o_ram_cas_n, o_ram_we_n; output reg [1:0] o_ram_bs; output reg [12:0] o_ram_addr; output reg o_ram_dmod; input [15:0] i_ram_data; output reg [15:0] o_ram_data; output reg [1:0] o_ram_dqm; output wire [31:0] o_debug; // Calculate some metrics // // First, do we *need* a refresh now --- i.e., must we break out of // whatever we are doing to issue a refresh command? // // The step size here must be such that 8192 charges may be done in // 64 ms. Thus for a clock of: // ClkRate(MHz) (64ms/1000(ms/s)*ClkRate)/8192 // 100 MHz 781 // 96 MHz 750 // 92 MHz 718 // 88 MHz 687 // 84 MHz 656 // 80 MHz 625 // reg need_refresh; reg [9:0] refresh_clk; wire refresh_cmd; assign refresh_cmd = (~o_ram_cs_n)&&(~o_ram_ras_n)&&(~o_ram_cas_n)&&(o_ram_we_n); initial refresh_clk = 0; always @(posedge i_clk) begin if (refresh_cmd) refresh_clk <= 10'd625; // Make suitable for 80 MHz clk else if (|refresh_clk) refresh_clk <= refresh_clk - 10'h1; end initial need_refresh = 1'b0; always @(posedge i_clk) need_refresh <= (refresh_clk == 10'h00)&&(~refresh_cmd); reg in_refresh; reg [2:0] in_refresh_clk; initial in_refresh_clk = 3'h0; always @(posedge i_clk) if (refresh_cmd) in_refresh_clk <= 3'h6; else if (|in_refresh_clk) in_refresh_clk <= in_refresh_clk - 3'h1; always @(posedge i_clk) in_refresh <= (in_refresh_clk != 3'h0)||(refresh_cmd); reg [2:0] bank_active [0:3]; reg [(RDLY-1):0] r_barrell_ack; reg r_pending; reg r_we; reg [22:0] r_addr; reg [31:0] r_data; reg [12:0] bank_row [0:3]; // // Second, do we *need* a precharge now --- must be break out of // whatever we are doing to issue a precharge command? // // Keep in mind, the number of clocks to wait has to be reduced by // the amount of time it may take us to go into a precharge state. // reg [3:0] need_precharge; genvar k; generate for(k=0; k<4; k=k+1) begin : precharge_genvar_loop wire precharge_cmd; assign precharge_cmd = ((~o_ram_cs_n)&&(~o_ram_ras_n)&&(o_ram_cas_n)&&(~o_ram_we_n) &&((o_ram_addr[10])||(o_ram_bs == k[1:0]))) // Also on read or write with precharge ||(~o_ram_cs_n)&&(o_ram_ras_n)&&(~o_ram_cas_n)&&(o_ram_addr[10]); reg [9:0] precharge_clk; initial precharge_clk = 0; always @(posedge i_clk) begin if ((precharge_cmd)||(bank_active[k] == 0)) precharge_clk <= 10'd1000; else if (|precharge_clk) precharge_clk <= precharge_clk - 10'h1; end initial need_precharge[k] = 1'b0; always @(posedge i_clk) need_precharge[k] <= ~(|precharge_clk); end // precharge_genvar_loop endgenerate reg [15:0] clocks_til_idle; reg [1:0] r_state; wire bus_cyc; assign bus_cyc = ((i_wb_cyc)&&(i_wb_stb)&&(~o_wb_stall)); reg nxt_dmod; // Pre-process pending operations wire pending; initial r_pending = 1'b0; reg [22:5] fwd_addr; always @(posedge i_clk) if (bus_cyc) begin r_pending <= 1'b1; r_we <= i_wb_we; r_addr <= i_wb_addr; r_data <= i_wb_data; fwd_addr <= i_wb_addr[22:5] + 18'h01; end else if ((~o_ram_cs_n)&&(o_ram_ras_n)&&(~o_ram_cas_n)) r_pending <= 1'b0; else if (~i_wb_cyc) r_pending <= 1'b0; reg r_bank_valid; initial r_bank_valid = 1'b0; always @(posedge i_clk) if (bus_cyc) r_bank_valid <=((bank_active[i_wb_addr[9:8]][2]) &&(bank_row[i_wb_addr[9:8]]==r_addr[22:10])); else r_bank_valid <= ((bank_active[r_addr[9:8]][2]) &&(bank_row[r_addr[9:8]]==r_addr[22:10])); reg fwd_bank_valid; initial fwd_bank_valid = 0; always @(posedge i_clk) fwd_bank_valid <= ((bank_active[fwd_addr[9:8]][2]) &&(bank_row[fwd_addr[9:8]]==fwd_addr[22:10])); assign pending = (r_pending)&&(o_wb_stall); // Address MAP: // 23-bits bits in, 24-bits out // // 222 1111 1111 1100 0000 0000 // 210 9876 5432 1098 7654 3210 // rrr rrrr rrrr rrBB cccc cccc 0 // 8765 4321 0 // initial r_barrell_ack = 0; initial r_state = `RAM_POWER_UP; initial clocks_til_idle = 16'd20500; initial o_wb_stall = 1'b1; initial o_ram_dmod = `DMOD_GETINPUT; initial nxt_dmod = `DMOD_GETINPUT; initial o_ram_cs_n = 1'b0; initial o_ram_ras_n = 1'b1; initial o_ram_cas_n = 1'b1; initial o_ram_we_n = 1'b1; initial o_ram_dqm = 2'b11; assign o_ram_cke = 1'b1; initial bank_active[0] = 3'b000; initial bank_active[1] = 3'b000; initial bank_active[2] = 3'b000; initial bank_active[3] = 3'b000; always @(posedge i_clk) if (r_state == `RAM_OPERATIONAL) begin o_wb_stall <= (r_pending)||(bus_cyc); r_barrell_ack <= r_barrell_ack >> 1; nxt_dmod <= `DMOD_GETINPUT; o_ram_dmod <= nxt_dmod; // bank_active[0] <= { bank_active[0][2], bank_active[0][2:1] }; bank_active[1] <= { bank_active[1][2], bank_active[1][2:1] }; bank_active[2] <= { bank_active[2][2], bank_active[2][2:1] }; bank_active[3] <= { bank_active[3][2], bank_active[3][2:1] }; // o_ram_cs_n <= (~i_wb_cyc); // o_ram_cke <= 1'b1; o_ram_dqm <= 2'b0; if (|clocks_til_idle[2:0]) clocks_til_idle[2:0] <= clocks_til_idle[2:0] - 3'h1; // Default command is a // NOOP if (i_wb_cyc) // Device deselect if (~i_wb_cyc) // o_ram_cs_n <= (~i_wb_cyc) above, NOOP o_ram_ras_n <= 1'b1; o_ram_cas_n <= 1'b1; o_ram_we_n <= 1'b1; // Don't drive the bus normally, let it float unless we wish // to give it a command o_ram_data <= r_data[15:0]; if ((~i_wb_cyc)||(|need_precharge)||(need_refresh)) begin // Issue a precharge all command (if any banks are open), // otherwise an autorefresh command if ((bank_active[0][2:1]==2'b10) ||(bank_active[1][2:1]==2'b10) ||(bank_active[2][2:1]==2'b10) ||(bank_active[3][2:1]==2'b10)) begin // Do nothing this clock // Can't precharge a bank immediately after // activating it end else if (bank_active[0][2] ||(bank_active[1][2]) ||(bank_active[2][2]) ||(bank_active[3][2])) begin // Close all active banks o_ram_cs_n <= 1'b0; o_ram_ras_n <= 1'b0; o_ram_cas_n <= 1'b1; o_ram_we_n <= 1'b0; o_ram_addr[10] <= 1'b1; bank_active[0][2] <= 1'b0; bank_active[1][2] <= 1'b0; bank_active[2][2] <= 1'b0; bank_active[3][2] <= 1'b0; end else if ((|bank_active[0]) ||(|bank_active[1]) ||(|bank_active[2]) ||(|bank_active[3])) // Can't precharge yet, the bus is still busy begin end else if ((~in_refresh)&&((refresh_clk[9:8]==2'b00)||(need_refresh))) begin // Send autorefresh command o_ram_cs_n <= 1'b0; o_ram_ras_n <= 1'b0; o_ram_cas_n <= 1'b0; o_ram_we_n <= 1'b1; end // Else just send NOOP's, the default command end else if (nxt_dmod) begin // Last half of a two cycle write o_ram_data <= r_data[15:0]; end else if (in_refresh) begin // NOOPS only here, until we are out of refresh end else if ((pending)&&(~r_bank_valid)&&(bank_active[r_addr[9:8]]==3'h0)) begin // Need to activate the requested bank o_ram_cs_n <= 1'b0; o_ram_ras_n <= 1'b0; o_ram_cas_n <= 1'b1; o_ram_we_n <= 1'b1; o_ram_addr <= r_addr[22:10]; o_ram_bs <= r_addr[9:8]; // clocks_til_idle[2:0] <= 1; bank_active[r_addr[9:8]][2] <= 1'b1; bank_row[r_addr[9:8]] <= r_addr[22:10]; // end else if ((pending)&&(~r_bank_valid) &&(bank_active[r_addr[9:8]]==3'b111)) begin // Need to close an active bank o_ram_cs_n <= 1'b0; o_ram_ras_n <= 1'b0; o_ram_cas_n <= 1'b1; o_ram_we_n <= 1'b0; // o_ram_addr <= r_addr[22:10]; o_ram_addr[10]<= 1'b0; o_ram_bs <= r_addr[9:8]; // clocks_til_idle[2:0] <= 1; bank_active[r_addr[9:8]][2] <= 1'b0; // bank_row[r_addr[9:8]] <= r_addr[22:10]; end else if ((pending)&&(~r_we) &&(bank_active[r_addr[9:8]][2]) &&(r_bank_valid) &&(clocks_til_idle[2:0] < 4)) begin // Issue the read command o_ram_cs_n <= 1'b0; o_ram_ras_n <= 1'b1; o_ram_cas_n <= 1'b0; o_ram_we_n <= 1'b1; o_ram_addr <= { 4'h0, r_addr[7:0], 1'b0 }; o_ram_bs <= r_addr[9:8]; clocks_til_idle[2:0] <= 4; o_wb_stall <= 1'b0; r_barrell_ack[(RDLY-1)] <= 1'b1; end else if ((pending)&&(r_we) &&(bank_active[r_addr[9:8]][2]) &&(r_bank_valid) &&(clocks_til_idle[2:0] == 0)) begin // Issue the write command o_ram_cs_n <= 1'b0; o_ram_ras_n <= 1'b1; o_ram_cas_n <= 1'b0; o_ram_we_n <= 1'b0; o_ram_addr <= { 4'h0, r_addr[7:0], 1'b0 }; o_ram_bs <= r_addr[9:8]; clocks_til_idle[2:0] <= 3'h1; o_wb_stall <= 1'b0; r_barrell_ack[1] <= 1'b1; o_ram_data <= r_data[31:16]; // o_ram_dmod <= `DMOD_PUTOUTPUT; nxt_dmod <= `DMOD_PUTOUTPUT; end else if ((r_pending)&&(r_addr[7:0] >= 8'hf0) &&(~fwd_bank_valid)) begin // Do I need to close the next bank I'll need? if (bank_active[fwd_addr[9:8]][2:1]==2'b11) begin // Need to close the bank first o_ram_cs_n <= 1'b0; o_ram_ras_n <= 1'b0; o_ram_cas_n <= 1'b1; o_ram_we_n <= 1'b0; o_ram_addr[10] <= 1'b0; o_ram_bs <= fwd_addr[9:8]; bank_active[fwd_addr[9:8]][2] <= 1'b0; end else if (bank_active[fwd_addr[9:8]]==3'b000) begin // Need to (pre-)activate the next bank o_ram_cs_n <= 1'b0; o_ram_ras_n <= 1'b0; o_ram_cas_n <= 1'b1; o_ram_we_n <= 1'b1; o_ram_addr <= fwd_addr[22:10]; o_ram_bs <= fwd_addr[9:8]; // clocks_til_idle[3:0] <= 1; bank_active[fwd_addr[9:8]] <= 3'h4; bank_row[fwd_addr[9:8]] <= fwd_addr[22:10]; end end end else if (r_state == `RAM_POWER_UP) begin // All signals must be held in NOOP state during powerup o_ram_dqm <= 2'b11; // o_ram_cke <= 1'b1; o_ram_cs_n <= 1'b0; o_ram_ras_n <= 1'b1; o_ram_cas_n <= 1'b1; o_ram_we_n <= 1'b1; o_ram_dmod <= `DMOD_GETINPUT; if (clocks_til_idle == 0) begin r_state <= `RAM_INITIAL_REFRESH; clocks_til_idle[3:0] <= 4'ha; o_ram_cs_n <= 1'b0; o_ram_ras_n <= 1'b0; o_ram_cas_n <= 1'b1; o_ram_we_n <= 1'b0; o_ram_addr[10] <= 1'b1; end else clocks_til_idle <= clocks_til_idle - 16'h01; o_wb_stall <= 1'b1; r_barrell_ack[(RDLY-1):0] <= 0; end else if (r_state == `RAM_INITIAL_REFRESH) begin // o_ram_cs_n <= 1'b0; o_ram_ras_n <= 1'b0; o_ram_cas_n <= 1'b0; o_ram_we_n <= 1'b1; o_ram_dmod <= `DMOD_GETINPUT; o_ram_addr <= { 3'b000, 1'b0, 2'b00, 3'b010, 1'b0, 3'b001 }; if (clocks_til_idle[3:0] == 4'h0) begin r_state <= `RAM_SET_MODE; o_ram_we_n <= 1'b0; clocks_til_idle[3:0] <= 4'h2; end else clocks_til_idle[3:0] <= clocks_til_idle[3:0] - 4'h1; o_wb_stall <= 1'b1; r_barrell_ack[(RDLY-1):0] <= 0; end else if (r_state == `RAM_SET_MODE) begin // Set mode cycle o_ram_cs_n <= 1'b1; o_ram_ras_n <= 1'b0; o_ram_cas_n <= 1'b0; o_ram_we_n <= 1'b0; o_ram_dmod <= `DMOD_GETINPUT; if (clocks_til_idle[3:0] == 4'h0) r_state <= `RAM_OPERATIONAL; else clocks_til_idle[3:0] <= clocks_til_idle[3:0]-4'h1; o_wb_stall <= 1'b1; r_barrell_ack[(RDLY-1):0] <= 0; end reg [15:0] last_ram_data; always @(posedge i_clk) last_ram_data <= i_ram_data; assign o_wb_ack = r_barrell_ack[0]; assign o_wb_data = { last_ram_data, i_ram_data }; // // The following outputs are not necessary for the functionality of // the SDRAM, but they can be used to feed an external "scope" to // get an idea of what the internals of this SDRAM are doing. // // Just be aware of the r_we: it is set based upon the currently pending // transaction, or (if none is pending) based upon the last transaction. // If you want to capture the first value "written" to the device, // you'll need to write a nothing value to the device to set r_we. // The first value "written" to the device can be caught in the next // interaction after that. // assign o_debug = { i_wb_cyc, i_wb_stb, i_wb_we, o_wb_ack, o_wb_stall, o_ram_cs_n, o_ram_ras_n, o_ram_cas_n, o_ram_we_n, o_ram_bs, o_ram_dmod, r_pending, // 13 of 32 o_ram_addr[10:0], // 11 more (r_we) ? { i_wb_data[23:20], i_wb_data[3:0] } : { o_wb_data[23:20], o_wb_data[3:0] } // i_ram_data[7:0] }; endmodule
Go to most recent revision | Compare with Previous | Blame | View Log