Audio Processing

The first step in my process was to design a state machine for the DE2 boards audio codec. To do this I configured the hardware such that the line-in port leads to the input of the ADC. The Cyclone board samples at a rate of 48 kHz, which is much slower than that of the FPGA clock (50 MHz). The I2C design protocol was perfect for this because it happens to be designed specifically so that slower devices can communicate with faster systems without slowing them down.

All I2C master and slave devices are connected by only two wires, the serial clock (SCL) and the serial data (SDA). These allow both the master and slave units to act as both a receiver and a transmitter.

I2C pic

Typical I2C communication calls for 16-bit transmissions. Each slave address has a unique 7 address. Communication starts with the master sending a start condition, followed by the recipient address and the data direction bit. If the data direction bit is “low” or zero, the master will write to the slave device. If the bit is “high” or 1, the master device reads data from the slave. Each byte of data is followed by an ack bit, which simply lets the sender know that the device is ready to receive information. Finally the master device generates a stop condition. This signals to other devices on the I2C bus that it is no longer in use.

i2C pic2

 

—————————————————————————————————————————————–

Verilog modules

module I2CMaster
(
input rst,
input inClock,
input [23:0] inData,
inout sda,
inout scl,
output reg ack, //returns 1 when data is recieved
output reg rdy
);

reg [1:0] txState, bytesCounter, ackNum;
reg [7:0] txData;
wire txReady, txAck;

always @*
begin
case(bytesCounter)
0: txData <= inData[23:16]; 

1: txData <= inData[15:8];

2: txData <= inData[7:0];

default: txData <= 1'b0;

endcase
end

always @(posedge reset or posedge txReady)
begin
if (rst)
begin
txState <= 1'b0;
bytesCounter <= 1'b0;
ready <= 1'b0;
ackNum <= 1'b0;
ack <= 1'b0;
end

else
begin
case(txState)
0: begin
txState <= 2'd1;
ready <= 1'b1;
ackNum <= 1'b0;
end

1: begin
if (bytesCounter == 2'd2)
begin
bytesCounter <= 1'b0;
txState <= 2'd2;
end

else bytesCounter <= bytesCounter + 1'b1;
ackNum = ackNum + txAck;
ack <= (ackNum == 2'd3);
end

2: begin
txState <= 2'd0;
ready <= 1'b0;
end
endcase
end
end

I2C_Logic txLogic
(
.rst(rst),
.inClock(inClock),
.inData(txData),
.mode(txState),
.sda(sda),
.scl(scl),
.ack(txAck),
.ready(txReady)
);

endmodule



module I2C_Logic
(
input reset,
input enable,
input inClock,
input [7:0] inData,
input [1:0] mode, // 0 - start, 1 - byte sending, 2 - stop
inout sda,
inout scl,
output reg ack,
output reg ready
);

reg SDApull, SCLpull;
reg [3:0] state;
reg [1:0] bitState;

assign sda = SDApull ? 1'b0 : 1'bz;
assign scl = SCLpull ? 1'b0 : 1'bz;

always @(posedge reset or posedge inClock)
begin
if (reset)
begin
ack <= 1'b0;
state <= 1'b0;
ready <= 1'b0;
bitState <= 1'b0;
SCLpull <= 1'b0;
SDApull <= 1'b0;
end

else
begin
case(mode)
// Start 
0: begin 
case(state)
0: begin
SCLpull <= 1'b0;
ready <= 1'b0;
end

1: SDApull <= 1'b1;

2: begin
SCLpull <= 1'b1;
ready <= 1'b1;
end
endcase

if (state == 2'd2) state <= 1'b0;
else state <= state + 1'b1;
end

1: begin

// First cycle
if (state == 4'd0 && bitState == 2'd0)
begin
ready <= 1'b0;
ack <= 1'b0;
end

//if we send bits
if (state != 4'd8)
begin
case(bitState)
0: SDApull <= ~(inData >> (3'd7 - state));

1: SCLpull <= 1'b0; 

3: begin
SCLpull <= 1'b1;
state <= state + 1'b1;
end
endcase
end

// If Ack is recieved
else
begin
case(bitState)
0: SDApull <= 1'b0;

1: SCLpull <= 1'b0;

2: ack <= ~sda;

3: begin
SCLpull <= 1'b1;
state <= 1'b0;
ready <= 1'b1;

end

endcase

end

bitState <= bitState + 1'b1;
end

// Stop
2: begin
case(state)
0: begin
SDApull <= 1'b1;
ready <= 1'b0;
end

1: SCLpull <= 1'b0;

2: begin
SDApull <= 1'b0;
ready <= 1'b1;
end
endcase

if (state == 2'd2) state <= 1'b0;
else state <= state + 1'b1;
end
endcase
end
end 
endmodule

module Codec
(
input rst,
input inclk,
inout sda,
inout scl,
output reg rdy,
output reg [3:0] ackNum
);

localparam ADDR = 8'h34;

reg I2C_clkDisable;
reg [23:0] data;
reg [2:0] s;
wire txReady, ack;

I2CMaster i2cm1
(
.reset(reset),
.inClock(I2C_clkDisable ? 1'b0 : inClock),
.inData(data),
.sda(sda),
.scl(scl),
.ready(txReady),
.ack(ack)
);

always @(posedge txReady or posedge reset)
begin
if (rst)
begin
I2C_clkDisable <= 1'b0;
s <= 1'b0;
ready <= 1'b0;
data <= 8'h00;
ackNum <= 1'b0;
end

else
begin
case(s) 

3'h0: data <= {ADDR, 7'h04, 9'b0_0000_0100}; // Send Analogue Audio Path reg

3'h1: data <= {ADDR, 7'h07, 9'b0_0100_0010}; // Send Digital Audio Interface reg

3'h2: data <= {ADDR, 7'h09, 9'b0_0000_0001}; // Send Active reg

3'h3: data <= {ADDR, 7'h06, 9'b0_0011_1001}; // Send Power Down reg

3'h4: data <= {ADDR, 7'h00, 9'b0_0001_0111}; // Send Left Line reg

3'h5: data <= {ADDR, 7'h01, 9'b0_0001_0111}; // Send Right Line reg

endcase

if (s == 3'h6)
begin
ready <= 1'b1;
I2C_clkDisable <= 1'b1;
end

else s <= s + 1'b1;
ackNum <= ackNum + ack; 
end
end

endmodule

module I2S #(wordSize = 16)
(
input rst,
input codecBitClock,
input codecLRClock,
input codecData,

output reg dataReady,
output reg [wordSize-1:0] outDataLeft,
output reg [wordSize-1:0] outDataRight
);

localparam buffSize = 32;

reg oldRLClock;
reg [buffSize-1:0] buffer;

wire [buffSize-1:0] nextBuffer;

assign nextBuffer = {buffer[buffSize-2:0], codecData};

always @(posedge reset or posedge codecBitClock)
begin
if (reset)
begin
outDataLeft <= 1'b0;
outDataRight <= 1'b0;
oldRLClock <= codecLRClock;
dataReady <= 1'b0;
end

else
begin
buffer <= nextBuffer;
oldRLClock <= codecLRClock;

if (codecLRClock != oldRLClock)
begin
if (oldRLClock)
begin
outDataLeft <= nextBuffer[buffSize-1:buffSize-wordSize-2];

dataRdy <= 1'b0;

end

else
begin
outDataRight <= nextBuffer[buffSize-1:buffSize-wordSize-2];

dataRdy <= 1'b1;

end
end
end
end

endmodule