- //傳輸頻率為400k Hz
- module I2C(
- clk,
- rst,
- w_ctrl, //control byte
- w_h_addr, //address high byte
- w_l_addr, //address low byte
- w_data, //write data
- r_ctrl, //control byte
- r_h_addr, //address high byte
- r_l_addr, //address low byte
- r_data, //read data
- scl,
- sda
- );
- input clk,rst;
- input [6:0] w_ctrl;
- input [7:0] w_h_addr;
- input [7:0] w_l_addr;
- input [7:0] w_data;
- input [6:0] r_ctrl;
- input [7:0] r_h_addr;
- input [7:0] r_l_addr;
- output [7:0] r_data;
- output scl;
- inout sda;
- reg [6:0] div_cnt;
- reg [3:0] bps_cnt;
- reg [7:0] reg_d;
- reg [7:0] in_d;
- reg [7:0] r_data;
- reg reg_sda;
- reg div_en;
- reg bps_en;
- reg sda_en;
- reg buf_sda;
- reg scl;
- //=====================================================
- // 狀態機
- //=====================================================
- parameter [4:0] W_START = 5'd0,
- W_CTRL_B = 5'd1,
- W_ACK0 = 5'd2,
- W_ADDR_H_B = 5'd3,
- W_ACK1 = 5'd4,
- W_ADDR_L_B = 5'd5,
- W_ACK2 = 5'd6,
- W_DATA = 5'd7,
- W_ACK3 = 5'd8,
- W_STOP = 5'd9,
- W_IDLE = 5'd10,
- R_START0 = 5'd11,
- R_CTRL_B0 = 5'd12,
- R_ACK0 = 5'd13,
- R_ADDR_H_B = 5'd14,
- R_ACK1 = 5'd15,
- R_ADDR_L_B = 5'd16,
- R_ACK2 = 5'd17,
- R_START1 = 5'd18,
- R_CTRL_B1 = 5'd19,
- R_ACK3 = 5'd20,
- R_DATA = 5'd21,
- R_NO_ACK = 5'd22,
- R_STOP = 5'd23,
- R_IDLE = 5'd24;
- reg [4:0] present_state, next_state;
- always@(posedge clk or negedge rst)
- begin
- if(!rst) present_state <= W_START;
- else present_state <= next_state;
- end
- always@(div_cnt or bps_cnt or present_state)
- begin
- case(present_state)
- W_START : if(div_cnt == 7'd124 && bps_cnt == 4'd0)
- next_state = W_CTRL_B;
- else
- next_state = W_START;
- W_CTRL_B : if(div_cnt == 7'd124 && bps_cnt == 4'd8)
- next_state = W_ACK0;
- else
- next_state = W_CTRL_B;
- W_ACK0 : if(div_cnt == 7'd124 && bps_cnt == 4'd0)
- next_state = W_ADDR_H_B;
- else
- next_state = W_ACK0;
- W_ADDR_H_B : if(div_cnt == 7'd124 && bps_cnt == 4'd8)
- next_state = W_ACK1;
- else
- next_state = W_ADDR_H_B;
- W_ACK1 : if(div_cnt == 7'd124 && bps_cnt == 4'd0)
- next_state = W_ADDR_L_B;
- else
- next_state = W_ACK1;
- W_ADDR_L_B : if(div_cnt == 7'd124 && bps_cnt == 4'd8)
- next_state = W_ACK2;
- else
- next_state = W_ADDR_L_B;
- W_ACK2 : if(div_cnt == 7'd124 && bps_cnt == 4'd0)
- next_state = W_DATA;
- else
- next_state = W_ACK2;
- W_DATA : if(div_cnt == 7'd124 && bps_cnt == 4'd8)
- next_state = W_ACK3;
- else
- next_state = W_DATA;
- W_ACK3 : if(div_cnt == 7'd124 && bps_cnt == 4'd0)
- next_state = W_STOP;
- else
- next_state = W_ACK3;
- W_STOP : if(div_cnt == 7'd124 && bps_cnt == 4'd1)
- next_state = W_IDLE;
- else
- next_state = W_STOP;
- W_IDLE : if(div_cnt == 7'd124 && bps_cnt == 4'd8)
- next_state = R_START0;
- else
- next_state = W_IDLE;
- R_START0 : if(div_cnt == 7'd124 && bps_cnt == 4'd0)
- next_state = R_CTRL_B0;
- else
- next_state = R_START0;
- R_CTRL_B0 : if(div_cnt == 7'd124 && bps_cnt == 4'd8)
- next_state = R_ACK0;
- else
- next_state = R_CTRL_B0;
- R_ACK0 : if(div_cnt == 7'd124 && bps_cnt == 4'd0)
- next_state = R_ADDR_H_B;
- else
- next_state = R_ACK0;
- R_ADDR_H_B : if(div_cnt == 7'd124 && bps_cnt == 4'd8)
- next_state = R_ACK1;
- else
- next_state = R_ADDR_H_B;
- R_ACK1 : if(div_cnt == 7'd124 && bps_cnt == 4'd0)
- next_state = R_ADDR_L_B;
- else
- next_state = R_ACK1;
- R_ADDR_L_B : if(div_cnt == 7'd124 && bps_cnt == 4'd8)
- next_state = R_ACK2;
- else
- next_state = R_ADDR_L_B;
- R_ACK2 : if(div_cnt == 7'd124)
- next_state = R_START1;
- else
- next_state = R_ACK2;
- R_START1 : if(div_cnt == 7'd124 && bps_cnt == 4'd0)
- next_state = R_CTRL_B1;
- else
- next_state = R_START1;
- R_CTRL_B1 : if(div_cnt == 7'd124 && bps_cnt == 4'd8)
- next_state = R_ACK3;
- else
- next_state = R_CTRL_B1;
- R_ACK3 : if(div_cnt == 7'd124 && bps_cnt == 4'd0)
- next_state = R_DATA;
- else
- next_state = R_ACK3;
- R_DATA : if(div_cnt == 7'd124 && bps_cnt == 4'd8)
- next_state = R_NO_ACK;
- else
- next_state = R_DATA;
- R_NO_ACK : if(div_cnt == 7'd124 && bps_cnt == 4'd0)
- next_state = R_STOP;
- else
- next_state = R_NO_ACK;
- R_STOP : if(div_cnt == 7'd124 && bps_cnt == 4'd1)
- next_state = R_IDLE;
- else
- next_state = R_STOP;
- R_IDLE : next_state = R_IDLE;
- default :;
- endcase
- end
- always@(posedge clk or negedge rst)
- begin
- if(!rst)
- begin
- div_en <= 1'b0;
- bps_en <= 1'b0;
- sda_en <= 1'b0;
- scl <=1'b0;
- end
- else
- begin
- case(present_state)
- W_START : begin
- div_en <= 1'b1;
- bps_en <= 1'b1;
- sda_en <= 1'b1;
- scl <= 1'b1;
- end
- W_CTRL_B : begin
- div_en <= 1'b1;
- bps_en <= 1'b1;
- sda_en <= 1'b1;
- scl <= (7'd31 < div_cnt && div_cnt < 7'd93) ? 1'b1 : 1'b0;
- end
- W_ACK0 : begin
- div_en <= 1'b1;
- bps_en <= 1'b1;
- sda_en <= 1'b0;
- scl <= (7'd31 < div_cnt && div_cnt < 7'd93) ? 1'b1 : 1'b0;
- end
- W_ADDR_H_B: begin
- div_en <= 1'b1;
- bps_en <= 1'b1;
- sda_en <= 1'b1;
- scl <= (7'd31 < div_cnt && div_cnt < 7'd93) ? 1'b1 : 1'b0;
- end
- W_ACK1 : begin
- div_en <= 1'b1;
- bps_en <= 1'b1;
- sda_en <= 1'b0;
- scl <= (7'd31 < div_cnt && div_cnt < 7'd93) ? 1'b1 : 1'b0;
- end
- W_ADDR_L_B : begin
- div_en <= 1'b1;
- bps_en <= 1'b1;
- sda_en <= 1'b1;
- scl <= (7'd31 < div_cnt && div_cnt < 7'd93) ? 1'b1 : 1'b0;
- end
- W_ACK2 : begin
- div_en <= 1'b1;
- bps_en <= 1'b1;
- sda_en <= 1'b0;
- scl <= (7'd31 < div_cnt && div_cnt < 7'd93) ? 1'b1 : 1'b0;
- end
- W_DATA : begin
- div_en <= 1'b1;
- bps_en <= 1'b1;
- sda_en <= 1'b1;
- scl <= (7'd31 < div_cnt && div_cnt < 7'd93) ? 1'b1 : 1'b0;
- end
- W_ACK3 : begin
- div_en <= 1'b1;
- bps_en <= 1'b1;
- sda_en <= 1'b0;
- scl <= (7'd31 < div_cnt && div_cnt < 7'd93) ? 1'b1 : 1'b0;
- end
- W_STOP : begin
- div_en <= 1'b1;
- bps_en <= 1'b1;
- sda_en <= 1'b1;
- scl <= 1'b1;
- end
- W_IDLE : begin
- div_en <= 1'b1;
- bps_en <= 1'b1;
- da_en <= 1'b1;
- scl <= 1'b1;
- end
- R_START0 : begin
- div_en <= 1'b1;
- bps_en <= 1'b1;
- sda_en <= 1'b1;
- scl <= 1'b1;
- end
- R_CTRL_B0 : begin
- div_en <= 1'b1;
- bps_en <= 1'b1;
- sda_en <= 1'b1;
- scl <= (7'd31 < div_cnt && div_cnt < 7'd93) ? 1'b1 : 1'b0;
- end
- R_ACK0 : begin
- div_en <= 1'b1;
- bps_en <= 1'b1;
- sda_en <= 1'b0;
- scl <= (7'd31 < div_cnt && div_cnt < 7'd93) ? 1'b1 : 1'b0;
- end
- R_ADDR_H_B : begin
- div_en <= 1'b1;
- bps_en <= 1'b1;
- sda_en <= 1'b1;
- scl <= (7'd31 < div_cnt && div_cnt < 7'd93) ? 1'b1 : 1'b0;
- end
- R_ACK1 : begin
- div_en <= 1'b1;
- bps_en <= 1'b1;
- sda_en <= 1'b0;
- scl <= (7'd31 < div_cnt && div_cnt < 7'd93) ? 1'b1 : 1'b0;
- end
- R_ADDR_L_B : begin
- div_en <= 1'b1;
- bps_en <= 1'b1;
- sda_en <= 1'b1;
- scl <= (7'd31 < div_cnt && div_cnt < 7'd93) ? 1'b1 : 1'b0;
- end
- R_ACK2 : begin
- div_en <= 1'b1;
- bps_en <= 1'b0;
- sda_en <= 1'b0;
- scl <= (7'd31 < div_cnt && div_cnt < 7'd93) ? 1'b1 : 1'b0;
- end
- R_START1 : begin
- div_en <= 1'b1;
- bps_en <= 1'b1;
- sda_en <= 1'b1;
- scl <= 1'b1;
- end
- R_CTRL_B1 : begin
- div_en <= 1'b1;
- bps_en <= 1'b1;
- sda_en <= 1'b1;
- scl <= (7'd31 < div_cnt && div_cnt < 7'd93) ? 1'b1 : 1'b0;
- end
- R_ACK3 : begin
- div_en <= 1'b1;
- bps_en <= 1'b1;
- sda_en <= 1'b0;
- scl <= (7'd31 < div_cnt && div_cnt < 7'd93) ? 1'b1 : 1'b0;
- end
- R_DATA : begin
- div_en <= 1'b1;
- bps_en <= 1'b1;
- sda_en <= 1'b0;
- scl <= (7'd31 < div_cnt && div_cnt < 7'd93) ? 1'b1 : 1'b0;
- end
- R_NO_ACK : begin
- div_en <= 1'b1;
- bps_en <= 1'b1;
- sda_en <= 1'b0;
- scl <= (7'd31 < div_cnt && div_cnt < 7'd93) ? 1'b1 : 1'b0;
- end
- R_STOP : begin
- div_en <= 1'b1;
- bps_en <= 1'b1;
- sda_en <= 1'b1;
- scl <= 1'b1;
- end
- R_IDLE : begin
- div_en <= 1'b0;
- bps_en <= 1'b0;
- sda_en <= 1'b1;
- scl <= 1'b1;
- end
- default :;
- endcase
- end
- end
- //=====================================================
- //分頻計數器
- //=====================================================
- always@(posedge clk or negedge rst)
- begin
- if(!rst) div_cnt <= 7'd0;
- else if(div_en)
- begin
- if(div_cnt == 7'd124) div_cnt <= 7'd0;
- else div_cnt <= div_cnt + 7'd1;
- end
- else div_cnt <= 7'd0;
- end
- always@(posedge clk or negedge rst)
- begin
- if(!rst) bps_cnt <= 4'd0;
- else if(bps_en)
- begin
- if(div_cnt == 7'd124)
- begin
- if(bps_cnt == 4'd8) bps_cnt <= 4'd0;
- else bps_cnt <= bps_cnt + 4'd1;
- end
- else bps_cnt <= bps_cnt;
- end
- else bps_cnt <= 4'd0;
- end
- //=====================================================
- // reg_sda
- //=====================================================
- always@(posedge clk or negedge rst)
- begin
- if(!rst)
- reg_sda <= 0;
- else if(present_state == W_START || present_state == R_START0 || present_state == R_START1)
- begin
- if(div_cnt < 7'd62)
- reg_sda <= 1'b1;
- else
- reg_sda <= 1'b0;
- end
- else if(present_state == W_CTRL_B)
- begin
- case(bps_cnt)
- 4'd1 : reg_sda <= w_ctrl[6];
- 4'd2 : reg_sda <= w_ctrl[5];
- 4'd3 : reg_sda <= w_ctrl[4];
- 4'd4 : reg_sda <= w_ctrl[3];
- 4'd5 : reg_sda <= w_ctrl[2];
- 4'd6 : reg_sda <= w_ctrl[1];
- 4'd7 : reg_sda <= w_ctrl[0];
- 4'd8 : reg_sda <= 1'b0;
- default :;
- endcase
- end
- else if(present_state == W_ADDR_H_B)
- begin
- case(bps_cnt)
- 4'd1 : reg_sda <= w_h_addr[7];
- 4'd2 : reg_sda <= w_h_addr[6];
- 4'd3 : reg_sda <= w_h_addr[5];
- 4'd4 : reg_sda <= w_h_addr[4];
- 4'd5 : reg_sda <= w_h_addr[3];
- 4'd6 : reg_sda <= w_h_addr[2];
- 4'd7 : reg_sda <= w_h_addr[1];
- 4'd8 : reg_sda <= w_h_addr[0];
- default :;
- endcase
- end
- else if(present_state == W_ADDR_L_B)
- begin
- case(bps_cnt)
- 4'd1 : reg_sda <= w_l_addr[7];
- 4'd2 : reg_sda <= w_l_addr[6];
- 4'd3 : reg_sda <= w_l_addr[5];
- 4'd4 : reg_sda <= w_l_addr[4];
- 4'd5 : reg_sda <= w_l_addr[3];
- 4'd6 : reg_sda <= w_l_addr[2];
- 4'd7 : reg_sda <= w_l_addr[1];
- 4'd8 : reg_sda <= w_l_addr[0];
- default :;
- endcase
- end
- else if(present_state == W_DATA)
- begin
- case(bps_cnt)
- 4'd1 : reg_sda <= w_data[7];
- 4'd2 : reg_sda <= w_data[6];
- 4'd3 : reg_sda <= w_data[5];
- 4'd4 : reg_sda <= w_data[4];
- 4'd5 : reg_sda <= w_data[3];
- 4'd6 : reg_sda <= w_data[2];
- 4'd7 : reg_sda <= w_data[1];
- 4'd8 : reg_sda <= w_data[0];
- default :;
- endcase
- end
- else if(present_state == R_CTRL_B0)
- begin
- case(bps_cnt)
- 4'd1 : reg_sda <= r_ctrl[6];
- 4'd2 : reg_sda <= r_ctrl[5];
- 4'd3 : reg_sda <= r_ctrl[4];
- 4'd4 : reg_sda <= r_ctrl[3];
- 4'd5 : reg_sda <= r_ctrl[2];
- 4'd6 : reg_sda <= r_ctrl[1];
- 4'd7 : reg_sda <= r_ctrl[0];
- 4'd8 : reg_sda <= 1'b0;
- default :;
- endcase
- end
- else if(present_state == R_ADDR_H_B)
- begin
- case(bps_cnt)
- 4'd1 : reg_sda <= r_h_addr[7];
- 4'd2 : reg_sda <= r_h_addr[6];
- 4'd3 : reg_sda <= r_h_addr[5];
- 4'd4 : reg_sda <= r_h_addr[4];
- 4'd5 : reg_sda <= r_h_addr[3];
- 4'd6 : reg_sda <= r_h_addr[2];
- 4'd7 : reg_sda <= r_h_addr[1];
- 4'd8 : reg_sda <= r_h_addr[0];
- default :;
- endcase
- end
- else if(present_state == R_ADDR_L_B)
- begin
- case(bps_cnt)
- 4'd1 : reg_sda <= r_l_addr[7];
- 4'd2 : reg_sda <= r_l_addr[6];
- 4'd3 : reg_sda <= r_l_addr[5];
- 4'd4 : reg_sda <= r_l_addr[4];
- 4'd5 : reg_sda <= r_l_addr[3];
- 4'd6 : reg_sda <= r_l_addr[2];
- 4'd7 : reg_sda <= r_l_addr[1];
- 4'd8 : reg_sda <= r_l_addr[0];
- default :;
- endcase
- end
- else if(present_state == R_CTRL_B1)
- begin
- case(bps_cnt)
- 4'd1 : reg_sda <= r_ctrl[6];
- 4'd2 : reg_sda <= r_ctrl[5];
- 4'd3 : reg_sda <= r_ctrl[4];
- 4'd4 : reg_sda <= r_ctrl[3];
- 4'd5 : reg_sda <= r_ctrl[2];
- 4'd6 : reg_sda <= r_ctrl[1];
- 4'd7 : reg_sda <= r_ctrl[0];
- 4'd8 : reg_sda <= 1'b1;
- default :;
- endcase
- end
- else if(present_state == W_STOP || present_state == R_STOP)
- begin
- if(div_cnt < 7'd62)
- reg_sda <= 1'b0;
- else
- reg_sda <= 1'b1;
- end
- else if(present_state == W_IDLE || present_state == R_IDLE)
- reg_sda <= 1'b1;
- else
- reg_sda <= reg_sda;
- end
- //=====================================================
- // r_data
- //=====================================================
- always@(posedge clk or negedge rst)
- begin
- if(!rst)
- r_data <= 0;
- else if(present_state == R_DATA)
- begin
- case(bps_cnt)
- 4'd1 : r_data[7] <= sda;
- 4'd2 : r_data[6] <= sda;
- 4'd3 : r_data[5] <= sda;
- 4'd4 : r_data[4] <= sda;
- 4'd5 : r_data[3] <= sda;
- 4'd6 : r_data[2] <= sda;
- 4'd7 : r_data[1] <= sda;
- 4'd8 : r_data[0] <= sda;
- default :;
- endcase
- end
- else
- r_data <= r_data;
- end
- //=====================================================
- //三態閘
- //=====================================================
- assign sda = sda_en ? reg_sda : 1'bz;
- //=====================================================
- endmodule
- `timescale 1ns/1ps
- module I2C_test1;
- reg clk;
- reg rst;
- reg [6:0] w_ctrl;
- reg [7:0] w_h_addr;
- reg [7:0] w_l_addr;
- reg [7:0] w_data;
- reg [6:0] r_ctrl;
- reg [7:0] r_h_addr;
- reg [7:0] r_l_addr;
- wire [7:0] r_data;
- wire scl;
- wire sda;
- I2C I2C(
- .clk(clk),
- .rst(rst),
- .w_ctrl(w_ctrl),
- .w_h_addr(w_h_addr),
- .w_l_addr(w_l_addr),
- .w_data(w_data),
- .r_ctrl(r_ctrl),
- .r_h_addr(r_h_addr),
- .r_l_addr(r_l_addr),
- .r_data(r_data),
- .scl(scl),
- .sda(sda)
- );
- // EEPROM仿真模型
- M24LC64 M24LC64(
- .A0(1'b0),
- .A1(1'b0),
- .A2(1'b0),
- .WP(1'b0),
- .SDA(sda),
- .SCL(scl),
- .RESET(!rst)
- );
- initial
- begin
- clk = 1'b1;
- forever #10 clk = ~clk;
- end
- initial
- begin
- #0
- rst = 1'b0;
- w_ctrl = 7'b101_0000;
- w_h_addr = 8'b0000_0000;
- w_l_addr = 8'b0000_0001;
- w_data = 8'b11000101;
- #1
- rst = 1'b1;
- r_ctrl = 7'b101_0000;
- r_h_addr = 8'b0000_0000;
- r_l_addr = 8'b0000_0001;
- #1000000
- $stop;
- #10
- $finish;
- end
- endmodule
因EEPROM仿真模型與真實器件相同,當輸出為高電平時設置為高阻態,寫入數據為11000101,輸出數據為zz000z0z,故
輸出正確!