-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathi2c_master.v
111 lines (93 loc) · 2.45 KB
/
i2c_master.v
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// taken and tweaked from MiSTer sys/
module i2c_master
(
input CLK,
input I2C_START,
input I2C_READ,
input [6:0] I2C_ADDR,
input [7:0] I2C_SUBADDR,
input [7:0] I2C_WDATA,
output [7:0] I2C_RDATA,
output reg I2C_END = 1,
output reg I2C_ACK = 0,
//I2C bus
inout I2C_SCL,
inout I2C_SDA
);
// Clock Setting
parameter CLK_Freq = 50_000_000; // 50 MHz
parameter I2C_Freq = 400_000; // 400 KHz
localparam I2C_FreqX2 = I2C_Freq*2;
reg I2C_CLOCK;
reg [31:0] cnt;
wire [31:0] cnt_next = cnt + I2C_FreqX2;
always @(posedge CLK) begin
cnt <= cnt_next;
if(cnt_next >= CLK_Freq) begin
cnt <= cnt_next - CLK_Freq;
I2C_CLOCK <= ~I2C_CLOCK;
end
end
reg SCLK;
reg [11:0] SDO;
reg [0:7] rdata;
reg [6:0] SD_COUNTER;
reg [0:42] SD;
assign I2C_SCL = (SCLK | I2C_CLOCK) ? 1'bZ : 1'b0;
assign I2C_SDA = ((CLK_Freq/I2C_Freq) > 250 ? SDO[11] : ((CLK_Freq/I2C_Freq) > 20 ? SDO[7] : SDO[3])) ? 1'bZ : 1'b0;
initial begin
SD_COUNTER = 'b1111111;
SD = {40'hFFFFFFFFFF, 3'b111};
SCLK = 1;
SDO = 12'hFFF;
end
assign I2C_RDATA = rdata;
always @(posedge CLK) begin
reg old_clk;
reg old_st;
reg rd;
reg sda_in;
old_clk <= I2C_CLOCK;
old_st <= I2C_START;
sda_in <= I2C_SDA;
// delay to make sure SDA changed while SCL is stabilized at low
if(old_clk && ~I2C_CLOCK && ~SD_COUNTER[6]) SDO[0] <= SD[SD_COUNTER[5:0]];
SDO[11:1] <= SDO[10:0];
if(~old_st && I2C_START) begin
SCLK <= 1;
SDO <= 12'hFFF;
I2C_ACK <= 0;
I2C_END <= 0;
rd <= I2C_READ;
if(I2C_READ) SD[0:42] <= {2'b10, I2C_ADDR, 1'b0, 1'b1, I2C_SUBADDR, 3'b110, I2C_ADDR, 1'b1, 1'b1, 8'b11111111, 4'b1011};
else SD[0:31] <= {2'b10, I2C_ADDR, 1'b0, 1'b1, I2C_SUBADDR, 1'b1, I2C_WDATA, 4'b1011};
SD_COUNTER <= 0;
end else begin
if(~old_clk && I2C_CLOCK && ~SD_COUNTER[6]) begin
SD_COUNTER <= SD_COUNTER + 6'd1;
case(SD_COUNTER)
01: SCLK <= 0;
10: I2C_ACK <= ~sda_in;
19: I2C_ACK <= ~sda_in;
20: if (rd) SCLK <= 1; // repeated start
21: if (rd) SCLK <= 0;
28: if (~rd) I2C_ACK <= ~sda_in;
29: if (~rd) SCLK <= 1;
30: if (rd) I2C_ACK <= ~sda_in;
32: if (~rd) begin
I2C_END <= 1;
SD_COUNTER <= 64;
end
40: SCLK <= 1;
42: begin
I2C_END <= 1;
SD_COUNTER <= 64;
end
64: SCLK <= 1;
default: ;
endcase
if(SD_COUNTER >= 31 && SD_COUNTER <= 38) rdata[SD_COUNTER[5:0]-31] <= sda_in;
end
end
end
endmodule