/////////////////////////////////////////////////////////////////////
////                                                             ////
////  WISHBONE DMA One Channel Register File                     ////
////                                                             ////
////                                                             ////
////  Author: Rudolf Usselmann                                   ////
////          rudi@asics.ws                                      ////
////                                                             ////
////                                                             ////
////  Downloaded from: http://www.opencores.org/cores/wb_dma/    ////
////                                                             ////
/////////////////////////////////////////////////////////////////////
////                                                             ////
//// Copyright (C) 2000-2002 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: wb_dma_ch_rf.v,v 1.5 2002-02-01 01:54:45 rudi Exp $
//
//  $Date: 2002-02-01 01:54:45 $
//  $Revision: 1.5 $
//  $Author: rudi $
//  $Locker:  $
//  $State: Exp $
//
// Change History:
//               $Log: not supported by cvs2svn $
//               Revision 1.4  2001/10/30 02:06:17  rudi
//
//               - Fixed problem where synthesis tools would instantiate latches instead of flip-flops
//
//               Revision 1.3  2001/10/19 04:35:04  rudi
//
//               - Made the core parameterized
//
//               Revision 1.2  2001/08/15 05:40:30  rudi
//
//               - Changed IO names to be more clear.
//               - Uniquifyed define names to be core specific.
//               - Added Section 3.10, describing DMA restart.
//
//               Revision 1.1  2001/07/29 08:57:02  rudi
//
//
//               1) Changed Directory Structure
//               2) Added restart signal (REST)
//
//               Revision 1.3  2001/06/14 08:50:01  rudi
//
//               Changed Module Name to match file name.
//
//               Revision 1.2  2001/06/13 02:26:48  rudi
//
//
//               Small changes after running lint.
//
//               Revision 1.1  2001/06/05 10:25:27  rudi
//
//
//               Initial checkin of register file for one channel.
//
//
//
//

`include "wb_dma_defines.v"

module wb_dma_ch_rf(	clk, rst,
			pointer, pointer_s, ch_csr, ch_txsz, ch_adr0, ch_adr1,
			ch_am0, ch_am1, sw_pointer, ch_stop, ch_dis, irq,

			wb_rf_din, wb_rf_adr, wb_rf_we, wb_rf_re,

			// DMA Registers Write Back Channel Select
			ch_sel, ndnr,

			// DMA Engine Status
			dma_busy, dma_err, dma_done, dma_done_all,

			// DMA Engine Reg File Update ctrl signals
			de_csr, de_txsz, de_adr0, de_adr1,
			de_csr_we, de_txsz_we, de_adr0_we, de_adr1_we,
			de_fetch_descr, dma_rest,
			ptr_set

		);

parameter	[4:0]	CH_NO    = 5'h0;  // This Instances Channel ID
parameter	[0:0]	CH_EN    = 1'b1;  // This channel exists
parameter	[0:0]	HAVE_ARS = 1'b1;  // 1=this Instance Supports ARS
parameter	[0:0]	HAVE_ED  = 1'b1;  // 1=this Instance Supports External Descriptors
parameter	[0:0]	HAVE_CBUF= 1'b1;  // 1=this Instance Supports Cyclic Buffers

input		clk, rst;

output	[31:0]	pointer;
output	[31:0]	pointer_s;
output	[31:0]	ch_csr;
output	[31:0]	ch_txsz;
output	[31:0]	ch_adr0;
output	[31:0]	ch_adr1;
output	[31:0]	ch_am0;
output	[31:0]	ch_am1;
output	[31:0]	sw_pointer;
output		ch_stop;
output		ch_dis;
output		irq;

input	[31:0]	wb_rf_din;
input	[7:0]	wb_rf_adr;
input		wb_rf_we;
input		wb_rf_re;

input	[4:0]	ch_sel;
input		ndnr;

// DMA Engine Status
input		dma_busy, dma_err, dma_done, dma_done_all;

// DMA Engine Reg File Update ctrl signals
input	[31:0]	de_csr;
input	[11:0]	de_txsz;
input	[31:0]	de_adr0;
input	[31:0]	de_adr1;
input		de_csr_we, de_txsz_we, de_adr0_we, de_adr1_we, ptr_set;
input		de_fetch_descr;
input		dma_rest;

////////////////////////////////////////////////////////////////////
//
// Local Wires and Registers
//

wire	[31:0]	pointer;
reg	[27:0]	pointer_r;
reg	[27:0]	pointer_sr;
reg		ptr_valid;
reg		ch_eol;

wire	[31:0]	ch_csr, ch_txsz;

reg	[8:0]	ch_csr_r;
reg	[2:0]	ch_csr_r2;
reg	[2:0]	ch_csr_r3;
reg	[2:0]	int_src_r;
reg		ch_err_r;
reg		ch_stop;
reg		ch_busy;
reg		ch_done;
reg		ch_err;
reg		rest_en;

reg	[10:0]	ch_chk_sz_r;
reg	[11:0]	ch_tot_sz_r;
// Transfer-size register
reg	[1:0]	ch_tr_sz_r;
reg	[22:0]	ch_txsz_s;
reg		ch_sz_inf;

wire	[31:0]	ch_adr0, ch_adr1;
reg	[31:0]	ch_adr0_r, ch_adr1_r;
wire	[31:0]	ch_am0, ch_am1;
reg	[27:0]	ch_am0_r, ch_am1_r;

reg	[31:0]	ch_adr0_s, ch_adr1_s;

reg	[29:0]	sw_pointer_r;
wire		sw_pointer_we;
wire	[28:0]	cmp_adr;
reg		ch_dis;
wire		ch_enable;

wire		pointer_we;
wire		ch_csr_we, ch_csr_re, ch_txsz_we, ch_adr0_we, ch_adr1_we;
wire		ch_am0_we, ch_am1_we;
reg		ch_rl;
wire		ch_done_we;
wire		ch_err_we;
wire		chunk_done_we;

wire		ch_csr_dewe, ch_txsz_dewe, ch_adr0_dewe, ch_adr1_dewe;

wire		this_ptr_set;
wire		ptr_inv;

////////////////////////////////////////////////////////////////////
//
// Aliases
//

assign ch_adr0		= CH_EN ? ch_adr0_r   : 32'h0;
assign ch_adr1		= CH_EN ? ch_adr1_r   : 32'h0;
assign ch_am0		= (CH_EN & HAVE_CBUF) ? {ch_am0_r, 4'h0}    : 32'hffff_fff0;
assign ch_am1		= (CH_EN & HAVE_CBUF) ? {ch_am1_r, 4'h0}    : 32'hffff_fff0;
assign sw_pointer	= (CH_EN & HAVE_CBUF) ? {sw_pointer_r,2'h0} : 32'h0;

assign pointer		= CH_EN ? {pointer_r, 3'h0, ptr_valid} : 32'h0;
assign pointer_s	= CH_EN ? {pointer_sr, 4'h0}  : 32'h0;
assign ch_csr		= CH_EN ? {9'h0, int_src_r, ch_csr_r3, rest_en, ch_csr_r2,
					ch_err, ch_done, ch_busy, 1'b0, ch_csr_r[8:1], ch_enable} : 32'h0;
assign ch_txsz		= CH_EN ? {5'h0, ch_chk_sz_r, ch_sz_inf, 1'h0, ch_tr_sz_r, ch_tot_sz_r} : 32'h0;

assign ch_enable	= CH_EN ? (ch_csr_r[`WDMA_CH_EN] & (HAVE_CBUF ? !ch_dis : 1'b1) ) : 1'b0;

////////////////////////////////////////////////////////////////////
//
// CH0 control signals
//

parameter	[4:0]	CH_ADR = CH_NO + 5'h1;

assign ch_csr_we	= CH_EN & wb_rf_we & (wb_rf_adr[7:3] == CH_ADR) & (wb_rf_adr[2:0] == 3'h0);
assign ch_csr_re	= CH_EN & wb_rf_re & (wb_rf_adr[7:3] == CH_ADR) & (wb_rf_adr[2:0] == 3'h0);
assign ch_txsz_we	= CH_EN & wb_rf_we & (wb_rf_adr[7:3] == CH_ADR) & (wb_rf_adr[2:0] == 3'h1);
assign ch_adr0_we	= CH_EN & wb_rf_we & (wb_rf_adr[7:3] == CH_ADR) & (wb_rf_adr[2:0] == 3'h2);
assign ch_am0_we	= CH_EN & wb_rf_we & (wb_rf_adr[7:3] == CH_ADR) & (wb_rf_adr[2:0] == 3'h3);
assign ch_adr1_we	= CH_EN & wb_rf_we & (wb_rf_adr[7:3] == CH_ADR) & (wb_rf_adr[2:0] == 3'h4);
assign ch_am1_we	= CH_EN & wb_rf_we & (wb_rf_adr[7:3] == CH_ADR) & (wb_rf_adr[2:0] == 3'h5);
assign pointer_we	= CH_EN & wb_rf_we & (wb_rf_adr[7:3] == CH_ADR) & (wb_rf_adr[2:0] == 3'h6);
assign sw_pointer_we	= CH_EN & wb_rf_we & (wb_rf_adr[7:3] == CH_ADR) & (wb_rf_adr[2:0] == 3'h7);

assign ch_done_we	= CH_EN & (((ch_sel==CH_NO) & dma_done_all) | ndnr) &
			  (ch_csr[`WDMA_USE_ED] ? ch_eol : !ch_csr[`WDMA_ARS]);
assign chunk_done_we	= CH_EN & (ch_sel==CH_NO) & dma_done;
assign ch_err_we	= CH_EN & (ch_sel==CH_NO) & dma_err;
assign ch_csr_dewe	= CH_EN & de_csr_we & (ch_sel==CH_NO);
assign ch_txsz_dewe	= CH_EN & de_txsz_we & (ch_sel==CH_NO);
assign ch_adr0_dewe	= CH_EN & de_adr0_we & (ch_sel==CH_NO);
assign ch_adr1_dewe	= CH_EN & de_adr1_we & (ch_sel==CH_NO);

assign ptr_inv		= CH_EN & ((ch_sel==CH_NO) & dma_done_all) | ndnr;
assign this_ptr_set	= CH_EN & ptr_set & (ch_sel==CH_NO);

always @(posedge clk)
	ch_rl <= #1	CH_EN & HAVE_ARS & (
			(rest_en & dma_rest) |
			((ch_sel==CH_NO) & dma_done_all & ch_csr[`WDMA_ARS] & !ch_csr[`WDMA_USE_ED])
			);

// ---------------------------------------------------
// Pointers

always @(posedge clk or negedge rst)
	if(!rst)			ptr_valid <= #1 1'b0;
	else
	if(CH_EN & HAVE_ED)
	   begin
		if( this_ptr_set | (rest_en & dma_rest) )
					ptr_valid <= #1 1'b1;
		else
		if(ptr_inv)		ptr_valid <= #1 1'b0;
	   end
	else				ptr_valid <= #1 1'b0;

always @(posedge clk or negedge rst)
	if(!rst)			ch_eol <= #1 1'b0;
	else
	if(CH_EN & HAVE_ED)
	   begin
		if(ch_csr_dewe)		ch_eol <= #1 de_csr[`WDMA_ED_EOL];
		else
		if(ch_done_we)		ch_eol <= #1 1'b0;
	   end
	else				ch_eol <= #1 1'b0;

always @(posedge clk)
	if(CH_EN & HAVE_ED)
	   begin
		if(pointer_we)		pointer_r <= #1 wb_rf_din[31:4];
		else
		if(this_ptr_set)	pointer_r <= #1 de_csr[31:4];
	   end
	else				pointer_r <= #1 1'b0;

always @(posedge clk)
	if(CH_EN & HAVE_ED)
	   begin
		if(this_ptr_set)	pointer_sr <= #1 pointer_r;
	   end
	else				pointer_sr <= #1 1'b0;

// ---------------------------------------------------
// CSR

always @(posedge clk or negedge rst)
	if(!rst)			ch_csr_r <= #1 1'b0;
	else
	if(CH_EN)
	   begin
		if(ch_csr_we)		ch_csr_r <= #1 wb_rf_din[8:0];
		else
		   begin
			if(ch_done_we)	ch_csr_r[`WDMA_CH_EN] <= #1 1'b0;
			if(ch_csr_dewe)	ch_csr_r[4:1] <= #1 de_csr[19:16];
		   end
	   end

// done bit
always @(posedge clk or negedge rst)
	if(!rst)		ch_done <= #1 1'b0;
	else
	if(CH_EN)
	   begin
		if(ch_csr_we)		ch_done <= #1 !wb_rf_din[`WDMA_CH_EN];
		else
		if(ch_done_we)		ch_done <= #1 1'b1;
	   end

// busy bit
always @(posedge clk)
	ch_busy <= #1 CH_EN & (ch_sel==CH_NO) & dma_busy;

// stop bit
always @(posedge clk)
	ch_stop <= #1 CH_EN & ch_csr_we & wb_rf_din[`WDMA_STOP];

// error bit
always @(posedge clk or negedge rst)
	if(!rst)			ch_err <= #1 1'b0;
	else
	if(CH_EN)
	   begin
		if(ch_err_we)		ch_err <= #1 1'b1;
		else
		if(ch_csr_re)		ch_err <= #1 1'b0;
	   end

// Priority Bits
always @(posedge clk or negedge rst)
	if(!rst)			ch_csr_r2 <= #1 3'h0;
	else
	if(CH_EN & ch_csr_we)		ch_csr_r2 <= #1 wb_rf_din[15:13];

// Restart Enable Bit (REST)
always @(posedge clk or negedge rst)
	if(!rst)			rest_en <= #1 1'b0;
	else
	if(CH_EN & ch_csr_we)		rest_en <= #1 wb_rf_din[16];

// irq Mask
always @(posedge clk or negedge rst)
	if(!rst)			ch_csr_r3 <= #1 3'h0;
	else
	if(CH_EN & ch_csr_we)		ch_csr_r3 <= #1 wb_rf_din[19:17];

// irq Source
always @(posedge clk or negedge rst)
	if(!rst)			int_src_r[2] <= #1 1'b0;
	else
	if(CH_EN)
	   begin
		if(chunk_done_we)	int_src_r[2] <= #1 1'b1;
		else
		if(ch_csr_re)		int_src_r[2] <= #1 1'b0;
	   end

always @(posedge clk or negedge rst)
	if(!rst)			int_src_r[1] <= #1 1'b0;
	else
	if(CH_EN)
	   begin
		if(ch_done_we)		int_src_r[1] <= #1 1'b1;
		else
		if(ch_csr_re)		int_src_r[1] <= #1 1'b0;
	   end

always @(posedge clk or negedge rst)
	if(!rst)			int_src_r[0] <= #1 1'b0;
	else
	if(CH_EN)
	   begin
		if(ch_err_we)		int_src_r[0] <= #1 1'b1;
		else
		if(ch_csr_re)		int_src_r[0] <= #1 1'b0;
	   end

// Interrupt Output
assign irq = |(int_src_r & ch_csr_r3) & CH_EN;

// ---------------------------------------------------
// TXZS
always @(posedge clk)
	if(CH_EN)
	   begin
		if(ch_txsz_we)		
			{ch_chk_sz_r, ch_tr_sz_r, ch_tot_sz_r} <= #1 {wb_rf_din[26:16], wb_rf_din[13:12], wb_rf_din[11:0]};
		else
		if(ch_txsz_dewe)
			ch_tot_sz_r <= #1 de_txsz; // TODO:
		else
		if(ch_rl) 
			{ch_chk_sz_r, ch_tot_sz_r} <= #1 ch_txsz_s; // TODO:
	   end

// txsz shadow register
always @(posedge clk)
	if(CH_EN & HAVE_ARS)
	   begin

		if(ch_txsz_we)	ch_txsz_s <= #1 {wb_rf_din[26:16], wb_rf_din[11:0]};
		else
		if(rest_en & ch_txsz_dewe & de_fetch_descr)
				ch_txsz_s[11:0] <= #1 de_txsz[11:0];
	   end

// Infinite Size indicator
always @(posedge clk)
	if(CH_EN)
	   begin
		if(ch_txsz_we)		ch_sz_inf <= #1 wb_rf_din[15];
	   end
	
// ---------------------------------------------------
// ADR0
always @(posedge clk)
	if(CH_EN)
	   begin
		if(ch_adr0_we)		ch_adr0_r <= #1 wb_rf_din[31:0];
		else
		if(ch_adr0_dewe)	ch_adr0_r <= #1 de_adr0[31:0];
		else
		if(ch_rl)		ch_adr0_r <= #1 ch_adr0_s;
	   end

// Adr0 shadow register
always @(posedge clk)
	if(CH_EN & HAVE_ARS)
	   begin
		if(ch_adr0_we)	ch_adr0_s <= #1 wb_rf_din[31:0];
		else
		if(rest_en & ch_adr0_dewe & de_fetch_descr)
				ch_adr0_s <= #1 de_adr0[31:0];
	   end

// ---------------------------------------------------
// AM0
always @(posedge clk or negedge rst)
	if(!rst)		ch_am0_r <= #1 28'hfffffff;
	else
	if(ch_am0_we)		ch_am0_r <= #1 wb_rf_din[31:4];

// ---------------------------------------------------
// ADR1
always @(posedge clk)
	if(CH_EN)
	   begin
		if(ch_adr1_we)		ch_adr1_r <= #1 wb_rf_din[31:0];
		else
		if(ch_adr1_dewe)	ch_adr1_r <= #1 de_adr1[31:0];
		else
		if(ch_rl)		ch_adr1_r <= #1 ch_adr1_s;
	   end

// Adr1 shadow register
always @(posedge clk)
	if(CH_EN & HAVE_ARS)
	   begin
		if(ch_adr1_we)	ch_adr1_s <= #1 wb_rf_din[31:0];
		else
		if(rest_en & ch_adr1_dewe & de_fetch_descr)
				ch_adr1_s <= #1 de_adr1[31:0];
	   end

// ---------------------------------------------------
// AM1
always @(posedge clk or negedge rst)
	if(!rst)				ch_am1_r <= #1 28'hfffffff;
	else
	if(ch_am1_we & CH_EN & HAVE_CBUF)	ch_am1_r <= #1 wb_rf_din[31:4];

// ---------------------------------------------------
// Software Pointer
always @(posedge clk or negedge rst)
	if(!rst)				sw_pointer_r <= #1 28'h0;
	else
	if(sw_pointer_we & CH_EN & HAVE_CBUF)	sw_pointer_r <= #1 wb_rf_din[31:4];

// ---------------------------------------------------
// Software Pointer Match logic

assign cmp_adr = ch_csr[2] ? ch_adr1[30:2] : ch_adr0[30:2];

always @(posedge clk)
	ch_dis <= #1 CH_EN & HAVE_CBUF & (sw_pointer[30:2] == cmp_adr) & sw_pointer[31];

endmodule


module wb_dma_ch_rf_dummy(clk, rst,
			pointer, pointer_s, ch_csr, ch_txsz, ch_adr0, ch_adr1,
			ch_am0, ch_am1, sw_pointer, ch_stop, ch_dis, irq,

			wb_rf_din, wb_rf_adr, wb_rf_we, wb_rf_re,

			// DMA Registers Write Back Channel Select
			ch_sel, ndnr,

			// DMA Engine Status
			dma_busy, dma_err, dma_done, dma_done_all,

			// DMA Engine Reg File Update ctrl signals
			de_csr, de_txsz, de_adr0, de_adr1,
			de_csr_we, de_txsz_we, de_adr0_we, de_adr1_we,
			de_fetch_descr, dma_rest,
			ptr_set

		);

parameter	CH_NO = 0;
parameter	HAVE_ARS = 1;
parameter	HAVE_ED  = 1;
parameter	HAVE_CBUF= 1;

input		clk, rst;

output	[31:0]	pointer;
output	[31:0]	pointer_s;
output	[31:0]	ch_csr;
output	[31:0]	ch_txsz;
output	[31:0]	ch_adr0;
output	[31:0]	ch_adr1;
output	[31:0]	ch_am0;
output	[31:0]	ch_am1;
output	[31:0]	sw_pointer;
output		ch_stop;
output		ch_dis;
output		irq;

input	[31:0]	wb_rf_din;
input	[7:0]	wb_rf_adr;
input		wb_rf_we;
input		wb_rf_re;

input	[4:0]	ch_sel;
input		ndnr;

// DMA Engine Status
input		dma_busy, dma_err, dma_done, dma_done_all;

// DMA Engine Reg File Update ctrl signals
input	[31:0]	de_csr;
input	[11:0]	de_txsz;
input	[31:0]	de_adr0;
input	[31:0]	de_adr1;
input		de_csr_we, de_txsz_we, de_adr0_we, de_adr1_we, ptr_set;
input		de_fetch_descr;
input		dma_rest;

assign		pointer = 32'h0;
assign		pointer_s = 32'h0;
assign		ch_csr = 32'h0;
assign		ch_txsz = 32'h0;
assign		ch_adr0 = 32'h0;
assign		ch_adr1 = 32'h0;
assign		ch_am0 = 32'h0;
assign		ch_am1 = 32'h0;
assign		sw_pointer = 32'h0;
assign		ch_stop = 1'b0;
assign		ch_dis = 1'b0;
assign		irq = 1'b0;

endmodule
