OpenCores
URL https://opencores.org/ocsvn/wbddr3/wbddr3/trunk

Subversion Repositories wbddr3

[/] [wbddr3/] [trunk/] [rtl/] [wbddrsdram.v] - Rev 9

Go to most recent revision | Compare with Previous | Blame | View Log

////////////////////////////////////////////////////////////////////////////////
//
// Filename: 	wbddrsdram.v
//
// Project:	OpenArty, an entirely open SoC based upon the Arty platform
//
// Purpose:	
//
/*
Stall logic:
	1. First clock sets r_* variables.
	2. Second clock sets need_* variables.  If need_open, need_close, or
		need_refresh are true, that should also set the o_wb_stall
		variable.  Well also move r_* info to s_* (think s=stall)
	3. If stalled, the next command comes from s_*.  Otherwise, from r_*.
	4. Bus FIFO fills from s_*
//
//
	For every transaction, one of 4 possibilities:
	1. Wait for refresh to complete
	2. Wait for precharge and activate to complete
	3. Wait for activate to complete
	4. Issue RW cmd
		5. Wait for bus transaction to complete
		6. ACK
*/
// Creator:	Dan Gisselquist, Ph.D.
//		Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2016, 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
//
//
////////////////////////////////////////////////////////////////////////////////
//
//
 
// Possible commands to the DDR3 memory.  These consist of settings for the
// bits: o_wb_cs_n, o_wb_ras_n, o_wb_cas_n, and o_wb_we_n, respectively.
`define	DDR_MRSET	4'b0000
`define	DDR_REFRESH	4'b0001
`define	DDR_PRECHARGE	4'b0010
`define	DDR_ACTIVATE	4'b0011
`define	DDR_WRITE	4'b0100
`define	DDR_READ	4'b0101
`define	DDR_ZQS		4'b0110
`define	DDR_NOOP	4'b0111
//`define	DDR_DESELECT	4'b1???
//
// In this controller, 24-bit commands tend to be passed around.  These 
// 'commands' are bit fields.  Here we specify the bits associated with
// the bit fields.
`define	DDR_RSTDONE	24	// End the reset sequence?
`define	DDR_RSTTIMER	23	// Does this reset command take multiple clocks?
`define	DDR_RSTBIT	22	// Value to place on reset_n
`define	DDR_CKEBIT	21	// Should this reset command set CKE?
//
// Refresh command bit fields
`define	DDR_NEEDREFRESH	23
`define	DDR_RFTIMER	22
`define	DDR_RFBEGIN	21
//
`define	DDR_CMDLEN	21
`define	DDR_CSBIT	20
`define	DDR_RASBIT	19
`define	DDR_CASBIT	18
`define	DDR_WEBIT	17
`define	DDR_NOPTIMER	16	// Steal this from BA bits
`define	DDR_BABITS	3	// BABITS are really from 18:16, they are 3 bits
`define	DDR_ADDR_BITS	14
//
`define	BUSREG	7
`define	BUSNOW	8
 
module	wbddrsdram(i_clk, i_reset,
		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_ddr_reset_n, o_ddr_cke,
		o_ddr_cs_n, o_ddr_ras_n, o_ddr_cas_n, o_ddr_we_n,
		o_ddr_dqs, o_ddr_dm, o_ddr_odt, o_ddr_bus_oe,
		o_ddr_addr, o_ddr_ba, o_ddr_data, i_ddr_data,
		o_cmd_accepted);
	parameter	CKREFI4 = 13'd6240, // 4 * 7.8us at 200 MHz clock
			CKRFC = 320,
			CKXPR = CKRFC+5+2; // Clocks per tXPR timeout
	input			i_clk, i_reset;
	// Wishbone inputs
	input			i_wb_cyc, i_wb_stb, i_wb_we;
	input		[25:0]	i_wb_addr;
	input		[31:0]	i_wb_data;
	// Wishbone outputs
	output	reg		o_wb_ack;
	output	reg		o_wb_stall;
	output	reg	[31:0]	o_wb_data;
	// DDR3 RAM Controller
	output	wire		o_ddr_reset_n, o_ddr_cke;
	// Control outputs
	output	reg		o_ddr_cs_n, o_ddr_ras_n, o_ddr_cas_n,o_ddr_we_n;
	// DQS outputs:set to 3'b010 when data is active, 3'b100 (i.e. 2'bzz) ow
	output	wire		o_ddr_dqs;
	output	reg		o_ddr_dm, o_ddr_odt, o_ddr_bus_oe;
	// Address outputs
	output	reg	[13:0]	o_ddr_addr;
	output	reg	[2:0]	o_ddr_ba;
	// And the data inputs and outputs
	output	reg	[31:0]	o_ddr_data;
	input		[31:0]	i_ddr_data;
	// And just for the test bench
	output	reg		o_cmd_accepted;
 
	always @(posedge i_clk)
		o_cmd_accepted <= (i_wb_stb)&&(~o_wb_stall);
 
	reg		drive_dqs;
 
	// The pending transaction
	reg	[31:0]	r_data;
	reg		r_pending, r_we;
	reg	[25:0]	r_addr;
	reg	[13:0]	r_row;
	reg	[2:0]	r_bank;
	reg	[9:0]	r_col;
	reg	[1:0]	r_sub;
	reg		r_move; // It was accepted, and can move to next stage
 
	// The pending transaction, one further into the pipeline.  This is
	// the stage where the read/write command is actually given to the
	// interface if we haven't stalled.
	reg	[31:0]	s_data;
	reg		s_pending, s_we, s_match;
	reg	[25:0]	s_addr;
	reg	[13:0]	s_row, s_nxt_row;
	reg	[2:0]	s_bank, s_nxt_bank;
	reg	[9:0]	s_col;
	reg	[1:0]	s_sub;
 
	// Can the pending transaction be satisfied with the current (ongoing)
	// transaction?
	reg		m_move, m_match, m_pending, m_we;
	reg	[25:0]	m_addr;
	reg	[13:0]	m_row;
	reg	[2:0]	m_bank;
	reg	[9:0]	m_col;
	reg	[1:0]	m_sub;
 
	// Can we preload the next bank?
	reg	[13:0]	r_nxt_row;
	reg	[2:0]	r_nxt_bank;
 
	reg	need_close_bank, need_close_this_bank,
			last_close_bank, maybe_close_next_bank,
			last_maybe_close,
		need_open_bank, last_open_bank, maybe_open_next_bank,
			last_maybe_open,
		valid_bank, last_valid_bank;
	reg	[(`DDR_CMDLEN-1):0]	close_bank_cmd, activate_bank_cmd,
					maybe_close_cmd, maybe_open_cmd, rw_cmd;
	reg	[1:0]	rw_sub;
	reg		rw_we;
 
	wire	w_this_closing_bank, w_this_opening_bank,
		w_this_maybe_close, w_this_maybe_open,
		w_this_rw_move;
	reg	last_closing_bank, last_opening_bank;
//
// tWTR = 7.5
// tRRD = 7.5
// tREFI= 7.8
// tFAW = 45
// tRTP = 7.5
// tCKE = 5.625
// tRFC = 160
// tRP  = 13.5
// tRAS = 36
// tRCD = 13.5
//
// RESET:
//	1. Hold o_reset_n = 1'b0; for 200 us, or 40,000 clocks (65536 perhaps?)
//		Hold cke low during this time as well
//		The clock should be free running into the chip during this time
//		Leave command in NOOP state: {cs,ras,cas,we} = 4'h7;
//		ODT must be held low
//	2. Hold cke low for another 500us, or 100,000 clocks
//	3. Raise CKE, continue outputting a NOOP for
//		tXPR, tDLLk, and tZQInit
//	4. Load MRS2, wait tMRD
//	4. Load MRS3, wait tMRD
//	4. Load MRS1, wait tMOD
// Before using the SDRAM, we'll need to program at least 3 of the mode
//	registers, if not all 4. 
//   tMOD clocks are required to program the mode registers, during which
//	time the RAM must be idle.
//
// NOOP: CS low, RAS, CAS, and WE high
 
//
// Reset logic should be simple, and is given as follows:
// note that it depends upon a ROM memory, reset_mem, and an address into that
// memory: reset_address.  Each memory location provides either a "command" to
// the DDR3 SDRAM, or a timer to wait until the next command.  Further, the
// timer commands indicate whether or not the command during the timer is to
// be set to idle, or whether the command is instead left as it was.
	reg		reset_override, reset_ztimer, maintenance_override;
	reg	[4:0]	reset_address;
	reg	[(`DDR_CMDLEN-1):0]	reset_cmd, cmd, refresh_cmd,
					maintenance_cmd;
	reg	[24:0]	reset_instruction;
	reg	[16:0]	reset_timer;
	initial	reset_override = 1'b1;
	initial	reset_address  = 5'h0;
	always @(posedge i_clk)
		if (i_reset)
		begin
			reset_override <= 1'b1;
			reset_cmd <= { `DDR_NOOP, reset_instruction[16:0]};
		end else if (reset_ztimer)
		begin
			if (reset_instruction[`DDR_RSTDONE])
				reset_override <= 1'b0;
			reset_cmd <= reset_instruction[20:0];
		end
	always @(posedge i_clk)
		if (i_reset)
			o_ddr_cke <= 1'b0;
		else if ((reset_override)&&(reset_ztimer))
			o_ddr_cke <= reset_instruction[`DDR_CKEBIT];
 
	initial	reset_ztimer = 1'b0;	// Is the timer zero?
	initial	reset_timer = 17'h02;
	always @(posedge i_clk)
		if (i_reset)
		begin
			reset_ztimer <= 1'b0;
			reset_timer <= 17'd2;
		end else if (!reset_ztimer)
		begin
			reset_ztimer <= (reset_timer == 17'h01);
			reset_timer <= reset_timer - 17'h01;
		end else if (reset_instruction[`DDR_RSTTIMER])
		begin
			reset_ztimer <= 1'b0;
			reset_timer <= reset_instruction[16:0];
		end
 
	wire	[16:0]	w_ckXPR = CKXPR, w_ckRST = 4, w_ckRP = 3,
			w_ckRFC = CKRFC;
	always @(posedge i_clk)
		if (i_reset)
			reset_instruction <= { 4'h4, `DDR_NOOP, 17'd40_000 };
		else if (reset_ztimer) case(reset_address) // RSTDONE, TIMER, CKE, ??
		// 1. Reset asserted (active low) for 200 us. (@200MHz)
		5'h0: reset_instruction <= { 4'h4, `DDR_NOOP, 17'd40_000 };
		// 2. Reset de-asserted, wait 500 us before asserting CKE
		5'h1: reset_instruction <= { 4'h6, `DDR_NOOP, 17'd100_000 };
		// 3. Assert CKE, wait minimum of Reset CKE Exit time
		5'h2: reset_instruction <= { 4'h7, `DDR_NOOP, w_ckXPR };
		// 4. Look MR2.  (1CK, no TIMER)
		5'h3: reset_instruction <= { 4'h3, `DDR_MRSET, 3'h2,
			3'h0, 2'b00, 1'b0, 1'b0, 1'b1, 3'b0, 3'b0 }; // MRS2
		// 3. Wait 4 clocks (tMRD)
		5'h4: reset_instruction <= { 4'h7, `DDR_NOOP, 17'h02 };
		// 5. Set MR1
		5'h5: reset_instruction <= { 4'h3, `DDR_MRSET, 3'h1,
			1'h0, // Reserved for Future Use (RFU)
			1'b0, // Qoff - output buffer enabled
			1'b1, // TDQS ... enabled
			1'b0, // RFU
			1'b0, // High order bit, Rtt_Nom (3'b011)
			1'b0, // RFU
			//
			1'b0, // Disable write-leveling
			1'b1, // Mid order bit of Rtt_Nom
			1'b0, // High order bit of Output Drvr Impedence Ctrl
			2'b0, // Additive latency = 0
			1'b1, // Low order bit of Rtt_Nom
			1'b1, // DIC set to 2'b01
			1'b1 }; // MRS1, DLL enable
		// 7. Wait another 4 clocks
		5'h6: reset_instruction <= { 4'h7, `DDR_NOOP, 17'h02 };
		// 8. Send MRS0
		5'h7: reset_instruction <= { 4'h3, `DDR_MRSET, 3'h0,
			1'b0, // Reserved for future use
			1'b0, // PPD control, (slow exit(DLL off))
			3'b1, // Write recovery for auto precharge
			1'b0, // DLL Reset (No)
			//
			1'b0, // TM mode normal
			3'b01, // High 3-bits, CAS latency (=4'b0010 = 4'd5)
			1'b0, // Read burst type = nibble sequential
			1'b0, // Low bit of cas latency
			2'b0 }; // Burst length = 8 (Fixed)
		// 9. Wait tMOD, is max(12 clocks, 15ns)
		5'h8: reset_instruction <= { 4'h7, `DDR_NOOP, 17'h0a };
		// 10. Issue a ZQCL command to start ZQ calibration, A10 is high
		5'h9: reset_instruction <= { 4'h3, `DDR_ZQS, 6'h0, 1'b1, 10'h0};
		//11.Wait for both tDLLK and tZQinit completed, both are 512 cks
		5'ha: reset_instruction <= { 4'h7, `DDR_NOOP, 17'd512 };
		// 12. Precharge all command
		5'hb: reset_instruction <= { 4'h3, `DDR_PRECHARGE, 6'h0, 1'b1, 10'h0 };
		// 13. Wait for the precharge to complete
		5'hc: reset_instruction <= { 4'h7, `DDR_NOOP, w_ckRP };
		// 14. A single Auto Refresh commands
		5'hd: reset_instruction <= { 4'h3, `DDR_REFRESH, 17'h00 };
		// 15. Wait for the auto refresh to complete
		5'he: reset_instruction <= { 4'h7, `DDR_NOOP, w_ckRFC };
		// Two Auto Refresh commands
		default:
			reset_instruction <={4'hb, `DDR_NOOP, 17'd00_000 };
		endcase
		// reset_instruction <= reset_mem[reset_address];
 
	initial	reset_address = 5'h0;
	always @(posedge i_clk)
		if (i_reset)
			reset_address <= 5'h1;
		else if ((reset_ztimer)&&(reset_override))
			reset_address <= reset_address + 5'h1;
//
// initial reset_mem =
//	 0.	!DONE, TIMER,RESET_N=0, CKE=0, CMD = NOOP, TIMER = 200us ( 40,000)
//	 1.	!DONE, TIMER,RESET_N=1, CKE=0, CMD = NOOP, TIMER = 500us (100,000)
//	 2.	!DONE, TIMER,RESET_N=1, CKE=1, CMD = NOOP, TIMER = (Look me up)
//	 3.	!DONE,!TIMER,RESET_N=1, CKE=1, CMD = MODE, MRS
//	 4.	!DONE,!TIMER,RESET_N=1, CKE=1, CMD = NOOP, TIMER = tMRS
//	 5.	!DONE,!TIMER,RESET_N=1, CKE=1, CMD = MODE, MRS3
//	 6.	!DONE,!TIMER,RESET_N=1, CKE=1, CMD = NOOP, TIMER = tMRS
//	 7.	!DONE,!TIMER,RESET_N=1, CKE=1, CMD = MODE, MRS1
//	 8.	!DONE,!TIMER,RESET_N=1, CKE=1, CMD = NOOP, TIMER = tMRS
//	 9.	!DONE,!TIMER,RESET_N=1, CKE=1, CMD = MODE, MRS1
//	10.	!DONE,!TIMER,RESET_N=1, CKE=1, CMD = NOOP, TIMER = tMOD
//	11.	!DONE,!TIMER,RESET_N=1, CKE=1, (Pre-charge all)
//	12.	!DONE,!TIMER,RESET_N=1, CKE=1, (wait)
//	13.	!DONE,!TIMER,RESET_N=1, CKE=1, (Auto-refresh)
//	14.	!DONE,!TIMER,RESET_N=1, CKE=1, (Auto-refresh)
//	15.	!DONE,!TIMER,RESET_N=1, CKE=1, (wait)
 
 
//
//
// Let's keep track of any open banks.  There are 8 of them to keep track of.
//
//	A precharge requires 3 clocks at 200MHz to complete, 2 clocks at 100MHz.
//	
//
//
	reg	need_refresh;
 
	wire	w_precharge_all;
	reg	banks_are_closing, all_banks_closed;
	reg	[3:0]	bank_status	[0:7];
	reg	[13:0]	bank_address	[0:7];
 
	always @(posedge i_clk)
	begin
		bank_status[0] <= { bank_status[0][2:0], bank_status[0][0] };
		bank_status[1] <= { bank_status[1][2:0], bank_status[1][0] };
		bank_status[2] <= { bank_status[2][2:0], bank_status[2][0] };
		bank_status[3] <= { bank_status[3][2:0], bank_status[3][0] };
		bank_status[4] <= { bank_status[4][2:0], bank_status[4][0] };
		bank_status[5] <= { bank_status[5][2:0], bank_status[5][0] };
		bank_status[6] <= { bank_status[6][2:0], bank_status[6][0] };
		bank_status[7] <= { bank_status[7][2:0], bank_status[7][0] };
		all_banks_closed <= (bank_status[0][2:0] == 3'b00)
					&&(bank_status[1][2:0] == 3'b00)
					&&(bank_status[2][2:0] == 3'b00)
					&&(bank_status[3][2:0] == 3'b00)
					&&(bank_status[4][2:0] == 3'b00)
					&&(bank_status[5][2:0] == 3'b00)
					&&(bank_status[6][2:0] == 3'b00)
					&&(bank_status[7][2:0] == 3'b00);
		if (reset_override)
		begin
			bank_status[0][0] <= 1'b0;
			bank_status[1][0] <= 1'b0;
			bank_status[2][0] <= 1'b0;
			bank_status[3][0] <= 1'b0;
			bank_status[4][0] <= 1'b0;
			bank_status[5][0] <= 1'b0;
			bank_status[6][0] <= 1'b0;
			bank_status[7][0] <= 1'b0;
			banks_are_closing <= 1'b1;
		end else if ((need_refresh)||(w_precharge_all))
		begin
			bank_status[0][0] <= 1'b0;
			bank_status[1][0] <= 1'b0;
			bank_status[2][0] <= 1'b0;
			bank_status[3][0] <= 1'b0;
			bank_status[4][0] <= 1'b0;
			bank_status[5][0] <= 1'b0;
			bank_status[6][0] <= 1'b0;
			bank_status[7][0] <= 1'b0;
			banks_are_closing <= 1'b1;
		end else if (need_close_bank)
		begin
			bank_status[close_bank_cmd[16:14]]
				<= { bank_status[close_bank_cmd[16:14]][2:0], 1'b0 };
			// bank_status[close_bank_cmd[16:14]][0] <= 1'b0;
		end else if (need_open_bank)
		begin
			bank_status[activate_bank_cmd[16:14]]
				<= { bank_status[activate_bank_cmd[16:14]][2:0], 1'b1 };
			// bank_status[activate_bank_cmd[16:14]][0] <= 1'b1;
			all_banks_closed <= 1'b0;
			banks_are_closing <= 1'b0;
		end else if ((valid_bank)&&(!r_move))
			;
		else if (maybe_close_next_bank)
		begin
			bank_status[maybe_close_cmd[16:14]]
				<= { bank_status[maybe_close_cmd[16:14]][2:0], 1'b0 };
		end else if (maybe_open_next_bank)
		begin
			bank_status[maybe_open_cmd[16:14]]
				<= { bank_status[maybe_open_cmd[16:14]][2:0], 1'b1 };
			// bank_status[activate_bank_cmd[16:14]][0] <= 1'b1;
			all_banks_closed <= 1'b0;
			banks_are_closing <= 1'b0;
		end
	end
 
	always @(posedge i_clk)
		// if (cmd[22:19] == `DDR_ACTIVATE)
		if (w_this_opening_bank)
			bank_address[activate_bank_cmd[16:14]]
				<= activate_bank_cmd[13:0];
		else if (!w_this_maybe_open)
			bank_address[maybe_open_cmd[16:14]]
				<= maybe_open_cmd[13:0];
 
//
//
// Okay, let's investigate when we need to do a refresh.  Our plan will be to
// do 4 refreshes every tREFI*4 seconds.  tREFI = 7.8us, but its a parameter
// in the number of clocks so that we can handle both 100MHz and 200MHz clocks.
//
// Note that 160ns are needed between refresh commands (JEDEC, p172), or
// 320 clocks @200MHz, or equivalently 160 clocks @100MHz.  Thus to issue 4
// of these refresh cycles will require 4*320=1280 clocks@200 MHz.  After this
// time, no more refreshes will be needed for 6240 clocks.
//
// Let's think this through:
//	REFRESH_COST = (n*(320)+24)/(n*1560)
// 
//
//
	reg		refresh_ztimer;
	reg	[16:0]	refresh_counter;
	reg	[3:0]	refresh_addr;
	reg	[23:0]	refresh_instruction;
	always @(posedge i_clk)
		if (reset_override)
			refresh_addr <= 4'hf;
		else if (refresh_ztimer)
			refresh_addr <= refresh_addr + 1;
		else if (refresh_instruction[`DDR_RFBEGIN])
			refresh_addr <= 4'h0;
 
	always @(posedge i_clk)
		if (reset_override)
		begin
			refresh_ztimer <= 1'b1;
			refresh_counter <= 17'd0;
		end else if (!refresh_ztimer)
		begin
			refresh_ztimer <= (refresh_counter == 17'h1);
			refresh_counter <= (refresh_counter - 17'h1);
		end else if (refresh_instruction[`DDR_RFTIMER])
		begin
			refresh_ztimer <= 1'b0;
			refresh_counter <= refresh_instruction[16:0];
		end
 
	wire	[16:0]	w_ckREFIn, w_ckREFRst;
	assign	w_ckREFIn[ 12: 0] = CKREFI4-5*CKRFC-2-10;
	assign	w_ckREFIn[ 16:13] = 4'h0;
	assign	w_ckREFRst[12: 0] = CKRFC-2-12;
	assign	w_ckREFRst[16:13] = 4'h0;
 
	always @(posedge i_clk)
		if (reset_override)
			refresh_instruction <= { 3'h0, `DDR_NOOP, w_ckREFIn };
		else if (refresh_ztimer)
			refresh_cmd <= refresh_instruction[20:0];
	always @(posedge i_clk)
		if (reset_override)
			need_refresh <= 1'b0;
		else if (refresh_ztimer)
			need_refresh <= refresh_instruction[`DDR_NEEDREFRESH];
 
	always @(posedge i_clk)
	if (refresh_ztimer)
		case(refresh_addr)//NEED-RFC, HAVE-TIMER, 
		4'h0: refresh_instruction <= { 3'h2, `DDR_NOOP, w_ckREFIn };
		// 17'd10 = time to complete write, plus write recovery time
		//		minus two (cause we can't count zero or one)
		//	= WL+4+tWR-2 = 10
		//	= 5+4+3-2 = 10
		4'h1: refresh_instruction <= { 3'h6, `DDR_NOOP, 17'd10 };
		4'h2: refresh_instruction <= { 3'h4, `DDR_PRECHARGE, 17'h0400 };
		4'h3: refresh_instruction <= { 3'h6, `DDR_NOOP, 17'd2 };
		4'h4: refresh_instruction <= { 3'h4, `DDR_REFRESH, 17'h00 };
		4'h5: refresh_instruction <= { 3'h6, `DDR_NOOP, w_ckRFC };
		4'h6: refresh_instruction <= { 3'h4, `DDR_REFRESH, 17'h00 };
		4'h7: refresh_instruction <= { 3'h6, `DDR_NOOP, w_ckRFC };
		4'h8: refresh_instruction <= { 3'h4, `DDR_REFRESH, 17'h00 };
		4'h9: refresh_instruction <= { 3'h6, `DDR_NOOP, w_ckRFC };
		4'ha: refresh_instruction <= { 3'h4, `DDR_REFRESH, 17'h00 };
		4'hb: refresh_instruction <= { 3'h6, `DDR_NOOP, w_ckRFC };
		4'hc: refresh_instruction <= { 3'h2, `DDR_NOOP, w_ckREFRst };
		default:
			refresh_instruction <= { 3'h1, `DDR_NOOP, 17'h00 };
		endcase
 
 
//
//
//	Let's track: when will our bus be active?  When will we be reading or
//	writing?
//
//
	reg	[`BUSNOW:0]	bus_active, bus_read, bus_new;
	reg	[1:0]	bus_subaddr	[`BUSNOW:0];
	initial	bus_active = 0;
	always @(posedge i_clk)
	begin
		bus_active[`BUSNOW:0] <= { bus_active[(`BUSNOW-1):0], 1'b0 };
		bus_read[`BUSNOW:0]   <= { bus_read[(`BUSNOW-1):0], 1'b0 }; // Drive the d-bus?
		// Is this a new command?  i.e., the start of a transaction?
		bus_new[`BUSNOW:0]   <= { bus_new[(`BUSNOW-1):0], 1'b0 };
		//bus_mask[8:0] <= { bus_mask[7:0], 1'b1 }; // Write this value?
		bus_subaddr[8]  <= bus_subaddr[7];
		bus_subaddr[7]  <= bus_subaddr[6];
		bus_subaddr[6]  <= bus_subaddr[5];
		bus_subaddr[5]  <= bus_subaddr[4];
		bus_subaddr[4]  <= bus_subaddr[3];
		bus_subaddr[3]  <= bus_subaddr[2];
		bus_subaddr[2]  <= bus_subaddr[1];
		bus_subaddr[1]  <= bus_subaddr[0];
		bus_subaddr[0]  <= 2'h3;
		if (w_this_rw_move)
		begin
			bus_active[3:0]<= 4'hf; // Once per clock
			bus_read[3:0]  <= 4'hf; // These will be reads
			bus_subaddr[3] <= 2'h0;
			bus_subaddr[2] <= 2'h1;
			bus_subaddr[1] <= 2'h2;
			bus_new[{ 2'b0, rw_sub }] <= 1'b1;
 
			bus_read[3:0] <= (rw_we)? 4'h0:4'hf;
		end
	end
 
	always @(posedge i_clk)
		drive_dqs <= (~bus_read[`BUSREG])&&(|bus_active[`BUSREG]);
 
//
//
// Now, let's see, can we issue a read command?
//
//
	wire	w_s_match;
	assign	w_s_match = (s_pending)&&(r_pending)&&(r_we == s_we)
				&&(r_row == s_row)&&(r_bank == s_bank)
				&&(r_col == s_col)
				&&(r_sub > s_sub);
	reg	pipe_stall;
	always @(posedge i_clk)
	begin
		r_pending <= (i_wb_stb)&&(~o_wb_stall)
				||(r_pending)&&(pipe_stall);
		if (~pipe_stall)
			s_pending <= r_pending;
		if (~pipe_stall)
		begin
			pipe_stall <= (r_pending)&&(((!w_r_valid)||(valid_bank))&&(!w_s_match));
			o_wb_stall <= (r_pending)&&(((!w_r_valid)||(valid_bank))&&(!w_s_match));
		end else begin // if (pipe_stall)
			pipe_stall <= (s_pending)&&((!w_s_valid)||(valid_bank)||(r_move)||(last_valid_bank));
			o_wb_stall <= (s_pending)&&((!w_s_valid)||(valid_bank)||(r_move)||(last_valid_bank));
		end
		if (need_refresh)
			o_wb_stall <= 1'b1;
 
		if (~pipe_stall)
		begin
			r_we   <= i_wb_we;
			r_addr <= i_wb_addr;
			r_data <= i_wb_data;
			r_row  <= i_wb_addr[25:12];
			r_bank <= i_wb_addr[11:9];
			r_col  <= { i_wb_addr[8:2], 3'b000 }; // 9:2
			r_sub  <= i_wb_addr[1:0];
 
			// pre-emptive work
			r_nxt_row  <= (i_wb_addr[11:9]==3'h7)?i_wb_addr[25:12]+14'h1:i_wb_addr[25:12];
			r_nxt_bank <= i_wb_addr[11:9]+3'h1;
		end
 
		if (~pipe_stall)
		begin
			// Moving one down the pipeline
			s_we   <= r_we;
			s_addr <= r_addr;
			s_data <= r_data;
			s_row  <= r_row;
			s_bank <= r_bank;
			s_col  <= r_col;
			s_sub  <= r_sub;
 
			// pre-emptive work
			s_nxt_row  <= r_nxt_row;
			s_nxt_bank <= r_nxt_bank;
 
			s_match <= w_s_match;
		end
	end
 
	wire	w_need_close_this_bank, w_need_open_bank,
		w_r_valid, w_s_valid;
	assign	w_need_close_this_bank = (r_pending)&&(bank_status[r_bank][0])
			&&(r_row != bank_address[r_bank])
			||(pipe_stall)&&(s_pending)&&(bank_status[s_bank][0])
				&&(s_row != bank_address[s_bank]);
	assign	w_need_open_bank = (r_pending)&&(bank_status[r_bank][1:0]==2'b00)
			||(pipe_stall)&&(s_pending)&&(bank_status[s_bank][1:0]==2'b00);
	assign	w_r_valid = (!need_refresh)&&(r_pending)
			&&(bank_status[r_bank][3])
			&&(bank_address[r_bank]==r_row)
			&&(!bus_active[0]);
	assign	w_s_valid = (!need_refresh)&&(s_pending)
			&&(bank_status[s_bank][3])
			&&(bank_address[s_bank]==s_row)
			&&(!bus_active[0]);
 
	always @(posedge i_clk)
	begin
		need_close_bank <= (w_need_close_this_bank)
				&&(!w_this_closing_bank)&&(!last_closing_bank);
 
		maybe_close_next_bank <= (r_pending)
			&&(bank_status[r_nxt_bank][0])
			&&(r_nxt_row != bank_address[r_nxt_bank])
			&&(!w_this_maybe_close)&&(!last_maybe_close);
 
		close_bank_cmd <= { `DDR_PRECHARGE, r_bank, r_row[13:11], 1'b0, r_row[9:0] };
		maybe_close_cmd <= { `DDR_PRECHARGE, r_nxt_bank, r_nxt_row[13:11], 1'b0, r_nxt_row[9:0] };
 
 
		need_open_bank <= (w_need_open_bank)
				&&(!w_this_opening_bank)&&(!last_opening_bank);
		last_open_bank <= (w_this_opening_bank);
 
		maybe_open_next_bank <= (r_pending)
			&&(bank_status[r_bank][0] == 1'b1)
			&&(bank_status[r_nxt_bank][1:0] == 2'b00)
			&&(!w_this_maybe_open)&&(!last_maybe_open);
		last_maybe_open <= (w_this_maybe_open);
 
		activate_bank_cmd<= { `DDR_ACTIVATE,  r_bank,     r_row[13:0] };
		maybe_open_cmd <= { `DDR_ACTIVATE,r_nxt_bank, r_nxt_row[13:0] };
 
 
 
		valid_bank <= ((w_r_valid)||(pipe_stall)&&(w_s_valid))
				&&(!last_valid_bank)&&(!r_move);
		last_valid_bank <= r_move;
 
		if ((s_pending)&&(pipe_stall))
			rw_cmd[`DDR_CSBIT:`DDR_WEBIT] <= (s_we)?`DDR_WRITE:`DDR_READ;
		else if (r_pending)
			rw_cmd[`DDR_CSBIT:`DDR_WEBIT] <= (r_we)?`DDR_WRITE:`DDR_READ;
		else
			rw_cmd[`DDR_CSBIT:`DDR_WEBIT] <= `DDR_NOOP;
		if ((s_pending)&&(pipe_stall))
			rw_cmd[`DDR_WEBIT-1:0] <= { s_bank, 3'h0, 1'b0, s_col };
		else
			rw_cmd[`DDR_WEBIT-1:0] <= { r_bank, 3'h0, 1'b0, r_col };
		if ((s_pending)&&(pipe_stall))
			rw_sub <= 2'b11 - s_sub;
		else
			rw_sub <= 2'b11 - r_sub;
		if ((s_pending)&&(pipe_stall))
			rw_we <= s_we;
		else
			rw_we <= r_we;
 
	end
 
//
//
// Okay, let's look at the last assignment in our chain.  It should look
// something like:
	always @(posedge i_clk)
		if (i_reset)
			o_ddr_reset_n <= 1'b0;
		else if (reset_ztimer)
			o_ddr_reset_n <= reset_instruction[`DDR_RSTBIT];
	always @(posedge i_clk)
		if (i_reset)
			o_ddr_cke <= 1'b0;
		else if (reset_ztimer)
			o_ddr_cke <= reset_instruction[`DDR_CKEBIT];
 
	always @(posedge i_clk)
		if (i_reset)
			maintenance_override <= 1'b1;
		else
			maintenance_override <= (reset_override)||(need_refresh);
 
	initial	maintenance_cmd = { `DDR_NOOP, 17'h00 };
	always @(posedge i_clk)
		if (i_reset)
			maintenance_cmd <= { `DDR_NOOP, 17'h00 };
		else
			maintenance_cmd <= (reset_override)?reset_cmd:refresh_cmd;
 
	assign	w_this_closing_bank = (!maintenance_override)
				&&(need_close_bank);
	assign	w_this_opening_bank = (!maintenance_override)
				&&(!need_close_bank)&&(need_open_bank);
	assign	w_this_rw_move = (!maintenance_override)
				&&(!need_close_bank)&&(!need_open_bank)
				&&(valid_bank)&&(!r_move);
	assign	w_this_maybe_close = (!maintenance_override)
				&&(!need_close_bank)&&(!need_open_bank)
				&&((!valid_bank)||(r_move))
				&&(maybe_close_next_bank);
	assign	w_this_maybe_open = (!maintenance_override)
				&&(!need_close_bank)&&(!need_open_bank)
				&&((!valid_bank)||(r_move))
				&&(!maybe_close_next_bank)
				&&(maybe_open_next_bank);
	always @(posedge i_clk)
	begin
		last_opening_bank <= 1'b0;
		last_closing_bank <= 1'b0;
		last_maybe_open   <= 1'b0;
		last_maybe_close  <= 1'b0;
		r_move <= 1'b0;
		if (maintenance_override) // Command from either reset or
			cmd <= maintenance_cmd; // refresh logic
		else if (need_close_bank)
		begin
			cmd <= close_bank_cmd;
			last_closing_bank <= 1'b1;
		end else if (need_open_bank)
		begin
			cmd <= activate_bank_cmd;
			last_opening_bank <= 1'b1;
		end else if ((valid_bank)&&(!r_move))
		begin
			cmd <= rw_cmd;
			r_move <= 1'b1;
		end else if (maybe_close_next_bank)
		begin
			cmd <= maybe_close_cmd;
			last_maybe_close <= 1'b1;
		end else if (maybe_open_next_bank)
		begin
			cmd <= maybe_open_cmd;
			last_maybe_open <= 1'b1;
		end else
			cmd <= { `DDR_NOOP, rw_cmd[(`DDR_WEBIT-1):0] };
	end
 
`define	LGFIFOLN	4
`define	FIFOLEN		16
	reg	[(`LGFIFOLN-1):0]	bus_fifo_head, bus_fifo_tail;
	reg	[31:0]	bus_fifo_data	[0:(`FIFOLEN-1)];
	reg	[1:0]	bus_fifo_sub	[0:(`FIFOLEN-1)];
	reg		bus_fifo_new	[0:(`FIFOLEN-1)];
	reg		pre_ack;
 
	// The bus R/W FIFO
	wire	w_bus_fifo_read_next_transaction;
	assign	w_bus_fifo_read_next_transaction =
		(bus_fifo_sub[bus_fifo_tail]==bus_subaddr[`BUSREG])
		&&(bus_fifo_tail != bus_fifo_head)
		&&(bus_active[`BUSREG])
		&&(bus_new[`BUSREG] == bus_fifo_new[bus_fifo_tail]);
	always @(posedge i_clk)
	begin
		pre_ack <= 1'b0;
		o_ddr_dm <= 1'b0;
		if ((i_reset)||(reset_override))
		begin
			bus_fifo_head <= 4'h0;
			bus_fifo_tail <= 4'h0;
			o_ddr_dm <= 1'b0;
		end else begin
			if ((w_this_rw_move)||((s_pending)&&(s_match)&&(!pipe_stall)))
				bus_fifo_head <= bus_fifo_head + 4'h1;
 
			o_ddr_dm <= (bus_active[`BUSREG])&&(!bus_read[`BUSREG]);
			if (w_bus_fifo_read_next_transaction)
			begin
				bus_fifo_tail <= bus_fifo_tail + 4'h1;
				pre_ack <= 1'b1;
				o_ddr_dm <= 1'b0;
			end
		end
		bus_fifo_data[bus_fifo_head] <= s_data;
		bus_fifo_sub[bus_fifo_head] <= s_sub;
		bus_fifo_new[bus_fifo_head] <= w_this_rw_move;
	end
 
 
	assign	o_ddr_cs_n  = cmd[`DDR_CSBIT];
	assign	o_ddr_ras_n = cmd[`DDR_RASBIT];
	assign	o_ddr_cas_n = cmd[`DDR_CASBIT];
	assign	o_ddr_we_n  = cmd[`DDR_WEBIT];
	assign	o_ddr_dqs   = drive_dqs;
	assign	o_ddr_addr  = cmd[(`DDR_ADDR_BITS-1):0];
	assign	o_ddr_ba    = cmd[(`DDR_BABITS+`DDR_ADDR_BITS-1):`DDR_ADDR_BITS];
	always @(posedge i_clk)
		o_ddr_data  <= bus_fifo_data[bus_fifo_tail];
	assign	w_precharge_all = (cmd[`DDR_CSBIT:`DDR_WEBIT]==`DDR_PRECHARGE)
				&&(o_ddr_addr[10]); // 5 bits
 
	// Need to set o_wb_dqs high one clock prior to any read.
	// As per spec, ODT = 0 during reads
	assign	o_ddr_bus_oe = ~bus_read[`BUSNOW];
 
	// ODT must be in high impedence while reset_n=0, then it can be set
	// to low or high.
	assign	o_ddr_odt = o_ddr_bus_oe;
 
	always @(posedge i_clk)
		o_wb_ack <= pre_ack;
	always @(posedge i_clk)
		o_wb_data <= i_ddr_data;
 
endmodule
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.