-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add glitch filters to I^2C inputs #285
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @elliotb-lowrisc this LGTM modulo some coding style nits.
On the meta-stability point we do have a synchroniser in the i2c top-level
sonata-system/vendor/lowrisc_ip/ip/i2c/rtl/i2c_core.sv
Lines 431 to 450 in 49e57ca
// sync the incoming SCL and SDA signals | |
prim_flop_2sync #( | |
.Width(1), | |
.ResetValue(1'b1) | |
) u_i2c_sync_scl ( | |
.clk_i, | |
.rst_ni, | |
.d_i (scl_i), | |
.q_o (scl_sync) | |
); | |
prim_flop_2sync #( | |
.Width(1), | |
.ResetValue(1'b1) | |
) u_i2c_sync_sda ( | |
.clk_i, | |
.rst_ni, | |
.d_i (sda_i), | |
.q_o (sda_sync) | |
); |
So I'd be tempted to remove that synchronizer and implement it inside here instead (i.e. apply prim_flop_2sync
to raw_i
in this module and then use the output of the synchronizer everywhere you currently use raw_i
). I'm a little nervous as to what a real glitch would do if it causes metastability here and given it gets synced eventually may as well push that upstream to here. What we'd like to avoid is repeating the synchronization though. I think in OT this filter is implemented via an analogue component, something like a Schmitt Trigger in the pad so it doesn't have the same metastability concerns.
It's a little fiddly to do this as you'll need to adjust the vendoring patch files and rerun the vendor script, @marnovandermaas or @alees24 can advise.
rtl/system/i2c_filter.sv
Outdated
// Width counter. Implement as shift register to keep logic | ||
// on the path from the counter to the datapath to a minimum. | ||
logic [FilterCycles-1:0] shift_counter_plus1; | ||
logic [FilterCycles-1:0] shift_counter; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use _d
and _q
suffixes. Here you'd have shift_counter_q
in place of shift_counter
and shift_counter_d
in place of shift_counter_plus1
. The always_ff
block below teeters on the edge of acceptable complexity for an always_ff
block (see https://github.com/lowRISC/style-guides/blob/master/VerilogCodingStyle.md#sequential-logic-registers) but I think I'm happy to leave it as is because it makes the shift_counter_d
logic simpler.
rtl/system/i2c_filter.sv
Outdated
logic [FilterCycles-1:0] shift_counter; | ||
|
||
// MUX select signal | ||
logic select; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd make this a more descriptive name select_raw
maybe?
rtl/system/i2c_filter.sv
Outdated
// MUX select signal | ||
logic select; | ||
// Internal filter output signal | ||
logic result; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'd be better to unify the names here, we've got prev_out
, result
and filtered_o
all effectively referring to the same bit, either the immediate combinational version of that bit or the flopped version.
Perhaps switch to filtered_o
, filtered_q
(previously prev_out
) and filtered_d
(previously result
).
rtl/system/i2c_filter.sv
Outdated
if (FilterCycles == 1) begin | ||
// Only one bit, so no shifting | ||
assign shift_counter_plus1 = 1'b1; | ||
end else if (FilterCycles == 2) begin | ||
// Two bits, so only one bit gets shifted | ||
assign shift_counter_plus1 = {shift_counter[0], 1'b1}; | ||
end else begin | ||
// Many bits, so a range get shifted | ||
assign shift_counter_plus1 = {shift_counter[FilterCycles-2:0], 1'b1}; | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add names to the generate blocks, e.g.
if (FilterCycles == 1) begin | |
// Only one bit, so no shifting | |
assign shift_counter_plus1 = 1'b1; | |
end else if (FilterCycles == 2) begin | |
// Two bits, so only one bit gets shifted | |
assign shift_counter_plus1 = {shift_counter[0], 1'b1}; | |
end else begin | |
// Many bits, so a range get shifted | |
assign shift_counter_plus1 = {shift_counter[FilterCycles-2:0], 1'b1}; | |
end | |
if (FilterCycles == 1) begin : g_one_filter_cycle | |
// Only one bit, so no shifting | |
assign shift_counter_plus1 = 1'b1; | |
end else if (FilterCycles == 2) begin : g_two_filter_cycles | |
// Two bits, so only one bit gets shifted | |
assign shift_counter_plus1 = {shift_counter[0], 1'b1}; | |
end else begin : g_many_filter_cycles | |
// Many bits, so a range get shifted | |
assign shift_counter_plus1 = {shift_counter[FilterCycles-2:0], 1'b1}; | |
end |
I should perhaps have thought of this sooner, but we do vendor in the primitive |
Ah yes I hadn't realised that existed! Apologies @elliotb-lowrisc but I think we're best off using the existing primitive, sorry for the wasted design time. Would you mind incorporating |
Righto, I'll have a go at the |
I^2C Fast-mode requires a 50 ns glitch filter on SCL and SDA inputs. This is an attempt at implementing such a filter using the existing prim_filter IP. Note, as it is operating in the digital domain it could fall victim to inexact clock division. Happily the clock divides nicely at our current 40 MHz system clock.
51e4dcc
to
ea4328c
Compare
Reworked to use |
This PR is currently causing my (2021.1) builds to fail timing by a considerable margin for some unknown reason. I shall attempt to investigate. |
Timing seems fine now |
Notable increase in area utilisation observed from this PR. Seems to be related to an I^2C distributed memory no longer being inferred, which may be related to some signals being merged that were not before. Will revisit after 1.0 release. |
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.
So far only tested in simulator in ideal conditions, not on real hardware nor exposed to glitches.