I2C示例
- 完结篇,直接给出一个完整的示例,当然代码不全贴了。
- 注意仿真的模型是从镁光官网直接拿的,这个自行实现ack很不现实。
- 另外注意状态机的绘图要特别注意逻辑,一旦出错很难排查。
- 理清逻辑,思路,那么代码实现就是顺其自然的,按照小梅哥的说法叫做,照图施工。
代码实现
- demo代码
assign iic_sda = (out_en == 1'b1) ? sda_obuf : 1'bz;
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
cnt <= 32'd0;
else
if (cnt < T_SCL - 1'b1 && cnt_en == 1'b1)
cnt <= cnt + 1'b1;
else
cnt <= 32'd0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
iic_scl <= 1'b1;
else
if (cnt < T_SCL/2)
iic_scl <= 1'b1;
else
iic_scl <= 1'b0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
flag_high <= 1'b0;
else
if (cnt == T_SCL/4 - 1'b1)
flag_high <= 1'b1;
else
flag_high <= 1'b0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
flag_low <= 1'b0;
else
if (cnt == (T_SCL * 3)/4 - 1'b1)
flag_low <= 1'b1;
else
flag_low <= 1'b0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
c_state <= IDLE;
else
c_state <= n_state;
end
always @ * begin
case (c_state)
IDLE : begin
if (start_flag == 1'b1)
n_state = START;
else
n_state = IDLE;
end
START : begin
if (flag_high == 1'b1)
n_state = CTRL;
else
n_state = START;
end
CTRL : begin
if (flag_low == 1'b1 && drive_cnt == 4'd8)
n_state = CTRL_ACK;
else
n_state = CTRL;
end
CTRL_ACK : begin
if (flag_high == 1'b1)
if (iic_sda == 1'b0)
if (addr_sel == 1'b1)
n_state = HADDR;
else
n_state = LADDR;
else
n_state = START;
else
n_state = CTRL_ACK;
end
HADDR : begin
if (flag_low == 1'b1 && drive_cnt == 4'd8)
n_state = HADDR_ACK;
else
n_state = HADDR;
end
HADDR_ACK : begin
if (flag_high == 1'b1)
if (iic_sda == 1'b0)
n_state = LADDR;
else
n_state = START;
else
n_state = HADDR_ACK;
end
LADDR : begin
if (flag_low == 1'b1 && drive_cnt == 4'd8)
n_state = LADDR_ACK;
else
n_state = LADDR;
end
LADDR_ACK : begin
if (flag_high == 1'b1)
if (iic_sda == 1'b0)
if (wren == 1'b1)
n_state = WR;
else
n_state = RD_START;
else
n_state = START;
else
n_state = LADDR_ACK;
end
WR : begin
if (flag_low == 1'b1 && drive_cnt == 4'd8)
n_state = WR_ACK;
else
n_state = WR;
end
WR_ACK : begin
if (flag_high == 1'b1)
if (iic_sda == 1'b0)
n_state = STOP;
else
n_state = START;
else
n_state = WR_ACK;
end
STOP : begin
if (flag_high == 1'b1)
n_state = IDLE;
else
n_state = STOP;
end
RD_START : begin
if (flag_high == 1'b1)
n_state = RD_CTRL;
else
n_state = RD_START;
end
RD_CTRL : begin
if (flag_low == 1'b1 && drive_cnt == 4'd8)
n_state = RD_CTRL_ACK;
else
n_state = RD_CTRL;
end
RD_CTRL_ACK : begin
if (flag_high == 1'b1)
if (iic_sda == 1'b0)
n_state = RD;
else
n_state = START;
else
n_state = RD_CTRL_ACK;
end
RD : begin
if (flag_low == 1'b1 && drive_cnt == 4'd8)
n_state = NO_ACK;
else
n_state = RD;
end
NO_ACK : begin
if (flag_high == 1'b1)
n_state = STOP;
else
n_state = NO_ACK;
end
default : n_state = IDLE;
endcase
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
cnt_en <= 1'b0;
else
case (c_state)
IDLE : cnt_en <= 1'b0;
CTRL_ACK,
HADDR_ACK,
LADDR_ACK,
WR_ACK,
RD_CTRL_ACK : begin
if (flag_high == 1'b1)
if (iic_sda == 1'b0)
cnt_en <= 1'b1;
else
cnt_en <= 1'b0;
else
cnt_en <= cnt_en;
end
default : cnt_en <= 1'b1;
endcase
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
out_en <= 1'b0;
else
case (c_state)
IDLE : out_en <= 1'b0;
START : out_en <= 1'b1;
CTRL : begin
if (flag_low == 1'b1 && drive_cnt == 4'd8)
out_en <= 1'b0;
else
out_en <= 1'b1;
end
CTRL_ACK : out_en <= 1'b0;
HADDR : begin
if (flag_low == 1'b1 && drive_cnt == 4'd8)
out_en <= 1'b0;
else
if (flag_low == 1'b1)
out_en <= 1'b1;
else
out_en <= out_en;
end
HADDR_ACK : out_en <= 1'b0;
LADDR : begin
if (flag_low == 1'b1 && drive_cnt == 4'd8)
out_en <= 1'b0;
else
if (flag_low == 1'b1)
out_en <= 1'b1;
else
out_en <= out_en;
end
LADDR_ACK : out_en <= 1'b0;
WR : begin
if (flag_low == 1'b1 && drive_cnt == 4'd8)
out_en <= 1'b0;
else
if (flag_low == 1'b1)
out_en <= 1'b1;
else
out_en <= out_en;
end
WR_ACK : out_en <= 1'b0;
STOP : begin
if (flag_low == 1'b1)
out_en <= 1'b1;
else
out_en <= out_en;
end
RD_START : begin
if (flag_low == 1'b1)
out_en <= 1'b1;
else
out_en <= out_en;
end
RD_CTRL : begin
if (flag_low == 1'b1 && drive_cnt == 4'd8)
out_en <= 1'b0;
else
out_en <= 1'b1;
end
RD_CTRL_ACK : out_en <= 1'b0;
RD : begin
if (flag_low == 1'b1 && drive_cnt == 4'd8)
out_en <= 1'b1;
else
out_en <= 1'b0;
end
NO_ACK : out_en <= 1'b1;
default : out_en <= 1'b0;
endcase
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
sda_obuf <= 1'b1;
else
case (c_state)
IDLE : sda_obuf <= 1'b1;
START : begin
if (flag_high == 1'b1)
sda_obuf <= 1'b0;
else
sda_obuf <= 1'b1;
end
CTRL : begin
if (flag_low == 1'b1 && drive_cnt < 4'd8)
sda_obuf <= temp[7];
else
sda_obuf <= sda_obuf;
end
CTRL_ACK : sda_obuf <= 1'b0;
HADDR : begin
if (flag_low == 1'b1 && drive_cnt < 4'd8)
sda_obuf <= temp[7];
else
sda_obuf <= sda_obuf;
end
HADDR_ACK : sda_obuf <= 1'b0;
LADDR : begin
if (flag_low == 1'b1 && drive_cnt < 4'd8)
sda_obuf <= temp[7];
else
sda_obuf <= sda_obuf;
end
LADDR_ACK : sda_obuf <= 1'b0;
WR : begin
if (flag_low == 1'b1 && drive_cnt < 4'd8)
sda_obuf <= temp[7];
else
sda_obuf <= sda_obuf;
end
WR_ACK : sda_obuf <= 1'b0;
STOP : begin
if (flag_low == 1'b1)
sda_obuf <= 1'b0;
else
if (flag_high == 1'b1)
sda_obuf <= 1'b1;
else
sda_obuf <= sda_obuf;
end
RD_START : begin
if (flag_low == 1'b1)
sda_obuf <= 1'b1;
else
if (flag_high == 1'b1)
sda_obuf <= 1'b0;
else
sda_obuf <= sda_obuf;
end
RD_CTRL : begin
if (flag_low == 1'b1 && drive_cnt < 4'd8)
sda_obuf <= temp[7];
else
sda_obuf <= sda_obuf;
end
RD_CTRL_ACK : sda_obuf <= 1'b0;
RD : begin
if (flag_low == 1'b1 && drive_cnt == 4'd8)
sda_obuf <= 1'b1;
else
sda_obuf <= sda_obuf;
end
NO_ACK : sda_obuf <= sda_obuf;
default : sda_obuf <= 1'b1;
endcase
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
drive_cnt <= 4'd0;
else
case (c_state)
IDLE : drive_cnt <= 4'd0;
START : drive_cnt <= 4'd0;
CTRL : begin
if (flag_low == 1'b1)
if (drive_cnt < 4'd8)
drive_cnt <= drive_cnt + 1'b1;
else
drive_cnt <= 4'd0;
else
drive_cnt <= drive_cnt;
end
CTRL_ACK : drive_cnt <= 4'd0;
HADDR : begin
if (flag_low == 1'b1)
if (drive_cnt < 4'd8)
drive_cnt <= drive_cnt + 1'b1;
else
drive_cnt <= 4'd0;
else
drive_cnt <= drive_cnt;
end
HADDR_ACK : drive_cnt <= 4'd0;
LADDR : begin
if (flag_low == 1'b1)
if (drive_cnt < 4'd8)
drive_cnt <= drive_cnt + 1'b1;
else
drive_cnt <= 4'd0;
else
drive_cnt <= drive_cnt;
end
LADDR_ACK : drive_cnt <= 4'd0;
WR : begin
if (flag_low == 1'b1)
if (drive_cnt < 4'd8)
drive_cnt <= drive_cnt + 1'b1;
else
drive_cnt <= 4'd0;
else
drive_cnt <= drive_cnt;
end
WR_ACK : drive_cnt <= 4'd0;
STOP : drive_cnt <= 4'd0;
RD_START : drive_cnt <= 4'd0;
RD_CTRL : begin
if (flag_low == 1'b1)
if (drive_cnt < 4'd8)
drive_cnt <= drive_cnt + 1'b1;
else
drive_cnt <= 4'd0;
else
drive_cnt <= drive_cnt;
end
RD_CTRL_ACK : drive_cnt <= 4'd0;
RD : begin
if (flag_high == 1'b1 && drive_cnt < 4'd8)
drive_cnt <= drive_cnt + 1'b1;
else
if (flag_low == 1'b1 && drive_cnt == 4'd8)
drive_cnt <= 4'd0;
else
drive_cnt <= drive_cnt;
end
NO_ACK : drive_cnt <= 4'd0;
default : drive_cnt <= 4'd0;
endcase
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
temp <= 8'd0;
else
case (c_state)
IDLE : temp <= 8'd0;
START : begin
if (flag_high == 1'b1)
temp <= {slave_addr, 1'b0};
else
temp <= temp;
end
CTRL : begin
if (flag_low == 1'b1 && drive_cnt < 4'd8)
temp <= temp << 1'b1;
else
temp <= temp;
end
CTRL_ACK : begin
if (flag_high == 1'b1 && iic_sda == 1'b0)
if (addr_sel == 1'b1)
temp <= addr[15:8];
else
temp <= addr[7:0];
else
temp <= temp;
end
HADDR : begin
if (flag_low == 1'b1 && drive_cnt < 4'd8)
temp <= temp << 1'b1;
else
temp <= temp;
end
HADDR_ACK : begin
if (flag_high == 1'b1 && iic_sda == 1'b0)
temp <= addr[7:0];
else
temp <= temp;
end
LADDR : begin
if (flag_low == 1'b1 && drive_cnt < 4'd8)
temp <= temp << 1'b1;
else
temp <= temp;
end
LADDR_ACK : begin
if (flag_high == 1'b1 && iic_sda == 1'b0)
if (wren == 1'b1)
temp <= wdata;
else
temp <= {slave_addr, 1'b1};
else
temp <= temp;
end
WR : begin
if (flag_low == 1'b1 && drive_cnt < 4'd8)
temp <= temp << 1'b1;
else
temp <= temp;
end
WR_ACK : temp <= 8'd0;
STOP : temp <= 8'd0;
RD_START : temp <= temp;
RD_CTRL : begin
if (flag_low == 1'b1 && drive_cnt < 4'd8)
temp <= temp << 1'b1;
else
temp <= temp;
end
RD_CTRL_ACK : temp <= 8'd0;
RD : begin
if (flag_high == 1'b1 && drive_cnt < 4'd8)
temp <= {temp[6:0], iic_sda};
else
temp <= temp;
end
NO_ACK : temp <= 8'd0;
default : temp <= 8'd0;
endcase
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
iic_done <= 1'b0;
else
if (c_state == STOP && flag_high == 1'b1)
iic_done <= 1'b1;
else
iic_done <= 1'b0;
end
always @ (posedge clk, negedge rst_n) begin
if (rst_n == 1'b0)
rdata <= 8'd0;
else
if (c_state == RD && flag_low == 1'b1 && drive_cnt == 4'd8)
rdata <= temp;
else
rdata <= rdata;
end
- 仿真展示

写在最后
- 提高visio画图能力,而不是老用ps搞。