📄 yadmc_sdram16.v
字号:
/* * Yet Another Dynamic Memory Controller * Copyright (C) 2008 Sebastien Bourdeauducq - http://lekernel.net * This file is part of Milkymist. * * Milkymist is free software; you can redistribute it and/or modify it * under the terms of the GNU Library General Public License as published * by the Free Software Foundation; either version 2, 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 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. *//* * This is for using 16-bit wide SDR SDRAM with YADMC, * with sequential bursts of length 8. * If using DDR or other bus widths, adapt/replace this file. * Also, some parameters are hardcoded for MT48LC16M16A2 at CL2. * Review this file before using with other configurations. */module yadmc_sdram16 #( parameter sdram_depth = 25, parameter sdram_columndepth = 9, parameter cache_depth = 10, /* * Cache line depth is always 2 for 16-bit SDRAM: * burst length is 8, which means 16 bytes are transferred per burst. * with a cache word size of 4 bytes, this yields 4 cache words per burst. * Thus the depth of 2 (to make FSMs simpler, we use cache line length = burst length). */ parameter cache_linedepth = 2, parameter sdram_rowdepth = sdram_depth-1-sdram_columndepth-2 /* -2 for the banks */) ( input sdram_clk, input sdram_rst, /* Command port */ input command_evict, input command_refill, output reg command_ack, input [sdram_depth-cache_linedepth-2-1:0] evict_adr, /* unregistered */ input [sdram_depth-cache_linedepth-2-1:0] refill_adr, /* unregistered */ /* Cache interface */ output [cache_depth+cache_linedepth-1:0] cache_adr, output [31:0] cache_dat_o, output reg [3:0] cache_we, input [31:0] cache_dat_i, /* SDRAM interface */ output reg sdram_cke, output reg sdram_cs_n, output reg sdram_we_n, output reg sdram_cas_n, output reg sdram_ras_n, output reg [1:0] sdram_dqm, output reg [12:0] sdram_adr, output reg [1:0] sdram_ba, inout [15:0] sdram_dq);/* Generate address parts *//* * The address is split as follows to help reduce the probability of row switches : * row address, then column address, then bank number. * * The three LSBs (cache_linedepth+1) of the column address are always 0, since we always use the same burst ordering. * The MSBs are from the address input, decoded as above. */wire [sdram_rowdepth-1:0] evict_row = evict_adr[sdram_depth-cache_linedepth-2-1:sdram_columndepth+2-3];wire [sdram_columndepth-1:0] evict_column = {evict_adr[sdram_columndepth+2-1-3:2], {(cache_linedepth+1){1'b0}}};wire [1:0] evict_bank = evict_adr[1:0];wire [sdram_rowdepth-1:0] refill_row = refill_adr[sdram_depth-cache_linedepth-2-1:sdram_columndepth+2-3];wire [sdram_columndepth-1:0] refill_column = {refill_adr[sdram_columndepth+2-1-3:2], {(cache_linedepth+1){1'b0}}};wire [1:0] refill_bank = refill_adr[1:0];/* Register all I/Os to keep Murphy away *//* Control signals */reg sdram_cke_r;reg sdram_cs_n_r;reg sdram_we_n_r;reg sdram_cas_n_r;reg sdram_ras_n_r;reg sdram_dqm_r;reg [1:0] sdram_ba_r;always @(posedge sdram_clk) begin sdram_cke <= sdram_cke_r; sdram_cs_n <= sdram_cs_n_r; sdram_we_n <= sdram_we_n_r; sdram_cas_n <= sdram_cas_n_r; sdram_ras_n <= sdram_ras_n_r; sdram_dqm <= {2{sdram_dqm_r}}; sdram_ba <= sdram_ba_r;end/* Data */reg [15:0] dq;assign sdram_dq = dq;reg sdram_dq_drive;wire [15:0] sdram_dq_out;reg [15:0] sdram_dq_in;always @(posedge sdram_clk) begin if(sdram_dq_drive) dq <= sdram_dq_out; else dq <= 16'hzzzz; sdram_dq_in <= sdram_dq;end/* Address lines */reg adr_load_mode;reg adr_load_evictrow;reg adr_load_evictcolumn;reg adr_load_refillrow;reg adr_load_refillcolumn;reg adr_load_A10;always @(posedge sdram_clk) begin /* * Mode register encoding : * See p. 18 of the Micron datasheet. * A12..A10 reserved, should be 000 * A9 burst access, 0 = burst enabled * A8 ..A7 reserved, should be 00 * A6 ..A4 CAS latency, 10 = CL2 * A3 burst type, 0 = sequential * A2 ..A0 burst length, 011 = 8 */ sdram_adr <= ({13{adr_load_mode }} & 13'b000_0_00_10_0_011) |({13{adr_load_evictrow }} & {{(13-sdram_rowdepth){1'b0}}, evict_row}) |({13{adr_load_evictcolumn}} & {{(13-sdram_columndepth){1'b0}}, evict_column}) |({13{adr_load_refillrow }} & {{(13-sdram_rowdepth){1'b0}}, refill_row}) |({13{adr_load_refillcolumn}} & {{(13-sdram_columndepth){1'b0}}, refill_column}) |({13{adr_load_A10 }} & 13'd1024);end/* * Various timing counters. * Check that the delays are appropriate for the particular SDRAM chip * you're using. *//* The number of clocks before we can use the SDRAM after power-up *//* This should be 100us */reg [13:0] init_counter;reg reload_init_counter;wire init_done = (init_counter == 14'b0);always @(posedge sdram_clk) begin if(reload_init_counter) init_counter <= 14'd12500; else if(~init_done) init_counter <= init_counter - 4'b1;end/* The number of clocks we must wait following a PRECHARGE ALL command */reg [2:0] prechargeall_counter;reg reload_prechargeall_counter;wire prechargeall_done = (prechargeall_counter == 3'b0);always @(posedge sdram_clk) begin if(reload_prechargeall_counter) prechargeall_counter <= 3'd4; else if(~prechargeall_done) prechargeall_counter <= prechargeall_counter - 3'b1;end/* The number of clocks we must wait following a PRECHARGE command */reg [2:0] precharge_counter;reg reload_precharge_counter;wire precharge_done = (precharge_counter == 3'b0);always @(posedge sdram_clk) begin if(reload_precharge_counter) precharge_counter <= 3'd4; else if(~precharge_done) precharge_counter <= precharge_counter - 3'b1;end/* The number of clocks we must wait following an AUTO REFRESH command */reg [3:0] autorefresh_counter;reg reload_autorefresh_counter;wire autorefresh_done = (autorefresh_counter == 4'b0);always @(posedge sdram_clk) begin if(reload_autorefresh_counter) autorefresh_counter <= 4'd9; else if(~autorefresh_done) autorefresh_counter <= autorefresh_counter - 4'b1;end/* The number of clocks we must wait following an ACTIVATE command. * This is the (in)famous CAS latency. */reg [2:0] activate_counter;reg reload_activate_counter;wire activate_done = (activate_counter == 3'b0);always @(posedge sdram_clk) begin if(reload_activate_counter) activate_counter <= 3'd2; else if(~activate_done) activate_counter <= activate_counter - 3'b1;end/* The number of clocks we have left before we must refresh one row in the SDRAM array. */reg [11:0] refresh_counter;reg reload_refresh_counter;wire needs_refresh = (refresh_counter == 12'b0);always @(posedge sdram_clk) begin if(reload_refresh_counter) /* * The period between *each* AUTO REFRESH command, in clock cycles. * Must be the refresh period divided by the number of cycles required * to refresh the entire array. * Typically 64ms divided by the number of rows, but check your SDRAM datasheet. */ refresh_counter <= 12'd740; else if(~needs_refresh) refresh_counter <= refresh_counter - 12'b1;end/* Keep track of the open row for each bank */reg active0;reg [sdram_rowdepth-1:0] openrow0;reg active1;reg [sdram_rowdepth-1:0] openrow1;reg active2;reg [sdram_rowdepth-1:0] openrow2;reg active3;reg [sdram_rowdepth-1:0] openrow3;reg [sdram_rowdepth-1:0] track_row;reg [1:0] track_bank;reg track_open;reg track_close;reg track_closeall;always @(posedge sdram_clk) begin if(track_closeall) begin active0 <= 1'b0; active1 <= 1'b0; active2 <= 1'b0; active3 <= 1'b0; end else begin if(track_close) begin case(track_bank) 2'b00: active0 <= 1'b0; 2'b01: active1 <= 1'b0; 2'b10: active2 <= 1'b0; 2'b11: active3 <= 1'b0; endcase end else if(track_open) begin case(track_bank) 2'b00: begin active0 <= 1'b1; openrow0 <= track_row; end 2'b01: begin active1 <= 1'b1; openrow1 <= track_row; end 2'b10: begin active2 <= 1'b1; openrow2 <= track_row; end 2'b11: begin active3 <= 1'b1; openrow3 <= track_row; end endcase end endendreg load_track_with_evict;reg load_track_with_refill;always @(posedge sdram_clk) begin if(load_track_with_evict) begin track_row <= evict_row; track_bank <= evict_bank; end if(load_track_with_refill) begin track_row <= refill_row; track_bank <= refill_bank; endend/* Check if we need to activate another row. * We add registers to improve timing (this is the critical path). */wire evict_needs_rowswitch = ((evict_bank == 2'b00) & ((openrow0 != evict_row) | ~active0)) |((evict_bank == 2'b01) & ((openrow1 != evict_row) | ~active1)) |((evict_bank == 2'b10) & ((openrow2 != evict_row) | ~active2)) |((evict_bank == 2'b11) & ((openrow3 != evict_row) | ~active3));reg evict_needs_rowswitch_r;always @(posedge sdram_clk) evict_needs_rowswitch_r <= evict_needs_rowswitch;wire refill_needs_rowswitch = ((refill_bank == 2'b00) & ((openrow0 != refill_row) | ~active0)) |((refill_bank == 2'b01) & ((openrow1 != refill_row) | ~active1)) |((refill_bank == 2'b10) & ((openrow2 != refill_row) | ~active2)) |((refill_bank == 2'b11) & ((openrow3 != refill_row) | ~active3));reg refill_needs_rowswitch_r;always @(posedge sdram_clk) refill_needs_rowswitch_r <= refill_needs_rowswitch;/* Burst counter */reg [3:0] burst_counter;reg reload_burst_counter;wire burst_finished = burst_counter[3];wire burst_last = burst_counter[0] & burst_counter[1] & burst_counter[2];reg [3:0] next_burst_counter;always @(reload_burst_counter or burst_finished or burst_counter) begin if(reload_burst_counter) next_burst_counter <= 4'b0000; else if(~burst_finished) next_burst_counter <= burst_counter + 1'b1; else next_burst_counter <= burst_counter;endalways @(posedge sdram_clk) burst_counter <= next_burst_counter;/* Generate cache addresses */reg load_cache_msb_evict;reg load_cache_msb_refill;reg [cache_depth-1:0] cache_adr_msb;always @(posedge sdram_clk) begin if(load_cache_msb_evict) begin $display("Reads from the cache begin at address %h", evict_adr[cache_depth-1:0]); cache_adr_msb <= evict_adr[cache_depth-1:0]; end else if(load_cache_msb_refill) begin $display("Writes to the cache begin at address %h", refill_adr[cache_depth-1:0]); cache_adr_msb <= refill_adr[cache_depth-1:0]; endendassign cache_adr = {cache_adr_msb, next_burst_counter[2:1]};/* Cache data */assign cache_dat_o = {sdram_dq_in, sdram_dq_in};assign sdram_dq_out = burst_counter[0] ? cache_dat_i[15:0] : cache_dat_i[31:16];/* FSM that runs everything */always @(posedge sdram_clk) begin if(command_evict) begin $display("EVICT addr %b", evict_adr); $display("REFILL addr %b", refill_adr); end if(command_refill) $display("REFILL addr %b", refill_adr); if(command_ack) $display("ACK addr %b", refill_adr);endreg command_evict_pending;reg command_refill_pending;always @(posedge sdram_clk) begin if(sdram_rst | command_ack) begin command_evict_pending <= 1'b0; command_refill_pending <= 1'b0; end else begin if(command_evict) command_evict_pending <= 1'b1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -