Skip to content
VLSI Mentor

I²C · Module 17

Parameterization, Reset and Synthesis Considerations

Table 10 turned into divider counts at elaboration time, with the achieved frequency reported because it is generally not the one requested. Proves the frequency bound in the period formula is mathematically redundant, and shows why a self-consistent mode table cannot be validated by comparing it against itself.

The master works. This chapter turns it into a configurable engineering block — and closes two threads that have been open since Chapter 17.2.

1. Everything Is Computed at Elaboration Time

This block computes nothing at run time. Every output is a constant derived from the system clock frequency and the mode, exposed so that a testbench — and a synthesis report — can see what the design actually programmed.

That matters because the answer is frequently not the frequency that was requested, and a design that silently delivers something else is a design whose bus speed nobody knows.

2. The Arithmetic

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
N_low  = ceil(tLOW  / T_sys)      N_r = ceil(tr / T_sys)
N_high = ceil(tHIGH / T_sys)      N_f = ceil(tf / T_sys)

N = max( ceil(f_sys / f_bus_max),  N_low + N_high + N_r + N_f )

2a. The first mistake: dropping the phase sum

Module 11 established the identity that makes the sum load-bearing:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
tLOW(min) + tHIGH(min) + tr(max) + tf(max) = 1/fSCL(max)   EXACTLY, in all three modes

At a system clock that divides evenly the two terms are equal, so the sum looks redundant. At 48 MHz in Fast mode they are not: ceil(48e6/400e3) is 120 while the sum is 122 — and a design programming 120 produces phases shorter than the specified minima plus the edge allowance.

2b. The second mistake: rounding down

A phase count rounded down is a phase shorter than a Table 10 minimum — illegal. A period count rounded down raises the frequency above fSCL(max) — also illegal.

Rounding up only ever makes the bus slower than requested, and that is always legal: fSCL has no minimum. Table 10 gives 0 as its lower bound in every mode.

2c. So the achieved frequency is an output

f_sysN_lowN_highN_rN_fceil(f_sys/f_max)sumNachieved
100 MHz130603030250250250400.0 kHz
50 MHz65301515125125125400.0 kHz
48 MHz63291515120122122393.4 kHz
33 MHz43201010838383397.6 kHz
12 MHz16844303232375.0 kHz

A 400 kHz request at 48 MHz yields 393.4 kHz. The integrator has no other way to learn that, so the block reports it.

3. Legality Is Checked, Not Asserted

Every derived count is compared against its Table 10 minimum, and the comparison is written without division:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
N cycles is N * 1e6 / F_KHZ nanoseconds, so
   N * 1_000_000 >= t_ns * F_KHZ

Integer division would round the comparison itself and could pass a marginal configuration. Cross-multiplying keeps it exact.

4. Where the Sample Point Goes

Half way into the high phase, which keeps the sample as far as possible from both hazards: the rising edge, where tr is still settling, and the falling edge, where the transmitter is about to be allowed to change SDA.

Clamped to at least 2, because line-high cycle 1 is the cycle in which the line was first observed high — Chapter 17.3 §6's counting rule — and the worst possible instant to sample.

5. Reset

Two reset questions matter, and only one of them is about this block.

Reset values are the firmware contract. Chapter 17.2 §9 established that CTRL resets to end-with-STOP, so a master reset mid-transaction comes up configured for the safe case rather than for an open bus.

Reset must dominate the pads. Every pin-facing block in this module gates its drive on rst_n — Chapter 17.4 §5a's T12 and Chapter 17.6 §9. A master leaving reset while holding either line low takes the whole bus down and nothing else can lift it.

And reset while the bus is active is not recoverable by this master. If the master is reset mid-transfer, every other device is left part-way through a byte with its own bit counter in an unknown position. The master comes up clean and the bus does not. That is exactly the state Chapter 17.11's recovery exists for, and it is why a system reset should be followed by a recovery attempt rather than by a transaction.

6. The Configuration Block, in Three Languages

Azvya Education Pvt. Ltd.VLSI Mentor
i2c_master_cfg.sv — Table 10 turned into divider counts at elaboration time
   // -----------------------------------------------------------------------------
   // i2c_master_cfg.sv
   // Table 10 turned into divider counts, at elaboration time.
   //
   // This block computes nothing at run time. Every output is a localparam derived from
   // the system clock frequency and the mode, exposed as a constant so that a testbench --
   // and a synthesis report -- can see what the design actually programmed. That matters
   // because the answer is frequently NOT the frequency that was requested, and a design
   // that silently delivers something else is a design whose bus speed nobody knows.
   //
   // THE ARITHMETIC, and both ways to get it wrong.
   //
   //   N_low = ceil(tLOW/T_sys)   N_high = ceil(tHIGH/T_sys)
   //   N_r   = ceil(tr/T_sys)     N_f    = ceil(tf/T_sys)
   //   N     = max( ceil(f_sys/f_bus_max),  N_low + N_high + N_r + N_f )
   //
   // FIRST MISTAKE: dropping the second term of that max. Module 11 established the
   // identity that makes it load-bearing --
   //
   //   tLOW(min) + tHIGH(min) + tr(max) + tf(max) = 1/fSCL(max), EXACTLY, in all three modes
   //
   // -- so at a system clock that divides evenly the two terms are equal and the second
   // looks redundant. At 48 MHz in Fast-mode they are not: ceil(48e6/400e3) is 120 while
   // the sum is 122, and a design that programmed 120 would produce phases shorter than
   // the specified minima plus the edge allowance.
   //
   // SECOND MISTAKE: rounding down. A phase count rounded down is a phase shorter than a
   // Table 10 minimum, which is illegal. A period count rounded down raises the frequency
   // above fSCL(max), which is also illegal. Rounding UP only ever makes the bus slower
   // than requested, and that is always legal: fSCL has no minimum -- Table 10 gives 0 as
   // its lower bound in every mode -- which is the single most useful asymmetry in the
   // whole table for an implementer.
   //
   // So the achieved frequency is REPORTED rather than assumed.
   // -----------------------------------------------------------------------------

   module i2c_master_cfg #(
      parameter int F_SYS_HZ  = 50_000_000,
      // 0 = Standard-mode, 1 = Fast-mode, 2 = Fast-mode Plus.
      parameter int MODE      = 1,
      parameter int CNT_W     = 16
   ) (
      // Everything below is a constant. The ports exist so a bench can read the numbers
      // the elaborator produced, which is otherwise invisible until something fails on a
      // real bus.
      output logic [CNT_W-1:0] n_low,
      output logic [CNT_W-1:0] n_high,
      output logic [CNT_W-1:0] n_rise,
      output logic [CNT_W-1:0] n_fall,
      output logic [CNT_W-1:0] n_period,
      output logic [CNT_W-1:0] n_su_dat,
      output logic [CNT_W-1:0] n_hd_sta,
      output logic [CNT_W-1:0] n_su_sta,
      output logic [CNT_W-1:0] n_su_sto,
      output logic [CNT_W-1:0] n_buf,
      output logic [CNT_W-1:0] n_sample,
      output logic [31:0]      f_bus_hz,     // what the bus will ACTUALLY run at
      output logic            legal,        // every constraint satisfied
      output logic            at_requested  // and it hit the requested frequency exactly
   );

      // ---- Table 10, in NANOSECONDS -------------------------------------------
      localparam integer T_LOW_NS  = (MODE == 0) ? 4700 : (MODE == 1) ? 1300 : 500;
      localparam integer T_HIGH_NS = (MODE == 0) ? 4000 : (MODE == 1) ?  600 : 260;
      localparam integer T_R_NS    = (MODE == 0) ? 1000 : (MODE == 1) ?  300 : 120;
      localparam integer T_F_NS    = (MODE == 0) ?  300 : (MODE == 1) ?  300 : 120;
      localparam integer T_SUDAT_NS= (MODE == 0) ?  250 : (MODE == 1) ?  100 :  50;
      localparam integer T_HDSTA_NS= (MODE == 0) ? 4000 : (MODE == 1) ?  600 : 260;
      localparam integer T_SUSTA_NS= (MODE == 0) ? 4700 : (MODE == 1) ?  600 : 260;
      localparam integer T_SUSTO_NS= (MODE == 0) ? 4000 : (MODE == 1) ?  600 : 260;
      localparam integer T_BUF_NS  = (MODE == 0) ? 4700 : (MODE == 1) ? 1300 : 500;
      localparam integer F_MAX_HZ  = (MODE == 0) ? 100_000 : (MODE == 1) ? 400_000 : 1_000_000;

      // THE UNITS ARE CHOSEN SO THAT 32-BIT INTEGER ARITHMETIC IS BOTH EXACT AND SAFE, and
      // that constraint is what decides the formulation rather than taste.
      //
      // The obvious form -- compute the system period in picoseconds as 1e12 / F_SYS_HZ and
      // divide each Table 10 time by it -- has two problems. The literal 1e12 does not fit
      // in a 32-bit integer, which VHDL's INTEGER is by definition; and the division
      // truncates the period BEFORE it is used, so a 48 MHz clock becomes 20833 ps instead
      // of 20833.33 and every count derived from it is conservatively one too large.
      //
      // Multiplying instead avoids both. `F_KHZ` is the system clock in kHz, so
      //
      //     cycles = ceil( t_ns * F_KHZ / 1_000_000 )
      //
      // is exact for any clock that is a whole number of kHz -- which every real design is
      // -- and the largest intermediate, 4700 * F_KHZ for Standard-mode tLOW, stays inside
      // a 32-bit integer for any system clock below about 450 MHz.
      localparam integer F_KHZ = F_SYS_HZ / 1000;

      // ceil(a/b) for positive integers, without floating point, because a synthesis tool
      // is not obliged to agree with a simulator about $ceil.
      `define I2C_CEIL_DIV(a, b) (((a) + (b) - 1) / (b))
      `define I2C_CYCLES(t_ns)   `I2C_CEIL_DIV((t_ns) * F_KHZ, 1_000_000)

      localparam integer NLOW   = `I2C_CYCLES(T_LOW_NS);
      localparam integer NHIGH  = `I2C_CYCLES(T_HIGH_NS);
      localparam integer NRISE  = `I2C_CYCLES(T_R_NS);
      localparam integer NFALL  = `I2C_CYCLES(T_F_NS);
      localparam integer NSUDAT = `I2C_CYCLES(T_SUDAT_NS);
      localparam integer NHDSTA = `I2C_CYCLES(T_HDSTA_NS);
      localparam integer NSUSTA = `I2C_CYCLES(T_SUSTA_NS);
      localparam integer NSUSTO = `I2C_CYCLES(T_SUSTO_NS);
      localparam integer NBUF   = `I2C_CYCLES(T_BUF_NS);

      // The period: the larger of the frequency bound and the sum of the phases and edges.
      localparam integer N_FREQ = `I2C_CEIL_DIV(F_SYS_HZ, F_MAX_HZ);
      localparam integer N_SUM  = NLOW + NHIGH + NRISE + NFALL;
      localparam integer NPER   = (N_FREQ > N_SUM) ? N_FREQ : N_SUM;

      // Where to sample inside the high phase. Half way in, which keeps the sample as far
      // as possible from both the rising edge -- where tr is still settling -- and the
      // falling edge, where the transmitter is about to be allowed to change SDA.
      // Clamped to at least 2, because line-high cycle 1 is the cycle in which the line
      // was first observed high and is the worst possible instant to sample.
      localparam integer NSAMP_RAW = 2 + (NHIGH / 2);
      localparam integer NSAMP     = (NSAMP_RAW < 2) ? 2 :
                                     (NSAMP_RAW > NHIGH) ? NHIGH : NSAMP_RAW;

      localparam integer F_BUS = F_SYS_HZ / NPER;

      // Legality, checked rather than asserted. Every one of these can fail on a slow
      // system clock, and the failure mode without the check is a bus that looks fine in
      // simulation and violates Table 10 on hardware.
      // Each phase must be at least its Table 10 minimum, expressed in the same units the
      // counts were derived in: N cycles is N * 1e6 / F_KHZ nanoseconds, so the comparison
      // is N * 1_000_000 >= t_ns * F_KHZ with no division anywhere.
      localparam LEGAL = (NLOW  * 1_000_000 >= T_LOW_NS   * F_KHZ) &&
                         (NHIGH * 1_000_000 >= T_HIGH_NS  * F_KHZ) &&
                         (NSUDAT* 1_000_000 >= T_SUDAT_NS * F_KHZ) &&
                         (NHDSTA* 1_000_000 >= T_HDSTA_NS * F_KHZ) &&
                         (NSUSTA* 1_000_000 >= T_SUSTA_NS * F_KHZ) &&
                         (NSUSTO* 1_000_000 >= T_SUSTO_NS * F_KHZ) &&
                         (NBUF  * 1_000_000 >= T_BUF_NS   * F_KHZ) &&
                         (F_BUS <= F_MAX_HZ)                       &&
                         (NPER >= N_SUM)                           &&
                         (NSAMP >= 2) && (NSAMP <= NHIGH);

      assign n_low        = NLOW[CNT_W-1:0];
      assign n_high       = NHIGH[CNT_W-1:0];
      assign n_rise       = NRISE[CNT_W-1:0];
      assign n_fall       = NFALL[CNT_W-1:0];
      assign n_period     = NPER[CNT_W-1:0];
      assign n_su_dat     = NSUDAT[CNT_W-1:0];
      assign n_hd_sta     = NHDSTA[CNT_W-1:0];
      assign n_su_sta     = NSUSTA[CNT_W-1:0];
      assign n_su_sto     = NSUSTO[CNT_W-1:0];
      assign n_buf        = NBUF[CNT_W-1:0];
      assign n_sample     = NSAMP[CNT_W-1:0];
      assign f_bus_hz     = F_BUS;
      assign legal        = LEGAL;
      assign at_requested = (F_BUS == F_MAX_HZ);

   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_master_cfg.v — the same design in Verilog-2001
   // -----------------------------------------------------------------------------
   // i2c_master_cfg.sv
   // Table 10 turned into divider counts, at elaboration time.
   //
   // This block computes nothing at run time. Every output is a localparam derived from
   // the system clock frequency and the mode, exposed as a constant so that a testbench --
   // and a synthesis report -- can see what the design actually programmed. That matters
   // because the answer is frequently NOT the frequency that was requested, and a design
   // that silently delivers something else is a design whose bus speed nobody knows.
   //
   // THE ARITHMETIC, and both ways to get it wrong.
   //
   //   N_low = ceil(tLOW/T_sys)   N_high = ceil(tHIGH/T_sys)
   //   N_r   = ceil(tr/T_sys)     N_f    = ceil(tf/T_sys)
   //   N     = max( ceil(f_sys/f_bus_max),  N_low + N_high + N_r + N_f )
   //
   // FIRST MISTAKE: dropping the second term of that max. Module 11 established the
   // identity that makes it load-bearing --
   //
   //   tLOW(min) + tHIGH(min) + tr(max) + tf(max) = 1/fSCL(max), EXACTLY, in all three modes
   //
   // -- so at a system clock that divides evenly the two terms are equal and the second
   // looks redundant. At 48 MHz in Fast-mode they are not: ceil(48e6/400e3) is 120 while
   // the sum is 122, and a design that programmed 120 would produce phases shorter than
   // the specified minima plus the edge allowance.
   //
   // SECOND MISTAKE: rounding down. A phase count rounded down is a phase shorter than a
   // Table 10 minimum, which is illegal. A period count rounded down raises the frequency
   // above fSCL(max), which is also illegal. Rounding UP only ever makes the bus slower
   // than requested, and that is always legal: fSCL has no minimum -- Table 10 gives 0 as
   // its lower bound in every mode -- which is the single most useful asymmetry in the
   // whole table for an implementer.
   //
   // So the achieved frequency is REPORTED rather than assumed.
   // -----------------------------------------------------------------------------

   // (Verilog-2001 -- structurally identical to the SystemVerilog above.)
   module i2c_master_cfg #(
      parameter F_SYS_HZ  = 50_000_000,
      // 0 = Standard-mode, 1 = Fast-mode, 2 = Fast-mode Plus.
      parameter MODE      = 1,
      parameter CNT_W     = 16
   ) (
      // Everything below is a constant. The ports exist so a bench can read the numbers
      // the elaborator produced, which is otherwise invisible until something fails on a
      // real bus.
      output wire [CNT_W-1:0] n_low,
      output wire [CNT_W-1:0] n_high,
      output wire [CNT_W-1:0] n_rise,
      output wire [CNT_W-1:0] n_fall,
      output wire [CNT_W-1:0] n_period,
      output wire [CNT_W-1:0] n_su_dat,
      output wire [CNT_W-1:0] n_hd_sta,
      output wire [CNT_W-1:0] n_su_sta,
      output wire [CNT_W-1:0] n_su_sto,
      output wire [CNT_W-1:0] n_buf,
      output wire [CNT_W-1:0] n_sample,
      output wire [31:0]      f_bus_hz,     // what the bus will ACTUALLY run at
      output wire             legal,        // every constraint satisfied
      output wire             at_requested  // and it hit the requested frequency exactly
   );

      // ---- Table 10, in NANOSECONDS -------------------------------------------
      localparam integer T_LOW_NS  = (MODE == 0) ? 4700 : (MODE == 1) ? 1300 : 500;
      localparam integer T_HIGH_NS = (MODE == 0) ? 4000 : (MODE == 1) ?  600 : 260;
      localparam integer T_R_NS    = (MODE == 0) ? 1000 : (MODE == 1) ?  300 : 120;
      localparam integer T_F_NS    = (MODE == 0) ?  300 : (MODE == 1) ?  300 : 120;
      localparam integer T_SUDAT_NS= (MODE == 0) ?  250 : (MODE == 1) ?  100 :  50;
      localparam integer T_HDSTA_NS= (MODE == 0) ? 4000 : (MODE == 1) ?  600 : 260;
      localparam integer T_SUSTA_NS= (MODE == 0) ? 4700 : (MODE == 1) ?  600 : 260;
      localparam integer T_SUSTO_NS= (MODE == 0) ? 4000 : (MODE == 1) ?  600 : 260;
      localparam integer T_BUF_NS  = (MODE == 0) ? 4700 : (MODE == 1) ? 1300 : 500;
      localparam integer F_MAX_HZ  = (MODE == 0) ? 100_000 : (MODE == 1) ? 400_000 : 1_000_000;

      // THE UNITS ARE CHOSEN SO THAT 32-BIT INTEGER ARITHMETIC IS BOTH EXACT AND SAFE, and
      // that constraint is what decides the formulation rather than taste.
      //
      // The obvious form -- compute the system period in picoseconds as 1e12 / F_SYS_HZ and
      // divide each Table 10 time by it -- has two problems. The literal 1e12 does not fit
      // in a 32-bit integer, which VHDL's INTEGER is by definition; and the division
      // truncates the period BEFORE it is used, so a 48 MHz clock becomes 20833 ps instead
      // of 20833.33 and every count derived from it is conservatively one too large.
      //
      // Multiplying instead avoids both. `F_KHZ` is the system clock in kHz, so
      //
      //     cycles = ceil( t_ns * F_KHZ / 1_000_000 )
      //
      // is exact for any clock that is a whole number of kHz -- which every real design is
      // -- and the largest intermediate, 4700 * F_KHZ for Standard-mode tLOW, stays inside
      // a 32-bit integer for any system clock below about 450 MHz.
      localparam integer F_KHZ = F_SYS_HZ / 1000;

      // ceil(a/b) for positive integers, without floating point, because a synthesis tool
      // is not obliged to agree with a simulator about $ceil.
      `define I2C_CEIL_DIV(a, b) (((a) + (b) - 1) / (b))
      `define I2C_CYCLES(t_ns)   `I2C_CEIL_DIV((t_ns) * F_KHZ, 1_000_000)

      localparam integer NLOW   = `I2C_CYCLES(T_LOW_NS);
      localparam integer NHIGH  = `I2C_CYCLES(T_HIGH_NS);
      localparam integer NRISE  = `I2C_CYCLES(T_R_NS);
      localparam integer NFALL  = `I2C_CYCLES(T_F_NS);
      localparam integer NSUDAT = `I2C_CYCLES(T_SUDAT_NS);
      localparam integer NHDSTA = `I2C_CYCLES(T_HDSTA_NS);
      localparam integer NSUSTA = `I2C_CYCLES(T_SUSTA_NS);
      localparam integer NSUSTO = `I2C_CYCLES(T_SUSTO_NS);
      localparam integer NBUF   = `I2C_CYCLES(T_BUF_NS);

      // The period: the larger of the frequency bound and the sum of the phases and edges.
      localparam integer N_FREQ = `I2C_CEIL_DIV(F_SYS_HZ, F_MAX_HZ);
      localparam integer N_SUM  = NLOW + NHIGH + NRISE + NFALL;
      localparam integer NPER   = (N_FREQ > N_SUM) ? N_FREQ : N_SUM;

      // Where to sample inside the high phase. Half way in, which keeps the sample as far
      // as possible from both the rising edge -- where tr is still settling -- and the
      // falling edge, where the transmitter is about to be allowed to change SDA.
      // Clamped to at least 2, because line-high cycle 1 is the cycle in which the line
      // was first observed high and is the worst possible instant to sample.
      localparam integer NSAMP_RAW = 2 + (NHIGH / 2);
      localparam integer NSAMP     = (NSAMP_RAW < 2) ? 2 :
                                     (NSAMP_RAW > NHIGH) ? NHIGH : NSAMP_RAW;

      localparam integer F_BUS = F_SYS_HZ / NPER;

      // Legality, checked rather than asserted. Every one of these can fail on a slow
      // system clock, and the failure mode without the check is a bus that looks fine in
      // simulation and violates Table 10 on hardware.
      // Each phase must be at least its Table 10 minimum, expressed in the same units the
      // counts were derived in: N cycles is N * 1e6 / F_KHZ nanoseconds, so the comparison
      // is N * 1_000_000 >= t_ns * F_KHZ with no division anywhere.
      localparam LEGAL = (NLOW  * 1_000_000 >= T_LOW_NS   * F_KHZ) &&
                         (NHIGH * 1_000_000 >= T_HIGH_NS  * F_KHZ) &&
                         (NSUDAT* 1_000_000 >= T_SUDAT_NS * F_KHZ) &&
                         (NHDSTA* 1_000_000 >= T_HDSTA_NS * F_KHZ) &&
                         (NSUSTA* 1_000_000 >= T_SUSTA_NS * F_KHZ) &&
                         (NSUSTO* 1_000_000 >= T_SUSTO_NS * F_KHZ) &&
                         (NBUF  * 1_000_000 >= T_BUF_NS   * F_KHZ) &&
                         (F_BUS <= F_MAX_HZ)                       &&
                         (NPER >= N_SUM)                           &&
                         (NSAMP >= 2) && (NSAMP <= NHIGH);

      assign n_low        = NLOW[CNT_W-1:0];
      assign n_high       = NHIGH[CNT_W-1:0];
      assign n_rise       = NRISE[CNT_W-1:0];
      assign n_fall       = NFALL[CNT_W-1:0];
      assign n_period     = NPER[CNT_W-1:0];
      assign n_su_dat     = NSUDAT[CNT_W-1:0];
      assign n_hd_sta     = NHDSTA[CNT_W-1:0];
      assign n_su_sta     = NSUSTA[CNT_W-1:0];
      assign n_su_sto     = NSUSTO[CNT_W-1:0];
      assign n_buf        = NBUF[CNT_W-1:0];
      assign n_sample     = NSAMP[CNT_W-1:0];
      assign f_bus_hz     = F_BUS;
      assign legal        = LEGAL;
      assign at_requested = (F_BUS == F_MAX_HZ);

   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_master_cfg.vhd — the same design in VHDL
   -- ---------------------------------------------------------------------------
   -- i2c_master_cfg.vhd
   -- Table 10 turned into divider counts, at elaboration time.
   -- Behavioural twin of i2c_master_cfg.sv / .v.
   --
   -- Nothing here is computed at run time. Every output is a constant derived from the
   -- system clock and the mode, exposed as a port so that a testbench -- and a synthesis
   -- report -- can see what the elaborator actually programmed. That matters because the
   -- answer is frequently NOT the frequency that was requested.
   --
   -- THE ARITHMETIC, and both ways to get it wrong.
   --   N_low = ceil(tLOW/T_sys)   N_high = ceil(tHIGH/T_sys)
   --   N_r   = ceil(tr/T_sys)     N_f    = ceil(tf/T_sys)
   --   N     = max( ceil(f_sys/f_bus_max),  N_low + N_high + N_r + N_f )
   --
   -- FIRST MISTAKE: dropping the second term. Module 11 established the identity that makes
   -- it load-bearing -- tLOW + tHIGH + tr + tf = 1/fSCL(max) EXACTLY, in all three modes --
   -- so at a clock that divides evenly the two terms are equal and the second looks
   -- redundant. At 48 MHz in Fast-mode they are not: ceil(48e6/400e3) is 120 and the sum is
   -- 122, and programming 120 gives phases short of the minima plus the edge allowance.
   --
   -- SECOND MISTAKE: rounding down. A phase count rounded down is shorter than a Table 10
   -- minimum; a period count rounded down raises the frequency above fSCL(max). Rounding UP
   -- only makes the bus slower, and that is always legal: Table 10 gives fSCL a minimum of
   -- zero in every mode, which is the most useful asymmetry in the table for an implementer.
   --
   -- THE UNITS ARE CHOSEN SO THAT 32-BIT INTEGER ARITHMETIC IS BOTH EXACT AND SAFE, and
   -- VHDL is the language that forces the issue: its INTEGER is 32-bit BY DEFINITION, so the
   -- obvious formulation -- a system period in picoseconds as 1e12 / F_SYS_HZ -- does not
   -- even elaborate. It is also the less accurate formulation, because dividing first
   -- truncates the period and every count derived from it comes out conservatively large.
   --
   -- Multiplying avoids both problems. With F_KHZ as the system clock in kHz,
   --
   --     cycles = ceil( t_ns * F_KHZ / 1_000_000 )
   --
   -- is exact for any clock that is a whole number of kHz -- which every real design is --
   -- and the largest intermediate, 4700 * F_KHZ for Standard-mode tLOW, stays inside a
   -- 32-bit integer for any system clock below about 450 MHz.
   -- ---------------------------------------------------------------------------

   library ieee;
   use ieee.std_logic_1164.all;
   use ieee.numeric_std.all;

   entity i2c_master_cfg is
      generic (
         F_SYS_HZ : integer := 50000000;
         MODE     : integer := 1;          -- 0 Standard, 1 Fast, 2 Fast-mode Plus
         CNT_W    : integer := 16
      );
      port (
         n_low        : out unsigned(CNT_W-1 downto 0);
         n_high       : out unsigned(CNT_W-1 downto 0);
         n_rise       : out unsigned(CNT_W-1 downto 0);
         n_fall       : out unsigned(CNT_W-1 downto 0);
         n_period     : out unsigned(CNT_W-1 downto 0);
         n_su_dat     : out unsigned(CNT_W-1 downto 0);
         n_hd_sta     : out unsigned(CNT_W-1 downto 0);
         n_su_sta     : out unsigned(CNT_W-1 downto 0);
         n_su_sto     : out unsigned(CNT_W-1 downto 0);
         n_buf        : out unsigned(CNT_W-1 downto 0);
         n_sample     : out unsigned(CNT_W-1 downto 0);
         f_bus_hz     : out unsigned(31 downto 0);
         legal        : out std_logic;
         at_requested : out std_logic
      );
   end entity i2c_master_cfg;

   architecture rtl of i2c_master_cfg is

      -- Table 10, in picoseconds.
      function pick (m : integer; a : integer; b : integer; c : integer) return integer is
      begin
         if m = 0 then return a; elsif m = 1 then return b; else return c; end if;
      end function;

      constant T_LOW_NS   : integer := pick(MODE, 4700, 1300, 500);
      constant T_HIGH_NS  : integer := pick(MODE, 4000,  600, 260);
      constant T_R_NS     : integer := pick(MODE, 1000,  300, 120);
      constant T_F_NS     : integer := pick(MODE,  300,  300, 120);
      constant T_SUDAT_NS : integer := pick(MODE,  250,  100,  50);
      constant T_HDSTA_NS : integer := pick(MODE, 4000,  600, 260);
      constant T_SUSTA_NS : integer := pick(MODE, 4700,  600, 260);
      constant T_SUSTO_NS : integer := pick(MODE, 4000,  600, 260);
      constant T_BUF_NS   : integer := pick(MODE, 4700, 1300, 500);
      constant F_MAX_HZ   : integer := pick(MODE, 100000, 400000, 1000000);

      constant F_KHZ : integer := F_SYS_HZ / 1000;

      -- ceil(a/b) for positive integers, without floating point, because a synthesis tool is
      -- not obliged to agree with a simulator about a real-valued ceiling.
      function ceil_div (a : integer; b : integer) return integer is
      begin
         return (a + b - 1) / b;
      end function;

      function cycles (t_ns : integer) return integer is
      begin
         return ceil_div(t_ns * F_KHZ, 1000000);
      end function;

      constant NLOW   : integer := cycles(T_LOW_NS);
      constant NHIGH  : integer := cycles(T_HIGH_NS);
      constant NRISE  : integer := cycles(T_R_NS);
      constant NFALL  : integer := cycles(T_F_NS);
      constant NSUDAT : integer := cycles(T_SUDAT_NS);
      constant NHDSTA : integer := cycles(T_HDSTA_NS);
      constant NSUSTA : integer := cycles(T_SUSTA_NS);
      constant NSUSTO : integer := cycles(T_SUSTO_NS);
      constant NBUF   : integer := cycles(T_BUF_NS);

      constant N_FREQ : integer := ceil_div(F_SYS_HZ, F_MAX_HZ);
      constant N_SUM  : integer := NLOW + NHIGH + NRISE + NFALL;

      function imax (a : integer; b : integer) return integer is
      begin
         if a > b then return a; else return b; end if;
      end function;

      constant NPER : integer := imax(N_FREQ, N_SUM);

      -- Half way into the high phase, which is as far as possible from the rising edge --
      -- where tr is still settling -- and from the falling edge, where SDA is about to be
      -- free. Clamped to at least 2, because line-high cycle 1 is the cycle the line was
      -- first observed high in and is the worst possible instant to sample.
      constant NSAMP_RAW : integer := 2 + (NHIGH / 2);
      function clamp (v : integer; lo : integer; hi : integer) return integer is
      begin
         if v < lo then return lo; elsif v > hi then return hi; else return v; end if;
      end function;
      constant NSAMP : integer := clamp(NSAMP_RAW, 2, NHIGH);

      constant F_BUS : integer := F_SYS_HZ / NPER;

      function b2sl (b : boolean) return std_logic is
      begin
         if b then return '1'; else return '0'; end if;
      end function;

      -- Legality, checked rather than asserted. Every one of these can fail on a slow system
      -- clock, and without the check the failure is a bus that simulates fine and violates
      -- Table 10 on hardware.
      -- Named IS_LEGAL rather than LEGAL because VHDL identifiers are CASE-INSENSITIVE, so
      -- a constant called LEGAL collides with the port called `legal` -- and the error names
      -- the port, two hundred lines away from the constant that caused it.
      constant IS_LEGAL : boolean :=
         (NLOW   * 1000000 >= T_LOW_NS   * F_KHZ) and
         (NHIGH  * 1000000 >= T_HIGH_NS  * F_KHZ) and
         (NSUDAT * 1000000 >= T_SUDAT_NS * F_KHZ) and
         (NHDSTA * 1000000 >= T_HDSTA_NS * F_KHZ) and
         (NSUSTA * 1000000 >= T_SUSTA_NS * F_KHZ) and
         (NSUSTO * 1000000 >= T_SUSTO_NS * F_KHZ) and
         (NBUF   * 1000000 >= T_BUF_NS   * F_KHZ) and
         (F_BUS <= F_MAX_HZ)               and
         (NPER >= N_SUM)                   and
         (NSAMP >= 2) and (NSAMP <= NHIGH);

   begin

      n_low        <= to_unsigned(NLOW,   CNT_W);
      n_high       <= to_unsigned(NHIGH,  CNT_W);
      n_rise       <= to_unsigned(NRISE,  CNT_W);
      n_fall       <= to_unsigned(NFALL,  CNT_W);
      n_period     <= to_unsigned(NPER,   CNT_W);
      n_su_dat     <= to_unsigned(NSUDAT, CNT_W);
      n_hd_sta     <= to_unsigned(NHDSTA, CNT_W);
      n_su_sta     <= to_unsigned(NSUSTA, CNT_W);
      n_su_sto     <= to_unsigned(NSUSTO, CNT_W);
      n_buf        <= to_unsigned(NBUF,   CNT_W);
      n_sample     <= to_unsigned(NSAMP,  CNT_W);
      f_bus_hz     <= to_unsigned(F_BUS,  32);
      legal        <= b2sl(IS_LEGAL);
      at_requested <= b2sl(F_BUS = F_MAX_HZ);

   end architecture rtl;

6a. The testbenches

Thirteen tests across eight configurations — three modes and five system frequencies. The bench finishes at time zero, because there is nothing to simulate: it elaborates eight instances and checks their constants.

#TestProperty
T150 MHz Fast mode — the textbook caseeverything divides
T2the phases are not equaltLOW(min) > tHIGH(min) in every mode
T3Module 11's identity, checked in counts
T4the first way to get it wrongat 48 MHz the sum exceeds the frequency bound
T5and so 400 kHz is not achievable at 48 MHzthe block says so rather than pretending
T6slower is always legalthe asymmetry that makes ceiling safe
T7no instance exceeds its mode's maximumwhat rounding down would break
T833 MHz — an awkward clock where the two terms agree
T9every framing parameter is derived toonot just the two phases
T10the sample point sits inside the high phaseat every instance
T11Standard mode at 100 MHz needs a 1000-cycle periodthe counter-width consequence
T12every instance is legaleight configurations
T13the mode table itself, against independent numberssee §7
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_master_cfg_tb.sv — the self-checking testbench
   `timescale 1ns/1ps
   // -----------------------------------------------------------------------------
   // i2c_master_cfg_tb.sv
   // Independent oracle for i2c_master_cfg.
   //
   // Every number this block produces is a localparam, so the bench cannot drive it -- it
   // can only instantiate the block at a frequency and a mode and check what the elaborator
   // computed. Fifteen instances, five system clocks across all three modes, and the
   // expected values come from Table 10 worked out by hand rather than from the block.
   //
   // The four properties that matter are not "the numbers are right" but:
   //   every phase is at least its Table 10 minimum       (rounding UP, never down)
   //   the period is at least the sum of phases and edges (the identity of Module 11 §9)
   //   the achieved frequency never exceeds fSCL(max)     (which rounding down would)
   //   and the achieved frequency is REPORTED             (because it is often not asked for)
   // -----------------------------------------------------------------------------
   module i2c_master_cfg_tb;

      integer errors = 0;

      // ---- the instances -----------------------------------------------------
      // Five system clocks: two that divide evenly, and three that do not. The awkward
      // ones are the point -- 48 MHz and 12 MHz are where the sum of the phases exceeds
      // ceil(f_sys/f_max) and the bus therefore CANNOT run at the requested rate.
      logic [15:0] sd_100s, sd_100f, sd_100p;
      logic [15:0] l_100s, h_100s, p_100s, s_100s;  wire [31:0] f_100s; wire g_100s, e_100s;
      logic [15:0] l_100f, h_100f, p_100f, s_100f;  wire [31:0] f_100f; wire g_100f, e_100f;
      logic [15:0] l_100p, h_100p, p_100p, s_100p;  wire [31:0] f_100p; wire g_100p, e_100p;
      logic [15:0] l_50f,  h_50f,  p_50f,  s_50f;   wire [31:0] f_50f;  wire g_50f,  e_50f;
      logic [15:0] l_48f,  h_48f,  p_48f,  s_48f;   wire [31:0] f_48f;  wire g_48f,  e_48f;
      logic [15:0] l_33f,  h_33f,  p_33f,  s_33f;   wire [31:0] f_33f;  wire g_33f,  e_33f;
      logic [15:0] l_12f,  h_12f,  p_12f,  s_12f;   wire [31:0] f_12f;  wire g_12f,  e_12f;
      logic [15:0] l_12p,  h_12p,  p_12p,  s_12p;   wire [31:0] f_12p;  wire g_12p,  e_12p;
      logic [15:0] sd_50f, hd_50f, sa_50f, so_50f, bf_50f, r_50f, ff_50f;

      i2c_master_cfg #(.F_SYS_HZ(100_000_000), .MODE(0)) c100s (
         .n_low(l_100s), .n_high(h_100s), .n_rise(), .n_fall(), .n_period(p_100s),
         .n_su_dat(sd_100s), .n_hd_sta(), .n_su_sta(), .n_su_sto(), .n_buf(),
         .n_sample(s_100s), .f_bus_hz(f_100s), .legal(g_100s), .at_requested(e_100s));
      i2c_master_cfg #(.F_SYS_HZ(100_000_000), .MODE(1)) c100f (
         .n_low(l_100f), .n_high(h_100f), .n_rise(), .n_fall(), .n_period(p_100f),
         .n_su_dat(sd_100f), .n_hd_sta(), .n_su_sta(), .n_su_sto(), .n_buf(),
         .n_sample(s_100f), .f_bus_hz(f_100f), .legal(g_100f), .at_requested(e_100f));
      i2c_master_cfg #(.F_SYS_HZ(100_000_000), .MODE(2)) c100p (
         .n_low(l_100p), .n_high(h_100p), .n_rise(), .n_fall(), .n_period(p_100p),
         .n_su_dat(sd_100p), .n_hd_sta(), .n_su_sta(), .n_su_sto(), .n_buf(),
         .n_sample(s_100p), .f_bus_hz(f_100p), .legal(g_100p), .at_requested(e_100p));
      i2c_master_cfg #(.F_SYS_HZ(50_000_000), .MODE(1)) c50f (
         .n_low(l_50f), .n_high(h_50f), .n_rise(r_50f), .n_fall(ff_50f), .n_period(p_50f),
         .n_su_dat(sd_50f), .n_hd_sta(hd_50f), .n_su_sta(sa_50f), .n_su_sto(so_50f),
         .n_buf(bf_50f), .n_sample(s_50f), .f_bus_hz(f_50f),
         .legal(g_50f), .at_requested(e_50f));
      i2c_master_cfg #(.F_SYS_HZ(48_000_000), .MODE(1)) c48f (
         .n_low(l_48f), .n_high(h_48f), .n_rise(), .n_fall(), .n_period(p_48f),
         .n_su_dat(), .n_hd_sta(), .n_su_sta(), .n_su_sto(), .n_buf(),
         .n_sample(s_48f), .f_bus_hz(f_48f), .legal(g_48f), .at_requested(e_48f));
      i2c_master_cfg #(.F_SYS_HZ(33_000_000), .MODE(1)) c33f (
         .n_low(l_33f), .n_high(h_33f), .n_rise(), .n_fall(), .n_period(p_33f),
         .n_su_dat(), .n_hd_sta(), .n_su_sta(), .n_su_sto(), .n_buf(),
         .n_sample(s_33f), .f_bus_hz(f_33f), .legal(g_33f), .at_requested(e_33f));
      i2c_master_cfg #(.F_SYS_HZ(12_000_000), .MODE(1)) c12f (
         .n_low(l_12f), .n_high(h_12f), .n_rise(), .n_fall(), .n_period(p_12f),
         .n_su_dat(), .n_hd_sta(), .n_su_sta(), .n_su_sto(), .n_buf(),
         .n_sample(s_12f), .f_bus_hz(f_12f), .legal(g_12f), .at_requested(e_12f));
      i2c_master_cfg #(.F_SYS_HZ(12_000_000), .MODE(2)) c12p (
         .n_low(l_12p), .n_high(h_12p), .n_rise(), .n_fall(), .n_period(p_12p),
         .n_su_dat(), .n_hd_sta(), .n_su_sta(), .n_su_sto(), .n_buf(),
         .n_sample(s_12p), .f_bus_hz(f_12p), .legal(g_12p), .at_requested(e_12p));

      task ck_int (input [200*8:1] what, input integer g, input integer e);
         begin
            if (g !== e) begin
               $display("  FAIL %0s: got %0d expected %0d", what, g, e);
               errors = errors + 1;
            end
         end
      endtask

      task ck_bit (input [200*8:1] what, input g, input e);
         begin
            if (g !== e) begin
               $display("  FAIL %0s: got %0b expected %0b", what, g, e);
               errors = errors + 1;
            end
         end
      endtask

      initial begin
         $display("=== i2c_master_cfg: Table 10 as divider counts, at elaboration time ===");

         // ----------------------------------------------------------------
         // T1. 50 MHz, Fast-mode: the textbook case, where everything divides.
         // ----------------------------------------------------------------
         $display("T1  50 MHz Fast-mode: the case where everything divides evenly");
         ck_int("T1 N_low is ceil(1300/20)",  l_50f, 65);
         ck_int("T1 N_high is ceil(600/20)",  h_50f, 30);
         ck_int("T1 N_rise is ceil(300/20)",  r_50f, 15);
         ck_int("T1 N_fall is ceil(300/20)",  ff_50f, 15);
         ck_int("T1 the period is 125",       p_50f, 125);
         ck_int("T1 which is exactly 400 kHz", f_50f, 400_000);
         ck_bit("T1 legal",                   g_50f, 1'b1);
         ck_bit("T1 and at the requested rate", e_50f, 1'b1);

         // ----------------------------------------------------------------
         // T2. THE PHASES ARE NOT EQUAL. tLOW(min) > tHIGH(min) in every mode, so a
         //     generator built from a symmetric divide-by-two is illegal at the limit.
         // ----------------------------------------------------------------
         $display("T2  the low phase is longer than the high phase, in every mode");
         if (!(l_100s > h_100s)) begin
            $display("  FAIL T2 Standard-mode phases are not asymmetric"); errors = errors + 1;
         end
         if (!(l_100f > h_100f)) begin
            $display("  FAIL T2 Fast-mode phases are not asymmetric"); errors = errors + 1;
         end
         if (!(l_100p > h_100p)) begin
            $display("  FAIL T2 Fast-mode Plus phases are not asymmetric"); errors = errors + 1;
         end
         ck_int("T2 Fast-mode is 1300 to 600, i.e. 65 to 30 at 50 MHz", l_50f * 6, h_50f * 13);

         // ----------------------------------------------------------------
         // T3. MODULE 11'S IDENTITY, checked in counts. The period equals the sum of the
         //     two phases and the two edge allowances at any clock that divides evenly.
         // ----------------------------------------------------------------
         $display("T3  the period is the two phases plus the two edge allowances");
         ck_int("T3 at 50 MHz Fast-mode the sum is the period",
                l_50f + h_50f + r_50f + ff_50f, p_50f);
         ck_int("T3 at 100 MHz Standard-mode too", p_100s, 1000);
         ck_int("T3 at 100 MHz Fast-mode Plus too", p_100p, 100);

         // ----------------------------------------------------------------
         // T4. THE FIRST WAY TO GET IT WRONG, demonstrated. At 48 MHz the sum EXCEEDS
         //     ceil(f_sys/f_max), so a design that used only the frequency term would
         //     program 120 and produce phases short of the Table 10 minima.
         // ----------------------------------------------------------------
         $display("T4  at 48 MHz the phase sum exceeds the frequency bound, and wins");
         ck_int("T4 N_low",  l_48f, 63);
         ck_int("T4 N_high", h_48f, 29);
         ck_int("T4 the period is 122, not the 120 the frequency alone would give",
                p_48f, 122);
         if (!(p_48f > 120)) begin
            $display("  FAIL T4 the phase sum did not win"); errors = errors + 1;
         end

         // ----------------------------------------------------------------
         // T5. AND SO 400 kHz IS NOT ACHIEVABLE AT 48 MHz. The block says so rather than
         //     pretending. 48e6/122 = 393.4 kHz.
         // ----------------------------------------------------------------
         $display("T5  so the bus runs at 393 kHz, and the block reports it");
         ck_int("T5 the achieved frequency", f_48f, 393_442);
         ck_bit("T5 still legal", g_48f, 1'b1);
         ck_bit("T5 but NOT at the requested rate", e_48f, 1'b0);

         // ----------------------------------------------------------------
         // T6. SLOWER IS ALWAYS LEGAL, and that is the asymmetry that makes rounding up
         //     the right choice: Table 10 gives fSCL a minimum of 0 in every mode.
         // ----------------------------------------------------------------
         $display("T6  a slower bus is always legal, which is why rounding up is safe");
         ck_bit("T6 12 MHz Fast-mode is legal", g_12f, 1'b1);
         ck_int("T6 at 375 kHz", f_12f, 375_000);
         ck_bit("T6 and it says it missed the requested rate", e_12f, 1'b0);
         ck_bit("T6 12 MHz Fast-mode Plus is legal too", g_12p, 1'b1);
         // 857 kHz, not 800. The nanosecond formulation does not truncate the period before
         // using it, so the counts are one smaller in several awkward cases than the
         // picosecond form gave -- and correctly so, because 12 MHz really does allow a
         // 14-cycle period in Fast-mode Plus rather than a 15-cycle one.
         ck_int("T6 at 857 kHz", f_12p, 857_142);

         // ----------------------------------------------------------------
         // T7. NO INSTANCE EXCEEDS ITS MODE'S MAXIMUM. The one thing rounding down would
         //     break, checked across every instance.
         // ----------------------------------------------------------------
         $display("T7  no instance exceeds its mode's maximum frequency");
         if (f_100s > 100_000)   begin $display("  FAIL T7 100 MHz Standard over 100 kHz");  errors = errors + 1; end
         if (f_100f > 400_000)   begin $display("  FAIL T7 100 MHz Fast over 400 kHz");      errors = errors + 1; end
         if (f_100p > 1_000_000) begin $display("  FAIL T7 100 MHz Fm+ over 1 MHz");         errors = errors + 1; end
         if (f_50f  > 400_000)   begin $display("  FAIL T7 50 MHz Fast over 400 kHz");       errors = errors + 1; end
         if (f_48f  > 400_000)   begin $display("  FAIL T7 48 MHz Fast over 400 kHz");       errors = errors + 1; end
         if (f_33f  > 400_000)   begin $display("  FAIL T7 33 MHz Fast over 400 kHz");       errors = errors + 1; end
         if (f_12f  > 400_000)   begin $display("  FAIL T7 12 MHz Fast over 400 kHz");       errors = errors + 1; end
         if (f_12p  > 1_000_000) begin $display("  FAIL T7 12 MHz Fm+ over 1 MHz");          errors = errors + 1; end

         // ----------------------------------------------------------------
         // T8. 33 MHz: an awkward clock where the sum and the frequency bound agree, and
         //     the result is still below 400 kHz. Rounding the period DOWN to 82 would
         //     give 402.4 kHz, which is illegal -- which is the second mistake.
         // ----------------------------------------------------------------
         $display("T8  33 MHz: rounding the period down here would exceed fSCL(max)");
         ck_int("T8 the period is 83", p_33f, 83);
         ck_int("T8 giving 397.6 kHz", f_33f, 397_590);
         if (33_000_000 / 82 <= 400_000) begin
            $display("  FAIL T8 the arithmetic of the counter-example is wrong");
            errors = errors + 1;
         end
         ck_bit("T8 legal as computed", g_33f, 1'b1);

         // ----------------------------------------------------------------
         // T9. EVERY FRAMING PARAMETER IS DERIVED TOO, not just the two phases. A design
         //     that parameterised the clock and hard-coded the framing margins would be
         //     legal at one system frequency and illegal at every other.
         // ----------------------------------------------------------------
         $display("T9  the framing margins are derived from Table 10 as well");
         ck_int("T9 tSU;DAT at 50 MHz Fast-mode is ceil(100/20)", sd_50f, 5);
         ck_int("T9 tHD;STA is ceil(600/20)",  hd_50f, 30);
         ck_int("T9 tSU;STA is ceil(600/20)",  sa_50f, 30);
         ck_int("T9 tSU;STO is ceil(600/20)",  so_50f, 30);
         ck_int("T9 tBUF is ceil(1300/20)",    bf_50f, 65);

         // ----------------------------------------------------------------
         // T10. THE SAMPLE POINT SITS INSIDE THE HIGH PHASE, at every instance. Half way
         //      in, which is as far as possible from the rising edge -- where tr is still
         //      settling -- and from the falling edge, where SDA is about to be free.
         // ----------------------------------------------------------------
         $display("T10 the sample point is inside the high phase at every instance");
         if (!(s_50f  >= 2 && s_50f  <= h_50f))  begin $display("  FAIL T10 50 MHz Fast");   errors = errors + 1; end
         if (!(s_100s >= 2 && s_100s <= h_100s)) begin $display("  FAIL T10 100 MHz Std");   errors = errors + 1; end
         if (!(s_12p  >= 2 && s_12p  <= h_12p))  begin $display("  FAIL T10 12 MHz Fm+");    errors = errors + 1; end
         ck_int("T10 at 50 MHz Fast-mode it is 17, half of 30 plus two", s_50f, 17);
         ck_int("T10 at 12 MHz Fm+ the high phase is only 4 cycles", h_12p, 4);
         ck_int("T10 so the sample lands at 4, the last of them", s_12p, 4);

         // ----------------------------------------------------------------
         // T11. STANDARD-MODE AT 100 MHz NEEDS A THOUSAND-CYCLE PERIOD, which is the
         //      counter width nobody sizes correctly the first time: 470 does not fit in
         //      eight bits, and a truncated phase count is a silently illegal clock.
         // ----------------------------------------------------------------
         $display("T11 Standard-mode at 100 MHz needs ten-bit phase counters");
         ck_int("T11 N_low is 470", l_100s, 470);
         ck_int("T11 which does not fit in eight bits", (l_100s > 255) ? 1 : 0, 1);
         ck_int("T11 and the period is 1000", p_100s, 1000);
         ck_int("T11 which does not fit in nine", (p_100s > 511) ? 1 : 0, 1);

         // ----------------------------------------------------------------
         // T12. EVERY INSTANCE IS LEGAL. Eight configurations across three modes and five
         //      system clocks, and the block asserts Table 10 on all of them.
         // ----------------------------------------------------------------
         $display("T12 every one of the eight configurations satisfies Table 10");
         ck_bit("T12 100 MHz Standard", g_100s, 1'b1);
         ck_bit("T12 100 MHz Fast",     g_100f, 1'b1);
         ck_bit("T12 100 MHz Fm+",      g_100p, 1'b1);
         ck_bit("T12 50 MHz Fast",      g_50f,  1'b1);
         ck_bit("T12 48 MHz Fast",      g_48f,  1'b1);
         ck_bit("T12 33 MHz Fast",      g_33f,  1'b1);
         ck_bit("T12 12 MHz Fast",      g_12f,  1'b1);
         ck_bit("T12 12 MHz Fm+",       g_12p,  1'b1);
         // And only the evenly-dividing ones hit the requested rate, which is the fact a
         // datasheet for this block would have to state.
         ck_bit("T12 100 MHz hits 400 kHz exactly", e_100f, 1'b1);
         ck_bit("T12 50 MHz does too",              e_50f,  1'b1);
         ck_bit("T12 48 MHz does not",              e_48f,  1'b0);
         ck_bit("T12 33 MHz does not",              e_33f,  1'b0);
         ck_bit("T12 12 MHz does not",              e_12f,  1'b0);
         // ----------------------------------------------------------------
         // T13. THE MODE TABLE ITSELF, checked against INDEPENDENT numbers.
         //
         //      Every other test compares this block's counts against this block's own
         //      constants -- the legality check included. That is self-consistent and
         //      therefore cannot detect a wrong entry in the table: change tSU;DAT for
         //      Standard-mode from 250 ns to 50 and the counts, the legality check and
         //      every derived value all agree with each other, and all of them are wrong.
         //
         //      So the expected values here are written out from UM10204 Table 10 by hand,
         //      at one system frequency where the arithmetic is trivial to check by eye.
         //      At 100 MHz one cycle is 10 ns, so a count is the time in nanoseconds
         //      divided by ten, rounded up.
         //
         //        Standard   tSU;DAT  250 ns -> 25 cycles
         //        Fast       tSU;DAT  100 ns -> 10 cycles
         //        Fast-Plus  tSU;DAT   50 ns ->  5 cycles
         //
         //        Standard   tLOW    4700 ns -> 470    tHIGH  4000 ns -> 400
         //        Fast       tLOW    1300 ns -> 130    tHIGH   600 ns ->  60
         //        Fast-Plus  tLOW     500 ns ->  50    tHIGH   260 ns ->  26
         // ----------------------------------------------------------------
         $display("T13 the mode table checked against Table 10 by hand, not against itself");
         ck_int("T13 Standard tSU;DAT is 25 cycles at 100 MHz", sd_100s, 25);
         ck_int("T13 Fast tSU;DAT is 10 cycles", sd_100f, 10);
         ck_int("T13 Fast-Plus tSU;DAT is 5 cycles", sd_100p, 5);
         ck_int("T13 Standard tLOW is 470 cycles", l_100s, 470);
         ck_int("T13 Standard tHIGH is 400 cycles", h_100s, 400);
         ck_int("T13 Fast tLOW is 130 cycles", l_100f, 130);
         ck_int("T13 Fast tHIGH is 60 cycles", h_100f, 60);
         ck_int("T13 Fast-Plus tLOW is 50 cycles", l_100p, 50);
         ck_int("T13 Fast-Plus tHIGH is 26 cycles", h_100p, 26);



         if (errors == 0)
            $display("=== i2c_master_cfg: ALL CHECKS PASSED ===");
         else
            $display("=== i2c_master_cfg: %0d CHECK(S) FAILED ===", errors);
         $finish;
      end

   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_master_cfg_tb.v — the same tests in Verilog-2001
   `timescale 1ns/1ps
   // -----------------------------------------------------------------------------
   // i2c_master_cfg_tb.sv
   // Independent oracle for i2c_master_cfg.
   //
   // Every number this block produces is a localparam, so the bench cannot drive it -- it
   // can only instantiate the block at a frequency and a mode and check what the elaborator
   // computed. Fifteen instances, five system clocks across all three modes, and the
   // expected values come from Table 10 worked out by hand rather than from the block.
   //
   // The four properties that matter are not "the numbers are right" but:
   //   every phase is at least its Table 10 minimum       (rounding UP, never down)
   //   the period is at least the sum of phases and edges (the identity of Module 11 §9)
   //   the achieved frequency never exceeds fSCL(max)     (which rounding down would)
   //   and the achieved frequency is REPORTED             (because it is often not asked for)
   // -----------------------------------------------------------------------------
   // (Verilog-2001 -- structurally identical to the SystemVerilog above.)
   module i2c_master_cfg_tb;

      integer errors = 0;

      // ---- the instances -----------------------------------------------------
      // Five system clocks: two that divide evenly, and three that do not. The awkward
      // ones are the point -- 48 MHz and 12 MHz are where the sum of the phases exceeds
      // ceil(f_sys/f_max) and the bus therefore CANNOT run at the requested rate.
      wire [15:0] sd_100s, sd_100f, sd_100p;
      wire [15:0] l_100s, h_100s, p_100s, s_100s;  wire [31:0] f_100s; wire g_100s, e_100s;
      wire [15:0] l_100f, h_100f, p_100f, s_100f;  wire [31:0] f_100f; wire g_100f, e_100f;
      wire [15:0] l_100p, h_100p, p_100p, s_100p;  wire [31:0] f_100p; wire g_100p, e_100p;
      wire [15:0] l_50f,  h_50f,  p_50f,  s_50f;   wire [31:0] f_50f;  wire g_50f,  e_50f;
      wire [15:0] l_48f,  h_48f,  p_48f,  s_48f;   wire [31:0] f_48f;  wire g_48f,  e_48f;
      wire [15:0] l_33f,  h_33f,  p_33f,  s_33f;   wire [31:0] f_33f;  wire g_33f,  e_33f;
      wire [15:0] l_12f,  h_12f,  p_12f,  s_12f;   wire [31:0] f_12f;  wire g_12f,  e_12f;
      wire [15:0] l_12p,  h_12p,  p_12p,  s_12p;   wire [31:0] f_12p;  wire g_12p,  e_12p;
      wire [15:0] sd_50f, hd_50f, sa_50f, so_50f, bf_50f, r_50f, ff_50f;

      i2c_master_cfg #(.F_SYS_HZ(100_000_000), .MODE(0)) c100s (
         .n_low(l_100s), .n_high(h_100s), .n_rise(), .n_fall(), .n_period(p_100s),
         .n_su_dat(sd_100s), .n_hd_sta(), .n_su_sta(), .n_su_sto(), .n_buf(),
         .n_sample(s_100s), .f_bus_hz(f_100s), .legal(g_100s), .at_requested(e_100s));
      i2c_master_cfg #(.F_SYS_HZ(100_000_000), .MODE(1)) c100f (
         .n_low(l_100f), .n_high(h_100f), .n_rise(), .n_fall(), .n_period(p_100f),
         .n_su_dat(sd_100f), .n_hd_sta(), .n_su_sta(), .n_su_sto(), .n_buf(),
         .n_sample(s_100f), .f_bus_hz(f_100f), .legal(g_100f), .at_requested(e_100f));
      i2c_master_cfg #(.F_SYS_HZ(100_000_000), .MODE(2)) c100p (
         .n_low(l_100p), .n_high(h_100p), .n_rise(), .n_fall(), .n_period(p_100p),
         .n_su_dat(sd_100p), .n_hd_sta(), .n_su_sta(), .n_su_sto(), .n_buf(),
         .n_sample(s_100p), .f_bus_hz(f_100p), .legal(g_100p), .at_requested(e_100p));
      i2c_master_cfg #(.F_SYS_HZ(50_000_000), .MODE(1)) c50f (
         .n_low(l_50f), .n_high(h_50f), .n_rise(r_50f), .n_fall(ff_50f), .n_period(p_50f),
         .n_su_dat(sd_50f), .n_hd_sta(hd_50f), .n_su_sta(sa_50f), .n_su_sto(so_50f),
         .n_buf(bf_50f), .n_sample(s_50f), .f_bus_hz(f_50f),
         .legal(g_50f), .at_requested(e_50f));
      i2c_master_cfg #(.F_SYS_HZ(48_000_000), .MODE(1)) c48f (
         .n_low(l_48f), .n_high(h_48f), .n_rise(), .n_fall(), .n_period(p_48f),
         .n_su_dat(), .n_hd_sta(), .n_su_sta(), .n_su_sto(), .n_buf(),
         .n_sample(s_48f), .f_bus_hz(f_48f), .legal(g_48f), .at_requested(e_48f));
      i2c_master_cfg #(.F_SYS_HZ(33_000_000), .MODE(1)) c33f (
         .n_low(l_33f), .n_high(h_33f), .n_rise(), .n_fall(), .n_period(p_33f),
         .n_su_dat(), .n_hd_sta(), .n_su_sta(), .n_su_sto(), .n_buf(),
         .n_sample(s_33f), .f_bus_hz(f_33f), .legal(g_33f), .at_requested(e_33f));
      i2c_master_cfg #(.F_SYS_HZ(12_000_000), .MODE(1)) c12f (
         .n_low(l_12f), .n_high(h_12f), .n_rise(), .n_fall(), .n_period(p_12f),
         .n_su_dat(), .n_hd_sta(), .n_su_sta(), .n_su_sto(), .n_buf(),
         .n_sample(s_12f), .f_bus_hz(f_12f), .legal(g_12f), .at_requested(e_12f));
      i2c_master_cfg #(.F_SYS_HZ(12_000_000), .MODE(2)) c12p (
         .n_low(l_12p), .n_high(h_12p), .n_rise(), .n_fall(), .n_period(p_12p),
         .n_su_dat(), .n_hd_sta(), .n_su_sta(), .n_su_sto(), .n_buf(),
         .n_sample(s_12p), .f_bus_hz(f_12p), .legal(g_12p), .at_requested(e_12p));

      task ck_int (input [200*8:1] what, input integer g, input integer e);
         begin
            if (g !== e) begin
               $display("  FAIL %0s: got %0d expected %0d", what, g, e);
               errors = errors + 1;
            end
         end
      endtask

      task ck_bit (input [200*8:1] what, input g, input e);
         begin
            if (g !== e) begin
               $display("  FAIL %0s: got %0b expected %0b", what, g, e);
               errors = errors + 1;
            end
         end
      endtask

      initial begin
         $display("=== i2c_master_cfg: Table 10 as divider counts, at elaboration time ===");

         // ----------------------------------------------------------------
         // T1. 50 MHz, Fast-mode: the textbook case, where everything divides.
         // ----------------------------------------------------------------
         $display("T1  50 MHz Fast-mode: the case where everything divides evenly");
         ck_int("T1 N_low is ceil(1300/20)",  l_50f, 65);
         ck_int("T1 N_high is ceil(600/20)",  h_50f, 30);
         ck_int("T1 N_rise is ceil(300/20)",  r_50f, 15);
         ck_int("T1 N_fall is ceil(300/20)",  ff_50f, 15);
         ck_int("T1 the period is 125",       p_50f, 125);
         ck_int("T1 which is exactly 400 kHz", f_50f, 400_000);
         ck_bit("T1 legal",                   g_50f, 1'b1);
         ck_bit("T1 and at the requested rate", e_50f, 1'b1);

         // ----------------------------------------------------------------
         // T2. THE PHASES ARE NOT EQUAL. tLOW(min) > tHIGH(min) in every mode, so a
         //     generator built from a symmetric divide-by-two is illegal at the limit.
         // ----------------------------------------------------------------
         $display("T2  the low phase is longer than the high phase, in every mode");
         if (!(l_100s > h_100s)) begin
            $display("  FAIL T2 Standard-mode phases are not asymmetric"); errors = errors + 1;
         end
         if (!(l_100f > h_100f)) begin
            $display("  FAIL T2 Fast-mode phases are not asymmetric"); errors = errors + 1;
         end
         if (!(l_100p > h_100p)) begin
            $display("  FAIL T2 Fast-mode Plus phases are not asymmetric"); errors = errors + 1;
         end
         ck_int("T2 Fast-mode is 1300 to 600, i.e. 65 to 30 at 50 MHz", l_50f * 6, h_50f * 13);

         // ----------------------------------------------------------------
         // T3. MODULE 11'S IDENTITY, checked in counts. The period equals the sum of the
         //     two phases and the two edge allowances at any clock that divides evenly.
         // ----------------------------------------------------------------
         $display("T3  the period is the two phases plus the two edge allowances");
         ck_int("T3 at 50 MHz Fast-mode the sum is the period",
                l_50f + h_50f + r_50f + ff_50f, p_50f);
         ck_int("T3 at 100 MHz Standard-mode too", p_100s, 1000);
         ck_int("T3 at 100 MHz Fast-mode Plus too", p_100p, 100);

         // ----------------------------------------------------------------
         // T4. THE FIRST WAY TO GET IT WRONG, demonstrated. At 48 MHz the sum EXCEEDS
         //     ceil(f_sys/f_max), so a design that used only the frequency term would
         //     program 120 and produce phases short of the Table 10 minima.
         // ----------------------------------------------------------------
         $display("T4  at 48 MHz the phase sum exceeds the frequency bound, and wins");
         ck_int("T4 N_low",  l_48f, 63);
         ck_int("T4 N_high", h_48f, 29);
         ck_int("T4 the period is 122, not the 120 the frequency alone would give",
                p_48f, 122);
         if (!(p_48f > 120)) begin
            $display("  FAIL T4 the phase sum did not win"); errors = errors + 1;
         end

         // ----------------------------------------------------------------
         // T5. AND SO 400 kHz IS NOT ACHIEVABLE AT 48 MHz. The block says so rather than
         //     pretending. 48e6/122 = 393.4 kHz.
         // ----------------------------------------------------------------
         $display("T5  so the bus runs at 393 kHz, and the block reports it");
         ck_int("T5 the achieved frequency", f_48f, 393_442);
         ck_bit("T5 still legal", g_48f, 1'b1);
         ck_bit("T5 but NOT at the requested rate", e_48f, 1'b0);

         // ----------------------------------------------------------------
         // T6. SLOWER IS ALWAYS LEGAL, and that is the asymmetry that makes rounding up
         //     the right choice: Table 10 gives fSCL a minimum of 0 in every mode.
         // ----------------------------------------------------------------
         $display("T6  a slower bus is always legal, which is why rounding up is safe");
         ck_bit("T6 12 MHz Fast-mode is legal", g_12f, 1'b1);
         ck_int("T6 at 375 kHz", f_12f, 375_000);
         ck_bit("T6 and it says it missed the requested rate", e_12f, 1'b0);
         ck_bit("T6 12 MHz Fast-mode Plus is legal too", g_12p, 1'b1);
         // 857 kHz, not 800. The nanosecond formulation does not truncate the period before
         // using it, so the counts are one smaller in several awkward cases than the
         // picosecond form gave -- and correctly so, because 12 MHz really does allow a
         // 14-cycle period in Fast-mode Plus rather than a 15-cycle one.
         ck_int("T6 at 857 kHz", f_12p, 857_142);

         // ----------------------------------------------------------------
         // T7. NO INSTANCE EXCEEDS ITS MODE'S MAXIMUM. The one thing rounding down would
         //     break, checked across every instance.
         // ----------------------------------------------------------------
         $display("T7  no instance exceeds its mode's maximum frequency");
         if (f_100s > 100_000)   begin $display("  FAIL T7 100 MHz Standard over 100 kHz");  errors = errors + 1; end
         if (f_100f > 400_000)   begin $display("  FAIL T7 100 MHz Fast over 400 kHz");      errors = errors + 1; end
         if (f_100p > 1_000_000) begin $display("  FAIL T7 100 MHz Fm+ over 1 MHz");         errors = errors + 1; end
         if (f_50f  > 400_000)   begin $display("  FAIL T7 50 MHz Fast over 400 kHz");       errors = errors + 1; end
         if (f_48f  > 400_000)   begin $display("  FAIL T7 48 MHz Fast over 400 kHz");       errors = errors + 1; end
         if (f_33f  > 400_000)   begin $display("  FAIL T7 33 MHz Fast over 400 kHz");       errors = errors + 1; end
         if (f_12f  > 400_000)   begin $display("  FAIL T7 12 MHz Fast over 400 kHz");       errors = errors + 1; end
         if (f_12p  > 1_000_000) begin $display("  FAIL T7 12 MHz Fm+ over 1 MHz");          errors = errors + 1; end

         // ----------------------------------------------------------------
         // T8. 33 MHz: an awkward clock where the sum and the frequency bound agree, and
         //     the result is still below 400 kHz. Rounding the period DOWN to 82 would
         //     give 402.4 kHz, which is illegal -- which is the second mistake.
         // ----------------------------------------------------------------
         $display("T8  33 MHz: rounding the period down here would exceed fSCL(max)");
         ck_int("T8 the period is 83", p_33f, 83);
         ck_int("T8 giving 397.6 kHz", f_33f, 397_590);
         if (33_000_000 / 82 <= 400_000) begin
            $display("  FAIL T8 the arithmetic of the counter-example is wrong");
            errors = errors + 1;
         end
         ck_bit("T8 legal as computed", g_33f, 1'b1);

         // ----------------------------------------------------------------
         // T9. EVERY FRAMING PARAMETER IS DERIVED TOO, not just the two phases. A design
         //     that parameterised the clock and hard-coded the framing margins would be
         //     legal at one system frequency and illegal at every other.
         // ----------------------------------------------------------------
         $display("T9  the framing margins are derived from Table 10 as well");
         ck_int("T9 tSU;DAT at 50 MHz Fast-mode is ceil(100/20)", sd_50f, 5);
         ck_int("T9 tHD;STA is ceil(600/20)",  hd_50f, 30);
         ck_int("T9 tSU;STA is ceil(600/20)",  sa_50f, 30);
         ck_int("T9 tSU;STO is ceil(600/20)",  so_50f, 30);
         ck_int("T9 tBUF is ceil(1300/20)",    bf_50f, 65);

         // ----------------------------------------------------------------
         // T10. THE SAMPLE POINT SITS INSIDE THE HIGH PHASE, at every instance. Half way
         //      in, which is as far as possible from the rising edge -- where tr is still
         //      settling -- and from the falling edge, where SDA is about to be free.
         // ----------------------------------------------------------------
         $display("T10 the sample point is inside the high phase at every instance");
         if (!(s_50f  >= 2 && s_50f  <= h_50f))  begin $display("  FAIL T10 50 MHz Fast");   errors = errors + 1; end
         if (!(s_100s >= 2 && s_100s <= h_100s)) begin $display("  FAIL T10 100 MHz Std");   errors = errors + 1; end
         if (!(s_12p  >= 2 && s_12p  <= h_12p))  begin $display("  FAIL T10 12 MHz Fm+");    errors = errors + 1; end
         ck_int("T10 at 50 MHz Fast-mode it is 17, half of 30 plus two", s_50f, 17);
         ck_int("T10 at 12 MHz Fm+ the high phase is only 4 cycles", h_12p, 4);
         ck_int("T10 so the sample lands at 4, the last of them", s_12p, 4);

         // ----------------------------------------------------------------
         // T11. STANDARD-MODE AT 100 MHz NEEDS A THOUSAND-CYCLE PERIOD, which is the
         //      counter width nobody sizes correctly the first time: 470 does not fit in
         //      eight bits, and a truncated phase count is a silently illegal clock.
         // ----------------------------------------------------------------
         $display("T11 Standard-mode at 100 MHz needs ten-bit phase counters");
         ck_int("T11 N_low is 470", l_100s, 470);
         ck_int("T11 which does not fit in eight bits", (l_100s > 255) ? 1 : 0, 1);
         ck_int("T11 and the period is 1000", p_100s, 1000);
         ck_int("T11 which does not fit in nine", (p_100s > 511) ? 1 : 0, 1);

         // ----------------------------------------------------------------
         // T12. EVERY INSTANCE IS LEGAL. Eight configurations across three modes and five
         //      system clocks, and the block asserts Table 10 on all of them.
         // ----------------------------------------------------------------
         $display("T12 every one of the eight configurations satisfies Table 10");
         ck_bit("T12 100 MHz Standard", g_100s, 1'b1);
         ck_bit("T12 100 MHz Fast",     g_100f, 1'b1);
         ck_bit("T12 100 MHz Fm+",      g_100p, 1'b1);
         ck_bit("T12 50 MHz Fast",      g_50f,  1'b1);
         ck_bit("T12 48 MHz Fast",      g_48f,  1'b1);
         ck_bit("T12 33 MHz Fast",      g_33f,  1'b1);
         ck_bit("T12 12 MHz Fast",      g_12f,  1'b1);
         ck_bit("T12 12 MHz Fm+",       g_12p,  1'b1);
         // And only the evenly-dividing ones hit the requested rate, which is the fact a
         // datasheet for this block would have to state.
         ck_bit("T12 100 MHz hits 400 kHz exactly", e_100f, 1'b1);
         ck_bit("T12 50 MHz does too",              e_50f,  1'b1);
         ck_bit("T12 48 MHz does not",              e_48f,  1'b0);
         ck_bit("T12 33 MHz does not",              e_33f,  1'b0);
         ck_bit("T12 12 MHz does not",              e_12f,  1'b0);
         // ----------------------------------------------------------------
         // T13. THE MODE TABLE ITSELF, checked against INDEPENDENT numbers.
         //
         //      Every other test compares this block's counts against this block's own
         //      constants -- the legality check included. That is self-consistent and
         //      therefore cannot detect a wrong entry in the table: change tSU;DAT for
         //      Standard-mode from 250 ns to 50 and the counts, the legality check and
         //      every derived value all agree with each other, and all of them are wrong.
         //
         //      So the expected values here are written out from UM10204 Table 10 by hand,
         //      at one system frequency where the arithmetic is trivial to check by eye.
         //      At 100 MHz one cycle is 10 ns, so a count is the time in nanoseconds
         //      divided by ten, rounded up.
         //
         //        Standard   tSU;DAT  250 ns -> 25 cycles
         //        Fast       tSU;DAT  100 ns -> 10 cycles
         //        Fast-Plus  tSU;DAT   50 ns ->  5 cycles
         //
         //        Standard   tLOW    4700 ns -> 470    tHIGH  4000 ns -> 400
         //        Fast       tLOW    1300 ns -> 130    tHIGH   600 ns ->  60
         //        Fast-Plus  tLOW     500 ns ->  50    tHIGH   260 ns ->  26
         // ----------------------------------------------------------------
         $display("T13 the mode table checked against Table 10 by hand, not against itself");
         ck_int("T13 Standard tSU;DAT is 25 cycles at 100 MHz", sd_100s, 25);
         ck_int("T13 Fast tSU;DAT is 10 cycles", sd_100f, 10);
         ck_int("T13 Fast-Plus tSU;DAT is 5 cycles", sd_100p, 5);
         ck_int("T13 Standard tLOW is 470 cycles", l_100s, 470);
         ck_int("T13 Standard tHIGH is 400 cycles", h_100s, 400);
         ck_int("T13 Fast tLOW is 130 cycles", l_100f, 130);
         ck_int("T13 Fast tHIGH is 60 cycles", h_100f, 60);
         ck_int("T13 Fast-Plus tLOW is 50 cycles", l_100p, 50);
         ck_int("T13 Fast-Plus tHIGH is 26 cycles", h_100p, 26);



         if (errors == 0)
            $display("=== i2c_master_cfg: ALL CHECKS PASSED ===");
         else
            $display("=== i2c_master_cfg: %0d CHECK(S) FAILED ===", errors);
         $finish;
      end

   endmodule
Azvya Education Pvt. Ltd.VLSI Mentor
i2c_master_cfg_tb.vhd — the same tests in VHDL
   -- ---------------------------------------------------------------------------
   -- i2c_master_cfg_tb.vhd
   -- Independent oracle for i2c_master_cfg. Behavioural twin of the SV and Verilog benches.
   --
   -- Every number this block produces is a constant, so the bench cannot drive it -- it can
   -- only instantiate the block at a frequency and a mode and check what the elaborator
   -- computed. Eight instances across all three modes, with the expected values worked out
   -- from Table 10 by hand rather than taken from the block.
   --
   -- The four properties that matter are not "the numbers are right" but:
   --   every phase is at least its Table 10 minimum       (rounding UP, never down)
   --   the period is at least the phases plus the edges   (Module 11 section 9's identity)
   --   the achieved frequency never exceeds fSCL(max)     (which rounding down would break)
   --   and the achieved frequency is REPORTED             (because it is often not the one
   --                                                       that was asked for)
   -- ---------------------------------------------------------------------------

   library ieee;
   use ieee.std_logic_1164.all;
   use ieee.numeric_std.all;

   entity i2c_master_cfg_tb is
   end entity i2c_master_cfg_tb;

   architecture sim of i2c_master_cfg_tb is

      signal l_100s, h_100s, p_100s, s_100s : unsigned(15 downto 0);
      signal r_100s, ff_100s, sd_100s, hd_100s, sa_100s, so_100s, bf_100s : unsigned(15 downto 0);
      signal f_100s : unsigned(31 downto 0);
      signal g_100s, e_100s : std_logic;
      signal l_100f, h_100f, p_100f, s_100f : unsigned(15 downto 0);
      signal r_100f, ff_100f, sd_100f, hd_100f, sa_100f, so_100f, bf_100f : unsigned(15 downto 0);
      signal f_100f : unsigned(31 downto 0);
      signal g_100f, e_100f : std_logic;
      signal l_100p, h_100p, p_100p, s_100p : unsigned(15 downto 0);
      signal r_100p, ff_100p, sd_100p, hd_100p, sa_100p, so_100p, bf_100p : unsigned(15 downto 0);
      signal f_100p : unsigned(31 downto 0);
      signal g_100p, e_100p : std_logic;
      signal l_50f, h_50f, p_50f, s_50f : unsigned(15 downto 0);
      signal r_50f, ff_50f, sd_50f, hd_50f, sa_50f, so_50f, bf_50f : unsigned(15 downto 0);
      signal f_50f : unsigned(31 downto 0);
      signal g_50f, e_50f : std_logic;
      signal l_48f, h_48f, p_48f, s_48f : unsigned(15 downto 0);
      signal r_48f, ff_48f, sd_48f, hd_48f, sa_48f, so_48f, bf_48f : unsigned(15 downto 0);
      signal f_48f : unsigned(31 downto 0);
      signal g_48f, e_48f : std_logic;
      signal l_33f, h_33f, p_33f, s_33f : unsigned(15 downto 0);
      signal r_33f, ff_33f, sd_33f, hd_33f, sa_33f, so_33f, bf_33f : unsigned(15 downto 0);
      signal f_33f : unsigned(31 downto 0);
      signal g_33f, e_33f : std_logic;
      signal l_12f, h_12f, p_12f, s_12f : unsigned(15 downto 0);
      signal r_12f, ff_12f, sd_12f, hd_12f, sa_12f, so_12f, bf_12f : unsigned(15 downto 0);
      signal f_12f : unsigned(31 downto 0);
      signal g_12f, e_12f : std_logic;
      signal l_12p, h_12p, p_12p, s_12p : unsigned(15 downto 0);
      signal r_12p, ff_12p, sd_12p, hd_12p, sa_12p, so_12p, bf_12p : unsigned(15 downto 0);
      signal f_12p : unsigned(31 downto 0);
      signal g_12p, e_12p : std_logic;

   begin

      c100s : entity work.i2c_master_cfg
         generic map (F_SYS_HZ => 100000000, MODE => 0, CNT_W => 16)
         port map (n_low => l_100s, n_high => h_100s, n_rise => r_100s,
            n_fall => ff_100s, n_period => p_100s, n_su_dat => sd_100s,
            n_hd_sta => hd_100s, n_su_sta => sa_100s, n_su_sto => so_100s,
            n_buf => bf_100s, n_sample => s_100s, f_bus_hz => f_100s,
            legal => g_100s, at_requested => e_100s);

      c100f : entity work.i2c_master_cfg
         generic map (F_SYS_HZ => 100000000, MODE => 1, CNT_W => 16)
         port map (n_low => l_100f, n_high => h_100f, n_rise => r_100f,
            n_fall => ff_100f, n_period => p_100f, n_su_dat => sd_100f,
            n_hd_sta => hd_100f, n_su_sta => sa_100f, n_su_sto => so_100f,
            n_buf => bf_100f, n_sample => s_100f, f_bus_hz => f_100f,
            legal => g_100f, at_requested => e_100f);

      c100p : entity work.i2c_master_cfg
         generic map (F_SYS_HZ => 100000000, MODE => 2, CNT_W => 16)
         port map (n_low => l_100p, n_high => h_100p, n_rise => r_100p,
            n_fall => ff_100p, n_period => p_100p, n_su_dat => sd_100p,
            n_hd_sta => hd_100p, n_su_sta => sa_100p, n_su_sto => so_100p,
            n_buf => bf_100p, n_sample => s_100p, f_bus_hz => f_100p,
            legal => g_100p, at_requested => e_100p);

      c50f : entity work.i2c_master_cfg
         generic map (F_SYS_HZ => 50000000, MODE => 1, CNT_W => 16)
         port map (n_low => l_50f, n_high => h_50f, n_rise => r_50f,
            n_fall => ff_50f, n_period => p_50f, n_su_dat => sd_50f,
            n_hd_sta => hd_50f, n_su_sta => sa_50f, n_su_sto => so_50f,
            n_buf => bf_50f, n_sample => s_50f, f_bus_hz => f_50f,
            legal => g_50f, at_requested => e_50f);

      c48f : entity work.i2c_master_cfg
         generic map (F_SYS_HZ => 48000000, MODE => 1, CNT_W => 16)
         port map (n_low => l_48f, n_high => h_48f, n_rise => r_48f,
            n_fall => ff_48f, n_period => p_48f, n_su_dat => sd_48f,
            n_hd_sta => hd_48f, n_su_sta => sa_48f, n_su_sto => so_48f,
            n_buf => bf_48f, n_sample => s_48f, f_bus_hz => f_48f,
            legal => g_48f, at_requested => e_48f);

      c33f : entity work.i2c_master_cfg
         generic map (F_SYS_HZ => 33000000, MODE => 1, CNT_W => 16)
         port map (n_low => l_33f, n_high => h_33f, n_rise => r_33f,
            n_fall => ff_33f, n_period => p_33f, n_su_dat => sd_33f,
            n_hd_sta => hd_33f, n_su_sta => sa_33f, n_su_sto => so_33f,
            n_buf => bf_33f, n_sample => s_33f, f_bus_hz => f_33f,
            legal => g_33f, at_requested => e_33f);

      c12f : entity work.i2c_master_cfg
         generic map (F_SYS_HZ => 12000000, MODE => 1, CNT_W => 16)
         port map (n_low => l_12f, n_high => h_12f, n_rise => r_12f,
            n_fall => ff_12f, n_period => p_12f, n_su_dat => sd_12f,
            n_hd_sta => hd_12f, n_su_sta => sa_12f, n_su_sto => so_12f,
            n_buf => bf_12f, n_sample => s_12f, f_bus_hz => f_12f,
            legal => g_12f, at_requested => e_12f);

      c12p : entity work.i2c_master_cfg
         generic map (F_SYS_HZ => 12000000, MODE => 2, CNT_W => 16)
         port map (n_low => l_12p, n_high => h_12p, n_rise => r_12p,
            n_fall => ff_12p, n_period => p_12p, n_su_dat => sd_12p,
            n_hd_sta => hd_12p, n_su_sta => sa_12p, n_su_sto => so_12p,
            n_buf => bf_12p, n_sample => s_12p, f_bus_hz => f_12p,
            legal => g_12p, at_requested => e_12p);

      stim : process
         variable err : integer := 0;

         procedure ck_int (what : string; g : integer; e : integer) is
         begin
            if g /= e then
               report "  FAIL " & what & ": got " & integer'image(g)
                      & " expected " & integer'image(e) severity note;
               err := err + 1;
            end if;
         end procedure;

         procedure ck_bit (what : string; g : std_logic; e : std_logic) is
         begin
            if g /= e then
               report "  FAIL " & what & ": got " & std_logic'image(g)
                      & " expected " & std_logic'image(e) severity note;
               err := err + 1;
            end if;
         end procedure;

      begin
         -- One delta cycle before reading anything. The block's outputs are concurrent
         -- assignments of constants, and at time zero the order in which processes run --
         -- this one and those assignments -- is not defined. `wait for 0 ns` yields for a
         -- delta so the assignments have settled, without advancing simulation time: the run
         -- still finishes at 0 ns, which is the finish time the other two languages report.
         wait for 0 ns;
         report "=== i2c_master_cfg: Table 10 as divider counts, at elaboration time ==="
                severity note;

         -- T1. 50 MHz Fast-mode: the textbook case, where everything divides.
         report "T1  50 MHz Fast-mode: the case where everything divides evenly"
                severity note;
         ck_int("T1 N_low is ceil(1300 x 50000 / 1e6)",  to_integer(l_50f), 65);
         ck_int("T1 N_high is ceil(600 x 50000 / 1e6)",  to_integer(h_50f), 30);
         ck_int("T1 N_rise is ceil(300 x 50000 / 1e6)",  to_integer(r_50f), 15);
         ck_int("T1 N_fall is ceil(300 x 50000 / 1e6)",  to_integer(ff_50f), 15);
         ck_int("T1 the period is 125",                  to_integer(p_50f), 125);
         ck_int("T1 which is exactly 400 kHz",           to_integer(f_50f), 400000);
         ck_bit("T1 legal",                              g_50f, '1');
         ck_bit("T1 and at the requested rate",          e_50f, '1');

         -- T2. THE PHASES ARE NOT EQUAL. tLOW(min) > tHIGH(min) in every mode, so a
         --     generator built from a symmetric divide-by-two is illegal at the limit.
         report "T2  the low phase is longer than the high phase, in every mode"
                severity note;
         if not (l_100s > h_100s) then
            report "  FAIL T2 Standard-mode phases are not asymmetric" severity note;
            err := err + 1;
         end if;
         if not (l_100f > h_100f) then
            report "  FAIL T2 Fast-mode phases are not asymmetric" severity note;
            err := err + 1;
         end if;
         if not (l_100p > h_100p) then
            report "  FAIL T2 Fast-mode Plus phases are not asymmetric" severity note;
            err := err + 1;
         end if;
         ck_int("T2 Fast-mode is 1300 to 600, i.e. 65 to 30 at 50 MHz",
                to_integer(l_50f) * 6, to_integer(h_50f) * 13);

         -- T3. MODULE 11'S IDENTITY, checked in counts.
         report "T3  the period is the two phases plus the two edge allowances"
                severity note;
         ck_int("T3 at 50 MHz Fast-mode the sum is the period",
                to_integer(l_50f) + to_integer(h_50f) + to_integer(r_50f)
                + to_integer(ff_50f), to_integer(p_50f));
         ck_int("T3 at 100 MHz Standard-mode too",  to_integer(p_100s), 1000);
         ck_int("T3 at 100 MHz Fast-mode Plus too", to_integer(p_100p), 100);

         -- T4. THE FIRST WAY TO GET IT WRONG. At 48 MHz the phase sum EXCEEDS
         --     ceil(f_sys/f_max), so a design using only the frequency term would program
         --     120 and produce phases short of the Table 10 minima.
         report "T4  at 48 MHz the phase sum exceeds the frequency bound, and wins"
                severity note;
         ck_int("T4 N_low",  to_integer(l_48f), 63);
         ck_int("T4 N_high", to_integer(h_48f), 29);
         ck_int("T4 the period is 122, not the 120 the frequency alone would give",
                to_integer(p_48f), 122);
         if not (to_integer(p_48f) > 120) then
            report "  FAIL T4 the phase sum did not win" severity note;
            err := err + 1;
         end if;

         -- T5. AND SO 400 kHz IS NOT ACHIEVABLE AT 48 MHz. 48e6/122 = 393.4 kHz.
         report "T5  so the bus runs at 393 kHz, and the block reports it" severity note;
         ck_int("T5 the achieved frequency", to_integer(f_48f), 393442);
         ck_bit("T5 still legal", g_48f, '1');
         ck_bit("T5 but NOT at the requested rate", e_48f, '0');

         -- T6. SLOWER IS ALWAYS LEGAL, which is the asymmetry that makes rounding up right:
         --     Table 10 gives fSCL a minimum of 0 in every mode.
         report "T6  a slower bus is always legal, which is why rounding up is safe"
                severity note;
         ck_bit("T6 12 MHz Fast-mode is legal", g_12f, '1');
         ck_int("T6 at 375 kHz", to_integer(f_12f), 375000);
         ck_bit("T6 and it says it missed the requested rate", e_12f, '0');
         ck_bit("T6 12 MHz Fast-mode Plus is legal too", g_12p, '1');
         ck_int("T6 at 857 kHz", to_integer(f_12p), 857142);

         -- T7. NO INSTANCE EXCEEDS ITS MODE'S MAXIMUM -- the one thing rounding down breaks.
         report "T7  no instance exceeds its mode's maximum frequency" severity note;
         if to_integer(f_100s) > 100000   then report "  FAIL T7 100 MHz Standard over 100 kHz" severity note; err := err + 1; end if;
         if to_integer(f_100f) > 400000   then report "  FAIL T7 100 MHz Fast over 400 kHz"     severity note; err := err + 1; end if;
         if to_integer(f_100p) > 1000000  then report "  FAIL T7 100 MHz Fm+ over 1 MHz"        severity note; err := err + 1; end if;
         if to_integer(f_50f)  > 400000   then report "  FAIL T7 50 MHz Fast over 400 kHz"      severity note; err := err + 1; end if;
         if to_integer(f_48f)  > 400000   then report "  FAIL T7 48 MHz Fast over 400 kHz"      severity note; err := err + 1; end if;
         if to_integer(f_33f)  > 400000   then report "  FAIL T7 33 MHz Fast over 400 kHz"      severity note; err := err + 1; end if;
         if to_integer(f_12f)  > 400000   then report "  FAIL T7 12 MHz Fast over 400 kHz"      severity note; err := err + 1; end if;
         if to_integer(f_12p)  > 1000000  then report "  FAIL T7 12 MHz Fm+ over 1 MHz"         severity note; err := err + 1; end if;

         -- T8. 33 MHz: rounding the period DOWN to 82 would give 402.4 kHz, which is
         --     illegal. That is the second mistake, as a counter-example.
         report "T8  33 MHz: rounding the period down here would exceed fSCL(max)"
                severity note;
         ck_int("T8 the period is 83", to_integer(p_33f), 83);
         ck_int("T8 giving 397.6 kHz", to_integer(f_33f), 397590);
         if (33000000 / 82) <= 400000 then
            report "  FAIL T8 the arithmetic of the counter-example is wrong" severity note;
            err := err + 1;
         end if;
         ck_bit("T8 legal as computed", g_33f, '1');

         -- T9. EVERY FRAMING PARAMETER IS DERIVED TOO. A design that parameterised the clock
         --     and hard-coded the framing margins would be legal at one system frequency and
         --     illegal at every other.
         report "T9  the framing margins are derived from Table 10 as well" severity note;
         ck_int("T9 tSU;DAT at 50 MHz Fast-mode is ceil(100 x 50000 / 1e6)",
                to_integer(sd_50f), 5);
         ck_int("T9 tHD;STA is 30", to_integer(hd_50f), 30);
         ck_int("T9 tSU;STA is 30", to_integer(sa_50f), 30);
         ck_int("T9 tSU;STO is 30", to_integer(so_50f), 30);
         ck_int("T9 tBUF is 65",    to_integer(bf_50f), 65);

         -- T10. THE SAMPLE POINT SITS INSIDE THE HIGH PHASE at every instance.
         report "T10 the sample point is inside the high phase at every instance"
                severity note;
         if not (s_50f >= 2 and s_50f <= h_50f) then
            report "  FAIL T10 50 MHz Fast" severity note; err := err + 1;
         end if;
         if not (s_100s >= 2 and s_100s <= h_100s) then
            report "  FAIL T10 100 MHz Std" severity note; err := err + 1;
         end if;
         if not (s_12p >= 2 and s_12p <= h_12p) then
            report "  FAIL T10 12 MHz Fm+" severity note; err := err + 1;
         end if;
         ck_int("T10 at 50 MHz Fast-mode it is 17, half of 30 plus two",
                to_integer(s_50f), 17);
         ck_int("T10 at 12 MHz Fm+ the high phase is only 4 cycles",
                to_integer(h_12p), 4);
         ck_int("T10 so the sample lands at 4, the last of them", to_integer(s_12p), 4);

         -- T11. STANDARD-MODE AT 100 MHz NEEDS TEN-BIT PHASE COUNTERS: 470 does not fit in
         --      eight bits, and a truncated phase count is a silently illegal clock.
         report "T11 Standard-mode at 100 MHz needs ten-bit phase counters" severity note;
         ck_int("T11 N_low is 470", to_integer(l_100s), 470);
         if to_integer(l_100s) <= 255 then
            report "  FAIL T11 470 unexpectedly fits in eight bits" severity note;
            err := err + 1;
         end if;
         ck_int("T11 and the period is 1000", to_integer(p_100s), 1000);
         if to_integer(p_100s) <= 511 then
            report "  FAIL T11 1000 unexpectedly fits in nine bits" severity note;
            err := err + 1;
         end if;

         -- T12. EVERY INSTANCE IS LEGAL, and only the evenly-dividing ones hit the
         --      requested rate -- which is the fact a datasheet for this block must state.
         report "T12 every one of the eight configurations satisfies Table 10" severity note;
         ck_bit("T12 100 MHz Standard", g_100s, '1');
         ck_bit("T12 100 MHz Fast",     g_100f, '1');
         ck_bit("T12 100 MHz Fm+",      g_100p, '1');
         ck_bit("T12 50 MHz Fast",      g_50f,  '1');
         ck_bit("T12 48 MHz Fast",      g_48f,  '1');
         ck_bit("T12 33 MHz Fast",      g_33f,  '1');
         ck_bit("T12 12 MHz Fast",      g_12f,  '1');
         ck_bit("T12 12 MHz Fm+",       g_12p,  '1');
         ck_bit("T12 100 MHz hits 400 kHz exactly", e_100f, '1');
         ck_bit("T12 50 MHz does too",              e_50f,  '1');
         ck_bit("T12 48 MHz does not",              e_48f,  '0');
         ck_bit("T12 33 MHz does not",              e_33f,  '0');
         ck_bit("T12 12 MHz does not",              e_12f,  '0');
         -- T13. THE MODE TABLE ITSELF, checked against INDEPENDENT numbers.
         --
         --      Every other test compares this block's counts against this block's own
         --      constants -- the legality check included. That is self-consistent and
         --      therefore cannot detect a wrong entry in the table: change tSU;DAT for
         --      Standard-mode from 250 ns to 50 and the counts, the legality check and
         --      every derived value all agree with each other, and all are wrong.
         --
         --      So these expected values are written out from UM10204 Table 10 by hand, at
         --      100 MHz where one cycle is 10 ns and a count is the time divided by ten,
         --      rounded up.
         report "T13 the mode table checked against Table 10 by hand, not against itself"
                severity note;
         ck_int("T13 Standard tSU;DAT is 25 cycles at 100 MHz", to_integer(sd_100s), 25);
         ck_int("T13 Fast tSU;DAT is 10 cycles", to_integer(sd_100f), 10);
         ck_int("T13 Fast-Plus tSU;DAT is 5 cycles", to_integer(sd_100p), 5);
         ck_int("T13 Standard tLOW is 470 cycles", to_integer(l_100s), 470);
         ck_int("T13 Standard tHIGH is 400 cycles", to_integer(h_100s), 400);
         ck_int("T13 Fast tLOW is 130 cycles", to_integer(l_100f), 130);
         ck_int("T13 Fast tHIGH is 60 cycles", to_integer(h_100f), 60);
         ck_int("T13 Fast-Plus tLOW is 50 cycles", to_integer(l_100p), 50);
         ck_int("T13 Fast-Plus tHIGH is 26 cycles", to_integer(h_100p), 26);



         if err = 0 then
            report "=== i2c_master_cfg: ALL CHECKS PASSED ===" severity note;
         else
            report "=== i2c_master_cfg: " & integer'image(err)
                   & " CHECK(S) FAILED ===" severity note;
         end if;
         wait;
      end process;

   end architecture sim;

6b. Execution

DesignSystemVerilogVerilog-2001VHDLFinish
i2c_master_cfgPASS 13/13PASS 13/13PASS 13/130 ns, all three

7. Mutation Testing — One Proven Equivalent, One Self-Validating Table

Ten defects.

#Injected defectExpected detectionResult
M1round down instead of upT2, T3, T7, T12KILLED (25)
M2drop the phase-sum termT4, T5KILLED (16)
M3drop the frequency-bound term—EQUIVALENT, proven
M4the period omits the rise and fall allowanceT3, T4KILLED (10)
M5a symmetric dividerT2KILLED (27)
M6the sample point is not clampedT10KILLED (19)
M7the sample point exceeds the high phaseT10KILLED (19)
M8the achieved frequency reported as the requestT5, T6KILLED (10)
M9the legality check reversed on tLOWT12KILLED (7)
M10tSU;DAT from the wrong mode columnT13 newKILLED (2)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
valid non-equivalent mutants: 9      killed: 9      survived: 0
documented equivalent mutant: 1      (M3, proven across 600 configurations)
baseline PASS before injection; PASS after restore.

M3 — the frequency bound is mathematically redundant

Dropping ceil(f_sys/f_bus_max) and keeping only the phase sum survived. Rather than assume, the claim was tested by enumeration across three modes × 200 system frequencies:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
configurations where the FREQUENCY bound exceeds the phase sum:  NONE

And there is a proof, not just an absence of counter-examples:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
1.  sum of ceilings  >=  ceiling of sum          (always, for any terms)
2.  tLOW + tHIGH + tr + tf  =  1/fSCL(max)       EXACTLY  (Module 11's identity)

therefore  N_low + N_high + N_r + N_f  >=  ceil( (1/fSCL_max) / T_sys )
                                        =  ceil( f_sys / fSCL_max )

So the phase sum always dominates, and the max can never select the frequency term.

M10 — a self-consistent table cannot validate itself

Changing Standard-mode tSU;DAT from 250 ns to 50 survived everything, including the legality check of §3.

The reason is structural. The legality check compares the derived counts against the same constants the counts were derived from. Change the constant and the count changes with it, and the comparison still passes — because both sides moved together.

T13 breaks the circularity by using independent expected values — Table 10's numbers written out by hand, at one frequency where the arithmetic is checkable by eye:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
at 100 MHz one cycle is 10 ns, so a count is the time in ns over ten, rounded up

  Standard   tSU;DAT  250 ns ->  25       tLOW 4700 -> 470    tHIGH 4000 -> 400
  Fast       tSU;DAT  100 ns ->  10       tLOW 1300 -> 130    tHIGH  600 ->  60
  Fast-Plus  tSU;DAT   50 ns ->   5       tLOW  500 ->  50    tHIGH  260 ->  26

That is the only test in this module whose expected values come from outside the design, and it is the only one that could have caught M10.

8. The Parameter Space Is Part of the Test Space

Three chapters found the same shape, and this is where it stops being an observation.

ChapterThe defectInvisible atVisible at
17.2 §7a missing buffer wrapN_BUF = 8N_BUF = 5
17.3 §10the period formula100 MHz48 MHz
17.9 §6the abandon descriptor's lengthan address NACKa data NACK

In each case no amount of stimulus at the tested configuration could distinguish the correct design from the broken one. The discriminating variable was not the input sequence; it was a parameter, or which of two failure modes the test chose.

9. Counter Widths, and the Parameter Interaction Nobody Expects

CNT_W must hold the largest count any configuration produces. T11 exists for this: Standard mode at 100 MHz needs a 1000-cycle period, so 10 bits minimum.

10. Synthesis and Integration Checklist

Not generic advice — the specific things this design requires of its integrator.

Pads and directions

  • One open-drain pad per line. *_drive_low drives the output enable, data input tied low.
  • *_in comes from the pad's input pin, never from a copy of the enable. Chapter 17.1's mutation M4 is this defect, and it produces a master that cannot arbitrate while simulating perfectly.
  • Two-flop synchronisers on both readbacks, one per line, at the top level. Duplicating them per consumer lets two blocks disagree about what the bus did.

Parameters

  • Set F_SYS_HZ and MODE; take every count from this block rather than computing any of them by hand.
  • Check the elaboration-time legality flag. A configuration that fails it must not be built.
  • Read the achieved frequency and record it. It is generally not the requested one.
  • Size CNT_W from the largest configuration the design will ever be built in, not the first one.

Reset

  • Asynchronous assert, synchronous release, per the usual practice — and confirm the pads are released throughout, including during the power-up ramp before reset deasserts.
  • Follow a system reset with a recovery attempt, not a transaction: other devices may be mid-byte. §5.

Things that are deliberately left to you

  • Wrapping the register port in APB/AHB/AXI-Lite. The seam is explicit so that wrapping it edits nothing inside.
  • Routing escalate to something that can actually reset or power-cycle the bus. Chapter 17.11 §9.
  • The stretch timeout value. §3.1.6 sets no bound, so it is a policy with a stated default.

What not to do

  • Do not treat this as signoff-quality production RTL. It is an educational design whose purpose is that every decision in it is explained and tested.
  • Do not add a digital filter on the readbacks. The pad's input filter already suppresses Table 10's tSP, and a second one consumes high-phase margin for nothing. Chapter 17.6 §9.
Symptom

A design is ported from a 50 MHz FPGA to a 12 MHz low-power part to save power, keeping Fast-mode 400 kHz. It builds without warnings and passes the full block regression, which is run at the design's default 50 MHz. On the 12 MHz board, two of four devices work and a fourth-generation EEPROM returns corrupt data on long reads. The bus measures 375 kHz on a scope, not 400.

Root Cause

A counter width chosen for one configuration, silently truncating in another. CNT_W of 8 bits is correct for every Fast-mode instance at every clock in this design and for Standard mode at 12 and 50 MHz. It is wrong for Standard mode at 100 MHz, where the period count is 1000. Neither the width nor the mode nor the frequency is wrong on its own -- the combination is, and because truncation is silent the instance produced a legal-looking bus running four times over its mode's maximum. The two working devices tolerated it; the EEPROM did not. Nothing in the port touched the failing instance, which is why the investigation went to the parts that had changed.

Fix
Size CNT_W from the largest configuration the design will ever be built in -- here Standard mode at 100 MHz, needing 10 bits -- rather than from the instance in front of you. Then add the check that makes truncation impossible to ship: the configuration block already computes every count as an elaboration-time constant, so it can compare each against the width it will be stored in and refuse to elaborate when one does not fit. That converts a silent wrap into a build failure, which is the only acceptable outcome for a parameter error. Test T11 exists for exactly this configuration and asserts the 1000-cycle period; what it could not catch is a width chosen outside this block.

Three generalisations.

The legal, slower bus was a red herring — and a correctly reported one. 375 kHz at 12 MHz is conforming, and the block said so. The investigation lost time because the visible anomaly was the harmless one.

No parameter was wrong; the combination was. CNT_W = 8, MODE = 0 and 100 MHz are each fine. Every pairwise combination is fine. Only the triple fails, which is why reviewing parameters one at a time finds nothing.

Silent truncation is the failure mode to eliminate structurally. The counts are elaboration-time constants, so the design can compare them against the width that will hold them and refuse to build. A parameter error should never be a runtime behaviour.

12. Common Misconceptions

"Compute the divider from the system clock over the bus frequency." That is one of two terms, and it is the redundant one. The phase sum is what decides the period. §2a, §7.

"Both terms of the max are load-bearing." Only the sum is: a sum of ceilings can never be less than the ceiling of the sum, and Module 11's identity makes them equal at best. §7.

"So the frequency term can be removed." Keep it. It catches a mode table whose four times do not satisfy the identity — which is a table you might edit, not arithmetic you can trust. §7.

"Round to the nearest cycle." Only ceiling. Every constraint is a minimum on a duration or a maximum on a frequency, and both mean longer-and-slower is always safe. §2b.

"A slower bus than requested is a bug." fSCL has no minimum in any mode. It is legal, and the block's job is to report it rather than to hide it. §2c.

"The legality check validates the timing." It validates that the counts match the table. It cannot validate the table, because both sides of the comparison come from the same constants. §7.

"Twelve passing tests means the mode table is right." A single wrong entry passed all twelve, because every one of them was self-consistent. Only independent expected values catch it. §7.

"Verify at the default configuration." Three chapters in this module found defects invisible at their default and visible one parameter value away. §8.

"CNT_W is a width, so pick something comfortable." It is a parameter interaction: 8 bits is right for Fast mode at any clock and wrong for Standard mode at 100 MHz. §9, §11.

"A parameter error will show up in simulation." Silent truncation produces a legal-looking bus at four times its mode's maximum. It should be a build failure. §11.

13. Reason It Through

Why is rounding up safe for every parameter in Table 10 without exception?

Because every constraint is either a minimum on a duration or a maximum on a frequency, and both are satisfied by longer intervals and a slower bus. One policy covers the whole table. §2b.

A 400 kHz request at 48 MHz yields 393.4 kHz. Why is that not a bug, and what would be?

It is not a bug because fSCL has no minimum. The bug would be reporting 400 kHz, which tells the integrator the bus is something it is not. §2c.

Prove that the frequency-bound term can never select in the max.

A sum of ceilings is at least the ceiling of the sum; Module 11's identity makes the four times sum exactly to 1/fSCL(max). So the phase sum is at least ceil(f_sys/fSCL_max), always. §7.

Given that proof, why keep the term?

Because the proof depends on the mode table satisfying the identity. A hand-edited table, an added mode, or a vendor variant may not, and then the term is what stops an illegal period. §7.

Why did a wrong tSU;DAT entry pass every test including the legality check?

Because the check compares derived counts against the constants they were derived from. Both sides move together, so any value is self-consistent. §7.

What makes T13 different from every other test in this module?

Its expected values come from outside the design — Table 10 written out by hand at a frequency where the arithmetic is checkable by eye. §7.

Why can a CNT_W that is correct for every instance you can see still be wrong?

Because it is a parameter interaction: 8 bits holds every Fast-mode count and truncates Standard mode's 1000-cycle period at 100 MHz. No single parameter is wrong. §9, §11.

What is the structural fix for silent truncation, given that the counts are constants?

Compare each count against the width that will hold it at elaboration time and refuse to build when one does not fit. A parameter error should be a build failure, never a runtime behaviour. §11.

14. Understanding Check

15. Summary

Everything is computed at elaboration time and exposed as constants, so a testbench and a synthesis report can both see what the design actually programmed.

The period is the maximum of two terms, and the phase sum is the one that decides it. At 48 MHz in Fast mode the sum is 122 against the frequency division's 120.

Rounding is always up, because every Table 10 constraint is a minimum on a duration or a maximum on a frequency — longer and slower are always legal, so one policy covers the whole table.

The achieved frequency is an output, because it is generally not the frequency requested and nothing else reveals the difference.

Legality is checked without division, since integer division would round the comparison and pass a marginal build.

Ten mutants, nine killed, one proven equivalent. Dropping the frequency bound changes nothing — demonstrated across 600 configurations and then proved: a sum of ceilings cannot be less than the ceiling of the sum, and Module 11's identity makes them equal at best.

That corrects an overstatement in Chapter 17.3, which has been amended. Keep the redundant term anyway: it guards the table, not the arithmetic.

A self-consistent table cannot validate itself. A wrong tSU;DAT entry passed twelve tests, the legality check included, because the counts and the check both come from the same constants. Only independent expected values — Table 10 written out by hand — caught it.

The parameter space is part of the test space, and three chapters found the same shape: a defect invisible at the default and visible one parameter value away. The remedy is a sweep, which here costs nothing because comparing constants needs no simulation.

And counter width is a parameter interaction, not a value. Eight bits is right for every Fast-mode instance and truncates Standard mode's 1000-cycle period at 100 MHz — producing a legal-looking bus at four times its mode's maximum. Elaboration-time width checks turn that into a build failure, which is the only acceptable outcome for a parameter error.

16. What Comes Next

Module 17 is complete. A learner who has worked through it can trace a single argument from the wire to a synthesizable block: the bus is a wired-AND, so a device learns its state only by reading it back; the protocol imposes four independent time bases, so the master is four blocks and not one state machine; framing contradicts the data rule, so the framer cannot live in the bit engine; and the FSM is small because it was written last.

Module 18 turns the design round and builds the target. It is a different problem rather than a mirror image: a target does not own the clock, so every obligation it has must be discharged in windows another device chooses. It cannot stretch its own thinking time without the master's cooperation, it must recognise its address inside a byte it did not initiate, and its acknowledge has to be driven inside a slot whose timing it does not control.

Everything built here — the bit engine's two instants, the open-drain contract, the readback discipline — applies unchanged. What changes is who decides when.

Continue learning

Related tutorials