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

Subversion Repositories wb2axip

[/] [wb2axip/] [trunk/] [rtl/] [aximrd2wbsp.v] - Rev 10

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

////////////////////////////////////////////////////////////////////////////////
//
// Filename: 	aximrd2wbsp.v
//
// Project:	Pipelined Wishbone to AXI converter
//
// Purpose:	Bridge an AXI read channel pair to a single wishbone read
//		channel.
//
// Creator:	Dan Gisselquist, Ph.D.
//		Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 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
//
//
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype	none
//
module	aximrd2wbsp #(
	parameter C_AXI_ID_WIDTH	= 6, // The AXI id width used for R&W
                                             // This is an int between 1-16
	parameter C_AXI_DATA_WIDTH	= 32,// Width of the AXI R&W data
	parameter C_AXI_ADDR_WIDTH	= 28,	// AXI Address width
	parameter AW			= 26,	// AXI Address width
	parameter LGFIFO                =  4
	// parameter	WBMODE		= "B4PIPELINE"
		// Could also be "BLOCK"
	) (
	input	wire			i_axi_clk,	// Bus clock
	input	wire			i_axi_reset_n,	// Bus reset
 
// AXI read address channel signals
	output	wire			o_axi_arready,	// Read address ready
	input	wire	[C_AXI_ID_WIDTH-1:0]	i_axi_arid,	// Read ID
	input	wire	[C_AXI_ADDR_WIDTH-1:0]	i_axi_araddr,	// Read address
	input	wire	[7:0]		i_axi_arlen,	// Read Burst Length
	input	wire	[2:0]		i_axi_arsize,	// Read Burst size
	input	wire	[1:0]		i_axi_arburst,	// Read Burst type
	input	wire	[0:0]		i_axi_arlock,	// Read lock type
	input	wire	[3:0]		i_axi_arcache,	// Read Cache type
	input	wire	[2:0]		i_axi_arprot,	// Read Protection type
	input	wire	[3:0]		i_axi_arqos,	// Read Protection type
	input	wire			i_axi_arvalid,	// Read address valid
 
// AXI read data channel signals   
	output	wire [C_AXI_ID_WIDTH-1:0] o_axi_rid,     // Response ID
	output	wire [1:0]		o_axi_rresp,   // Read response
	output	reg			o_axi_rvalid,  // Read reponse valid
	output	wire [C_AXI_DATA_WIDTH-1:0] o_axi_rdata,    // Read data
	output	wire 			o_axi_rlast,    // Read last
	input	wire			i_axi_rready,  // Read Response ready
 
	// We'll share the clock and the reset
	output	reg				o_wb_cyc,
	output	reg				o_wb_stb,
	output	wire [(AW-1):0]	o_wb_addr,
	input	wire				i_wb_ack,
	input	wire				i_wb_stall,
	input	[(C_AXI_DATA_WIDTH-1):0]	i_wb_data,
	input	wire				i_wb_err
	);
 
	localparam	DW = C_AXI_DATA_WIDTH;
 
	wire	w_reset;
	assign	w_reset = (i_axi_reset_n == 1'b0);
 
 
	reg	[(C_AXI_ID_WIDTH+AW+1)-1:0]	afifo	[0:((1<<(LGFIFO))-1)];
	reg	[(DW+1)-1:0]			dfifo	[0:((1<<(LGFIFO))-1)];
	reg	[(C_AXI_ID_WIDTH+AW+1)-1:0]	fifo_at_neck, afifo_at_tail;
	reg	[(DW+1)-1:0]			dfifo_at_tail;
 
	// We're going to need to keep track of transaction bursts in progress,
	// since the wishbone doesn't.  For this, we'll use a FIFO, but with
	// multiple pointers:
	//
	//	fifo_head	- pointer to where to write the next incoming
	//				bus request .. adjusted when
	//				(o_axi_arready)&&(i_axi_arvalid)
	//	fifo_neck	- pointer to where to read from the FIFO in
	//				order to issue another request.  Used
	//				when (o_wb_stb)&&(!i_wb_stall)
	//	fifo_torso	- pointer to where to write a wishbone
	//				transaction upon return.
	//				when (i_ack)
	//	fifo_tail	- pointer to where the last transaction is to
	//				be retired when
	//					(i_axi_rvalid)&&(i_axi_rready)
	//
	// All of these are to be set to zero upon a reset signal.
	//
	reg	[LGFIFO-1:0]	fifo_head, fifo_neck, fifo_torso, fifo_tail;
 
	// Since we need to insure that these pointers wrap properly at
	// LGFIFO bits, and since it is confusing to do that within IF 
	// statements, 
	wire	[LGFIFO-1:0]	next_head, next_neck, next_torso, next_tail,
				almost_head;
	wire		fifo_full;
	assign	next_head  = fifo_head  + 1;
	assign	next_neck  = fifo_neck  + 1;
	assign	next_torso = fifo_torso + 1;
	assign	next_tail  = fifo_tail  + 1;
	assign	almost_head = fifo_head  + 1;
 
	assign fifo_full = (almost_head == fifo_tail);
 
	reg	wr_last, filling_fifo, incr;
	reg	[7:0]			len;
	reg	[(AW-1):0]		wr_fifo_addr;
	reg	[(C_AXI_ID_WIDTH-1):0]	wr_fifo_id;
 
	//
	//
	//
	//
	// Here's our plan: Any time READY & VALID are both true, initiate a
	// transfer (unless one is ongoing).  Hold READY false while initiating
	// any burst transaction.  Keep the request RID and burst length stuffs
	// into a FIFO.
	// queue are both valid, issue the wishbone read request.  Once a read
	// request returns, retire the value in the FIFO queue.
	//
	// The FIFO queue *must* include:
	//
	//	RQ, ADDR, LAST
	//
	initial len          = 0;
	initial filling_fifo = 0;
	initial fifo_head    = 0;
	always @(posedge i_axi_clk)
	begin
		wr_last <= 1'b0;
 
		if (filling_fifo)
		begin
			if (!fifo_full)
			begin
				len <= len - 1;
				if (len == 1)
					filling_fifo <= 1'b0;
				fifo_head <= next_head;
				wr_fifo_addr <= wr_fifo_addr
					+ {{(AW-1){1'b0}}, incr};
				wr_last <= (len == 1);
			end
		end else begin
			wr_fifo_addr <= i_axi_araddr[(C_AXI_ADDR_WIDTH-1):(C_AXI_ADDR_WIDTH-AW)];
			wr_fifo_id <= i_axi_arid;
			incr <= i_axi_arburst[0];
			if ((o_axi_arready)&&(i_axi_arvalid))
			begin
				fifo_head <= next_head;
				len <= i_axi_arlen;
				filling_fifo <= (i_axi_arlen != 0);
				wr_last <= 1'b1;
			end
		end
 
		if (w_reset)
		begin
			len <= 0;
			filling_fifo <= 1'b0;
			fifo_head <= 0;
		end
	end
 
	always @(posedge i_axi_clk)
		afifo[fifo_head] <= { wr_fifo_id, wr_last, wr_fifo_addr };
 
	reg	err_state;	
	initial o_wb_cyc   = 1'b0;
	initial o_wb_stb   = 1'b0;
	initial fifo_neck  = 0;
	initial fifo_torso = 0;
	initial err_state  = 0;
	always @(posedge i_axi_clk)
	begin
		if (w_reset)
		begin
			o_wb_cyc <= 1'b0;
			o_wb_stb <= 1'b0;
 
			fifo_neck  <= 0;
			fifo_torso <= 0;
 
			err_state <= 0;
		end else if (o_wb_stb)
		begin
			if (i_wb_err)
			begin
				o_wb_stb <= 1'b0;
				err_state <= 1'b1;
			end else if (!i_wb_stall)
			begin
				o_wb_stb <= (fifo_head != next_neck);
						// && (WBMODE != "B3SINGLE");
				// o_wb_cyc <= (WBMODE != "B3SINGLE");
			end
 
			if (!i_wb_stall)
				fifo_neck <= next_neck;
			if (i_wb_ack)
				fifo_torso <= next_torso;
		end else if (err_state)
		begin
			fifo_torso <= next_torso;
			if (fifo_neck == next_torso)
				err_state <= 1'b0;
			o_wb_cyc <= 1'b0;
		end else if (o_wb_cyc)
		begin
			if (i_wb_ack)
				fifo_torso <= next_torso;
			if (fifo_neck == next_torso)
				o_wb_cyc <= 1'b0;
		end else if (fifo_neck != fifo_head)
		begin
			o_wb_cyc <= 1'b1;
			o_wb_stb <= 1'b1;
		end
	end
 
	always @(posedge i_axi_clk)
		fifo_at_neck <= afifo[fifo_neck];
	assign	o_wb_addr = fifo_at_neck[(AW-1):0];
 
	always @(posedge i_axi_clk)
		dfifo[fifo_torso] <= { (err_state)||(i_wb_err), i_wb_data };
 
 
	always @(posedge i_axi_clk)
		if (w_reset)
			fifo_tail <= 0;
		else if ((o_axi_rvalid)&&(i_axi_rready))
			fifo_tail <= next_tail;
 
	always @(posedge i_axi_clk)
	begin
		afifo_at_tail <= afifo[fifo_tail];
		dfifo_at_tail <= dfifo[fifo_tail];
		// o_axi_rdata <= dfifo[fifo_tail];
		// o_axi_rlast <= afifo[fifo_tail];
		// o_axi_rid   <= afifo[fifo_tail];
	end
	assign	o_axi_rlast = afifo_at_tail[AW];
	assign	o_axi_rid   = afifo_at_tail[(C_AXI_ID_WIDTH+AW):(AW+1)];
	assign	o_axi_rresp = { (2){dfifo_at_tail[DW]} };
	assign	o_axi_rdata = dfifo_at_tail[(DW-1):0];
 
	initial	o_axi_rvalid = 1'b0;
	always @(posedge i_axi_clk)
		if (w_reset)
			o_axi_rvalid <= 0;
		else if (fifo_tail != fifo_neck)
			o_axi_rvalid <= (fifo_tail != fifo_neck + 1);
 
	assign o_axi_arready = (!fifo_full)&&(!filling_fifo);
 
	// Make Verilator happy
	// verilator lint_off UNUSED
	wire	[(C_AXI_ID_WIDTH+1)+(C_AXI_ADDR_WIDTH-AW)
		+3+1+1+4+3+4-1:0]	unused;
	assign	unused = { i_axi_arsize, i_axi_arburst[1],
			i_axi_arlock, i_axi_arcache, i_axi_arprot,
			i_axi_arqos,
			fifo_at_neck[(C_AXI_ID_WIDTH+AW+1)-1:AW],
			i_axi_araddr[(C_AXI_ADDR_WIDTH-AW-1):0] };
	// verilator lint_on  UNUSED
 
`ifdef	FORMAL
	reg	f_past_valid;
	initial f_past_valid = 1'b0;
	always @(posedge i_axi_clk)
		f_past_valid <= 1'b1;
 
	wire	[LGFIFO-1:0]	f_fifo_used, f_fifo_neck_used,
				f_fifo_torso_used;
	assign	f_fifo_used       = fifo_head - fifo_tail;
	assign	f_fifo_neck_used  = fifo_head - fifo_neck;
	assign	f_fifo_torso_used = fifo_head - fifo_torso;
 
	always @(*)
		assert((f_fifo_used < {(LGFIFO){1'b1}})||(!o_axi_arready));
	always @(*)
		assert(f_fifo_neck_used  <= f_fifo_used);
	always @(*)
		assert(f_fifo_torso_used <= f_fifo_used);
 
	always @(posedge i_axi_clk)
	if ((f_past_valid)&&(!$past(i_axi_reset_n)))
		assert(f_fifo_used == 0);
 
`endif
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.