// delayline2.v -- Delay line using external RAM resources of XS40 and XStend boards
// Ed Doering
// 07/24/2000
//
// * Variable-length delay line, 8-bit data, single channel.
// * 'I$Length' input sets delay line length; exact length is I$Length plus one
// * I$Length is a 17-bit value, but the XS40/XStend combination provides RAM only
//   up to address 98,303 (three banks of 32Kx8 RAM, although the XStend RAMs
//   are optional -- they may not be installed on the board).
// * Use separate input and output RAM data bus rather than bidirectional (keep
//   bidirectional part in top-level module).
//
/*
DelayLine U? (
	// Inputs: 
	.I$Clock	( connect to master clock signal ),
	.I$Reset	( active high asynchronous reset ),
	.I$Data		( 8-bit data applied to delay line ),
	.I$DataValid	( indicates input data is valid, active high ),
	.I$Length	( 17-bit value indicating length of delay line ),
	.I$RAMData	( 8-bit value from RAM data bus ),

	// Outputs: 
	.O$Data	( 8-bit output data from delay line),
	.O$DataValid	( active high, indicates that output data is valid),
	.O$_RAMCSXS40	( chip select for RAM on XS40 board, active low),
	.O$_RAMCSXStendL	( chip select for left RAM bank on XStend board ),
	.O$_RAMCSXStendR	( chip select for right RAM bank on XStend board ),
	.O$_RAMOE	( RAM output enable (common to all RAMs), active low ),
	.O$_RAMWE	( RAM write enable (common to all RAMs), active low ),
	.O$RAMAddress	( 15-bit RAM address common to all RAMs ),
	.O$RAMData	( 8-bit value to RAM data bus ),
	.O$RAMDataBusIsOutput	( RAM data bus direction (1=>active output, 0=>hi-Z) )
); 
*/

//-------------------------------------------------------------------------------

module DelayLine ( 
	// Variable-length delay line using external RAM resources of XS40 and XSTend boards 

	// Inputs: 
	I$Clock	// Master clock
,
	I$Reset	// Master reset, active high
,
	I$Data	// Input data value
,
	I$DataValid	// Input data value valid (active high)
,
	I$Length	// Length of delay line in samples (actual delay is the value
			// of I$Length plus one)
,
	I$RAMData	// 8-bit value from RAM data bus
,

	// Outputs: 
	O$Data	// Output data value
,
	O$DataValid	// Indicates when data output is valid (active high)
,
	O$_RAMCSXS40	// RAM chip select, XS40
,
	O$_RAMCSXStendL	// RAM chip select, XStend board left bank
,
	O$_RAMCSXStendR	// RAM chip select, XStend board right bank
,
	O$_RAMOE	// RAM output enable
,
	O$_RAMWE	// RAM write enable
,
	O$RAMAddress	// RAM address
,
	O$RAMData	// 8-bit value to RAM data bus
,
	O$RAMDataBusIsOutput	// RAM data bus direction (1=>active output, 0=>hi-Z)	
); 

// Constant parameter declarations:
parameter	p$DataWidth = 8;
parameter	p$RAMDataWidth = 8;		// property of hardware... don't change
parameter	p$RAMAddressWidth = 15;		// property of hardware... don't change

// State machine declarations:
parameter p$Idle		= 8'b0000_0001,
		p$Wait		= 8'b0000_0010,
		p$ReadCycle1	= 8'b0000_0100,
		p$ReadCycle2	= 8'b0000_1000,
		p$WriteCycle1	= 8'b0001_0000,
		p$WriteCycle2	= 8'b0010_0000,
		p$WriteCycle3	= 8'b0100_0000,
		p$BumpPointer	= 8'b1000_0000;
parameter	p$NumStates	= 8;


// Port mode declarations:
input	I$Clock;
input	I$Reset;
input	[p$DataWidth-1:0]	I$Data;
input	I$DataValid;
input	[16:0]	I$Length;
input	[p$RAMDataWidth-1:0]	I$RAMData;
output	[p$DataWidth-1:0]	O$Data;
output	O$DataValid;
output	O$_RAMCSXS40;
output	O$_RAMCSXStendL;
output	O$_RAMCSXStendR;
output	O$_RAMOE;
output	O$_RAMWE;
output	[p$RAMAddressWidth-1:0]	O$RAMAddress;
output	[p$RAMDataWidth-1:0]	O$RAMData;
output	O$RAMDataBusIsOutput;

// Registered variable declarations:
reg	[p$DataWidth-1:0]	O$Data;
reg	O$DataValid;
reg	O$_RAMCSXS40;
reg	O$_RAMCSXStendL;
reg	O$_RAMCSXStendR;
reg	O$_RAMOE;
reg	O$_RAMWE;
//reg	[p$RAMAddressWidth-1:0]	O$RAMAddress;
//reg	[p$RAMDataWidth-1:0]	IO$RAMData;

reg	[16:0]	r$Pointer;
reg	r$IncrementPointer;
reg	r$CaptureRAMData;
reg	O$RAMDataBusIsOutput;

reg	[p$NumStates-1:0]	r$State;
reg	[p$NumStates-1:0]	r$NextState;


//
// Connect RAM output data bus to delay line input data
//
assign O$RAMData = I$Data;

//
// RAM address and chip select decoding:
//

// Pointer (RAM address). Pointer increments when enabled by r$IncrementPointer
// signal. Pointer is reset to zero when it reaches the value of I$Length.
always @ (posedge I$Clock or posedge I$Reset)
	if (I$Reset == 1)
		r$Pointer <= 0;
	else
	if (r$IncrementPointer)
		r$Pointer <= (r$Pointer == I$Length) ? 0 : r$Pointer + 1;

// RAM chip select decoding
always @ (r$Pointer) begin

	// Nominal values are all high (deselected)
	O$_RAMCSXS40 <= 1;
	O$_RAMCSXStendL <= 1;
	O$_RAMCSXStendR <= 1;

	case (r$Pointer[16:15])
		2'b00: O$_RAMCSXS40 <= 0;
		2'b01: O$_RAMCSXStendL <= 0;
		2'b10: O$_RAMCSXStendR <= 0;
	endcase
end

// Connect lower 15 bits of pointer to RAM address bus
assign O$RAMAddress = r$Pointer[14:0];


//
// Output register:
//

always @ (posedge I$Clock or posedge I$Reset)
	if (I$Reset == 1)
		O$Data <= 0;
	else if (r$CaptureRAMData) O$Data <= I$RAMData;


//
// Controller:
//

// State register
always @ (posedge I$Clock or posedge I$Reset)
	if (I$Reset == 1)
		r$State <= p$Idle;
	else
		r$State <= r$NextState;

// Next-state and output decoder
always @ (I$DataValid or r$State) begin

	// Nominal output values
	r$NextState <= p$Idle;
	O$DataValid <= 0;
	O$_RAMOE <= 1;
	r$CaptureRAMData <= 0;
	O$RAMDataBusIsOutput <= 0;
	O$_RAMWE <= 1;
	r$IncrementPointer <= 0;

	// State-dependent output values
	case (r$State)
	p$Idle: begin
		// Signal that present data output is valid
		O$DataValid <= 1;

		// Stay in idle state while input data is valid, otherwise
		// advance to the wait state
		r$NextState <= (I$DataValid) ? p$Idle : p$Wait;
		end

	p$Wait: begin
		// Stay in wait state while input data is invalid, then
		// advance to read cycle
		r$NextState <= (!I$DataValid) ? p$Wait : p$ReadCycle1;
		end

	p$ReadCycle1: begin
		// Assert the RAM output enable
		O$_RAMOE <= 0;

		// Enable the "capture data" (output data register will
		// latch RAM data at end of this state, so RAM output will
		// have been enabled for a full clock cycle before the 
		// data is latched)
		r$CaptureRAMData <= 1;

		// Move to second part of read cycle
		r$NextState <= p$ReadCycle2;
		end

	p$ReadCycle2: begin
		// Maintain assertion of RAM output enable to guarantee
		// stable data during latching. (note... this state may
		// not be necessary, since the FPGA flip-flops have zero
		// hold time requirement).
		O$_RAMOE <= 0;

		// Move to write cycle
		r$NextState <= p$WriteCycle1;
		end

	p$WriteCycle1: begin
		// Set data bus direction to output
		O$RAMDataBusIsOutput <= 1;

		// Move to next write cycle
		r$NextState <= p$WriteCycle2;
		end

	p$WriteCycle2: begin
		// Maintain data bus direction as output
		O$RAMDataBusIsOutput <= 1;

		// Assert the RAM write enable for one state
		O$_RAMWE <= 0;

		// Move to next write cycle
		r$NextState <= p$WriteCycle3;
		end

	p$WriteCycle3: begin
		// Maintain data bus direction as output
		O$RAMDataBusIsOutput <= 1;

		// Move to next "bump pointer" state
		r$NextState <= p$BumpPointer;
		end

	p$BumpPointer: begin
		// Enable the RAM pointer to increment
		r$IncrementPointer <= 1;

		// All done, so go back to idle state
		r$NextState <= p$Idle;
		end
	endcase
end


//
// End of description!
//

endmodule