www.pudn.com > USB2.0_rtl_ipcore_verilog.rar > usbf_idma.v


///////////////////////////////////////////////////////////////////// 
////                                                             //// 
////  Internal DMA Engine                                        //// 
////                                                             //// 
////                                                             //// 
////  Author: Rudolf Usselmann                                   //// 
////          rudi@asics.ws                                      //// 
////                                                             //// 
////                                                             //// 
////  Downloaded from: http://www.opencores.org/cores/usb/       //// 
////                                                             //// 
///////////////////////////////////////////////////////////////////// 
////                                                             //// 
//// Copyright (C) 2000-2003 Rudolf Usselmann                    //// 
////                         www.asics.ws                        //// 
////                         rudi@asics.ws                       //// 
////                                                             //// 
//// This source file may be used and distributed without        //// 
//// restriction provided that this copyright statement is not   //// 
//// removed from the file and that any derivative work contains //// 
//// the original copyright notice and the associated disclaimer.//// 
////                                                             //// 
////     THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY     //// 
//// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED   //// 
//// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS   //// 
//// FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL THE AUTHOR      //// 
//// OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,         //// 
//// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES    //// 
//// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE   //// 
//// GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR        //// 
//// BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF  //// 
//// LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT  //// 
//// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT  //// 
//// OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE         //// 
//// POSSIBILITY OF SUCH DAMAGE.                                 //// 
////                                                             //// 
///////////////////////////////////////////////////////////////////// 
 
//  CVS Log 
// 
//  $Id: usbf_idma.v,v 1.8 2003/10/17 02:36:57 rudi Exp $ 
// 
//  $Date: 2003/10/17 02:36:57 $ 
//  $Revision: 1.8 $ 
//  $Author: rudi $ 
//  $Locker:  $ 
//  $State: Exp $ 
// 
// Change History: 
//               $Log: usbf_idma.v,v $ 
//               Revision 1.8  2003/10/17 02:36:57  rudi 
//               - Disabling bit stuffing and NRZI encoding during speed negotiation 
//               - Now the core can send zero size packets 
//               - Fixed register addresses for some of the higher endpoints 
//                 (conversion between decimal/hex was wrong) 
//               - The core now does properly evaluate the function address to 
//                 determine if the packet was intended for it. 
//               - Various other minor bugs and typos 
// 
//               Revision 1.7  2001/11/04 12:22:45  rudi 
// 
//               - Fixed previous fix (brocke something else ...) 
//               - Majore Synthesis cleanup 
// 
//               Revision 1.6  2001/11/03 03:26:22  rudi 
// 
//               - Fixed several interrupt and error condition reporting bugs 
// 
//               Revision 1.5  2001/09/24 01:15:28  rudi 
// 
//               Changed reset to be active high async. 
// 
//               Revision 1.4  2001/09/23 08:39:33  rudi 
// 
//               Renamed DEBUG and VERBOSE_DEBUG to USBF_DEBUG and USBF_VERBOSE_DEBUG ... 
// 
//               Revision 1.3  2001/09/19 14:38:57  rudi 
// 
//               Fixed TxValid handling bug. 
// 
//               Revision 1.2  2001/09/13 13:14:02  rudi 
// 
//               Fixed a problem that would sometimes prevent the core to come out of 
//               reset and immediately be operational ... 
// 
//               Revision 1.1  2001/08/03 05:30:09  rudi 
// 
// 
//               1) Reorganized directory structure 
// 
//               Revision 1.2  2001/03/31 13:00:51  rudi 
// 
//               - Added Core configuration 
//               - Added handling of OUT packets less than MAX_PL_SZ in DMA mode 
//               - Modified WISHBONE interface and sync logic 
//               - Moved SSRAM outside the core (added interface) 
//               - Many small bug fixes ... 
// 
//               Revision 1.0  2001/03/07 09:17:12  rudi 
// 
// 
//               Changed all revisions to revision 1.0. This is because OpenCores CVS 
//               interface could not handle the original '0.1' revision .... 
// 
//               Revision 0.1.0.1  2001/02/28 08:10:50  rudi 
//               Initial Release 
// 
//                             
 
`include "usbf_defines.v" 
 
module usbf_idma(	clk, rst, 
 
		// Packet Disassembler/Assembler interface 
		rx_data_st, rx_data_valid, rx_data_done,  
		send_data, tx_data_st, rd_next, 
 
		// Protocol Engine 
		rx_dma_en, tx_dma_en, 
		abort, idma_done, 
		buf_size, dma_en, 
		send_zero_length, 
 
		// Register File Manager Interface 
		adr, size, sizu_c, 
 
		// Memory Arb interface 
		madr, mdout, mdin, mwe, mreq, mack 
		); 
 
parameter	SSRAM_HADR = 14; 
 
// Packet Disassembler/Assembler interface 
input		clk, rst; 
input	[7:0]	rx_data_st; 
input		rx_data_valid; 
input		rx_data_done; 
output		send_data; 
output	[7:0]	tx_data_st; 
input		rd_next; 
 
// Protocol Engine 
input		rx_dma_en;	// Allows the data to be stored 
input		tx_dma_en;	// Allows for data to be retrieved 
input		abort;		// Abort Transfer (time_out, crc_err or rx_error) 
output		idma_done;	// DMA is done 
input	[13:0]	buf_size;	// Actual buffer size 
input		dma_en;		// External DMA enabled 
input		send_zero_length; 
 
// Register File Manager Interface 
input	[SSRAM_HADR + 2:0]	adr;	// Byte Address 
input	[13:0]	size;		// Size in bytes 
output	[10:0]	sizu_c;		// Up and Down counting size registers, used to update 
 
// Memory Arb interface 
output	[SSRAM_HADR:0]	madr;	// word address 
output	[31:0]	mdout; 
input	[31:0]	mdin; 
output		mwe; 
output		mreq; 
input		mack; 
 
/////////////////////////////////////////////////////////////////// 
// 
// Local Wires and Registers 
// 
 
parameter	[7:0]	// synopsys enum state 
		IDLE		= 8'b00000001, 
		WAIT_MRD	= 8'b00000010, 
		MEM_WR		= 8'b00000100, 
		MEM_WR1		= 8'b00001000, 
		MEM_WR2		= 8'b00010000, 
		MEM_RD1		= 8'b00100000, 
		MEM_RD2		= 8'b01000000, 
		MEM_RD3		= 8'b10000000; 
 
reg	[7:0]	/* synopsys enum state */ state, next_state; 
// synopsys state_vector state 
 
reg		tx_dma_en_r, rx_dma_en_r; 
 
reg	[SSRAM_HADR:0]	adr_cw;		// Internal word address counter 
reg	[2:0]	adr_cb;			// Internal byte address counter 
reg	[SSRAM_HADR:0]	adrw_next;	// next address 
reg	[SSRAM_HADR:0]	adrw_next1;	// next address (after overrun check) 
reg	[SSRAM_HADR:0]	last_buf_adr;	// Last Buffer Address 
reg	[2:0]	adrb_next;		// next byte address 
reg	[13:0]	sizd_c;			// Internal size counter 
reg	[10:0]	sizu_c;			// Internal size counter 
wire		adr_incw; 
wire		adr_incb; 
wire		siz_dec; 
wire		siz_inc; 
 
reg		word_done;		// Indicates that a word has been 
					// assembled 
reg		mreq_d;			// Memory request from State Machine 
reg	[31:0]	dtmp_r;			// Temp data assembly register 
reg	[31:0]	dout_r;			// Data output register 
reg		mwe_d;			// Memory Write enable 
reg		dtmp_sel;		// Selects tmp data register for pre-fetch 
 
reg		sizd_is_zero;		// Indicates when all bytes have been 
					// transferred 
wire		sizd_is_zero_d; 
 
reg	[7:0]	tx_data_st;		// Data output to packet assembler 
reg	[31:0]	rd_buf0, rd_buf1;	// Mem Rd. buffers for TX 
reg		rd_first;		// Indicates initial fill of buffers 
 
reg		idma_done;		// DMA transfer is done 
 
reg		mack_r; 
wire		send_data;		// Enable UTMI Transmitter 
reg		send_data_r; 
 
reg		word_done_r; 
reg		wr_last; 
reg		wr_last_en; 
reg		wr_done; 
reg		wr_done_r; 
reg		dtmp_sel_r; 
reg		mwe; 
reg		rx_data_done_r2; 
wire		fill_buf0, fill_buf1; 
wire		adrb_is_3; 
 
reg		rx_data_done_r; 
reg		rx_data_valid_r; 
reg	[7:0]	rx_data_st_r; 
 
reg		send_zero_length_r; 
 
/////////////////////////////////////////////////////////////////// 
// 
// Memory Arb interface 
// 
 
// Memory Request 
assign mreq = (mreq_d & !mack_r) | word_done_r; 
 
// Output Data 
assign mdout = dout_r; 
 
// Memory Address 
assign madr = adr_cw; 
 
always @(posedge clk) 
	mwe <= mwe_d; 
 
always @(posedge clk) 
	mack_r <= mreq & mack; 
 
/////////////////////////////////////////////////////////////////// 
// 
// Misc Logic 
// 
 
always @(posedge clk) 
	rx_data_valid_r <= rx_data_valid; 
 
always @(posedge clk) 
	rx_data_st_r <= rx_data_st; 
 
always @(posedge clk) 
	rx_data_done_r <= rx_data_done; 
 
always @(posedge clk) 
	rx_data_done_r2 <= rx_data_done_r; 
 
// Generate one cycle pulses for tx and rx dma enable 
always @(posedge clk) 
	tx_dma_en_r <= tx_dma_en; 
 
always @(posedge clk) 
	rx_dma_en_r <= rx_dma_en; 
 
always @(posedge clk) 
	send_zero_length_r <= send_zero_length; 
 
// address counter 
always @(posedge clk) 
	if(rx_dma_en_r || tx_dma_en_r)	adr_cw <= adr[SSRAM_HADR + 2:2]; 
	else				adr_cw <= adrw_next1; 
 
always @(posedge clk) 
	last_buf_adr <= adr + { {SSRAM_HADR+2-13{1'b0}}, buf_size }; 
 
always @(dma_en or adrw_next or last_buf_adr) 
	if(adrw_next == last_buf_adr && dma_en)	adrw_next1 = {SSRAM_HADR+1{1'b0}}; 
	else					adrw_next1 = adrw_next; 
 
always @(adr_incw or adr_cw) 
	if(adr_incw)	adrw_next = adr_cw + {{SSRAM_HADR{1'b0}}, 1'b1}; 
	else		adrw_next = adr_cw; 
 
`ifdef USBF_ASYNC_RESET 
always @(posedge clk or negedge rst) 
`else 
always @(posedge clk) 
`endif 
	if(!rst)			adr_cb <= 3'h0; 
	else 
	if(rx_dma_en_r || tx_dma_en_r)	adr_cb <= adr[2:0]; 
	else				adr_cb <= adrb_next; 
 
always @(adr_incb or adr_cb) 
	if(adr_incb)	adrb_next = adr_cb + 3'h1; 
	else		adrb_next = adr_cb; 
 
assign adr_incb = rx_data_valid_r | rd_next; 
assign adr_incw = !dtmp_sel_r & mack_r; 
 
// Size Counter (counting backward from input size) 
`ifdef USBF_ASYNC_RESET 
always @(posedge clk or negedge rst) 
`else 
always @(posedge clk) 
`endif 
	if(!rst)			sizd_c <= 14'h3fff; 
	else 
	if(tx_dma_en || tx_dma_en_r)	sizd_c <= size; 
	else 
	if(siz_dec)			sizd_c <= sizd_c - 14'h1; 
 
assign siz_dec = (rd_first & mack_r) | (rd_next & (sizd_c != 14'h0)); 
 
assign sizd_is_zero_d = sizd_c == 14'h0; 
 
always @(posedge clk) 
	sizd_is_zero <= sizd_is_zero_d; 
 
// Size Counter (counting up from zero) 
`ifdef USBF_ASYNC_RESET 
always @(posedge clk or negedge rst) 
`else 
always @(posedge clk) 
`endif 
	if(!rst)		sizu_c <= 11'h0; 
	else 
	// Do I need to add "abort" in the next line ??? 
	if(rx_dma_en_r)		sizu_c <= 11'h0; 
	else 
	if(siz_inc)		sizu_c <= sizu_c + 11'h1; 
 
assign siz_inc = rx_data_valid_r; 
 
// DMA Done Indicator 
always @(posedge clk) 
	idma_done <= (rx_data_done_r | sizd_is_zero_d); // & !tx_dma_en; 
 
/////////////////////////////////////////////////////////////////// 
// 
// RX Logic 
// 
 
always @(posedge clk) 
	dtmp_sel_r <= dtmp_sel; 
 
// Memory data input 
always @(posedge clk) 
	if(dtmp_sel_r)			dtmp_r <= mdin; 
	else 
	if(rx_data_valid_r) 
	   begin 
		if(adr_cb[1:0] == 2'h0)	dtmp_r[07:00] <= rx_data_st_r; 
		if(adr_cb[1:0] == 2'h1)	dtmp_r[15:08] <= rx_data_st_r; 
		if(adr_cb[1:0] == 2'h2)	dtmp_r[23:16] <= rx_data_st_r; 
		if(adr_cb[1:0] == 2'h3)	dtmp_r[31:24] <= rx_data_st_r; 
	   end 
 
always @(posedge clk) 
	word_done <= ((adr_cb[1:0] == 2'h3) & rx_data_valid_r) | wr_last; 
 
always @(posedge clk) 
	word_done_r <= word_done & !word_done_r; 
 
// Store output data and address when we got a word 
always @(posedge clk) 
	if(word_done)	dout_r <= dtmp_r; 
 
always @(posedge clk) 
	wr_last <= (adr_cb[1:0] != 2'h0) & !rx_data_valid_r & wr_last_en; 
 
always @(posedge clk) 
	wr_done_r <= rx_data_done_r; 
 
always @(posedge clk) 
	wr_done <= wr_done_r; 
 
/////////////////////////////////////////////////////////////////// 
// 
// TX Logic 
// 
 
// Fill TX Buffers 
always @(posedge clk) 
	if(fill_buf0)	rd_buf0 <= mdin; 
 
always @(posedge clk) 
	if(fill_buf1)	rd_buf1 <= mdin; 
 
always @(adrb_next or rd_buf0 or rd_buf1) 
	case(adrb_next[2:0])	// synopsys full_case parallel_case 
	   3'h0: tx_data_st = rd_buf0[07:00]; 
	   3'h1: tx_data_st = rd_buf0[15:08]; 
	   3'h2: tx_data_st = rd_buf0[23:16]; 
	   3'h3: tx_data_st = rd_buf0[31:24]; 
	   3'h4: tx_data_st = rd_buf1[07:00]; 
	   3'h5: tx_data_st = rd_buf1[15:08]; 
	   3'h6: tx_data_st = rd_buf1[23:16]; 
	   3'h7: tx_data_st = rd_buf1[31:24]; 
	endcase 
 
assign fill_buf0 = !adr_cw[0] & mack_r; 
assign fill_buf1 =  adr_cw[0] & mack_r; 
 
assign	adrb_is_3 = adr_cb[1:0] == 2'h3; 
 
`ifdef USBF_ASYNC_RESET 
always @(posedge clk or negedge rst) 
`else 
always @(posedge clk) 
`endif 
	if(!rst)		send_data_r <= 1'b0; 
	else 
	if(rd_first)		send_data_r <= 1'b1; 
	else 
	if(((sizd_c==14'h1) && rd_next) || sizd_is_zero_d)	send_data_r <= 1'b0; 
 
assign send_data = send_data_r | send_zero_length_r; 
 
/////////////////////////////////////////////////////////////////// 
// 
// IDMA Load/Store State Machine 
// 
 
// store incoming data to memory until rx_data done 
// First pre-fetch data from memory, so that bytes can be stuffed properly 
 
`ifdef USBF_ASYNC_RESET 
always @(posedge clk or negedge rst) 
`else 
always @(posedge clk) 
`endif 
	if(!rst)	state <= IDLE; 
	else		state <= next_state; 
 
always @(state or mack_r or abort or rx_dma_en_r or tx_dma_en_r or  
	sizd_is_zero or wr_last or wr_done or rx_data_done_r2 or  
	rd_next or adrb_is_3 or send_zero_length_r or !rst) 
   begin 
	next_state = state;	// Default do not change state 
	mreq_d = 1'b0; 
	mwe_d = 1'b0; 
	rd_first = 1'b0; 
	dtmp_sel = 1'b0; 
	wr_last_en = 1'b0; 
 
	case(state)	// synopsys full_case parallel_case 
	   IDLE: 
		   begin 
 
// synopsys translate_off 
`ifdef USBF_VERBOSE_DEBUG 
//$display("IDMA: Entered IDLE state (%t)", $time); 
`endif 
`ifdef USBF_DEBUG 
if(rst) 
begin 
if(rx_dma_en_r === 1'bx)	$display("ERROR: IDMA: IDLE: rx_dma_en_r is unknown. (%t)", $time); 
if(tx_dma_en_r === 1'bx)	$display("ERROR: IDMA: IDLE: tx_dma_en_r is unknown. (%t)", $time); 
if(abort === 1'bx)		$display("ERROR: IDMA: IDLE: abort is unknown. (%t)", $time); 
end 
`endif 
// synopsys translate_on 
 
			if(rx_dma_en_r && !abort) 
			   begin 
				next_state = WAIT_MRD; 
			   end 
			if(tx_dma_en_r && !abort && !send_zero_length_r) 
			   begin 
				next_state = MEM_RD1; 
			   end 
		   end 
 
	   WAIT_MRD:	// Pre-fetch a word from memory 
		   begin 
 
// synopsys translate_off 
`ifdef USBF_VERBOSE_DEBUG 
$display("IDMA: Entered WAIT_MRD state (%t)", $time); 
`endif 
`ifdef USBF_DEBUG 
if(abort === 1'bx)	$display("ERROR: IDMA: WAIT_MRD: abort is unknown. (%t)", $time); 
if(mack_r === 1'bx)	$display("ERROR: IDMA: WAIT_MRD: mack_r is unknown. (%t)", $time); 
`endif 
// synopsys translate_on 
 
			if(abort)	next_state = IDLE; 
			else 
			if(mack_r)	next_state = MEM_WR; 
			else 
			   begin 
				dtmp_sel = 1'b1; 
				mreq_d = 1'b1; 
			   end 
		   end 
 
	   MEM_WR: 
		   begin 
 
// synopsys translate_off 
`ifdef USBF_VERBOSE_DEBUG 
$display("IDMA: Entered MEM_WR state (%t)", $time); 
`endif 
`ifdef USBF_DEBUG 
if(abort === 1'bx)	$display("ERROR: IDMA: MEM_WR: abort is unknown. (%t)", $time); 
if(rx_data_done_r2 === 1'bx)	$display("ERROR: IDMA: MEM_WR: rx_data_done_r2 is unknown. (%t)", $time); 
`endif 
// synopsys translate_on 
 
			mwe_d = 1'b1; 
			if(abort)			next_state = IDLE; 
			else 
			if(rx_data_done_r2)	 
			   begin 
				wr_last_en = 1'b1; 
				next_state = MEM_WR1; 
			   end 
 
		   end 
	   MEM_WR1: 
		   begin 
 
// synopsys translate_off 
`ifdef USBF_VERBOSE_DEBUG 
//$display("IDMA: Entered MEM_WR1 state (%t)", $time); 
`endif 
`ifdef USBF_DEBUG 
if(abort === 1'bx)	$display("ERROR: IDMA: MEM_WR1: abort is unknown. (%t)", $time); 
if(wr_last === 1'bx)	$display("ERROR: IDMA: MEM_WR1: wr_last is unknown. (%t)", $time); 
if(wr_done === 1'bx)	$display("ERROR: IDMA: MEM_WR1: wr_done is unknown. (%t)", $time); 
`endif 
// synopsys translate_on 
 
			mwe_d = 1'b1; 
			wr_last_en = 1'b1; 
			if(abort)			next_state = IDLE; 
			else 
			if(wr_last)			next_state = MEM_WR2; 
			else 
			if(wr_done)			next_state = IDLE; 
		   end 
 
	   MEM_WR2: 
		   begin 
 
// synopsys translate_off 
`ifdef USBF_VERBOSE_DEBUG 
//$display("IDMA: Entered MEM_WR2 state (%t)", $time); 
`endif 
`ifdef USBF_DEBUG 
if(mack_r === 1'bx)	$display("ERROR: IDMA: MEM_WR2: mack_r is unknown. (%t)", $time); 
`endif 
// synopsys translate_on 
 
			mwe_d = 1'b1; 
			if(mack_r)			next_state = IDLE; 
		   end 
 
	   MEM_RD1: 
		   begin 
 
// synopsys translate_off 
`ifdef USBF_VERBOSE_DEBUG 
//$display("IDMA: Entered MEM_RD1 state (%t)", $time); 
`endif 
`ifdef USBF_DEBUG 
if(abort === 1'bx)	$display("ERROR: IDMA: MEM_RD1: abort is unknown. (%t)", $time); 
if(mack_r === 1'bx)	$display("ERROR: IDMA: MEM_RD1: mack_r is unknown. (%t)", $time); 
`endif 
// synopsys translate_on 
 
			mreq_d = 1'b1; 
			if(mack_r)		rd_first = 1'b1; 
			if(abort)		next_state = IDLE; 
			else 
			if(mack_r)		next_state = MEM_RD2; 
		   end 
	   MEM_RD2: 
		   begin 
 
// synopsys translate_off 
`ifdef USBF_VERBOSE_DEBUG 
//$display("IDMA: Entered MEM_RD2 state (%t)", $time); 
`endif 
`ifdef USBF_DEBUG 
if(abort === 1'bx)	$display("ERROR: IDMA: MEM_RD2: abort is unknown. (%t)", $time); 
if(mack_r === 1'bx)	$display("ERROR: IDMA: MEM_RD2: mack_r is unknown. (%t)", $time); 
`endif 
// synopsys translate_on 
 
			mreq_d = 1'b1; 
			if(abort)		next_state = IDLE; 
			else 
			if(mack_r)		next_state = MEM_RD3; 
		   end 
	   MEM_RD3: 
		   begin 
 
// synopsys translate_off 
`ifdef USBF_VERBOSE_DEBUG 
//$display("IDMA: Entered MEM_RD3 state (%t)", $time); 
`endif 
`ifdef USBF_DEBUG 
if(abort === 1'bx)	$display("ERROR: IDMA: MEM_RD3: abort is unknown. (%t)", $time); 
if(sizd_is_zero===1'bx) $display("ERROR: IDMA: MEM_RD3: sizd_is_zero is unknown. (%t)", $time); 
if(adrb_is_3 === 1'bx)	$display("ERROR: IDMA: MEM_RD3: adrb_is_3 is unknown. (%t)", $time); 
if(rd_next === 1'bx)	$display("ERROR: IDMA: MEM_RD3: rd_next is unknown. (%t)", $time); 
`endif 
// synopsys translate_on 
 
			if(sizd_is_zero || abort)	next_state = IDLE; 
			else 
			if(adrb_is_3 && rd_next)	next_state = MEM_RD2; 
		   end 
	endcase 
 
   end 
 
endmodule