//
//	audio2.v - serializer/deserializer for 2-channel 24-bit audio CODEC
//
//					(C) Copyright 2004-2009 John B. Stephensen
//
// This Verilog source file and all its derivatives are licensed only for
// personal non-profit educational use in the Amateur Radio Service and
// the license is not transferrable. The information is provided as-is for
// experimental purposes and the author does not warranty its freedom
// from defects or its suitability for any specific application.
//
// This logic module is a serializer/deserializer for an Alesis AL1101 ADC
// and AL1201 DAC. The word length is 24 bits and data is shifted MSB first
// into and out of the CODEC. 0-14 kHz audio is supported.
//
// 24-bit words are sent to the DAC by writing the least significant byte into
// the even output port and then writing the most significant word into the odd
// output port. Ports 0-1 are for the right channel and ports 2-3 are for the
// left channel. For 16-bit data, write a 0 to one even port and then use only
// the odd ports.
//
// Output Ports:
//	    15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
//    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//  0 |       Right Channel LSB       |                               |
//    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//  1 |                       Right Channel MSW                       |
//    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//  2 |       Left Channel LSB        |                               |
//    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//  3 |                       Left Channel MSW                        |
//    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//
// Input Ports:
//	    15  14  13  12  11  10   9   8   7   6   5   4   3   2   1   0
//    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//  0 |            LSB Data           |                               |
//    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//  1 |                            MSW Data                           |
//    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//  2 |                                                               |
//    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//  3 |                                       |TXE|TXR|   | L |RXF|RXR|
//    +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
//
// RXR - receiver ready with data
// RXF - receiver full (overflow)
//	  L - left (1) or right (0) channel data
// TXR - transmitter ready for data
// TXE - transmitter empty (underflow)
//
// Max. clock rate is 155 MHz and 88 slices are used.
//
// Normal Warnings:
//		<ioaddr<2>> is never used.
//
// History:
//		M001	2009-2-2	modify for use as I/O port
//		M002	3-3-09	sync. output for 2-cycle read
//
module audio2 (
			ioaddr,iocs,din,iowr,dout,iord,
			clk,rst,
			sdi,sdo,sfs
			);
    input [2:0] ioaddr;	// register address
	 input iocs;			// device select
    input [15:0] din;	// parallel data input
	 input iowr;			// write data
    output [15:0] dout;	// parallel data output
	 input iord;			// read data
    input clk;				// CPU clock (64 MHz)
	 input rst;				// synchronous reset of enable register
    input sdi;				// serial data input
    output sdo;			// seial data output
    output sfs;			// serial frame synchronization pulse
// internal data paths
reg [7:0] txlsb;			// least significant byte of transmitted word
wire iv,left,oe;			// data strobes
wire [23:0] pdo;			// multiplexed parallel data output from FIFO
reg [31:0] rsr;			// receive and transmit shift registers
reg [23:0] tsr;
wire [23:0] tsrin;		// TSR parallel input
wire rxe,rxf,txe,txf;	// FIFO flags
reg [10:0] wdiv;			// divide by 1250 for word clock
reg [5:0] bdiv;			// divide by 39 for bit clock
wire wtc,btc;				// terminal counts for dividers
wire istb,ostb;			// bit input and output strobes
wire wstb;					// delay to generate word clock
// generate clocks to unload words from FIFO and shift into CODEC
// and to reads bits shifted out of CODEC and write to FIFO
// Word clock = 80 MHz / 1250 = 64 kHz
// Bit clock = 80 MHz / 39 = 2.051 MHz (sync. to 64 kHz for 2.048 MHz)
assign wtc = (wdiv == 1249);
assign btc = (bdiv == 38);
always @ (posedge clk)
begin
	if (rst|wtc) wdiv <= 0;
	else wdiv <= wdiv + 1;
	if (rst|wtc|btc) bdiv <= 0;
	else bdiv <= bdiv + 1;
end
// generate read and write strobes for FIFO
assign wstb = (wdiv == 10);
// generate bit clocks for shift registers - offset prevent extra strobe
assign ostb = (bdiv == 10);	// beginning of bit cell on transmit
assign istb = (bdiv == 30);	// middle of bit cell on receive
// serial I/O for I2S audio CODEC
// transmit data shifted out MSB first - 24 bit shift register used as last 8 bits are zero
// received data shifted in MSB first - 32 bit shift register with least significant 8 bits ignored
always @ (posedge clk)
begin
	if (wstb) tsr <= tsrin;		// parallel load to get bit 0 - WSTB and OSB are coincident
	else if (ostb) tsr <= {tsr[22:0],1'b0};
	if (rst) rsr[31:8] <= 0;	// first sample is zero - next sample is previous 32 bits on SDI
	else if (istb) rsr <= {rsr[30:0],sdi};
end
// parallel I/O
// save LSB and write to FIFO when MSW present
always @ (posedge clk)
begin
	if (rst) txlsb <= 0;
	else if (iocs & ~ioaddr[0] & iowr) txlsb <= din [15:8];
end
assign iv = iocs & ioaddr[0] & iowr;
// ports 0-1 for left and ports 2-3 for right channel
fifo16x25s txfifo (
	.pdi({ioaddr[1],din,txlsb}),	// load when all 24-bits available
	.iv(iv),
	.pdo({sfs,tsrin}),
	.oe(wstb),							// get new entry when MSB due
	.clk(clk),
	.rst(rst),
	.empty(txe),
	.full(txf)
	);
fifo16x25s rxfifo (
	.pdi({sfs,rsr[31:8]}),
	.iv(wstb),					// save accumulated bits just before MSB sampled
	.pdo({left,pdo}),
	.oe(oe),
	.clk(clk),
	.rst(rst),
	.empty(rxe),
	.full(rxf)
	);
// output multiplexer - 2 words for incoming data plus 1 status word
assign oe = iocs & (ioaddr[1:0] == 2'b01) & iord; // read MSW to get next entry
MUX4X16S omux (
	.D0({pdo[7:0],8'h00}),
	.D1({pdo[23:8]}),
	.D2(16'h0000),
	.D3({10'h000,txe,~txf,1'b0,left,rxf,~rxe}),
	.S(ioaddr[1:0]),
	.Q(dout),
	.CE(iord),
	.CLK(clk),
	.RST(rst)
	);
// connect outputs
assign sdo = tsr[23];
endmodule
