You are on page 1of 5

`timescale 1ns/1ps

module I2C_master(clk, start, reset, write_data, read_data, write, address, ready, error, sda,
scl);

input clk, start, reset;


input [6:0] address;
input [7:0] write_data;
input write;

output reg ready, error;


output reg [7:0] read_data;
inout sda;
output reg scl;

wire sda_in;
reg sda_dir;
reg sda_out;

assign sda_in = sda;


assign sda = sda_dir ? sda_out: 1'bZ;

reg ack, done_address;


reg [7:0] to_out;
reg [3:0] counter;
reg [3:0] cstate, nstate;

parameter idle = 4'd0;


parameter start_a = 4'd1;
parameter start_b = 4'd2;
parameter write_a = 4'd3;
parameter write_b = 4'd4;
parameter write_c = 4'd5;
parameter read_a = 4'd6;
parameter read_b = 4'd7;
parameter read_c = 4'd8;
parameter stop_a = 4'd9;
parameter stop_b = 4'd10;
parameter stop_c = 4'd11;

//cstate block
always@(*) begin
if(reset)
cstate <= idle;
else
cstate <= nstate;
end

always@(posedge clk) begin


if(reset) begin
nstate <= idle;
sda_dir <= 0;
ready <= 0;
error <= 0;
sda_out <= 0;
scl <= 1;
ack <= 0;
done_address <= 0;
to_out <= 0;
end
else begin
case(cstate)
idle: begin
if (start == 1) begin
ready <= 0;
error <= 0;
done_address <= 0;
ack <= 0;
nstate <= start_a ;
end else
nstate <= idle;
end
start_a: begin
sda_dir <= 1;
sda_out <= 0;
nstate <= start_b;
end
start_b: begin
scl <= 0;
counter <= 4'd8;
nstate <= write_a;
if( write == 1)
to_out <= {address, 1'b0};
else
to_out <= {address, 1'b1};

end

write_a: begin
sda_dir <= 1;
if(done_address == 0) begin//send address
sda_out <= to_out[counter - 4'd1]; //(address)
counter <= counter - 4'd1;
nstate <= write_b;

end else begin


if(write == 1) begin //send data
sda_out <= to_out[counter - 4'd1]; //(data)
counter <= counter - 4'd1;
nstate <= write_b;

end else begin


sda_out <= 0;
nstate <= stop_a;
end
end
end
write_b: begin
scl <= 1;
nstate <= write_c;
end
write_c: begin
scl <= 0;
if (counter == 0)
nstate <= read_a;
else
nstate <= write_a;
end
read_a: begin
sda_dir <= 0;
nstate <= read_b;
end
read_b: begin
scl <= 1;
ack <= sda_in;
nstate <= read_c;
end
read_c: begin
scl <= 0;
if(done_address == 0) begin //receive ack

if (ack == 1) begin
error <= 1;
nstate <= stop_a;
end else begin
done_address <= 1'b1;
counter <= 4'd8;

if (write == 1) begin
to_out <= write_data;
nstate <= write_a;
end else
nstate <= read_a; // read data
end
end else begin
if(write == 1) begin //receive ack
if(ack == 1) begin
error <= 1 ;
nstate <= stop_a;
end else begin
if (done_address == 1'b1)
nstate <= stop_a;
end
end
else begin //receive data
to_out[counter - 4'd1] <= sda_in;
counter <= counter - 4'd1;
if( counter == 0) begin
read_data <= to_out;
nstate <= write_a;
end
else
nstate <= read_a;
end
end
end

stop_a: begin
sda_dir <= 1;
sda_out <= 0;
nstate <= stop_b;
end
stop_b: begin
scl <= 1;
nstate <= stop_c;
end
stop_c: begin
sda_out <=1;
nstate <= idle;
ready <= 1;
end
default: begin
sda_dir <= 0;
ready <= 0;
error <= 0;
sda_out <= 0;
scl <= 1;
counter <= 0;
ack <= 0;
to_out <= 0;
end
endcase
end
end
endmodule

You might also like