// V2.2 18th February 2006 // Full duplex Xylo interface for Wolfson WM8785 A/D converter // Sends 48k/24 bit A/D data via FIFO 4 and receives 48k/16 bit data. // A flag of 0x80_00_00 is sent prior to 4 bytes of control data then 3 bytes of // left data and 3 bytes of right data. The left/right data is then repeated // then every 512 bytes the sync and control data is is repeated // The format for A/D data to the PC is:- // // <0x80><0x00><0x00>... etc // // where Cn is a control byte - see protocol design document for full description. // Left Right //0---------------------------------31-----------------------------------63 //0 8 16 24 40 48 56 //< MSB >< >< LSB > < MSB >< >< LSB > // Sync and control bytes are sent as follows: // 0 1 2 3 4 5 6 7 //<0x80><0x00><0x00> // The format for D/A data from the PC is the same control sequence followed by 48k/16 bit data:- // // Left Right // <0x80><0x00><0x00>< MSB >< LSB >< MSB >< LSB > etc... // // A/D data is in 2's complement format. // Wolfson is set via I2C to be 48kHz, 24 bit // and Left Justify mode. I2C command string is // 34 00 25, 34 02 02, send this prior to using. // Wolfson clock is 24.576MHz (AD_clock on pin 66) // This version uses 2 state machines, one to manage A/D // and one to manage the FX2 USB interface. The two are needed // since the FX2 USB must run off the FX2 24MHz clock and the // A/D must run of the 24.576MHz clock. // // Uses single bit D/A converters using 16 bits. // // // Built with Quartus II v5.1 // // Change log: First working version 14th Jan 2006. // Start Rx FIFO version 17th Jan 2006. // 2048 byte Rx FIFO implemented 21 Jan 2006. // Start Tx fifo 21 Jan 2006 // Modifications to Rx fifo (so that it works!) 23 Jan 2006 // Modifications to A/D state machine to tidy code 31st Jan 2006 // Modifications to use four byte sync every 512 bytes // Mods to use 3 byte sync and control bytes 9th Feb 2006 // Push button to test data bytes added to control Red LED 9th Feb 2006 // Mods to use 48k/24 bit from A/D and 48k/16 bit to D/A 10th Feb 2006 // Increased Rx_fifo size to 4096 bytes and start read at 12 bytes 17th Feb 2006 // Control 3 holds FIFO Rx_used and control 4 loops back to PC 18th Feb 2006 // //////////////////////////////////////////////////// // WM8785 connections to Xylo board // // 24.576MHZ clock - pin 66 (AD_CLK) // BCLK - pin 72 // DOUT - pin 74 // LRCLK - pin 71 // Left_PWM_out - pin 90 // Right_PWM_out - pin 88 // button - pin 97 // alt_clk_i - pin 34 (hardwired to alt osc socket on xylo) // alt_clk_o - pin 69 - pass alt osc clock thru to wolson board, it send it back on 66 // ad_reset - pin 70 - reset pin on a/d chip - active low (chip held in reset while low) // dbgout - pin 89 // // Pins 87 and 85 are reserved for ref counter ///////////////////////////////////////////////////// module Duplex( FX2_CLK, AD_CLK, FX2_FD, FX2_SLRD, FX2_SLWR, FX2_flags, FX2_PA, BCLK, DOUT, LRCLK, LED, TxD, Left_PWM_out, Right_PWM_out, button, alt_clk_i, alt_clk_o, dbgout, ad_reset ); input AD_CLK; // From A/D converter 24.576MHz input FX2_CLK; // FX2 clock - 24MHz input BCLK, DOUT, LRCLK; inout [7:0] FX2_FD; // bidirectional FIFO data to/from the FX2 //output [7:0] FX2_FD; // bidirectional FIFO data to/from the FX2 *******************************8 // ***** use this so simulation works input [2:0] FX2_flags; // Flags input from the FX2 input button; // push button in Xylo board, active low, pin 97 output reg FX2_SLWR; // FX2's write line output reg FX2_SLRD; // FX2's FIFO read line inout [7:0] FX2_PA; // bidirectional FX2's Port A data output [1:0] LED; // Red and Green LEDs on Xylo board output TxD; // RS232 Tx data output Left_PWM_out; // PWM D/A converter output output Right_PWM_out; input alt_clk_i; output alt_clk_o; output dbgout; // wjt added -- tied to 89 output ad_reset; reg ad_reset; reg dbgout; assign alt_clk_o = alt_clk_i; // pass thru alt osc clock /* meaning of FX2 flags etc FIFO2 data available FX2_flags[0] = 1 FIFO3 data available FX2_flags[1] = 1 FIFO4 ready to accept data FX2_flags[2] = 1 FIFO5 ready to accept data FX2_PA[7] = 1 set FX2_PA[0] = 1'b1; set FX2_PA[1] = 1'b1; set FX2_PA[3] = 1'b1; FX2_PA[2] = FIFO_DATAOUT_OE; FX2_PA[6] = FIFO_PKTEND; FX2_PA[5:4] = FIFO_FIFOADR; FX2_FD is a bi-directional data bus */ // set up FX2 Port A assign FX2_PA[0] = 1'b1; // always set on assign FX2_PA[1] = 1'b1; // always set on assign FX2_PA[2] = FIFO_DATA_OUTPUT_ENABLE; assign FX2_PA[3] = 1'b1; // always set on assign FX2_PA[5:4] = FIFO_ADDRESS; assign FX2_PA[6] = 1'b1; // set packet end to false reg [2:0] state_A_D; // state for A/D reg [2:0] state_FX; // state for FX2 reg [7:0] q; reg [5:0]count; // counts from 0 to 63 reg data_flag; // set when A/D has data ready reg [7:0] register; // A/D uses this to send its data to FX2 reg [6:0] loop_counter; // counts number of times round loop reg [11:0] sync_Rx_used; // holds # of bytes in Rx_fifo for use by PC reg [11:0] rx_avail; // kd5tfd -- how much space is avail in the rx fifo // hold the A/D chip in reset until 2**24 (16.6 million) ad_clocks have passed - about 3/4 a second // kd5tfd added reg ad_enabled; reg [23:0] ad_count; always @ (posedge AD_CLK) begin ad_count <= ad_count + 1; if ( ad_enabled == 0 && ad_count[23] ) begin ad_enabled <= 1; ad_reset <= 1; // turn on the ad_converter end end ////////////////////////////////////////////////////////////// // // State Machine to manage A/D converter // ////////////////////////////////////////////////////////////// /* The code is required to send the sync bytes and contol bytes as well as the A/D samples. TODO: Explain the need to loop through the code 83 + 1 times since first time throught we get 8 + 6 bytes and subsequently get 6 etc. */ always @ (posedge AD_CLK) begin // sync_Rx_used <= Rx_used; // kd5tfd sync_Rx_used[11:0] <= Rx_used_rdside; // kd5tfd // sync_Rx_used[11] <= 0; case(state_A_D) // state 0 - loop until LRCLK goes low 3'h0: begin if (LRCLK) state_A_D <= 3'h0; // wait here until LRCLK goes low else state_A_D <= 3'h1; end // state 1 - loop until LRCLK goes high 3'h1: if (LRCLK) // wait for A/D LRCLK to go high begin if (loop_counter == 84) loop_counter <= 0; count <= 0; q <= q; state_A_D <= 3'h2; end else begin state_A_D <= 3'h1; count <= 0; q <= q; if (loop_counter == 84) loop_counter <= 0; end // state 2 - left shift A/D data, check count and take action if necessary 3'h2: if (!BCLK) state_A_D <= 3'h2; // wait for A/D BCLK clock to go high else begin q[7:0] <= {q[6:0], DOUT}; // shift current data left and add new data count <= count + 1; if (count == 8 || count == 16 || count == 24 || count == 40 || count == 48 || count == 56) begin register <= q; state_A_D <= 3'h3; // strobe Tx_fifo end else if (loop_counter == 0 && count == 0) begin register <= 8'b10000000; // send 0x80 of sync word state_A_D <= 3'h3; // strobe Tx_fifo end else if (loop_counter == 0 && (count == 1 || count == 2 || count == 4 /* || count == 5 */ )) begin register <= 8'b00000000; // send 0x00 of sync word state_A_D <= 3'h3; // strobe Tx_fifo end else if (loop_counter == 0 && count == 3) begin register <= {register[7:1], button}; // control 0 bit 0 has PTT status rx_avail <= 12'd4095 - sync_Rx_used; // kd5tfd added -- must match rx fifo size state_A_D <= 3'h3; end else if ( loop_counter == 0 && count == 5 ) begin register[7:1] <= 0; register[0] <= write_full; state_A_D <= 3'h3; end else if (loop_counter == 0 && count == 6) // control 3 - send # of bytes in Rx fifo begin // register[7:0] <= sync_Rx_used[11:4]; register[7:0] <= rx_avail[11:4]; // kd5tfd state_A_D <= 3'h3; end else if (loop_counter == 0 && count == 7) // control 4 - return counter from PC begin register <= Rx_control_4; state_A_D <= 3'h3; end else state_A_D <= 3'h4; end // end begin // state 3 - set data flag to strobe data into Tx_fifo 3'h3: begin data_flag <= 1; // create a one clock cycle wide pulse state_A_D <= 3'h4; // to let FX2 know that there is data to send via USB end // state 4 - reset data flag wait for BCLK to go low and either loop again or start over if counter = 63 3'h4: begin data_flag <= 0; // clear data flag if (!BCLK) begin if (count == 63)begin state_A_D <= 3'h0; // go and wait for LRCLK to be 0. loop_counter <= loop_counter + 1; end else state_A_D <= 3'h2; end else state_A_D <= 3'h4; // wait for BCLK to go low end default: state_A_D <= 3'h0; endcase end /////////////////////////////////////////////////////////////// // // Tx_fifo - 512 Bytes // ////////////////////////////////////////////////////////////// Tx_fifo Tx_fifo(.wrclk (data_flag),.rdreq (1'b1),.rdclk (Tx_read_clock),.wrreq (1'b1), .data (register),.rdempty (Tx_rdempty), .wrempty (Tx_write_empty), .wrfull (Tx_write_full),.q (Tx_register), .wrusedw(write_used)); wire Tx_rdempty; // High when Tx fifo empty wire [7:0] Tx_register; // holds data from A/D to send to TFX2 wire Tx_write_full; wire Tx_write_empty; reg syncd_Tx_write_full; reg Tx_read_clock; // when goes high sends data to Tx_register wire [8:0] write_used; // indicates how may bytes in the Tx buffer reg [8:0] syncd_write_used; // ditto but synced to FX2 clock ////////////////////////////////////////////////////////////// // // State Machine to manage FX2 USB interface // ////////////////////////////////////////////////////////////// /* The state machine checks if there are characters to be read in the FX2 Rx FIFO by checking 'fifo_data_available' If set it loads the byte read into the Rx_register. On the next clock it checks if the Tx_data_fag is set - if so it sends the data to the Tx FIFO. After the Tx data has been sent it checks 'fifo_data_available' etc. */ wire fifo_data_available = FX2_flags[0]; // goes high when FIFO 2 has data available wire fifo_ready = FX2_flags[2]; // high when we can write to FIFO 4 reg [7:0] Rx_register; // data from PC goes here reg [1:0] FIFO_ADDRESS; // FIFO address selector, uses 2 bits reg FIFO_DATA_OUTPUT_ENABLE; // select FX2 FIFO to read or write always @ (posedge FX2_CLK) begin syncd_write_used <= write_used; case(state_FX) // state 0 - check for Rx data 3'h0: begin FX2_SLWR <= 1; // reset FX2 FIFO write stobe Tx_read_clock <= 1'b0; // reset Tx fifo read strobe if (fifo_data_available) // does FX2 FIFO have data begin state_FX <= 3'h1; // yes,we have Rx data so set up to get it FIFO_ADDRESS <= 2'b00; // select FIFO 2 FIFO_DATA_OUTPUT_ENABLE <= 1'b0; // enable receive on FIFO bus end else begin state_FX <= 3'h2; // no Rx data so check for Tx data end end // state 1 - get Rx data 3'h1: begin FX2_SLRD <= 0; // set read strobe low Rx_register[7:0] <= FX2_FD[7:0]; // get data from FIFO state_FX <= 3'h2; end // state 2 - check for Tx data - Tx fifo must be at least half full before we Tx 3'h2: begin FX2_SLRD <= 1; // reset read strobe if (syncd_write_used[8] == 1'b1) begin // data available, so let's start the xfer... Tx_read_clock <= 1'b1; // start transfer from Tx fifo FX2_SLWR <= 1; state_FX <= 3'h3; FIFO_ADDRESS <= 2'b10; // select FIFO 4 FIFO_DATA_OUTPUT_ENABLE <= 1'b1; // enable send on FIFO bus end else begin // No Tx data so check for Rx data FX2_SLWR <= 1; state_FX <= 3'h0; end end // state 3 - check Tx FIFO is ready then set Write strobe 3'h3: begin if (fifo_ready) begin // if FIFO 4 is ready, write to it and exit this state Tx_read_clock <= 1'b0; // end of transfer from Tx fifo FX2_SLWR <= 0; state_FX <= 3'h0; end else begin // otherwise, hang out here until fifo is rdy. FX2_SLWR <= 1; state_FX <= 3'h3; end end default: state_FX <= 3'h0; endcase end ///////////////////////////////////////////////////////////// // // Rx_fifo (4096 bytes) Dual clock FIFO - Altera Megafunction (dcfifo) // ///////////////////////////////////////////////////////////// /* The write clock of the FIFO is FX2_SLRD and the read clock BCLK. Data from the FX2_FIFO is written to the FIFO using FX2_SLRD. Data from the FIFO is read on the positive edge of BCLK when fifo_enable is high. The FIFO is 4096 bytes long. NB: The output flags are only valid after a read/write clock has taken place */ wire [7:0] Rx_data; wire write_full; // high when tx side of fifo is full //wire read_full; // high when rx side of fifo is full //wire read_empty; // high when rx side of fifo is empty wire [11:0] Rx_used; // how many bytes in FX2 side Rx fifo wire [11:0] Rx_used_rdside; // read side count // kd5tfd added Rx_fifo Rx_fifo(.wrclk (FX2_SLRD),.rdreq (fifo_enable),.rdclk (BCLK),.wrreq (1'b1), .data (Rx_register),.q (Rx_data), .wrfull (write_full),.wrusedw(Rx_used) , .rdusedw(Rx_used_rdside) // kd5tfd added ); ////////////////////////////////////////////////////////////// // // State Machine to manage PWM interface // ////////////////////////////////////////////////////////////// /* The state machine changes state on the negative edge of the BCLK pulse. The code loops looking for a sync sequence (0x80_00_00) then stores the next 5 bytes which are control bytes. It then concatenates the next two bytes (Left audio MSB and Left audio LSB) to form a 16 bit word and the next two bytes (Right audio MSB and Right audio LSB) to send to the PWM D/A converters. The word sent to the converters must be send at the sample rate of the A/D converter so is synced on the positive edge of the LRCLK. Further reads of Rx_data are inhibited until the Rx FIFO has at least 12 bytes in it. TODO: Explain need for loop counter and why it is 125 + 1 */ reg [3:0] state_PWM; // state for PWM counts 0 to E reg [15:0] Left_PWM; reg [15:0] Right_PWM; // Right 16 bit PCM data for D/A converter reg set_PWM; // flag cleared when no more data required. reg fifo_enable; // controls reading of dual clock fifo reg test; reg [11:0] synced_Rx_used; // how may bytes in FX2 side Rx fifos synced to BCLK reg [6:0] byte_count; // counts number of times round loop reg [7:0] Rx_control_0; // control 0 from PC reg [7:0] Rx_control_1; // control 1 from PC reg [7:0] Rx_control_2; // control 2 from PC reg [7:0] Rx_control_3; // control 3 from PC reg [7:0] Rx_control_4; // control 4 from PC always @ (negedge BCLK) begin synced_Rx_used <= Rx_used; case(state_PWM) // state 0 - check for 0x80 of sync character 0: begin //if (byte_count == 126) byte_count <= 0; byte_count <= 0; fifo_enable <= 1'b1; // enable read of dual clock fifo if (Rx_data == 8'h80) state_PWM <= 1; // have start of sync else state_PWM <= 0; // loop here if not end // state 1 - check for 0x00 of sync character 1: begin if (Rx_data == 8'h00) state_PWM <= 2; // have end of sync else state_PWM <= 0; // not sync, restart end // state 2 - check for 0x00 of sync character 2: begin if (Rx_data == 8'h00) state_PWM <= 3; // have end of sync else state_PWM <= 0; // not sync, restart end // state 3 - We have sync so get Rx_control_0 3: begin Rx_control_0 <= Rx_data; state_PWM <= 4; end // state 4 - get Rx_control_1 4: begin Rx_control_1 <= Rx_data; state_PWM <= 5; end // state 5 - get Rx_control_2 5: begin Rx_control_2 <= Rx_data; state_PWM <= 6; end // state 6 - get Rx_control_3 6: begin Rx_control_3 <= Rx_data; state_PWM <= 7; end // state 7- get Rx_control_4 7: begin Rx_control_4 <= Rx_data; state_PWM <= 8; end // state 8 - get MSB of Left audio 8: begin fifo_enable <= 1'b1; // enable Rx_fifo Left_PWM[15:8] <= Rx_data[7:0]; state_PWM <= 9; end // state 9 - get LSB of Left audio amd combine with MSB to form 16 bit PWM data 9: begin Left_PWM[7:0] <= Rx_data[7:0]; state_PWM <= 10; end // state 10 - get MSB of Right audio 10: begin Right_PWM[15:8] <= Rx_data[7:0]; state_PWM <= 11; end // state 11 - get LSB of Right audio amd combine with MSB to form 16 bit PWM data 11: begin Right_PWM[7:0] <= Rx_data[7:0]; state_PWM <= 12; // look for next sync end // state 12 - check that LRCLK is low 12: begin fifo_enable <= 1'b0; // disable read of dual clock fifo if (LRCLK) state_PWM <= 12; // wait for A/D LRCLK to go low so we are in sync else state_PWM <= 13; end // state 13 - wait for leading edge of LRCLK 13: begin if (LRCLK) // wait for A/D LRCLK to go high so we are in sync begin byte_count <= byte_count + 1'b1; Left_Data[15:0] <= Left_PWM[15:0]; // set up Left D/A data Right_Data[15:0]<= Right_PWM[15:0]; // set up Right D/A data state_PWM <= 14; end else state_PWM <= 13; end // state 14 - wait until Rx fifo has at least 12 bytes in it before restarting 14: begin if (synced_Rx_used >11) begin if(byte_count == 126) state_PWM <= 0; else state_PWM <= 8; end else state_PWM <= 14; end default: state_PWM <= 4'h0; endcase end ///////////////////////////////////////////////////////////////// // // Single bit PWM 16 bit D/A converters // ///////////////////////////////////////////////////////////////// reg [15:0] Left_Data; reg [15:0] Right_Data; reg [15:0] Left_Data_in; reg [15:0] Right_Data_in; reg [16:0] Left_PWM_accumulator; reg [16:0] Right_PWM_accumulator; always @(negedge BCLK) begin Left_Data_in <= Left_Data + 16'h8000; // so that 0 in gives 50:50 mark/space Right_Data_in <= Right_Data + 16'h8000; Left_PWM_accumulator <= Left_PWM_accumulator[15:0] + Left_Data_in; Right_PWM_accumulator <= Right_PWM_accumulator[15:0] + Right_Data_in; end assign Left_PWM_out = Left_PWM_accumulator[16]; assign Right_PWM_out = Right_PWM_accumulator[16]; // assign Left_PWM_out = 0; // assign Right_PWM_out = 0; /////////////////////////////////////////////////////// // // RS232 Transmitter (for debug!) // /////////////////////////////////////////////////////// /* Pin 68 is used as Tx data out to the PC at 115kB. Data to be send is loaded into outputdata and send when TxD_start goes high. Can use Rx_data_flag and Rx_register to display what comes from RX FIFO */ /* parameter ClkFrequency = 24000000; // make sure Xylo is set to this! wire [7:0] outputdata = RxD_data; async_transmitter TX(.clk(FX2_CLK), .TxD(TxD), .TxD_start(TxD_start), .TxD_data(outputdata)); defparam TX.ClkFrequency = ClkFrequency; */ // FX2_FD is tristate when write is hi, otherwise it's the Tx_register value. assign FX2_FD[7:0] = FX2_SLWR ? 8'bZ : Tx_register[7:0]; // Flash the LEDs to show something is working! //assign LED[0] = Rx_control_0[0]; // Red lights Rx fifo is full. assign LED[0] = write_full; // Red lights Rx fifo is full. assign LED[1] = FX2_flags[2]; // Green LED on when we can write to FX2 FIFO endmodule