Skip to content

Commit

Permalink
[i2c,dif] Test i2c dif clock stretching cycle bounds
Browse files Browse the repository at this point in the history
The i2c block requires a minimum value (for the current setup, at least
4 cycles) for the SCL high and low time in order for clock stretching to
function. This functionality was previously added to the DIF, and this
commit updates the relevant unit tests to also check for these
conditional cases. Modified input clock periods are used that have been
calculated to execute minimum boundary branch cases, and a static
assertion before the test ensures that these stay relevant.

Signed-off-by: Alex Jones <[email protected]>
  • Loading branch information
AlexJones0 authored and engdoreis committed Sep 6, 2024
1 parent 752d5ed commit 2dd2bfe
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 8 deletions.
14 changes: 6 additions & 8 deletions sw/device/lib/dif/dif_i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -235,14 +235,12 @@ dif_result_t dif_i2c_compute_timing(dif_i2c_timing_config_t timing_config,
}

// For clock stretching detection to work, the SCL high and low time must be
// at leats 4 cycles.
// TODO The i2c IP has a parameter (InputDelayCycles) for that, use this when
// topgen supports exposing it in the headers (Issue#23786).
if (config->scl_time_high_cycles < 4) {
config->scl_time_high_cycles = 4;
}
if (config->scl_time_low_cycles < 4) {
config->scl_time_low_cycles = 4;
// at least 4 cycles.
if (config->scl_time_high_cycles < kDifI2cInputDelayCycles) {
config->scl_time_high_cycles = kDifI2cInputDelayCycles;
}
if (config->scl_time_low_cycles < kDifI2cInputDelayCycles) {
config->scl_time_low_cycles = kDifI2cInputDelayCycles;
}

return kDifOk;
Expand Down
10 changes: 10 additions & 0 deletions sw/device/lib/dif/dif_i2c.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,16 @@ typedef enum dif_i2c_speed {
kDifI2cSpeedFastPlus,
} dif_i2c_speed_t;

// TODO(#23786) The i2c IP has a parameter (InputDelayCycles), use this when
// topgen supports exposing it in the headers.
enum {
/**
* Input Delay Cycles; for clock stretching detection to work, the SCL high
* and low time must be at least 4 cycles.
*/
kDifI2cInputDelayCycles = 4,
};

/**
* Timing configuration parameters for I2C.
*
Expand Down
78 changes: 78 additions & 0 deletions sw/device/lib/dif/dif_i2c_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,32 @@ TEST(ComputeTimingTest, StandardSpeed) {
};
EXPECT_DIF_OK(dif_i2c_compute_timing(config, &params));
EXPECT_EQ(params, expected);

// Test that the SCL high and low times are bounded at a minimum
// of 4 cycles. for these branches to execute we need to have
// scl_time_high_cycles < 4 and scl_time_low_cycles < 4
// and lengthened_high_cycles (calculated in the DIF) < 4.
// Based on our config and the DIFs calculations we choose the
// smallest valid multiple of 100, which is 1600.
static_assert(kDifI2cInputDelayCycles == 4,
"I2C DIF unit tests are hardcoded for 4 input delay cycles.");
config = kBaseConfigSlow;
config.lowest_target_device_speed = kDifI2cSpeedStandard;
config.clock_period_nanos = 1600;
expected = {
.scl_time_high_cycles = 4,
.scl_time_low_cycles = 4,
.rise_cycles = 1,
.fall_cycles = 1,
.start_signal_setup_cycles = 3,
.start_signal_hold_cycles = 3,
.data_signal_setup_cycles = 1,
.data_signal_hold_cycles = 1,
.stop_signal_setup_cycles = 3,
.stop_signal_hold_cycles = 3,
};
EXPECT_DIF_OK(dif_i2c_compute_timing(config, &params));
EXPECT_EQ(params, expected);
}

TEST(ComputeTimingTest, FastSpeed) {
Expand Down Expand Up @@ -219,6 +245,32 @@ TEST(ComputeTimingTest, FastSpeed) {
};
EXPECT_DIF_OK(dif_i2c_compute_timing(config, &params));
EXPECT_EQ(params, expected);

// Test that the SCL high and low times are bounded at a minimum
// of 4 cycles. for these branches to execute we need to have
// scl_time_high_cycles < 4 and scl_time_low_cycles < 4
// and lengthened_high_cycles (calculated in the DIF) < 4.
// Based on our config and the DIFs calculations we choose the
// smallest valid multiple of 100, which is 500.
static_assert(kDifI2cInputDelayCycles == 4,
"I2C DIF unit tests are hardcoded for 4 input delay cycles.");
config = kBaseConfigSlow;
config.lowest_target_device_speed = kDifI2cSpeedFast;
config.clock_period_nanos = 500;
expected = {
.scl_time_high_cycles = 4,
.scl_time_low_cycles = 4,
.rise_cycles = 1,
.fall_cycles = 1,
.start_signal_setup_cycles = 2,
.start_signal_hold_cycles = 2,
.data_signal_setup_cycles = 1,
.data_signal_hold_cycles = 1,
.stop_signal_setup_cycles = 2,
.stop_signal_hold_cycles = 3,
};
EXPECT_DIF_OK(dif_i2c_compute_timing(config, &params));
EXPECT_EQ(params, expected);
}

TEST(ComputeTimingTest, FastPlusSpeed) {
Expand Down Expand Up @@ -259,6 +311,32 @@ TEST(ComputeTimingTest, FastPlusSpeed) {
};
EXPECT_DIF_OK(dif_i2c_compute_timing(config, &params));
EXPECT_EQ(params, expected);

// Test that the SCL high and low times are bounded at a minimum
// of 4 cycles. for these branches to execute we need to have
// scl_time_high_cycles < 4 and scl_time_low_cycles < 4
// and lengthened_high_cycles (calculated in the DIF) < 4.
// Based on our config and the DIFs calculations we choose the
// smallest valid multiple of 100, which is 200.
static_assert(kDifI2cInputDelayCycles == 4,
"I2C DIF unit tests are hardcoded for 4 input delay cycles.");
config = kBaseConfigFast;
config.lowest_target_device_speed = kDifI2cSpeedFastPlus;
config.clock_period_nanos = 200;
expected = {
.scl_time_high_cycles = 4,
.scl_time_low_cycles = 4,
.rise_cycles = 1,
.fall_cycles = 1,
.start_signal_setup_cycles = 2,
.start_signal_hold_cycles = 2,
.data_signal_setup_cycles = 1,
.data_signal_hold_cycles = 1,
.stop_signal_setup_cycles = 2,
.stop_signal_hold_cycles = 3,
};
EXPECT_DIF_OK(dif_i2c_compute_timing(config, &params));
EXPECT_EQ(params, expected);
}

TEST(ComputeTimingTest, NullArgs) {
Expand Down

0 comments on commit 2dd2bfe

Please sign in to comment.