-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathI2C_to_GPIO.v
226 lines (196 loc) · 7.92 KB
/
I2C_to_GPIO.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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
/************************************************************************************************************
I2C to GPIO Port expander. Date: December 21, 2006.
************************************************************************************************************/
`timescale 1us/10ns
/* Top module */
module I2C_to_GPIO ( clk, sda_ext, sclk_ext, GPIO_input, GPIO_output);
input clk;
inout sda_ext; // Bidirectional SDA
input sclk_ext; // Bidirectional SCLK
input [7:0] GPIO_input;
output [7:0] GPIO_output;
parameter slave_address = 7'h00; // can be changed as per the protocol
parameter n = 8;
reg start_stop; // Indicated if the device has detected a start and is busy in some operation
reg start, stop; // Goes high temperarily when a start or stop is detectd
reg some_other_device; // to reset start_stop, when the adress on the bus is different from the device address;
reg repeat_start; // to reset everything if repeat start is detected.
reg write_flag; // to set when write oper begins
reg reset; // Used to reset the start , stop registers
reg sda_out; // To force the SDA output Low. Remains tristated for sending High Logic.
reg done; // To Indicate data transfer done
reg [3:0]count; // Down Counter ( 7 to 0 + ack bit )
reg [n-1:0] GPIO_output; // parallel store
reg [n-1:0] GPIO_input_reg; // Stores the Values at the input pins before sending to master.
reg data_or_address; // high for data, low for address
reg sda_ack, read_oper, add_is_matching; // high for ack; high for master read; high for address matching
reg start_reset = 0;
/********************************************************************************************************/
wire sda_int_or, sda_int_and;
wire sclk_int_or, sclk_int_and;
reg sda, sclk;
reg [2:0] sclk_reg;
reg [2:0] sda_reg;
/*
wire oscena;
wire osc;
internal_osc int_osc (oscena, osc);
assign oscena = 1'b1;
*/
assign sclk_int_or = sclk_reg[2] | sclk_reg[1] | sclk_reg[0];
assign sclk_int_and = sclk_reg[2] & sclk_reg[1] & sclk_reg[0];
assign sda_int_or = sda_reg[2] | sda_reg[1] | sda_reg[0];
assign sda_int_and = sda_reg[2] & sda_reg[1] & sda_reg[0];
always @ (posedge clk) begin
sclk_reg <= {sclk_reg[1:0], sclk_ext};
sda_reg <= {sda_reg[1:0], sda_ext};
if (sda & ~sda_int_or) sda <= 1'b0;
else if (~sda & sda_int_and) sda <= 1'b1;
if (sclk & ~sclk_int_or) sclk <= 1'b0;
else if (~sclk & sclk_int_and) sclk <= 1'b1;
end
/* Detecting STOP Condition on the SDA Bus */
always @ (posedge sda or posedge reset)
if (reset) begin
stop <= 0;
end else if (sclk) begin
stop <= 1;
end
/* Detecting START Condition on the SDA Bus */
always @ (negedge sda or posedge reset)
if (reset) begin
start <= 0;
end else if (sclk) begin
start <= 1;
end
/*
always @ (negedge sda or posedge start_reset)
if (start_reset) begin
start <= 0;
end else begin
start <= sclk;
end
always @ (negedge sclk)
start_reset <= start;
*/
/* Tracking START_STOP related status of the device */
always @ (negedge sclk)
if (start_stop & start) begin
repeat_start <= 1;
reset <= 1;
end else if (start) begin
start_stop <= 1;
reset <= 1;
repeat_start <= 0;
end else if (stop) begin
start_stop <= 0;
reset <= 1;
repeat_start <= 0;
end else if (some_other_device) begin
start_stop <= 0;
repeat_start <= 1;
end else begin
reset <= 0;
repeat_start <= 0;
end
/***********************************************************************************************************/
/* Counting of bits. First eight bits are data and the last one is ack. */
wire sda_is_ack = count[3] & count [2]; // High for ack , count = 1111
/* Down Counter, 1111 is ack */
always @ (negedge sclk)
if (start) begin
count <= 4'h7; // it would decreae to 7 on the first pos edge of sclk.
end
else if (start_stop) begin
if (sda_is_ack) begin // count is restored to 7
count <= 4'h7;
end else begin
count <= count - 4'h1; // count is decremented until zero.
end
end
/***********************************************************************************************************/
/* Taking the Inputs from SDA line and Comparing with Address */
always @(posedge sclk)
if (start_stop) begin
if (~sda_is_ack) begin
sda_ack <= 0;
if (~data_or_address | repeat_start) begin
if (repeat_start) begin
add_is_matching <= 1; // high means matching
data_or_address <= 0; // default is address
some_other_device <= 0; // High means that device address does not match with SDA data
write_flag <= 0;
done <= 0; // Low means Transfer is pending
end
if (count == 7 & sda != slave_address[6]) add_is_matching <= 0;
if (count == 6 & sda != slave_address[5]) add_is_matching <= 0;
if (count == 5 & sda != slave_address[4]) add_is_matching <= 0;
if (count == 4 & sda != slave_address[3]) add_is_matching <= 0;
if (count == 3 & sda != slave_address[2]) add_is_matching <= 0;
if (count == 2 & sda != slave_address[1]) add_is_matching <= 0;
if (count == 1 & sda != slave_address[0]) add_is_matching <= 0;
if (count == 0 & ~sda )begin
read_oper <= 0;
data_or_address <= 1;
end else if (count == 0) begin
read_oper <= 1;
data_or_address <= 1;
end
end else if (~read_oper & ~done) begin
if (count == 7) GPIO_output[7] <= sda;
if (count == 6) GPIO_output[6] <= sda;
if (count == 5) GPIO_output[5] <= sda;
if (count == 4) GPIO_output[4] <= sda;
if (count == 3) GPIO_output[3] <= sda;
if (count == 2) GPIO_output[2] <= sda;
if (count == 1) GPIO_output[1] <= sda;
if (count == 0) begin
GPIO_output[0] <= sda;
done <= 1;
end
end else begin
if (count == 0) done <= 1;
end
end else begin
if (add_is_matching) begin
sda_ack <= 1;
if (read_oper) write_flag <= 1;
end else begin
data_or_address <= 0;
some_other_device <= 1;
end
end
end else begin
add_is_matching <= 1; // high means matching
sda_ack <= 0; // high means ack sent, drives sda low
data_or_address <= 0; // default is address
some_other_device <= 0;
write_flag <= 0;
done <= 0;
end
/***********************************************************************************************************/
/* Writing ACK and DATA on the SDA Line*/
wire ack_flag = (count[3]|count[2]|count[1]|count[0]); //becomes low when count is 0
always@(negedge sclk)
if (start_stop) begin
if (~ack_flag) begin
if ( add_is_matching & ~write_flag)begin sda_out <= 1'b0;
end else begin sda_out <= 1'bz;
end
if (read_oper & ~done) GPIO_input_reg <= GPIO_input;
end else if (read_oper & data_or_address & ~done) begin
if (ack_flag) begin
if (~GPIO_input_reg[7]) begin
sda_out <= 1'b0;
end else begin
sda_out <= 1'bz;
end
GPIO_input_reg <= GPIO_input_reg << 1;
end
end else begin
sda_out <= 1'bz;
end
end
wire sda_ext;
assign sda_ext = sda_out;
endmodule