Skip to content

Commit 4a41d47

Browse files
committed
Add I2C single reg module
Signed-off-by: Alex Forencich <[email protected]>
1 parent ad307a0 commit 4a41d47

File tree

1 file changed

+267
-0
lines changed

1 file changed

+267
-0
lines changed

rtl/i2c_single_reg.v

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
/*
2+
3+
Copyright (c) 2023 Alex Forencich
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.
22+
23+
*/
24+
25+
// Language: Verilog 2001
26+
27+
`resetall
28+
`timescale 1ns / 1ps
29+
`default_nettype none
30+
31+
/*
32+
* I2C single register
33+
*/
34+
module i2c_single_reg #(
35+
parameter FILTER_LEN = 4,
36+
parameter DEV_ADDR = 7'h70
37+
)
38+
(
39+
input wire clk,
40+
input wire rst,
41+
42+
/*
43+
* I2C interface
44+
*/
45+
input wire scl_i,
46+
output wire scl_o,
47+
output wire scl_t,
48+
input wire sda_i,
49+
output wire sda_o,
50+
output wire sda_t,
51+
52+
/*
53+
* Data register
54+
*/
55+
input wire [7:0] data_in,
56+
input wire data_latch,
57+
output wire [7:0] data_out
58+
);
59+
60+
localparam [4:0]
61+
STATE_IDLE = 4'd0,
62+
STATE_ADDRESS = 4'd1,
63+
STATE_ACK = 4'd2,
64+
STATE_WRITE_1 = 4'd3,
65+
STATE_WRITE_2 = 4'd4,
66+
STATE_READ_1 = 4'd5,
67+
STATE_READ_2 = 4'd6,
68+
STATE_READ_3 = 4'd7;
69+
70+
reg [4:0] state_reg = STATE_IDLE;
71+
72+
reg [7:0] data_reg = 8'd0;
73+
reg [7:0] shift_reg = 8'd0;
74+
75+
reg mode_read_reg = 1'b0;
76+
77+
reg [3:0] bit_count_reg = 4'd0;
78+
79+
reg [FILTER_LEN-1:0] scl_i_filter_reg = {FILTER_LEN{1'b1}};
80+
reg [FILTER_LEN-1:0] sda_i_filter_reg = {FILTER_LEN{1'b1}};
81+
82+
reg scl_i_reg = 1'b1;
83+
reg sda_i_reg = 1'b1;
84+
85+
reg sda_o_reg = 1'b1;
86+
87+
reg last_scl_i_reg = 1'b1;
88+
reg last_sda_i_reg = 1'b1;
89+
90+
assign scl_o = 1'b1;
91+
assign scl_t = 1'b1;
92+
assign sda_o = sda_o_reg;
93+
assign sda_t = sda_o_reg;
94+
95+
assign data_out = data_reg;
96+
97+
wire scl_posedge = scl_i_reg && !last_scl_i_reg;
98+
wire scl_negedge = !scl_i_reg && last_scl_i_reg;
99+
wire sda_posedge = sda_i_reg && !last_sda_i_reg;
100+
wire sda_negedge = !sda_i_reg && last_sda_i_reg;
101+
102+
wire start_bit = sda_negedge && scl_i_reg;
103+
wire stop_bit = sda_posedge && scl_i_reg;
104+
105+
always @(posedge clk) begin
106+
107+
if (start_bit) begin
108+
sda_o_reg <= 1'b1;
109+
110+
bit_count_reg = 4'd7;
111+
state_reg <= STATE_ADDRESS;
112+
end else if (stop_bit) begin
113+
sda_o_reg <= 1'b1;
114+
115+
state_reg <= STATE_IDLE;
116+
end else begin
117+
case (state_reg)
118+
STATE_IDLE: begin
119+
// line idle
120+
sda_o_reg <= 1'b1;
121+
122+
state_reg <= STATE_IDLE;
123+
end
124+
STATE_ADDRESS: begin
125+
// read address
126+
sda_o_reg <= 1'b1;
127+
128+
if (scl_posedge) begin
129+
if (bit_count_reg > 0) begin
130+
// shift in address
131+
bit_count_reg <= bit_count_reg-1;
132+
shift_reg <= {shift_reg[6:0], sda_i_reg};
133+
state_reg <= STATE_ADDRESS;
134+
end else begin
135+
// check address
136+
mode_read_reg <= sda_i_reg;
137+
if (shift_reg[6:0] == DEV_ADDR) begin
138+
// it's a match, send ACK
139+
state_reg <= STATE_ACK;
140+
end else begin
141+
// no match, return to idle
142+
state_reg <= STATE_IDLE;
143+
end
144+
end
145+
end else begin
146+
state_reg <= STATE_ADDRESS;
147+
end
148+
end
149+
STATE_ACK: begin
150+
// send ACK bit
151+
if (scl_negedge) begin
152+
sda_o_reg <= 1'b0;
153+
bit_count_reg <= 4'd7;
154+
if (mode_read_reg) begin
155+
// reading
156+
shift_reg <= data_reg;
157+
state_reg <= STATE_READ_1;
158+
end else begin
159+
// writing
160+
state_reg <= STATE_WRITE_1;
161+
end
162+
end else begin
163+
state_reg <= STATE_ACK;
164+
end
165+
end
166+
STATE_WRITE_1: begin
167+
// write data byte
168+
if (scl_negedge) begin
169+
sda_o_reg <= 1'b1;
170+
state_reg <= STATE_WRITE_2;
171+
end else begin
172+
state_reg <= STATE_WRITE_1;
173+
end
174+
end
175+
STATE_WRITE_2: begin
176+
// write data byte
177+
sda_o_reg <= 1'b1;
178+
if (scl_posedge) begin
179+
// shift in data bit
180+
shift_reg <= {shift_reg[6:0], sda_i_reg};
181+
if (bit_count_reg > 0) begin
182+
bit_count_reg <= bit_count_reg-1;
183+
state_reg <= STATE_WRITE_2;
184+
end else begin
185+
data_reg <= {shift_reg[6:0], sda_i_reg};
186+
state_reg <= STATE_ACK;
187+
end
188+
end else begin
189+
state_reg <= STATE_WRITE_2;
190+
end
191+
end
192+
STATE_READ_1: begin
193+
// read data byte
194+
if (scl_negedge) begin
195+
// shift out data bit
196+
{sda_o_reg, shift_reg} = {shift_reg, sda_i_reg};
197+
198+
if (bit_count_reg > 0) begin
199+
bit_count_reg = bit_count_reg-1;
200+
state_reg = STATE_READ_1;
201+
end else begin
202+
state_reg = STATE_READ_2;
203+
end
204+
end else begin
205+
state_reg = STATE_READ_1;
206+
end
207+
end
208+
STATE_READ_2: begin
209+
// read ACK bit
210+
if (scl_negedge) begin
211+
// release SDA
212+
sda_o_reg <= 1'b1;
213+
state_reg <= STATE_READ_3;
214+
end else begin
215+
state_reg <= STATE_READ_2;
216+
end
217+
end
218+
STATE_READ_3: begin
219+
// read ACK bit
220+
if (scl_posedge) begin
221+
if (sda_i_reg) begin
222+
// NACK, return to idle
223+
state_reg <= STATE_IDLE;
224+
end else begin
225+
// ACK, read another byte
226+
bit_count_reg <= 4'd7;
227+
shift_reg <= data_reg;
228+
state_reg <= STATE_READ_1;
229+
end
230+
end else begin
231+
state_reg <= STATE_READ_3;
232+
end
233+
end
234+
endcase
235+
end
236+
237+
if (data_latch) begin
238+
data_reg <= data_in;
239+
end
240+
241+
scl_i_filter_reg <= (scl_i_filter_reg << 1) | scl_i;
242+
sda_i_filter_reg <= (sda_i_filter_reg << 1) | sda_i;
243+
244+
if (scl_i_filter_reg == {FILTER_LEN{1'b1}}) begin
245+
scl_i_reg <= 1'b1;
246+
end else if (scl_i_filter_reg == {FILTER_LEN{1'b0}}) begin
247+
scl_i_reg <= 1'b0;
248+
end
249+
250+
if (sda_i_filter_reg == {FILTER_LEN{1'b1}}) begin
251+
sda_i_reg <= 1'b1;
252+
end else if (sda_i_filter_reg == {FILTER_LEN{1'b0}}) begin
253+
sda_i_reg <= 1'b0;
254+
end
255+
256+
last_scl_i_reg <= scl_i_reg;
257+
last_sda_i_reg <= sda_i_reg;
258+
259+
if (rst) begin
260+
state_reg <= STATE_IDLE;
261+
sda_o_reg <= 1'b1;
262+
end
263+
end
264+
265+
endmodule
266+
267+
`resetall

0 commit comments

Comments
 (0)