OpenFPGA/openfpga_flow/benchmarks/iwls2005/usb_funct/rtl/usbf_idma.v

637 lines
18 KiB
Verilog

/////////////////////////////////////////////////////////////////////
//// ////
//// 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)
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