From 51e4dcc2e5810246d8fae372bf0fa142ea841c2c Mon Sep 17 00:00:00 2001 From: Elliot Baptist Date: Tue, 29 Oct 2024 08:58:58 +0000 Subject: [PATCH] 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. --- rtl/system/i2c_filter.sv | 101 ++++++++++++++++++++++++++++++++++++ rtl/system/sonata_system.sv | 25 ++++++++- sonata_system.core | 1 + 3 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 rtl/system/i2c_filter.sv diff --git a/rtl/system/i2c_filter.sv b/rtl/system/i2c_filter.sv new file mode 100644 index 000000000..4f5238637 --- /dev/null +++ b/rtl/system/i2c_filter.sv @@ -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 diff --git a/rtl/system/sonata_system.sv b/rtl/system/sonata_system.sv index 69ef0e704..a70948a5a 100644 --- a/rtl/system/sonata_system.sv +++ b/rtl/system/sonata_system.sv @@ -910,10 +910,31 @@ module sonata_system logic i2c_scl_h2d [I2C_NUM]; logic i2c_scl_en_h2d[I2C_NUM]; logic i2c_scl_d2h [I2C_NUM]; + logic i2c_scl_d2h_f [I2C_NUM]; logic i2c_sda_h2d [I2C_NUM]; logic i2c_sda_en_h2d[I2C_NUM]; logic i2c_sda_d2h [I2C_NUM]; + logic i2c_sda_d2h_f [I2C_NUM]; for (genvar i = 0; i < I2C_NUM; i++) begin : gen_i2c_hosts + // SCL & SDA input glitch filters for I^2C Fast-mode + i2c_filter #( + .SysClkFreq(SysClkFreq) + ) u_i2c_filter_scl ( + .clk_i (clk_sys_i), + .rst_ni (rst_sys_ni), + .raw_i (i2c_scl_d2h[i]), + .filtered_o (i2c_scl_d2h_f[i]) + ); + i2c_filter #( + .SysClkFreq(SysClkFreq) + ) u_i2c_filter_sda ( + .clk_i (clk_sys_i), + .rst_ni (rst_sys_ni), + .raw_i (i2c_sda_d2h[i]), + .filtered_o (i2c_sda_d2h_f[i]) + ); + + // I^2C host i2c u_i2c ( .clk_i (clk_sys_i), .rst_ni (rst_sys_ni), @@ -924,10 +945,10 @@ module sonata_system .tl_o (tl_i2c_d2h[i]), // Generic IO. - .cio_scl_i (i2c_scl_d2h [i]), + .cio_scl_i (i2c_scl_d2h_f [i]), .cio_scl_o (i2c_scl_h2d [i]), .cio_scl_en_o (i2c_scl_en_h2d[i]), - .cio_sda_i (i2c_sda_d2h [i]), + .cio_sda_i (i2c_sda_d2h_f [i]), .cio_sda_o (i2c_sda_h2d [i]), .cio_sda_en_o (i2c_sda_en_h2d[i]), diff --git a/sonata_system.core b/sonata_system.core index 7d3525463..4781a1e91 100644 --- a/sonata_system.core +++ b/sonata_system.core @@ -41,6 +41,7 @@ filesets: - rtl/system/sram.sv - rtl/system/rst_sync.sv - rtl/system/rv_timer.sv + - rtl/system/i2c_filter.sv - vendor/cheriot_safe/ip/msftDvIp_mmreg.sv file_type: systemVerilogSource