1

I'm writing my first ever I2C program in Verilog and I'm struggling with the TestBench.

I want to test the I2C Slave in isolation, but I'm unable to set different SDA values: SDA is always 1, or X.

Am I going about testing the wrong way? My thoughts are I should be able to on every clock, set the SDA and see what happens.

Playground here: https://edaplayground.com/x/6snM

module Slave(
    inout SDA,
    input SCL);

  reg [4:0] IDLE        = 4'b0000;
  reg [4:0] START       = 4'b0001;
  reg [4:0] ADDRESS     = 4'b0010;
  reg [4:0] READ_WRITE  = 4'b0011;
  reg [4:0] DATA        = 4'b0100;
  reg [4:0] WRITE_ACK   = 4'b0101;
  reg [4:0] STOP        = 4'b0110;

  reg [4:0] state       = 4'b0000;

  reg [6:0] slaveAddress = 7'b0001000;
  reg [6:0] addressCounter = 7'b0000000;

  reg readWrite         = 1'b0;

  reg write_ack         = 0;

  assign SDA  = write_ack ? 0 : 1'bz;

  always @(posedge SCL)
    begin
      case (state)
        IDLE:
          begin
            if (SDA == 0 && SCL == 1)
            begin
                state <= ADDRESS;
            end
          end
        START: 
          begin
            readWrite <= 0;
          end
        ADDRESS: 
          begin
            if (slaveAddress[addressCounter] != SDA)
            begin
              state <= STOP;
              addressCounter <= 0;
            end

            addressCounter <= addressCounter + 1;
      
            if (addressCounter >= 6) 
                begin
                  write_ack <= 1;
                  state <= READ_WRITE;
                end
          end
        READ_WRITE:
          begin
            write_ack <= 0;
            state <= DATA;
          end
      endcase
    end

endmodule

I want to drive SDA so that I can pass in a sequence of bits, but whatever I do, SDA remains the same (1) or X.

module Slave_TB ();

  reg clk;

  wire SDA;
  wire SCL;

  pullup(SDA);
  pullup(SCL);

  reg [8:0] bitsToSend = 9'b000100010;

  integer ii=0;

  initial begin
        clk = 0;
        forever begin
            clk = #1 ~clk;
        end     
    end


  Slave UUT
    (.SDA(SDA),
     .SCL(SCL));

  initial 
    begin
      $display("Starting Testbench...");
  
      clk = 0;
      force SCL = clk;
  
      #10

      for(ii=0; ii<9; ii=ii+1)
        $display("SDA %h to %h", SDA, bitsToSend[ii]);
        #1 force SDA = bitsToSend[ii];

      #10;
      $finish();
    end

  initial 
  begin
    // Required to dump signals to EPWave
    $dumpfile("dump.vcd");
    $dumpvars(0);
  end

endmodule
toolic
  • 57,801
  • 17
  • 75
  • 117
Chris
  • 2,739
  • 4
  • 29
  • 57

1 Answers1

1

I added $time to the $display statement, and this clearly showed that SDA was changing, but it was changing all at the same time (10):

              10 SDA 1 to 0
              10 SDA 1 to 1
              10 SDA 1 to 0
              10 SDA 1 to 0
              10 SDA 1 to 0
              10 SDA 1 to 1
              10 SDA 1 to 0
              10 SDA 1 to 0
              10 SDA 1 to 0

Your indentation of the for loop is misleading because the #1 delay is not part of the loop. You need to add begin/end keywords to include the delay in your for loop:

  for(ii=0; ii<9; ii=ii+1) begin
    $display($time, " SDA %h to %h", SDA, bitsToSend[ii]);
    #1 force SDA = bitsToSend[ii];
  end

When I make that change, I see SDA go low in the waveforms. Here is the new print out:

              10 SDA 1 to 0
              11 SDA 0 to 1
              12 SDA 1 to 0
              13 SDA 0 to 0
              14 SDA 0 to 0
              15 SDA 0 to 1
              16 SDA 1 to 0
              17 SDA 0 to 0
              18 SDA 0 to 0
toolic
  • 57,801
  • 17
  • 75
  • 117
  • Related with simulation diagram: https://stackoverflow.com/questions/67559702/testbench-i2c-slave-sda-wont-go-low – Chris May 16 '21 at 17:34