Mon, 09 Aug 2010 20:45:49 +0100
add clock generator DCM and preliminary homebrew WISHBONE SDRAM controller
| philpem@0 | 1 | /**************************************************************************** |
| philpem@0 | 2 | * |
| philpem@0 | 3 | * |
| philpem@0 | 4 | ****************************************************************************/ |
| philpem@0 | 5 | |
| philpem@0 | 6 | module wb_sdram ( |
| philpem@0 | 7 | // Clocks and resets |
| philpem@0 | 8 | input wb_clk_i, // WISHBONE clock |
| philpem@0 | 9 | input wb_rst_i, // WISHBONE reset |
| philpem@0 | 10 | |
| philpem@0 | 11 | // WISHBONE bus |
| philpem@0 | 12 | input [31:0] wb_adr_i, // WISHBONE address |
| philpem@0 | 13 | input [31:0] wb_dat_i, // WISHBONE data in |
| philpem@0 | 14 | output reg [31:0] wb_dat_o, // WISHBONE data out |
| philpem@0 | 15 | input [3:0] wb_sel_i, // WISHBONE byte select |
| philpem@0 | 16 | input wb_we_i, // WISHBONE write enable (R/#W) |
| philpem@0 | 17 | input wb_cyc_i, // WISHBONE cycle |
| philpem@0 | 18 | input wb_stb_i, // WISHBONE strobe |
| philpem@0 | 19 | output wb_ack_o, // WISHBONE cycle acknowledge (data available, DTACK) |
| philpem@0 | 20 | output wb_err_o, // WISHBONE bus error |
| philpem@0 | 21 | output wb_rty_o, // WISHBONE retry-later |
| philpem@0 | 22 | |
| philpem@0 | 23 | // SDRAM |
| philpem@0 | 24 | output reg sdram_cke, // SDRAM clock enable |
| philpem@0 | 25 | output sdram_cs_n, // SDRAM chip select (active low) |
| philpem@0 | 26 | output sdram_ras_n, // SDRAM row address strobe (active low) |
| philpem@0 | 27 | output sdram_cas_n, // SDRAM column address strobe (active low) |
| philpem@0 | 28 | output sdram_we_n, // SDRAM write enable (active low) |
| philpem@0 | 29 | output [11:0] sdram_a, // SDRAM address |
| philpem@0 | 30 | output reg [1:0] sdram_ba, // SDRAM bank address |
| philpem@0 | 31 | output reg [3:0] sdram_dqm, // SDRAM data mask (OE#; 0=active, 1=disabled) |
| philpem@0 | 32 | inout [31:0] sdram_dq, // SDRAM data bus |
| philpem@0 | 33 | |
| philpem@0 | 34 | // Debugging |
| philpem@0 | 35 | output reg [2:0] debug // debug bits |
| philpem@0 | 36 | ); |
| philpem@0 | 37 | |
| philpem@0 | 38 | |
| philpem@0 | 39 | /**** |
| philpem@0 | 40 | * SDRAM data output buffer |
| philpem@0 | 41 | ****/ |
| philpem@0 | 42 | // OE=1 for output mode, 0 for input |
| philpem@0 | 43 | reg sdram_dq_oe; |
| philpem@0 | 44 | // SDRAM output register |
| philpem@0 | 45 | reg [31:0] sdram_dq_r; |
| philpem@0 | 46 | assign sdram_dq = sdram_dq_oe ? sdram_dq_r : 32'hZZZZ; |
| philpem@0 | 47 | |
| philpem@0 | 48 | |
| philpem@0 | 49 | |
| philpem@0 | 50 | /**** |
| philpem@0 | 51 | * State timer |
| philpem@0 | 52 | * This is used to ensure that the state machine abides by RAM timing |
| philpem@0 | 53 | * restrictions. |
| philpem@0 | 54 | ****/ |
| philpem@0 | 55 | reg [31:0] timer; |
| philpem@0 | 56 | |
| philpem@0 | 57 | |
| philpem@0 | 58 | /**** |
| philpem@0 | 59 | * MODE logic |
| philpem@0 | 60 | ****/ |
| philpem@0 | 61 | reg [5:0] sdram_mode; |
| philpem@0 | 62 | reg [11:0] sdram_addr; |
| philpem@0 | 63 | assign sdram_cs_n = sdram_mode[3]; |
| philpem@0 | 64 | assign sdram_ras_n = sdram_mode[2]; |
| philpem@0 | 65 | assign sdram_cas_n = sdram_mode[1]; |
| philpem@0 | 66 | assign sdram_we_n = sdram_mode[0]; |
| philpem@0 | 67 | assign sdram_a = {sdram_addr[11], (sdram_mode[5] ? sdram_mode[4] : sdram_addr[10]), sdram_addr[9:0]}; |
| philpem@0 | 68 | |
| philpem@0 | 69 | // SDRAM chip instructions |
| philpem@0 | 70 | // The bit order is as specified in the ISSI datasheet: A10 Override, A10, CS#, RAS#, CAS#, WE#. |
| philpem@0 | 71 | // If A10 Override is set, then A10 will be overridden to the value specified in the M_ constant. |
| philpem@0 | 72 | localparam M_BankActivate = 6'b0X0011; |
| philpem@0 | 73 | localparam M_PrechargeBank = 6'b100010; |
| philpem@0 | 74 | localparam M_PrechargeAll = 6'b110010; |
| philpem@0 | 75 | localparam M_Write = 6'b100100; |
| philpem@0 | 76 | localparam M_WritePrecharge = 6'b110100; |
| philpem@0 | 77 | localparam M_Read = 6'b100101; |
| philpem@0 | 78 | localparam M_ReadPrecharge = 6'b110101; |
| philpem@0 | 79 | localparam M_ModeRegister = 6'b0X0000; |
| philpem@0 | 80 | localparam M_Nop = 6'b0X0111; |
| philpem@0 | 81 | localparam M_BurstStop = 6'b0X0110; |
| philpem@0 | 82 | localparam M_Inhibit = 6'b0X1XXX; // maybe X1111? |
| philpem@0 | 83 | localparam M_AutoRefresh = 6'b0X0001; |
| philpem@0 | 84 | |
| philpem@0 | 85 | |
| philpem@0 | 86 | /**** |
| philpem@0 | 87 | * Finite State Machine |
| philpem@0 | 88 | ****/ |
| philpem@0 | 89 | localparam ST_INIT1 = 32'd0; |
| philpem@0 | 90 | localparam ST_INIT2 = 32'd1; |
| philpem@0 | 91 | localparam ST_NOP = 32'd999; |
| philpem@0 | 92 | |
| philpem@0 | 93 | reg [31:0] state; |
| philpem@0 | 94 | always @(posedge wb_clk_i) begin |
| philpem@0 | 95 | if (wb_rst_i) begin |
| philpem@0 | 96 | // Initialise state machine and timer |
| philpem@0 | 97 | state <= ST_INIT1; |
| philpem@0 | 98 | debug <= 3'd0; |
| philpem@0 | 99 | timer <= 32'd0; |
| philpem@0 | 100 | |
| philpem@0 | 101 | // Initialisation state for SDRAM |
| philpem@0 | 102 | sdram_cke <= 1'b0; |
| philpem@0 | 103 | sdram_mode <= M_Inhibit; |
| philpem@0 | 104 | sdram_addr <= 12'h000; |
| philpem@0 | 105 | sdram_ba <= 2'b00; |
| philpem@0 | 106 | sdram_dqm <= 4'b0000; |
| philpem@0 | 107 | sdram_dq_oe <= 1'b0; // data output disabled |
| philpem@0 | 108 | sdram_dq_r <= 32'd0; |
| philpem@0 | 109 | end else begin |
| philpem@0 | 110 | // timer logic |
| philpem@0 | 111 | if (timer > 32'd0) begin |
| philpem@0 | 112 | timer <= timer - 32'd1; |
| philpem@0 | 113 | end |
| philpem@0 | 114 | |
| philpem@0 | 115 | // state machine logic |
| philpem@0 | 116 | case (state) |
| philpem@0 | 117 | ST_INIT1: begin |
| philpem@0 | 118 | // INIT1: Set up for initial power-up wait |
| philpem@0 | 119 | state <= ST_INIT2; |
| philpem@0 | 120 | timer <= 32'd50_000; // TODO: dependent on core clock rate. Needs to be >= 100us |
| philpem@0 | 121 | |
| philpem@0 | 122 | // SDRAM state |
| philpem@0 | 123 | sdram_cke <= 1'b0; // clock disabled |
| philpem@0 | 124 | sdram_mode <= M_Inhibit; |
| philpem@0 | 125 | sdram_addr <= 12'h000; |
| philpem@0 | 126 | sdram_ba <= 2'b00; |
| philpem@0 | 127 | sdram_dqm <= 4'b1111; |
| philpem@0 | 128 | sdram_dq_oe <= 1'b0; // data output disabled |
| philpem@0 | 129 | sdram_dq_r <= 32'd0; |
| philpem@0 | 130 | end |
| philpem@0 | 131 | |
| philpem@0 | 132 | ST_INIT2: begin |
| philpem@0 | 133 | // INIT2: Power-up wait. Keep CKE low until ~50 cycles before |
| philpem@0 | 134 | // the end of the power-up wait, then bring CKE high. |
| philpem@0 | 135 | if (timer == 32'd0) begin |
| philpem@0 | 136 | // Timer hit zero. Send a NOP. |
| philpem@0 | 137 | state <= ST_NOP; |
| philpem@0 | 138 | end else if (timer <= 32'd50) begin |
| philpem@0 | 139 | // Timer value is more than zero but less than 50; CKE is on, but |
| philpem@0 | 140 | // keep waiting for the timer to actually expire. |
| philpem@0 | 141 | sdram_cke <= 1'b1; |
| philpem@0 | 142 | state <= ST_INIT2; |
| philpem@0 | 143 | debug <= 3'd1; |
| philpem@0 | 144 | end |
| philpem@0 | 145 | sdram_mode <= M_Inhibit; |
| philpem@0 | 146 | end |
| philpem@0 | 147 | |
| philpem@0 | 148 | ST_NOP: begin |
| philpem@0 | 149 | // Spinstate. Hold SDRAM in NOP. |
| philpem@0 | 150 | debug <= 3'd7; |
| philpem@0 | 151 | state <= ST_NOP; |
| philpem@0 | 152 | end |
| philpem@0 | 153 | endcase |
| philpem@0 | 154 | end |
| philpem@0 | 155 | end |
| philpem@0 | 156 | |
| philpem@0 | 157 | endmodule |