gateware

A collection of little open source FPGA hobby projects
git clone http://frotz.net/git/gateware.git
Log | Files | Refs | README

sdram.sv (11761B)


      1 // Copyright 2020, Brian Swetland <swetland@frotz.net>
      2 // Licensed under the Apache License, Version 2.0.
      3 
      4 `default_nettype none
      5 
      6 // tRCD (RAS# to CAS# Delay Time)
      7 // Min clocks betwen ACTIVE of a bank and a READ or WRITE of that bank
      8 //
      9 // tRC (RAS# Cycle Time)
     10 // Min clocks between ACTIVE of one row in a bank and ACTIVE of a
     11 // different row in the /same/ bank.  PRECHARGE must happen in between.
     12 //
     13 // tRRD (Row Activate to Row Activate Delay)
     14 // Min time between ACTIVE of a row in one bank and the ACTIVE of
     15 // a row in a /different/ bank.
     16 //
     17 // tRP (Precharge to Refresh/Activate)
     18 // Min time between PREFRESH of a bank and REFRESH or ACTIVE of it
     19 //
     20 // tWR (Write Recovery Time)
     21 // Min clocks between the last word of a write and PRECHARGE of that bank
     22 
     23 module sdram #(
     24 	// Memory Geometry
     25 	parameter BANKBITS = 1,    // 2^BANKBITS banks of
     26 	parameter ROWBITS = 11,    // 2^ROWBITS rows by
     27 	parameter COLBITS = 8,     // 2^COLBITS columns of
     28 	parameter DWIDTH = 16,      // DWIDTH bit wide words
     29 
     30 	// Memory Timing
     31 	parameter T_RI = 1900,     // Refresh Interval
     32 	parameter T_RC = 8,        // RAS# Cycle Time
     33 	parameter T_RCD = 3,       // RAS# CAS# Delay
     34 	parameter T_RRD = 3,       // Row Row Delay
     35 	parameter T_RP = 3,        // Precharge to Refresh/Activate
     36 	parameter T_WR = 2,        // Write Recovery TimeA
     37 	parameter T_MRD = 3,       // Mode Register Delay
     38 	parameter T_PWR_UP = 25000, // Power on delay
     39 
     40 	// Fine TuningA
     41 	parameter CLK_SHIFT = 0,   // 1 = delay clock by 1/2 cycle (if 1)
     42 	parameter CLK_DELAY = 0    // 1..128 = delay clock by N x 25pS (ECP5)
     43 	) (
     44 	input wire clk,
     45 	input wire reset,
     46 	output wire pin_clk,
     47 	output wire pin_ras_n,
     48 	output wire pin_cas_n,
     49 	output wire pin_we_n,
     50 	inout wire [DWIDTH-1:0]pin_data,
     51 	output wire [AWIDTH-1:0]pin_addr,
     52 
     53 	input wire [XWIDTH-1:0]rd_addr,
     54 	input wire [3:0]rd_len,
     55 	input wire rd_req,
     56 	output reg rd_ack = 0,
     57 
     58 	output reg [DWIDTH-1:0]rd_data,
     59 	output reg rd_rdy = 0,
     60 
     61 	input wire [XWIDTH-1:0]wr_addr,
     62 	input wire [DWIDTH-1:0]wr_data,
     63 	input wire [3:0]wr_len,
     64 	input wire wr_req,
     65 	output reg wr_ack = 0
     66 	);
     67 
     68 // sdram addr is wide enough for row + bank
     69 localparam AWIDTH = (ROWBITS + BANKBITS);
     70 
     71 // full addr is rowbits + bankbits + colbits wide
     72 localparam XWIDTH = (ROWBITS + BANKBITS + COLBITS);
     73 
     74 localparam BANKCOUNT = (1 << BANKBITS);
     75 
     76 // validate T_RCD and adjust MODE bits and read latency configuration
     77 if ((T_RCD != 2) && (T_RCD != 3))
     78 	$error("T_RCD must be 2 or 3");
     79 
     80 localparam PMSB = T_RCD;
     81 localparam [1:0]SDRAM_CL = T_RCD;
     82 localparam [9:0]SDRAM_MODE = { 4'b000, SDRAM_CL, 4'b0000 };
     83 
     84 integer i; // used by various for loops
     85 
     86 // split input address into bank, row, col
     87 wire [BANKBITS-1:0]wr_bank;
     88 wire [ROWBITS-1:0]wr_row;
     89 wire [COLBITS-1:0]wr_col;
     90 assign {wr_row, wr_bank, wr_col} = wr_addr;
     91 
     92 // split input address into bank, row, col
     93 wire [BANKBITS-1:0]rd_bank;
     94 wire [ROWBITS-1:0]rd_row;
     95 wire [COLBITS-1:0]rd_col;
     96 assign {rd_row, rd_bank, rd_col} = rd_addr;
     97 
     98 // sdram io address management
     99 reg [XWIDTH-1:0]io_addr = 0;
    100 reg [XWIDTH-1:0]io_addr_next;
    101 
    102 wire [COLBITS-1:0]io_col;
    103 wire [BANKBITS-1:0]io_bank;
    104 wire [ROWBITS-1:0]io_row;
    105 assign {io_row, io_bank, io_col} = io_addr;
    106 wire [COLBITS-1:0]io_col_add1 = io_col + {{COLBITS-1{1'b0}},1'b1};
    107 
    108 reg [DWIDTH-1:0]rd_data_next;
    109 reg rd_ack_next;
    110 reg rd_rdy_next;
    111 reg wr_ack_next;
    112 
    113 // signals to sdram_glue module
    114 wire ras_n;
    115 wire cas_n;
    116 wire we_n;
    117 wire [AWIDTH-1:0]addr;
    118 wire [DWIDTH-1:0]data_i;
    119 reg [DWIDTH-1:0]data_o = 0;
    120 reg data_oe = 0;
    121 
    122 reg [DWIDTH-1:0]data_o_next;
    123 reg data_oe_next;
    124 
    125 reg [2:0]cmd = 3'b111;
    126 reg [2:0]cmd_next;
    127 
    128 // next refresh down counter
    129 reg [15:0]refresh = T_PWR_UP;
    130 reg [15:0]refresh_next;
    131 wire [15:0]refresh_sub1 = refresh - 16'd1;
    132 wire refresh_done = refresh[15];
    133 
    134 // sdram bank state
    135 reg bank_active[0:BANKCOUNT-1];
    136 reg bank_active_next[0:BANKCOUNT-1];
    137 reg [ROWBITS-1:0]bank_row[0:BANKCOUNT-1];
    138 reg [ROWBITS-1:0]bank_row_next[0:BANKCOUNT-1];
    139 
    140 // state machine state
    141 localparam START = 4'd0;
    142 localparam IDLE = 4'd1;
    143 localparam INIT0 = 4'd2;
    144 localparam INIT1 = 4'd3;
    145 localparam INIT2 = 4'd4;
    146 localparam REFRESH = 4'd5;
    147 localparam ACTIVE = 4'd6;
    148 localparam READ = 4'd7;
    149 localparam WRITE = 4'd8;
    150 localparam START_READ = 4'd9;
    151 localparam START_WRITE = 4'd10;
    152 
    153 reg [3:0]state = START;
    154 reg [3:0]state_next;
    155 
    156 // sdram commands
    157 localparam CMD_SET_MODE =  3'b000;  // A0-9 mode, A10+ SBZ
    158 localparam CMD_REFRESH =   3'b001;
    159 localparam CMD_PRECHARGE = 3'b010;  // BA*=bankno, A10=ALL
    160 localparam CMD_ACTIVE =    3'b011;  // BA*=bankno, A*=ROW
    161 localparam CMD_WRITE =     3'b100;  // BA*=bankno, A10=AP, COLADDR
    162 localparam CMD_READ =      3'b101;  // BA*=bankno, A10=AP, COLADDR
    163 localparam CMD_STOP =      3'b110;
    164 localparam CMD_NOP =       3'b111;
    165 
    166 reg [PMSB:0]rd_pipe_rdy = 0;
    167 reg [PMSB:0]rd_pipe_bsy = 0;
    168 reg [PMSB:0]rd_pipe_rdy_next;
    169 reg [PMSB:0]rd_pipe_bsy_next;
    170 
    171 reg [3:0]burst = 0;
    172 reg [3:0]burst_next;
    173 wire [3:0]burst_sub1;
    174 wire burst_done;
    175 assign { burst_done, burst_sub1 } = { 1'b0, burst } - 5'd1;
    176 
    177 reg io_sel_a10 = 0;
    178 reg io_sel_a10_next;
    179 reg io_sel_row = 0;
    180 reg io_sel_row_next;
    181 
    182 // general purpose down counter
    183 //reg [4:0]count = 0;
    184 //reg [4:0]count_next;
    185 //wire [4:0]count_sub1 = count - 5'd1;
    186 //wire count_done = count[4];
    187 
    188 reg [8:0]count = 0;
    189 reg [8:0]count_next;
    190 reg count_done = 1;
    191 reg count_done_next;
    192 
    193 reg io_do_rd = 0;
    194 reg io_do_rd_next;
    195 
    196 always_comb begin
    197 	state_next = state;
    198 //	count_next = count_done ? count : count_sub1;
    199 	refresh_next = refresh_done ? refresh : refresh_sub1;
    200 	cmd_next = CMD_NOP;
    201 	data_o_next = data_o;
    202 	data_oe_next = 0;
    203 	wr_ack_next = 0;
    204 	rd_rdy_next = 0;
    205 	rd_ack_next = 0;
    206 	rd_data_next = rd_data;
    207 	burst_next = burst;
    208 	io_addr_next = io_addr;
    209 	io_do_rd_next = io_do_rd;
    210 	io_sel_a10_next = io_sel_a10;
    211 	io_sel_row_next = io_sel_row;
    212 
    213 	count_done_next = count_done | count[0];
    214 	count_next = { 1'b0, count[8:1] };
    215 
    216 	for (i = 0; i < BANKCOUNT; i++) begin
    217 		bank_active_next[i] = bank_active[i];
    218 		bank_row_next[i] = bank_row[i];
    219 	end
    220 
    221 	// read pipe regs track inbound read data (rdy)
    222 	// and hold off writes (bsy) to avoid bus conflict
    223 	rd_pipe_rdy_next = { 1'b0, rd_pipe_rdy[PMSB:1] };
    224 	rd_pipe_bsy_next = { 1'b0, rd_pipe_bsy[PMSB:1] };
    225 	
    226 	if (rd_pipe_rdy[0]) begin
    227 		rd_rdy_next = 1;
    228 		rd_data_next = data_i;
    229 	end
    230 
    231 	if (count_done) // state can only advance if counter is 0
    232 	case (state)
    233 	START: begin
    234 		refresh_next = T_PWR_UP;
    235 		state_next = INIT0;
    236 	end
    237 	IDLE: begin
    238 		if (refresh_done) begin
    239 			// refresh counter has expired, precharge all and refresh
    240 			state_next = REFRESH;
    241 			cmd_next = CMD_PRECHARGE;
    242 			io_sel_row_next = 0;
    243 			io_sel_a10_next = 1; // ALL BANKS
    244 			//count_next = T_RP - 2;
    245 			count_next[T_RP-2] = 1; count_done_next = 0;
    246 		end else
    247 			if (rd_req) begin
    248 			io_do_rd_next = 1;
    249 			io_addr_next = rd_addr;
    250 			burst_next = rd_len;
    251 			state_next = START_READ;
    252 			rd_ack_next = 1;
    253 		end else if (wr_req) begin
    254 			io_do_rd_next = 0;
    255 			io_addr_next = wr_addr;
    256 			data_o_next = wr_data;
    257 			burst_next = wr_len;
    258 			state_next = START_WRITE;
    259 			wr_ack_next = 1;
    260 		end
    261 	end
    262 	START_READ: begin
    263 		if (!bank_active[io_bank] || (bank_row[io_bank] != io_row)) begin
    264 			state_next = ACTIVE;
    265 			cmd_next = CMD_PRECHARGE;
    266 			io_sel_row_next = 0; // column addr
    267 			io_sel_a10_next = 0; // one bank only
    268 			//count_next = T_RP - 2;
    269 			count_next[T_RP-2] = 1; count_done_next = 0;
    270 		end else begin
    271 			cmd_next = CMD_READ;
    272 			io_sel_row_next = 0; // column addr
    273 			io_sel_a10_next = 0; // no auto precharge
    274 			rd_pipe_rdy_next = { 1'b1, rd_pipe_rdy[PMSB:1] };
    275 			rd_pipe_bsy_next = { PMSB + 1 { 1'b1 } };
    276 			if (burst == 4'd0) begin
    277 				state_next = IDLE;
    278 			end else begin
    279 				state_next = READ;
    280 				burst_next = burst_sub1;
    281 			end
    282 		end
    283 	end
    284 	START_WRITE: if (!rd_pipe_bsy[0]) begin
    285 		if (!bank_active[io_bank] || (bank_row[io_bank] != io_row)) begin
    286 			state_next = ACTIVE;
    287 			// precharge one bank (a10=0)
    288 			cmd_next = CMD_PRECHARGE;
    289 			io_sel_row_next = 0; // column addr
    290 			io_sel_a10_next = 0; // one bank only
    291 			//count_next = T_RP - 2;
    292 			count_next[T_RP-2] = 1; count_done_next = 0;
    293 		end else begin
    294 			cmd_next = CMD_WRITE;
    295 			io_sel_row_next = 0; // column addr
    296 			io_sel_a10_next = 0; // no auto precharge
    297 			data_oe_next = 1;
    298 			if (burst == 4'd0) begin
    299 				state_next = IDLE;
    300 				burst_next = burst_sub1;
    301 				count_next[2] = 1; count_done_next = 0; // WR?
    302 			end else begin
    303 				state_next = WRITE;
    304 			end
    305 		end
    306 	end
    307 	ACTIVE: begin
    308 		state_next = io_do_rd ? START_READ : START_WRITE;
    309 		//count_next = T_RCD - 2;
    310 		count_next[T_RCD-2] = 1; count_done_next = 0;
    311 		cmd_next = CMD_ACTIVE;
    312 		io_sel_row_next = 1; // row address
    313 		bank_active_next[io_bank] = 1;
    314 		bank_row_next[io_bank] = io_row;
    315 	end
    316 	READ: begin
    317 		if (burst == 4'd0) begin
    318 			state_next = IDLE;
    319 		end else begin
    320 			burst_next = burst_sub1;
    321 		end
    322 		// column addressing pre-selected from initial read
    323 		io_addr_next[COLBITS-1:0] = io_col_add1;
    324 		cmd_next = CMD_READ;
    325 		rd_pipe_rdy_next = { 1'b1, rd_pipe_rdy[PMSB:1] };
    326 		rd_pipe_bsy_next = { PMSB + 1 { 1'b1 } };
    327 	end
    328 	WRITE: begin
    329 		if (burst == 4'd0) begin
    330 			state_next = IDLE;
    331 			count_next[2] = 1; count_done_next = 0; // WR?
    332 		end else begin
    333 			burst_next = burst_sub1;
    334 		end
    335 		// column addressing pre-selected from initial write
    336 		io_addr_next[COLBITS-1:0] = io_col_add1;
    337 		cmd_next = CMD_WRITE;
    338 		data_o_next = wr_data; // handshake?
    339 		data_oe_next = 1;
    340 	end
    341 	INIT0: if (refresh_done) begin
    342 		state_next = INIT1;
    343 		//count_next = 2;
    344 		count_next[2] = 1; count_done_next = 0;
    345 		cmd_next = CMD_PRECHARGE;
    346 		io_sel_row_next = 0; // column addressing
    347 		io_sel_a10_next = 1; // ALL BANKS
    348 	end
    349 	INIT1: begin
    350 		state_next = INIT2;
    351 		cmd_next = CMD_SET_MODE;
    352 		//count_next = T_MRD - 2;
    353 		count_next[T_MRD-2] = 1; count_done_next = 0;
    354 		io_addr_next[XWIDTH-1:COLBITS] =
    355 			{ {ROWBITS-10{1'b0}}, SDRAM_MODE, {BANKBITS{1'b0}} };
    356 		io_sel_row_next = 1; // row addressing
    357 	end
    358 	INIT2: begin
    359 		state_next = REFRESH;
    360 		cmd_next = CMD_REFRESH;
    361 		//count_next = T_RC - 2;
    362 		count_next[T_RC-2] = 1; count_done_next = 0;
    363 	end
    364 	REFRESH: begin
    365 		state_next = IDLE;
    366 		cmd_next = CMD_REFRESH;
    367 		//count_next = T_RC - 2;
    368 		count_next[T_RC-2] = 1; count_done_next = 0;
    369 		refresh_next = T_RI - 1;
    370 
    371 		// we got here after a precharge all
    372 		for (i = 0; i < BANKCOUNT; i++)
    373 			bank_active_next[i] = 0;
    374 	end
    375 	default: begin
    376 		//state_next = START;
    377 	end
    378 	endcase
    379 end
    380 
    381 always_ff @(posedge clk) begin
    382 	state <= reset ? START : state_next;
    383 	count <= count_next;
    384 	count_done <= count_done_next;
    385 	burst <= burst_next;
    386 	refresh <= refresh_next;
    387 	cmd <= cmd_next;
    388 	io_do_rd <= io_do_rd_next;
    389 	io_sel_a10 <= io_sel_a10_next;
    390 	io_sel_row <= io_sel_row_next;
    391 	io_addr <= io_addr_next;
    392 	data_o <= data_o_next;
    393 	data_oe <= data_oe_next;
    394 	wr_ack <= wr_ack_next;
    395 	rd_ack <= rd_ack_next;
    396 	rd_rdy <= rd_rdy_next;
    397 	rd_data <= rd_data_next;
    398 	for (i = 0; i < BANKCOUNT; i++) begin
    399 		bank_active[i] <= bank_active_next[i];
    400 		bank_row[i] <= bank_row_next[i];
    401 	end
    402 	rd_pipe_rdy <= rd_pipe_rdy_next;
    403 	rd_pipe_bsy <= rd_pipe_bsy_next;
    404 end
    405 
    406 assign { ras_n, cas_n, we_n } = cmd;
    407 
    408 wire [(ROWBITS-COLBITS)-1:0]io_misc = {{ROWBITS-10-1{1'b0}}, io_sel_a10, {10-COLBITS{1'b0}}};
    409 wire [ROWBITS-1:0]io_low = io_sel_row ? io_row : { io_misc, io_col };
    410 assign addr = { io_bank, io_low };
    411 
    412 `ifdef verilator
    413 assign pin_clk = clk;
    414 assign pin_ras_n = ras_n;
    415 assign pin_cas_n = cas_n;
    416 assign pin_we_n = we_n;
    417 assign pin_addr = addr;
    418 
    419 // TODO: fix me
    420 assign pin_data = data_o;
    421 assign data_i = pin_data;
    422 
    423 `else
    424 sdram_glue #(
    425 	.AWIDTH(AWIDTH),
    426 	.DWIDTH(DWIDTH),
    427 	.CLK_SHIFT(CLK_SHIFT),
    428 	.CLK_DELAY(CLK_DELAY)
    429 	) glue (
    430 	.clk(clk),
    431 	.pin_clk(pin_clk),
    432 	.pin_ras_n(pin_ras_n),
    433 	.pin_cas_n(pin_cas_n),
    434 	.pin_we_n(pin_we_n),
    435 	.pin_addr(pin_addr),
    436 	.pin_data(pin_data),
    437 	.ras_n(ras_n),
    438 	.cas_n(cas_n),
    439 	.we_n(we_n),
    440 	.addr(addr),
    441 	.data_i(data_i),
    442 	.data_o(data_o),
    443 	.data_oe(data_oe)
    444 );
    445 `endif
    446 
    447 endmodule
    448