TestBench I2C Slave SDA 不会变低 [英] TestBench I2C Slave SDA won't go low

查看:56
本文介绍了TestBench I2C Slave SDA 不会变低的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试编写一个 I2C Slave 并对其进行隔离测试.

我有一个模拟,当 write_ack 为高(也由红点突出显示)时,它应该将 SDA 拉低.但是,您可以看到 SDA 保持不变.

我的一部分认为这与我使用 force 方法和延迟进行测试的方式有关.

感谢任何帮助.

我发现关键字 release 似乎有帮助.

下面的代码 &EDA 游乐场在这里:

/**I2C Slave 仅读取/写入 8 位数据*/`时间刻度 1ns/1ps模块从站(输入线SDA,输入线 SCL);reg [4:0] 空闲 = 4'b0000;reg [4:0] 开始 = 4'b0001;reg [4:0] READ_ADDRESS = 4'b0010;reg [4:0] READ_WRITE = 4'b0011;reg [4:0] 数据 = 4'b0100;reg [4:0] DATA_ACK = 4'b0101;reg [4:0] 停止 = 4'b0110;reg [4:0] ADDRESS_ACK = 4'b0111;reg [4:0] 状态 = 4'b0010;reg [6:0] slaveAddress = 7'b0001000;reg [7:0] 地址;reg [6:0] addressCounter = 7'b0000000;reg [7:0] 数据;reg [6:0] dataCounter = 7'b0000000;reg readWrite = 1'b0;注册开始= 0;reg write_ack = 0;分配 SDA = (write_ack == 1) ?0 : 'b1z;总是@(negedge SDA) 开始if ((开始 == 0) && (SCL == 1))开始开始 <= 1;地址计数器 <= 0;数据计数器 <= 0;结尾结尾总是@(posedge SDA) 开始如果(状态 == DATA_ACK && SCL == 1)开始开始 <= 0;状态 <= READ_ADDRESS;结尾结尾总是@(posedge SCL)开始如果(开始== 1)开始案例(状态)读取地址:开始addr[addressCounter] <= SDA;addressCounter <= addressCounter + 1;如果(地址计数器 == 6)开始状态 <= READ_WRITE;结尾结尾读_写:开始读写 <= SDA;状态 <= ADDRESS_ACK;结尾地址_确认:开始写确认 <= 1;状态 <= 数据;结尾数据:开始write_ack <= 0;数据[数据计数器] <= SDA;数据计数器 <= 数据计数器 + 1;如果(数据计数器 == 8)开始状态 <= DATA_ACK;写确认 <= 1;结尾结尾数据确认:开始write_ack <= 0;状态 <= 停止;结尾停止:开始开始 <= 0;状态 <= READ_ADDRESS;结尾尾箱结尾结尾结束模块

测试代码

/**测试 I2C Slace 以仅读取/写入 8 位数据*/`时间刻度 1ns/1ps模块 Slave_TB();注册时钟;线材SDA;线 SCL;上拉(SDA);上拉(SCL);reg [6:0] addressToSend = 7'b0001000;reg readWite = 1'b1;reg [7:0] dataToSend = 8'b01100111;整数 ii=0;最初的开始时钟 = 0;强制 SCL = 时钟;永远的开始clk = #1 ~clk;强制 SCL = 时钟;结尾结尾从机#() UUT(.SDA(SDA),.SCL(SCL));最初的开始$display(启动测试平台...");时钟 = 0;强制 SCL = 时钟;#11//设置 SDA 低开始强制 SDA = 0;//写地址for(ii=0; ii<7; ii=ii+1)开始$display("Address SDA %h to %h", SDA, addressToSend[ii]);#2 强制 SDA = addressToSend[ii];结尾//我们是要从设备读取还是写入?$display(读/写 %h SDA: %h", readWite, SDA);#2 强制 SDA = readWite;$display("SDA: %h", SDA);#2;//等待ACK位for(ii=0; ii<8; ii=ii+1)开始$display("Data SDA %h to %h", SDA, dataToSend[ii]);#2 强制 SDA = dataToSend[ii];结尾#2;//等待ACK位//再次强制 SDA 为高,我们完成了#2 强制 SDA = 1;#100;$完成();结尾最初的开始//需要将信号转储到 EPWave$dumpfile(dump.vcd");$dumpvars(0);结尾结束模块

解决方案

代替使用 force,更传统的方法是向测试台添加三态缓冲区,就像您在设计中所做的那样.

对于SDA,创建缓冲区控制信号(drive_sda)和测试平台数据信号(sda_tb).使用 task 驱动一个字节并等待 ACK.

由于SCL不是inout,所以不需要上拉,直接用clk驱动即可.

module Slave_TB;注册时钟;线材SDA;线 SCL = 时钟;上拉(SDA);reg [6:0] addressToSend = 7'b000_1000;//8reg readWite = 1'b1;//写reg [7:0] dataToSend = 8'b0110_0111;//103 = 0x67注册 sda_tb;reg drive_sda;分配 SDA = (drive_sda) ?sda_tb : 1'bz;整数 ii=0;最初的开始时钟 = 0;永远的开始clk = #1 ~clk;结尾结尾从属 UUT(.SDA(SDA),.SCL(SCL));最初的开始$display(启动测试平台...");驱动器_sda = 0;sda_tb = 1;#11;//设置 SDA 低开始驱动器_sda = 1;sda_tb = 0;写({addressToSend,readWite});写(数据发送);//再次强制 SDA 为高,我们完成了#2;驱动器_sda = 1;sda_tb = 1;#50;$完成;结尾任务写入(reg [7:0] 数据);整数 ii;对于 (ii=7; ii>=0; ii=ii-1) 开始$display("Data SDA %h 到 %h", SDA, data[ii]);#2;驱动器_sda = 1;sda_tb = 数据[ii];结尾#2 drive_sda = 0;结束任务最初的开始//需要将信号转储到 EPWave$dumpfile(dump.vcd");$dumpvars(0);结尾结束模块

I'm trying to write an I2C Slave and test it in isolation.

I have a simulation that should be pulling SDA low when write_ack is high (Also highlighted by the red dots). However, you can see that SDA remains the same.

Part of me thinks it's to do with the way I'm testing with the force methods and the delays.

Any help appreciated.

I have found the keyword release which seems to help.

Code below & EDA Playground is here: https://edaplayground.com/x/6snM

/**
I2C Slave to Read/Write 8 bits of data only
*/

`timescale 1ns / 1ps

module Slave(
    inout wire SDA,
    input wire SCL);

  reg [4:0] IDLE            = 4'b0000;
  reg [4:0] START           = 4'b0001;
  reg [4:0] READ_ADDRESS    = 4'b0010;
  reg [4:0] READ_WRITE      = 4'b0011;
  reg [4:0] DATA            = 4'b0100;
  reg [4:0] DATA_ACK        = 4'b0101;
  reg [4:0] STOP            = 4'b0110;
  reg [4:0] ADDRESS_ACK     = 4'b0111;

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

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

  reg [7:0] data;
  reg [6:0] dataCounter     = 7'b0000000;

  reg readWrite         = 1'b0;
  reg start = 0;
  reg write_ack         = 0;

  assign SDA = (write_ack == 1) ? 0 : 'b1z;

  always @(negedge SDA) begin
    if ((start == 0) && (SCL == 1)) 
    begin
        start <= 1;
        addressCounter <= 0;
        dataCounter <= 0;
    end
  end

  always @(posedge SDA) begin
    if (state == DATA_ACK && SCL == 1)
      begin
        start <= 0;
        state <= READ_ADDRESS;
      end
    end

  always @(posedge SCL)
    begin
        if (start == 1)
        begin
          case (state)
            READ_ADDRESS: 
              begin
                addr[addressCounter] <= SDA;
                addressCounter <= addressCounter + 1;
                if (addressCounter == 6) 
                    begin
                     state <= READ_WRITE;
                   end
             end
           READ_WRITE:
             begin
                readWrite <= SDA;
                state <= ADDRESS_ACK;
              end
            ADDRESS_ACK:
              begin
                write_ack <= 1;
                state <= DATA;
              end
            DATA:
              begin
                write_ack <= 0;
            
                data[dataCounter] <= SDA;
                dataCounter <= dataCounter + 1;
                if (dataCounter == 8) 
                    begin
                     state <= DATA_ACK;
                      write_ack <= 1;
                   end
              end
            DATA_ACK:
             begin
               write_ack <= 0;
               state <= STOP;
              end
            STOP:
              begin
                start <= 0;
                state <= READ_ADDRESS;
              end
          endcase
        end
    end


endmodule

Test Code

/**
Testing I2C Slace for reading/writing 8 bits of data only
*/

`timescale 1ns / 1ps

module Slave_TB ();

  reg clk;

  wire SDA;
  wire SCL;

  pullup(SDA);
  pullup(SCL);

  reg [6:0] addressToSend   = 7'b0001000;
  reg readWite              = 1'b1;
  reg [7:0] dataToSend      = 8'b01100111;

  integer ii=0;

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


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

  initial 
    begin
      $display("Starting Testbench...");
  
      clk = 0;
      force SCL = clk;
  
      #11
  
      // Set SDA Low to start
      force SDA = 0;

      // Write address
      for(ii=0; ii<7; ii=ii+1)
        begin
          $display("Address SDA %h to %h", SDA, addressToSend[ii]);
          #2 force SDA = addressToSend[ii];
        end
  
      // Are we wanting to read or write to/from the device?
      $display("Read/Write %h SDA: %h", readWite, SDA);
      #2 force SDA = readWite;
  
      $display("SDA: %h", SDA);
      #2; // Wait for ACK bit
  
      for(ii=0; ii<8; ii=ii+1)
        begin
          $display("Data SDA %h to %h", SDA, dataToSend[ii]);
          #2 force SDA = dataToSend[ii];
        end
  
      #2; // Wait for ACK bit
  
      // Force SDA high again, we are done
      #2 force SDA = 1;

      #100;
      $finish();
    end

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

endmodule

解决方案

Instead of using force, a more conventional approach is to add a tristate buffer to the testbench, just like you have in the design.

For SDA, create a buffer control signal (drive_sda) and a testbench data signal (sda_tb). Use a task to drive a byte and wait for the ACK.

Since SCL is not an inout, there is no need for a pullup, and it can be directly driven by clk.

module Slave_TB;
    reg clk;

    wire SDA;
    wire SCL = clk;

    pullup(SDA);

    reg [6:0] addressToSend   = 7'b000_1000;  //8
    reg readWite              = 1'b1;         //write
    reg [7:0] dataToSend      = 8'b0110_0111; //103 = 0x67
    reg sda_tb;
    reg drive_sda;
    assign SDA = (drive_sda) ? sda_tb : 1'bz;

    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...");

    drive_sda = 0;
    sda_tb = 1;

    #11;

    // Set SDA Low to start
    drive_sda = 1;
    sda_tb = 0;

    write({addressToSend, readWite});
    write(dataToSend);

    // Force SDA high again, we are done
    #2;
    drive_sda = 1;
    sda_tb = 1;

    #50;
    $finish;
end

task write (reg [7:0] data);
    integer ii;
    for (ii=7; ii>=0; ii=ii-1) begin
        $display("Data SDA %h to %h", SDA, data[ii]);
        #2;
        drive_sda = 1;
        sda_tb = data[ii];
    end
    #2 drive_sda = 0;
endtask

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

这篇关于TestBench I2C Slave SDA 不会变低的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆