module TERASIC_I2S_TX(
	// clock
	s_clk,
	s_reset,
	
	// slave
	s_chipselect,
	s_address,
	s_read,
	s_readdata,
	s_write,
	s_writedata,
	s_irq,
	
	// interface to hardware
	i2s_sck,
	i2s_ws,
	i2s_sd
);

input				s_clk;
input				s_reset;
input				s_chipselect;
input		[1:0]	s_address;
input				s_read;
output	reg	[31:0]	s_readdata;
input				s_write;
input		[31:0]	s_writedata;
output	reg			s_irq;

input				i2s_sck;
output	reg			i2s_ws;
output				i2s_sd;



`define REG_CSR				0
`define REG_FIFO			1
`define REG_IRQ_CLEAR		2
`define REG_FIFO_USEDW		3

`define CSR_GO				32'b0000
`define CSR_FIFO_FULL		32'b0010
`define CSR_FIFO_EMPTY		32'b0100




//
reg	i2s_go;

//=========================================
// master write data to fifo
//=========================================
always @(posedge s_clk or posedge s_reset)
begin
	if (s_reset)
	begin
		i2s_go <= 1'b0;
		s_irq <= 1'b0;
	end
	else if (s_chipselect & s_write)
	begin
		if (s_address == `REG_CSR)
			i2s_go <= s_writedata[0];
		else if (s_address == `REG_IRQ_CLEAR)
			s_irq <= 1'b0;
		
	end
	else if (s_chipselect & s_read)
	begin
		if (s_address == `REG_CSR)
			s_readdata <= {fifo_empty, fifo_full, i2s_go};
		else if (s_address == `REG_FIFO_USEDW)
			s_readdata <= fifo_usedw;
	end
	else if (i2s_go)
		s_irq <= fifo_almost_empty;
	else
		s_irq <= 1'b0;
end

//=========================================
// stream data from fito to i2s device
//=========================================
reg [31:0] i2s_next_data;
reg [15:0] i2s_left_data;
reg [15:0] i2s_right_data;
reg [3:0]  i2s_index;  // 0~15
reg		   i2s_left_ch;
reg		   i2s_pre_ws;
reg        i2s_serialize;
reg 	   i2s_bit;

assign i2s_sd = i2s_bit; 



// channel check, and read data to fifo
always @(posedge i2s_sck)
begin
	if (s_reset)
	begin
//		fifo_read <= 1'b0;
		i2s_left_ch <= 1'b1;
		i2s_serialize <= 1'b0;
	end	
	else if (i2s_pre_ws != i2s_ws)  // word select chagne        
	begin  
		if ({i2s_pre_ws,i2s_ws} == 2'b10)
		begin // left channel start
			//i2s_next_data <= fifo_read_data;
			{i2s_left_data, i2s_right_data} <= i2s_next_data; 
			//fifo_read <= 1'b1;
			i2s_left_ch <= 1'b1;
		end
		else	// righ channel start
		begin
			//fifo_read <= 1'b0;
			i2s_left_ch <= 1'b0;
			i2s_index <= 4'd15;
			i2s_serialize <= 1'b1;
		end
		
		//
		i2s_index <= 4'd15;
		i2s_serialize <= 1'b1;
	end	
	else
	begin
		//fifo_read <= 1'b0;
		if (i2s_index > 0)
			i2s_index <= i2s_index - 1;
		else	 
			i2s_serialize <= 1'b0;
	end	
	
	
	//
	i2s_pre_ws <= i2s_ws;
		
end


// generate ws (change every 32 clk)
reg	[5:0] sample_cnt;
always @(negedge i2s_sck)
begin
	i2s_ws <= sample_cnt[5];
	sample_cnt <= sample_cnt + 1;
end

// serialize to i2s data port
always @(negedge i2s_sck)
begin
	if (!i2s_serialize)
		i2s_bit <= 1'b0;
	else if (i2s_left_ch)	
		i2s_bit <= i2s_left_data[i2s_index];
	else	
		i2s_bit <= i2s_right_data[i2s_index];
end

// read fifo data (left channel start)
always @(negedge fifo_read_clk or posedge s_reset)
begin
	if (s_reset)
		fifo_read <= 1'b0;
	else if (!fifo_empty)
	begin
		i2s_next_data <= fifo_read_data;
		fifo_read <= 1'b1;
	end	
	else		
		fifo_read <= 1'b0;
end


// read fifo status
always @(posedge fifo_read_clk)
begin
	if (i2s_go && fifo_read_usedw < 64 && !s_irq)
		fifo_almost_empty <= 1'b1;
	else	
		fifo_almost_empty <= 1'b0;

	fifo_usedw <= fifo_read_usedw;			
end

//=========================================
// fifo
//=========================================
wire [31:0]	fifo_read_data;
wire		fifo_read_clk;
reg			fifo_read;
wire [31:0]	fifo_write_data;
wire		fifo_write_clk;
wire		fifo_write;
wire		fifo_empty;
wire		fifo_full;
wire [7:0]  fifo_read_usedw;
reg			fifo_almost_empty;
reg	 [7:0]	fifo_usedw;

assign fifo_write_clk = s_clk;
assign fifo_read_clk = i2s_ws; //i2s_sck;
assign fifo_write = (s_chipselect && s_write && s_address == `REG_FIFO)?1'b1:1'b0;
assign fifo_write_data = s_writedata;

I2S_TX_FIFO fifo(
	.data(fifo_write_data),
	.rdclk(fifo_read_clk),
	.rdreq(fifo_read),
	.wrclk(fifo_write_clk),
	.wrreq(fifo_write),
	.q(fifo_read_data),
	.rdempty(fifo_empty),
	.wrfull(fifo_full),
	.rdusedw(fifo_read_usedw)
	);

endmodule
