I2C 主设备 - 必要的信息不会锁存在状态机的某一状态

问题描述 投票:0回答:1

我正在创建一个 i2c master,我总是用 3 个块构建一个有限状态机。通过接口写入/读取数据后,会出现WACK2状态,其中从机通过设置ACK来确认写入,或者当主机设置NACK时读取结束。就我而言,我正在记录,并且传输数据的最后一位分别在转换到 WACK2 状态时设置,从机无法对 sda 线设置任何内容,因为线方向尚未改变。最终数据位被捕捉到接收总线,而不是来自从机的响应。

我想问题是我的状态根据与前一个状态中的数据所暴露的相同的时钟而改变。我尝试使新数据的状态和锁存分别由 posege 和 negedge 发生,反之亦然,但这没有给出任何结果。

我需要将从从机接收到的信息锁存到 WACK2 状态下的 rx 总线中,而不是正在传输的数据位

I2C 主控代码:

`timescale 1ns / 1ps


module I2C_master(
    input        clk_i, rst_i,                   
    input        start_cmd, stop_cmd,  
    input        rnw_i,     //     read/ not write        
    input        wack2_master_i,
    input  [6:0] addr_i, 
    input  [7:0] din_i,     // data_in
    output [7:0] dout_o,            
    output       i2c_scl_o,
    inout        i2c_sda_io,
    output       ack_o,
    output       ready_o    //data ready             
    );

   
    localparam ACK  = 1'b0;
    localparam NACK = 1'b1;
    logic sda_r, scl_r;    //sda, scl registers
    logic sda_dir;         //direction for sda line
    logic ready_r;
  
    assign i2c_sda_io = sda_dir? sda_r : 1'bz;
    assign i2c_scl_o = scl_r;

    enum logic[3:0]
  {
     IDLE    = 4'd0,
     START   = 4'd1,       
     ADDR    = 4'd2,
     RW      = 4'd3,
     WACK    = 4'd4,
     READ    = 4'd5,
     WRITE   = 4'd6,
     WACK2   = 4'd7,
     HOLD    = 4'd8,
     STOP    = 4'd9  
  }state, new_state;

    logic [2:0] cnt;
    logic [8:0] rx;
    
    assign scl_r = ((state == START) || (state == STOP) || (state == IDLE))? 1 : clk_i;
 
    //3-FSM start
    always_ff@(posedge clk_i) 
        if(rst_i) begin
           state <= IDLE;
           rx    <= '0;
        end
        else state <= new_state;  

    always_comb begin
        new_state = state; 
        case(state)
          IDLE : if(start_cmd)                      new_state = START;
          START:                                    new_state = ADDR;
          ADDR : if(cnt == 0)                       new_state = RW;
          RW   :                                    new_state = WACK;
          WACK : if(i2c_sda_io == ACK)begin 
                    if(rnw_i)                         new_state = READ; 
                    else                            new_state = WRITE;
                 end                                     
          READ : if(cnt == 0)                       new_state = WACK2;
          WRITE: if(cnt == 0)                       new_state = WACK2;
          WACK2:                                    new_state = HOLD;
          HOLD : if(rnw_i) begin
                  if (rx[0] == NACK)begin
                    if(stop_cmd)                    new_state = STOP;
                  end              
                  else                              new_state = READ; 
                 end
                 else    begin
                  if (rx[0] == ACK) begin
                    if (stop_cmd)                   new_state = STOP;
                    else                            new_state = WRITE;
                  end
                  else                              new_state = STOP;
                 end

          STOP :                                    new_state = IDLE;
        endcase
    end

    always_ff@(posedge clk_i) begin
        case(state)
          IDLE : 
               begin
                ready_r     <= 1'b0;
                sda_dir     <= 1'b1;
                sda_r       <= 1'b1;
               end
          START: 
               begin
                 sda_dir    <= 1'b1;
                 sda_r      <= 1'b0;
                 cnt        <= 3'd6;
               end
          ADDR : 
               begin
                 sda_dir    <= 1'b1;
                 sda_r      <= addr_i[cnt];
                 cnt        <= cnt - 1;
               end
          RW   :
               begin
                 sda_dir    <= 1'b1;
                 sda_r      <= rnw_i;
               end
          WACK : 
               begin
                 sda_dir    <= 1'b0;
                 cnt        <= 3'd7; 
               end
                 
          READ :
               begin
                 ready_r    <= 1'b0;
                 sda_dir    <= 1'b0;
                 rx         <= {rx[8:1], i2c_sda_io};
                 cnt        <= cnt - 1;
               end
          WRITE:
               begin
                ready_r     <= 1'b0;
                sda_dir     <= 1'b1;
                sda_r       <= din_i[cnt];
                cnt         <= cnt - 1;
               end
          WACK2: 
               begin
                 if(rnw_i) begin
                   sda_dir <= 1'b1;
                   sda_r   <= wack2_master_i;
                   rx      <= wack2_master_i;
                   ready_r <= 1'b1;
                 end
                 else begin
                   sda_dir = 1'b0;
                   rx      = {rx[8:1], i2c_sda_io};
                   ready_r = 1'b1;
                 end
               end 
          HOLD : cnt        <= 3'd7;            
          STOP : 
               begin
                sda_dir     <= 1'b1;
                sda_r       <= 1'b1;
               end
        endcase
    end
   //3-FSM end

   assign dout_o  = rx[8:1];
   assign ack_o   = rx[0];  
   assign ready_o = ready_r;
endmodule

测试班:

`timescale 1ns / 1ps

module tb;

    // Inputs
    reg clk_i;
    reg rst_i;
    reg start_cmd;
    reg stop_cmd;
    reg rnw;
    reg [6:0] addr_i;
    reg [7:0] din_i;
    reg wack2_master;
    // Outputs
    wire [7:0] dout_o;
    wire i2c_scl_o;
    wire ack_o;
    wire ready_o;

    // Bidirectional
    wire i2c_sda_io;
    reg i2c_sda_io_reg;
    assign i2c_sda_io = i2c_sda_io_reg;

    // Instantiate the I2C_master
    I2C_master uut (
        .clk_i(clk_i),
        .rst_i(rst_i),
        .start_cmd(start_cmd),
        .stop_cmd(stop_cmd),
        .rnw_i(rnw),
        .addr_i(addr_i),
        .din_i(din_i),
        .dout_o(dout_o),
        .i2c_scl_o(i2c_scl_o),
        .i2c_sda_io(i2c_sda_io),
        .ack_o(ack_o),
        .ready_o(ready_o),
        .wack2_master_i(wack2_master)
    );

    // Clock generation
    initial begin
        clk_i = 0;
        forever #5 clk_i = ~clk_i; // 100MHz clock
    end

    // Test sequence
    initial begin
        // Initialize inputs
        rst_i = 1;
        start_cmd = 0;
        stop_cmd = 0;
        rnw = 0;
        addr_i = 7'b1010000; // Example I2C address
        din_i = 8'h00;
        i2c_sda_io_reg = 1'bz; // High impedance

        // Reset the device
        #10 rst_i = 0;

        // Test 1: Write operation
        #10 start_cmd = 1;
        #10 start_cmd = 0;
        rnw = 0; // Write operation
        din_i = 8'h55; // Example data to write
        #95 i2c_sda_io_reg = 1'b0;
        #5  i2c_sda_io_reg = 1'bz;
        #85 i2c_sda_io_reg = 1'b0;
        #100 stop_cmd = 1;
        #10 stop_cmd = 0;

        i2c_sda_io_reg = 1'bz;
       
       
        // Finish simulation
        #100 $finish;
    end

endmodule

timing diagram

verilog system-verilog i2c fsm
1个回答
0
投票

您的 Verilog 代码存在多个问题。

该代码在 EDA Playground 网站上的 4 个不同模拟器上存在编译错误。 将您的代码复制到此处以查看错误消息。 您应该修复这些错误。 例如,从第一个

rx
块中删除
always_ff
将其重置到第二个
always_ff
块中:

always_ff@(posedge clk_i) begin
    if(rst_i)
       rx    <= '0;
    else
    case(state)

您没有将 wack2_master 测试平台信号驱动到已知值,而是将 x 传播到设计中。

© www.soinside.com 2019 - 2024. All rights reserved.