代码很完善,很适合大家学习IIC协议的FPGA实现
`timescale 1ns / 1ps
//
module i2c_master
#(
parameter CLK_DIV = 30
)
(
input i_clk,
input i_rst_n,
input i_start,
input wr,//1:read 0:write
input [6:0] dev_addr,
input [7:0] reg_addr,
input [7:0] write_data,
output reg [7:0]read_data,
output reg o_bsy,
output reg o_read_valid,
//debug
output reg [3:0] state,
output reg sda_enable,
//
inout tri o_sda,
output o_scl
);
localparam [3:0]
S_IDLE = 0,
S_START = 1,
S_DEVICE = 3,
S_CMD = 4,
S_WAIT_CMD_ACK = 5,
S_REGADDR = 6,
S_WAIT_REGADDR_ACK = 7,
S_WRITE_DATA = 8,
S_WAIT_WRITE_ACK = 9,
S_READ_DATA = 10,
S_WRITE_ACK = 11,
S_STOP = 12;
reg [15:0] clk_cnt;
reg clk_posedge;
reg clk_negedge;
reg [3:0] bit_cnt;
//reg sda_enable;
reg sda;
reg scl;
//state machine
//reg [3:0] state;
reg [3:0] next_state;
reg ack;
reg read_cycle_en;
reg start;
reg wr_lock;
reg [6:0] dev_addr_lock;
reg [7:0] reg_addr_lock;
reg [7:0] write_data_lock;
always @(posedge i_clk or negedge i_rst_n) begin
if(!i_rst_n)
clk_cnt <= 'd0;
else if(clk_cnt < CLK_DIV - 1)
clk_cnt <= clk_cnt + 1'b1;
else clk_cnt <= 'd0;
end
always @(posedge i_clk or negedge i_rst_n) begin
if(!i_rst_n)
clk_posedge <= 1'b0;
else if(clk_cnt==CLK_DIV/2 - 1)
clk_posedge <= 1'b1;
else clk_posedge <= 1'b0;
end
always @(posedge i_clk or negedge i_rst_n) begin
if(!i_rst_n)
clk_negedge <= 1'b0;
else if(clk_cnt==CLK_DIV - 1)
clk_negedge <= 1'b1;
else clk_negedge <= 1'b0;
end
always @(posedge i_clk) begin
if(state == S_IDLE)begin
if(i_start)
start <= 1'b1;
else
start <= 1'b0;
end
else
start <= 1'b0;
end
always @(posedge i_clk) begin
if(state == S_IDLE && i_start)begin
wr_lock <= wr;
dev_addr_lock <= dev_addr;
reg_addr_lock <= reg_addr;
write_data_lock <= write_data;
end
end
always @(posedge i_clk) begin
if(state == S_WAIT_CMD_ACK || state == S_WAIT_REGADDR_ACK || state == S_WAIT_WRITE_ACK)
ack <= o_sda;
else
ack <= 1'b1;
end
always @(posedge i_clk) begin
if(state == S_IDLE)
read_cycle_en <= 1'b0;
else if(state == S_WAIT_REGADDR_ACK && wr_lock && clk_posedge)
read_cycle_en <= 1'b1;
end
always @(posedge i_clk or negedge i_rst_n) begin
if(!i_rst_n)
state <= S_IDLE;
else
state <= next_state;
end
always @(*)begin
if(!i_rst_n)
next_state = S_IDLE;
else begin
case(state)
S_IDLE :begin
if(start && clk_posedge)
next_state = S_START;
else
next_state = S_IDLE;
end
S_START :begin
if(clk_posedge)
next_state = S_DEVICE;
else
next_state = S_START;
end
S_DEVICE :begin
if(bit_cnt == 'd6 && clk_posedge)
next_state = S_CMD;
else
next_state = S_DEVICE;
end
S_CMD :begin
if(clk_posedge)
next_state = S_WAIT_CMD_ACK;
else
next_state = S_CMD;
end
S_WAIT_CMD_ACK :begin
if(clk_posedge)begin
if(ack)
next_state = S_IDLE;
else begin
if(read_cycle_en)
next_state = S_READ_DATA;
else
next_state = S_REGADDR;
end
end
else
next_state = S_WAIT_CMD_ACK;
end
S_REGADDR :begin
if(clk_posedge && bit_cnt == 'd7)
next_state = S_WAIT_REGADDR_ACK;
else
next_state = S_REGADDR;
end
S_WAIT_REGADDR_ACK :begin
if(clk_posedge)begin
if(ack)
next_state = S_IDLE;
else if(wr_lock)
next_state = S_START;
else
next_state = S_WRITE_DATA;
end
else
next_state = S_WAIT_REGADDR_ACK;
end
S_WRITE_DATA :begin
if(clk_posedge && bit_cnt == 'd7)
next_state = S_WAIT_WRITE_ACK;
else
next_state = S_WRITE_DATA;
end
S_WAIT_WRITE_ACK :begin
if(clk_posedge)begin
if(ack)
next_state = S_IDLE;
else
next_state = S_STOP;
end
else
next_state = S_WAIT_WRITE_ACK;
end
S_READ_DATA :begin
if(clk_posedge && bit_cnt == 'd7)
next_state = S_WRITE_ACK;
else
next_state = S_READ_DATA;
end
S_WRITE_ACK :begin
if(clk_posedge)
next_state = S_STOP;
else
next_state = S_WRITE_ACK;
end
S_STOP :begin
if(clk_posedge && bit_cnt == 1)
next_state = S_IDLE;
else
next_state = S_STOP;
end
default:;
endcase
end
end
always @(posedge i_clk) begin
if(state == S_IDLE)
scl <= 1'b1;
else if(state == S_STOP)begin
if(clk_negedge)
scl <= 1'b1;
end
else if(clk_posedge)
scl <= 1'b0;
else if(clk_negedge)
scl <= 1'b1;
end
assign o_sda = (sda_enable)?sda:1'hz;
assign o_scl = scl;
always @(posedge i_clk) begin
if(state == S_WAIT_CMD_ACK || state == S_WAIT_WRITE_ACK || state == S_WAIT_REGADDR_ACK || state == S_READ_DATA)
sda_enable <= 1'b0;
else
sda_enable <= 1'b1;
end
always @(posedge i_clk) begin
if(state == S_IDLE)
bit_cnt <= 'd0;
else if(state != next_state)
bit_cnt <= 'd0;
else if(clk_posedge)
bit_cnt <= bit_cnt + 1'b1;
end
always @(posedge i_clk ) begin
case(state)
S_IDLE :begin
sda <= 1'b1;
end
S_START :begin
if(!o_scl)
sda <= 1'b1;
else if(clk_cnt > CLK_DIV/4)
sda <= 1'b0;
end
S_DEVICE :begin
if(bit_cnt < 'd7)
sda <= dev_addr_lock >> (6 - bit_cnt);
end
S_CMD :begin
sda <= wr_lock;
end
S_REGADDR :begin
if(bit_cnt < 'd8)
sda <= reg_addr_lock >> (7 - bit_cnt);
end
S_WRITE_DATA :begin
if(bit_cnt < 'd8)
sda <= write_data_lock >> (7 - bit_cnt);
end
S_WRITE_ACK :begin
sda <= 1'b1;
end
S_STOP :begin
if(o_scl)begin
if(clk_posedge)
sda <= 1'b1;
end
else
sda <= 1'b0;
end
endcase
end
always @(posedge i_clk) begin
if(state == S_IDLE)
read_data <= 8'hff;
else if(state == S_READ_DATA && clk_posedge)
read_data <= {o_sda,read_data[7:1]};
end
always @(posedge i_clk) begin
if(o_read_valid)
o_read_valid <= 1'b0;
else if(state == S_READ_DATA && bit_cnt == 7 && clk_posedge)
o_read_valid <= 1'b1;
end
always @(posedge i_clk) begin
if(state == S_IDLE)
o_bsy <= 1'b0;
else
o_bsy <= 1'b1;
end
endmodule