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.

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.

—————————————————————————————————————————————–
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