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

Subversion Repositories s6soc

[/] [s6soc/] [trunk/] [rtl/] [qflashxpress.v] - Rev 51

Compare with Previous | Blame | View Log

////////////////////////////////////////////////////////////////////////////////
//
// Filename:	qflashxpress.v
//
// Project:	CMod S6 System on a Chip, ZipCPU demonstration project
//
// Purpose:	To provide wishbone controlled read access (and read access
//		*only*) to the QSPI flash, using a flash clock of 80MHz, and
//	nothing more.  Indeed, this is designed to be a *very* stripped down
//	version of a flash driver, with the goal of providing 1) very fast
//	access for 2) very low logic count.
//
//	Two modes/states of operation:
//
//	STARTUP
//	 1. Waits for the flash to come on line
//		Start out idle for 300 uS
//	 2. Sends a signal to remove the flash from any QSPI read mode.  In our
//		case, we'll send several clocks of an empty command.  In SPI
//		mode, it'll get ignored.  In QSPI mode, it'll remove us from
//		QSPI mode.
//	 3. Explicitly places and leaves the flash into QSPI mode
//		0xEB 3(0xa0) 0xa0 0xa0 0xa0 4(0x00)
//	 4. All done
//
//	NORMAL-OPS
//	ODATA <- ?, 3xADDR, 0xa0, 0x00, 0x00 | 0x00, 0x00, 0x00, 0x00 ? (22nibs)
//	STALL <- TRUE until closed at the end
//	MODE  <- 2'b10 for 4 clks, then 2'b11
//	CLK   <- 2'b10 before starting, then 2'b01 until the end
//	CSN   <- 0 any time CLK != 2'b11
//
//
//
// Creator:	Dan Gisselquist, Ph.D.
//		Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2017, 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	OPT_FLASH_PIPELINE
module	qflashxpress(i_clk,
		i_wb_cyc, i_wb_stb, i_wb_addr,
			o_wb_ack, o_wb_stall, o_wb_data,
		o_qspi_sck, o_qspi_cs_n, o_qspi_mod, o_qspi_dat, i_qspi_dat);
	localparam	AW=24-2;
	input			i_clk;
	//
	input			i_wb_cyc, i_wb_stb;
	input		[(AW-1):0] i_wb_addr;
	//
	output	reg		o_wb_ack, o_wb_stall;
	output	reg	[31:0]	o_wb_data;
	//
	output	wire	[1:0]	o_qspi_sck;
	output	wire		o_qspi_cs_n;
	output	wire	[1:0]	o_qspi_mod;
	output	wire	[3:0]	o_qspi_dat;
	input	wire	[3:0]	i_qspi_dat;
 
	//
	//
	// Maintenance / startup portion
	//
	//
	reg	maintenance;
	reg	[14:0]	m_counter;
	reg	[1:0]	m_state;
	reg	[1:0]	m_mod;
	reg		m_cs_n;
	reg	[1:0]	m_clk;
	reg	[31:0]	m_data;
	wire	[3:0]	m_dat;
 
	initial	maintenance = 1'b1;
	initial	m_counter   = 0;
	initial	m_state     = 2'b00;
	always @(posedge i_clk)
	begin
		if (maintenance)
			m_counter <= m_counter + 1'b1;
		m_mod <= 2'b00; // SPI mode always for maintenance
		case(m_state)
		2'b00: begin
			// Step one: wait for the flash device to initialize.
			// Perhaps this is more for form than anything else,
			// especially if we just loaded our configuration from
			// the flash, but in case we did not--we do this anyway.
			maintenance <= 1'b1;
			if (m_counter[14:0]==15'h7fff) // 24000 is the limit
				m_state <= 2'b01;
			m_cs_n <= 1'b1;
			m_clk  <= 2'b11;
			end
		2'b01: begin
			// Now that the flash has had a chance to start up, feed
			// it with chip selects with no clocks.   This is
			// guaranteed to remove us from any XIP mode we might
			// be in upon startup.  We do this so that we might be
			// placed into a known mode--albeit the wrong one, but
			// a known one.
			maintenance <= 1'b1;
			//
			// 1111 0000 1111 0000 1111 0000 1111 0000
			// 1111 0000 1111 0000 1111 0000 1111 0000
			// 1111 ==> 17 * 4 clocks, or 68 clocks in total
			//
			if (m_counter[14:0] == 15'd138)
				m_state <= 2'b10;
			m_cs_n <= 1'b0;
			m_clk  <= {(2){!m_counter[2]}};
			m_data <= { 32'hfff0f0ff }; // EB command
			m_data[31:28] <= 0; // just ... not yet
			end
		2'b10: begin
			// Rest, before issuing our initial read command
			maintenance <= 1'b1;
			if (m_counter[14:0] == 15'd138 + 15'd48)
				m_state <= 2'b11;
			m_cs_n <= 1'b1;	// Rest the interface
			m_clk  <= 2'b11;
			m_data <= { 32'hfff0f0ff }; // EB command
			end
		2'b11: begin
			if (m_counter[14:0] == 15'd138+15'd48+15'd10)
				maintenance <= 1'b0;
			m_cs_n <= 1'b0;
			m_clk  <= (m_clk == 2'b11)? 2'b10 : 2'b01;
			if (m_clk == 2'b01) // EB QuadIO Read Cmd
				m_data <= {m_data[27:0], 4'h0};
			// We depend upon the non-maintenance code to provide
			// our first (bogus) address, mode, dummy cycles, and
			// data bits.
			end
		endcase
	end
	assign	m_dat = m_data[31:28];
 
	//
	//
	// Data / access portion
	//
	//
	reg	[21:0]	busy_pipe;
	reg	[31:0]	data_pipe;
	reg		pre_ack;
	initial	data_pipe = 0;
	always @(posedge i_clk)
		if (((i_wb_stb)&&(!o_wb_stall))||(maintenance))
			data_pipe <= { i_wb_addr, 2'b00, 8'ha0 };
		else if (o_qspi_sck == 2'b01)
			data_pipe <= { data_pipe[27:0], 4'h0 };
	assign	o_qspi_dat = (maintenance)? m_dat : data_pipe[31:28];
 
`ifdef	OPT_FLASH_PIPELINE
	reg	pipe_req;
 
	reg	[(AW-1):0]	last_addr;
	always  @(posedge i_clk)
		if ((i_wb_stb)&&(!o_wb_stall))
			last_addr <= i_wb_addr;
 
	initial	pipe_req = 1'b0;
	always @(posedge i_clk)
		pipe_req <= (pre_ack)&&(i_wb_stb)
				&&(last_addr + 1'b1 == i_wb_addr);
`else
	wire	pipe_req;
	assign	pipe_req = 1'b0;
`endif
 
 
	initial	pre_ack = 0;
	always @(posedge i_clk)
		if ((maintenance)||(!i_wb_cyc))
			pre_ack <= 1'b0;
		else if ((i_wb_stb)&&(!o_wb_stall))
			pre_ack <= 1'b1;
		else if ((o_wb_ack)&&(!pipe_req))
			pre_ack <= 1'b0;
 
	reg	[43:0]	clk_pipe;
	initial	clk_pipe = -1;
	always @(posedge i_clk)
		if (((i_wb_stb)&&(!o_wb_stall)&&(!pipe_req))||(maintenance))
			clk_pipe <= { 2'b00, {(21){2'b01}}};
		else if (((i_wb_stb)&&(!o_wb_stall))||(maintenance))
			clk_pipe <= { {(8){2'b01}}, {(14){2'b11}} };
		else
			clk_pipe <= { clk_pipe[41:0], 2'b11 };
	assign	o_qspi_sck = (maintenance)? m_clk : clk_pipe[43:42];
	assign	o_qspi_cs_n= (maintenance)?m_cs_n : (clk_pipe[43:42] == 2'b11);
 
	reg	[9:0]	mod_pipe;
	always @(posedge i_clk)
		if(((i_wb_stb)&&(!o_wb_stall)&&(!pipe_req))||(maintenance))
			mod_pipe <= { 10'h0 }; // Always quad, but in/out
		else
			mod_pipe <= { mod_pipe[8:0], 1'b1 }; // Add input at end
	assign	o_qspi_mod = (maintenance) ? m_mod :(mod_pipe[9]? 2'b11:2'b10);
 
	initial	busy_pipe = 22'h3fffff;
	always @(posedge i_clk)
		if (((i_wb_stb)&&(!o_wb_stall)&&(!pipe_req))||(maintenance))
			busy_pipe <= { 22'h3fffff };
		else if ((i_wb_stb)&&(!o_wb_stall))
			busy_pipe <= { 22'h3fc000 };
		else
			busy_pipe <= { busy_pipe[20:0], 1'b0 };
 
	initial	o_wb_stall = 1'b1;
	always @(posedge i_clk)
		o_wb_stall <= ((i_wb_stb)&&(!o_wb_stall))
			||(busy_pipe[19])||((busy_pipe[20])&&(!pipe_req));
 
	reg	ack_pipe;
	initial	ack_pipe = 1'b0;
	always @(posedge i_clk)
		ack_pipe <= (pre_ack)&&(busy_pipe[20:19] == 2'b10);
	initial	o_wb_ack = 1'b0;
	always @(posedge i_clk)
		o_wb_ack <= (pre_ack)&&(ack_pipe);
 
	always @(posedge i_clk)
		o_wb_data <= { o_wb_data[27:0], i_qspi_dat };
 
endmodule
 
 

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.