-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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.
- Loading branch information
1 parent
fe432d4
commit 51e4dcc
Showing
3 changed files
with
125 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
// Copyright lowRISC contributors. | ||
// Licensed under the Apache License, Version 2.0, see LICENSE for details. | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
// I^2C Fast-mode spike suppression filter | ||
// | ||
// Suppress input spikes with a pulse width less than or equal to 50 ns (t_sp). | ||
// To be used on SDA and SCL inputs that are to operate at Fast-mode or above. | ||
// See I^2C User Manual for details. | ||
// | ||
// The filter is implemented as a MUX that selects between a registered copy | ||
// of the previous output and a combinatorial passthrough of the current input. | ||
// | ||
// The pulse width filter threshold is rounded down to whole clock cycles. | ||
// Pulses with a width equal to or less than the threshold (in clock cycles) | ||
// will NOT be passed through to the filter output. | ||
// | ||
// Note that neither metastability nor inexact clock cycle division have been | ||
// accounted for in the design of this filter. | ||
|
||
module i2c_filter #( | ||
parameter int unsigned SysClkFreq = 40_000_000 | ||
) ( | ||
input clk_i, | ||
input rst_ni, | ||
|
||
input raw_i, | ||
output filtered_o | ||
); | ||
|
||
// Calculate the number whole clock cycles that fit in a 50 ns period. | ||
// Round down rather than to closest to avoid violating the 50 ns | ||
// maximum pulse width specification for the filter. | ||
localparam int unsigned FilterCycles = int'($floor(50e-9 * SysClkFreq)); | ||
|
||
// Previous filtered output | ||
logic prev_out; | ||
// 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; | ||
|
||
// MUX select signal | ||
logic select; | ||
// Internal filter output signal | ||
logic result; | ||
|
||
// Register output of filter for use next cycle | ||
always_ff @(posedge clk_i or negedge rst_ni) begin | ||
if (!rst_ni) begin | ||
prev_out <= '0; | ||
end else begin | ||
prev_out <= result; | ||
end | ||
end | ||
|
||
// Implement shift-counter | ||
always_ff @(posedge clk_i or negedge rst_ni) begin | ||
if (!rst_ni) begin | ||
shift_counter <= '0; | ||
end else begin | ||
if (raw_i == prev_out) begin | ||
// Clear counter | ||
shift_counter <= '0; | ||
end else begin | ||
// Increment counter | ||
shift_counter <= shift_counter_plus1; | ||
end | ||
end | ||
end | ||
|
||
// Incremented counter signal (shift right, filling LSB with a 'one'). | ||
// Exact implementation depends on counter width. | ||
// Have to do conditional generation outside of an always block. | ||
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 | ||
|
||
// Use MSB (final bit) of shift counter selection trigger. | ||
// The design of the counter avoids the need for additional logic to | ||
// detect the target count has been reached, keeping the timing path fast. | ||
assign select = shift_counter[FilterCycles-1]; | ||
|
||
// MUX to select whether to output the current input or the registered | ||
// previous output. If an input pulse matches the threshold width | ||
// (in clock cycles) exactly, then the MUX select changes for one cycle | ||
// but the filter output remains unchanged due to the input having reverted. | ||
// Otherwise, if the pulse is longer the new value reaches the output and | ||
// is registered, or if it is shorter then the MUX select never fires. | ||
assign result = select ? raw_i : prev_out; | ||
|
||
assign filtered_o = result; | ||
|
||
endmodule |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters