19. Open-Drain I2C SDA line

Design i2c_line to model an open-drain I²C SDA line using tri1.

  • Module: i2c_line
  • Input: drive_low (1 = actively pull line low, 0 = release)
  • Output: sda (idle high when undriven)

Testbench Code

`timescale 1ns/1ps
module tb_i2c_line;
  reg  drive_low;
  wire sda;
  wire expected_sda;
  wire mismatch;

  i2c_line dut(.drive_low(drive_low), .sda(sda));

  // Expected model: released -> 1 (pull-up), else 0
  assign expected_sda = drive_low ? 1'b0 : 1'b1;
  assign mismatch     = (sda !== expected_sda);

  integer TOTAL_TEST_CASES, TOTAL_PASSED_TEST_CASES, TOTAL_FAILED_TEST_CASES;
  reg [8*20-1:0] tc_name; // simple fixed-width "string"

  // VCD: dump only inputs, outputs, expected, mismatch
  initial begin
    $dumpfile("tb_i2c_line.vcd");
    $dumpvars(0, drive_low, sda, expected_sda, mismatch);
  end

  // Verilog-2005 task style (non-ANSI), no 'bit' type
  task run_case;
    input dl;                       // 1-bit scalar
    input [8*20-1:0] name;          // case label
    begin
      drive_low = dl; tc_name = name; #1;
      $display("CASE=%s : drive_low=%0b | sda=%0b expected_sda=%0b %s",
               tc_name, drive_low, sda, expected_sda,
               mismatch ? "MISMATCH" : "OK");
      TOTAL_TEST_CASES = TOTAL_TEST_CASES + 1;
      if (!mismatch) TOTAL_PASSED_TEST_CASES = TOTAL_PASSED_TEST_CASES + 1;
      else begin
        TOTAL_FAILED_TEST_CASES = TOTAL_FAILED_TEST_CASES + 1;
        $display("  FAILED INPUTS: drive_low=%0b  EXPECTED: expected_sda=%0b",
                 drive_low, expected_sda);
      end
      #4; // spacing in waveform
    end
  endtask

  initial begin
    TOTAL_TEST_CASES = 0; TOTAL_PASSED_TEST_CASES = 0; TOTAL_FAILED_TEST_CASES = 0;

    // ≤ 32 selected VCD cases
    run_case(0, "released_idle_high");  // expect 1
    run_case(1, "pulled_low");          // expect 0

    // Summary logging
    $display("TOTAL_TEST_CASES=%0d", TOTAL_TEST_CASES);
    $display("TOTAL_PASSED_TEST_CASES=%0d", TOTAL_PASSED_TEST_CASES);
    $display("TOTAL_FAILED_TEST_CASES=%0d", TOTAL_FAILED_TEST_CASES);
    $display("ALL_TEST_CASES_PASSED=%s", (TOTAL_FAILED_TEST_CASES==0) ? "true" : "false");

    $finish;
  end
endmodule