Skip to content

Commit 51e4dcc

Browse files
Add glitch filters to I^2C inputs
I^2C Fast-mode requires a 50 ns glitch filter on SCL and SDA inputs. This is an attempt at implementing such a filter. Note, as it is operating in the digital domain and on an unregistered input, it could fall victim to inexact clock division and metastability. Happily the clock divides nicely at our current 40 MHz system clock.
1 parent fe432d4 commit 51e4dcc

3 files changed

Lines changed: 125 additions & 2 deletions

File tree

rtl/system/i2c_filter.sv

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Copyright lowRISC contributors.
2+
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
// I^2C Fast-mode spike suppression filter
6+
//
7+
// Suppress input spikes with a pulse width less than or equal to 50 ns (t_sp).
8+
// To be used on SDA and SCL inputs that are to operate at Fast-mode or above.
9+
// See I^2C User Manual for details.
10+
//
11+
// The filter is implemented as a MUX that selects between a registered copy
12+
// of the previous output and a combinatorial passthrough of the current input.
13+
//
14+
// The pulse width filter threshold is rounded down to whole clock cycles.
15+
// Pulses with a width equal to or less than the threshold (in clock cycles)
16+
// will NOT be passed through to the filter output.
17+
//
18+
// Note that neither metastability nor inexact clock cycle division have been
19+
// accounted for in the design of this filter.
20+
21+
module i2c_filter #(
22+
parameter int unsigned SysClkFreq = 40_000_000
23+
) (
24+
input clk_i,
25+
input rst_ni,
26+
27+
input raw_i,
28+
output filtered_o
29+
);
30+
31+
// Calculate the number whole clock cycles that fit in a 50 ns period.
32+
// Round down rather than to closest to avoid violating the 50 ns
33+
// maximum pulse width specification for the filter.
34+
localparam int unsigned FilterCycles = int'($floor(50e-9 * SysClkFreq));
35+
36+
// Previous filtered output
37+
logic prev_out;
38+
// Width counter. Implement as shift register to keep logic
39+
// on the path from the counter to the datapath to a minimum.
40+
logic [FilterCycles-1:0] shift_counter_plus1;
41+
logic [FilterCycles-1:0] shift_counter;
42+
43+
// MUX select signal
44+
logic select;
45+
// Internal filter output signal
46+
logic result;
47+
48+
// Register output of filter for use next cycle
49+
always_ff @(posedge clk_i or negedge rst_ni) begin
50+
if (!rst_ni) begin
51+
prev_out <= '0;
52+
end else begin
53+
prev_out <= result;
54+
end
55+
end
56+
57+
// Implement shift-counter
58+
always_ff @(posedge clk_i or negedge rst_ni) begin
59+
if (!rst_ni) begin
60+
shift_counter <= '0;
61+
end else begin
62+
if (raw_i == prev_out) begin
63+
// Clear counter
64+
shift_counter <= '0;
65+
end else begin
66+
// Increment counter
67+
shift_counter <= shift_counter_plus1;
68+
end
69+
end
70+
end
71+
72+
// Incremented counter signal (shift right, filling LSB with a 'one').
73+
// Exact implementation depends on counter width.
74+
// Have to do conditional generation outside of an always block.
75+
if (FilterCycles == 1) begin
76+
// Only one bit, so no shifting
77+
assign shift_counter_plus1 = 1'b1;
78+
end else if (FilterCycles == 2) begin
79+
// Two bits, so only one bit gets shifted
80+
assign shift_counter_plus1 = {shift_counter[0], 1'b1};
81+
end else begin
82+
// Many bits, so a range get shifted
83+
assign shift_counter_plus1 = {shift_counter[FilterCycles-2:0], 1'b1};
84+
end
85+
86+
// Use MSB (final bit) of shift counter selection trigger.
87+
// The design of the counter avoids the need for additional logic to
88+
// detect the target count has been reached, keeping the timing path fast.
89+
assign select = shift_counter[FilterCycles-1];
90+
91+
// MUX to select whether to output the current input or the registered
92+
// previous output. If an input pulse matches the threshold width
93+
// (in clock cycles) exactly, then the MUX select changes for one cycle
94+
// but the filter output remains unchanged due to the input having reverted.
95+
// Otherwise, if the pulse is longer the new value reaches the output and
96+
// is registered, or if it is shorter then the MUX select never fires.
97+
assign result = select ? raw_i : prev_out;
98+
99+
assign filtered_o = result;
100+
101+
endmodule

rtl/system/sonata_system.sv

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -910,10 +910,31 @@ module sonata_system
910910
logic i2c_scl_h2d [I2C_NUM];
911911
logic i2c_scl_en_h2d[I2C_NUM];
912912
logic i2c_scl_d2h [I2C_NUM];
913+
logic i2c_scl_d2h_f [I2C_NUM];
913914
logic i2c_sda_h2d [I2C_NUM];
914915
logic i2c_sda_en_h2d[I2C_NUM];
915916
logic i2c_sda_d2h [I2C_NUM];
917+
logic i2c_sda_d2h_f [I2C_NUM];
916918
for (genvar i = 0; i < I2C_NUM; i++) begin : gen_i2c_hosts
919+
// SCL & SDA input glitch filters for I^2C Fast-mode
920+
i2c_filter #(
921+
.SysClkFreq(SysClkFreq)
922+
) u_i2c_filter_scl (
923+
.clk_i (clk_sys_i),
924+
.rst_ni (rst_sys_ni),
925+
.raw_i (i2c_scl_d2h[i]),
926+
.filtered_o (i2c_scl_d2h_f[i])
927+
);
928+
i2c_filter #(
929+
.SysClkFreq(SysClkFreq)
930+
) u_i2c_filter_sda (
931+
.clk_i (clk_sys_i),
932+
.rst_ni (rst_sys_ni),
933+
.raw_i (i2c_sda_d2h[i]),
934+
.filtered_o (i2c_sda_d2h_f[i])
935+
);
936+
937+
// I^2C host
917938
i2c u_i2c (
918939
.clk_i (clk_sys_i),
919940
.rst_ni (rst_sys_ni),
@@ -924,10 +945,10 @@ module sonata_system
924945
.tl_o (tl_i2c_d2h[i]),
925946

926947
// Generic IO.
927-
.cio_scl_i (i2c_scl_d2h [i]),
948+
.cio_scl_i (i2c_scl_d2h_f [i]),
928949
.cio_scl_o (i2c_scl_h2d [i]),
929950
.cio_scl_en_o (i2c_scl_en_h2d[i]),
930-
.cio_sda_i (i2c_sda_d2h [i]),
951+
.cio_sda_i (i2c_sda_d2h_f [i]),
931952
.cio_sda_o (i2c_sda_h2d [i]),
932953
.cio_sda_en_o (i2c_sda_en_h2d[i]),
933954

sonata_system.core

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ filesets:
4141
- rtl/system/sram.sv
4242
- rtl/system/rst_sync.sv
4343
- rtl/system/rv_timer.sv
44+
- rtl/system/i2c_filter.sv
4445
- vendor/cheriot_safe/ip/msftDvIp_mmreg.sv
4546
file_type: systemVerilogSource
4647

0 commit comments

Comments
 (0)