verilog练习:i2c slave 模块设计

文章目录

  • 前言
  • [1. 结构](#1. 结构)
  • 2.代码
    • [2.1 iic_slave.v](#2.1 iic_slave.v)
    • [2.2 sync.v](#2.2 sync.v)
    • [2.3 wr_fsm.v](#2.3 wr_fsm.v)
      • [2.3.1 状态机状态解释](#2.3.1 状态机状态解释)
    • [2.4 ram.v](#2.4 ram.v)
  • [3. 波形展示](#3. 波形展示)
  • [4. 建议](#4. 建议)
  • [5. 资料总结](#5. 资料总结)

前言

首先就不啰嗦iic协议了,网上有不少资料都是叙述此协议的。

下面将是我本次设计的一些局部设计汇总,如果对读者有借鉴意义那最好,如果没有的话也无所谓,互相交流而已。(这是我早期的版本,注释比较少,代码编写比较混乱,读者自便)

希望读者发现问题可在下方留言,我会及时回答或者修改。


1. 结构

顶层结构图

master结构图

slave结构图

2.代码

2.1 iic_slave.v

c 复制代码
`timescale 1ns/1ps

module iic_slave (
    input rstn,
    input clk,
    input scl,
    inout sda,
    input [7:0] q,  // RAM data to slave
    output wen,
    output [7:0] d, // Slave data to RAM
    output [7:0] a  // Slave address to RAM
);

// Internal signals
wire sync_scl_1;
wire sync_sda_1;
wire sda_posedge;
wire sda_negedge;
wire scl_posedge;
wire scl_negedge;
wire sda_out;
wire sda_oen;

// Three-state gate for SDA
assign sda = (sda_oen) ? sda_out : 1'bz;

// Instantiate sync module
sync sync (
    .clk(clk),
    .rstn(rstn),
    .scl(scl),
    .sda(sda),
    .sync_scl_1(sync_scl_1),
    .sync_sda_1(sync_sda_1),
    .sda_posedge(sda_posedge),
    .sda_negedge(sda_negedge),
    .scl_posedge(scl_posedge),
    .scl_negedge(scl_negedge)
);

// Instantiate RAM module
ram ram (
    .clk(clk),
    .rstn(rstn),
    .d(d),
    .a(a),
    .q(q),
    .wen(wen)
);

// Instantiate write FSM module
wr_fsm wr_fsm (
    .clk(clk),
    .rstn(rstn),
    .sync_scl_1(sync_scl_1),
    .sync_sda_1(sync_sda_1),
    .scl_posedge(scl_posedge),
    .scl_negedge(scl_negedge),
    .sda_posedge(sda_posedge),
    .sda_negedge(sda_negedge),
    .d(d),
    .a(a),
    .q(q),
    .wen(wen),
    .sda_out(sda_out),
    .sda_oen(sda_oen)
);

endmodule

2.2 sync.v

c 复制代码
`timescale 1ns/1ps
module sync (
    rstn,
    clk,
    scl,
    sda,
    sync_scl_1,
    sync_sda_1,
    sda_posedge,
    sda_negedge,
    scl_posedge,
    scl_negedge
);

    input rstn;
    input clk;
    input scl;
    input sda;

    output sync_scl_1;
    output sync_sda_1;
    output sda_posedge;
    output sda_negedge;
    output scl_posedge;
    output scl_negedge;

    reg sync_scl_1;
    reg sync_sda_1;
    reg sync_scl_0;
    reg sync_sda_0;

    always @(posedge clk or negedge rstn) begin
        if (!rstn) begin
            sync_scl_1 <= 1'b0;
            sync_sda_1 <= 1'b0;
            sync_scl_0 <= 1'b0;
            sync_sda_0 <= 1'b0;
        end else begin
            sync_scl_0 <= scl;
            sync_sda_0 <= sda;
            sync_scl_1 <= sync_scl_0;
            sync_sda_1 <= sync_sda_0;
        end
    end

    assign sda_posedge = (sync_sda_0) & (~sync_sda_1);
    assign sda_negedge = (~sync_sda_0) & (sync_sda_1);
    assign scl_posedge = (sync_scl_0) & (~sync_scl_1);
    assign scl_negedge = (~sync_scl_0) & (sync_scl_1);

endmodule

2.3 wr_fsm.v

c 复制代码
`timescale 1ns/1ps
module wr_fsm (
    rstn,
    clk,
    sync_scl_1,
    sync_sda_1,
    scl_posedge,
    scl_negedge,
    sda_posedge,
    sda_negedge,
    q,
    d,
    a,
    wen,
    sda_out,
    sda_oen
);

    input rstn, clk;
    input sync_scl_1;
    input sync_sda_1;
    input scl_posedge;
    input scl_negedge;
    input sda_posedge;
    input sda_negedge;
    input [7:0] q;
    output [7:0] d;
    output [7:0] a;
    output wen;
    output sda_out;
    output sda_oen;

    reg wen; // write and read flags reg
    reg [7:0] scl_cnt; // clk delay counter
    reg [3:0] bit_cnt; // valid transfer byte
    reg [7:0] a; // a = save word addr, shift data to ram
    reg sda_out; // data out reg
    reg sda_oen; // three state gate flag bit
    reg [7:0] save_ctrl; // store ctrl word
    reg [7:0] save_q_data; // store data of q
    wire [7:0] q;

    parameter slave_addr = 7'b1101101; // parameter slave addr
    parameter scl_cnt_max = 60-1;

    reg [3:0] state; // state transform
    parameter idle       = 4'd0,
              w_start   = 4'd1,
              w_ctrl    = 4'd2,
              ack1      = 4'd3,
              w_addr    = 4'd4,
              ack2      = 4'd5,
              w_data    = 4'd6,
              ack3      = 4'd7,
              r_start   = 4'd8,
              r_ctrl    = 4'd9,
              ack4      = 4'd10,
              r_data    = 4'd11,
              ack5      = 4'd12,
              stop      = 4'd13;

    always @(posedge clk or negedge rstn)
    begin
        if (!rstn) begin
            state <= idle;
            sda_oen <= 1'b0;
            sda_out <= 1'b1;
            scl_cnt <= 8'b0;
            bit_cnt <= 4'b0;
            sda_out <= 1'b0;
        end else begin
            case (state)
                idle: begin
                    // Initialize state and signals
                    state <= w_start;
                    sda_oen <= 1'b0;
                    sda_out <= 1'b1;
                    scl_cnt <= 8'b0;
                    bit_cnt <= 4'b0;
                    sda_out <= 1'b0;
                end

                w_start: begin
                    // Wait for start condition
                    if (sync_scl_1 && sda_negedge)
                        begin
                            state <= w_ctrl;
                            bit_cnt <= 4'd8;
                        end
                    else
                        state <= w_start;
                end

                w_ctrl: begin
                    // Control word transfer
                    if (scl_negedge)
                        begin
                            save_ctrl <= {save_ctrl[6:0], sync_sda_1};
                            bit_cnt <= bit_cnt - 1;
                            if (bit_cnt == 4'd0)
                                begin
                                    state <= ack1;
                                    bit_cnt <= 4'd8;
                                end
                            else
                                state <= w_ctrl;
                        end
                    else
                        state <= w_ctrl;
                end

                ack1: begin
                    // Acknowledge control word
                    if (save_ctrl[7:1] == slave_addr)
                        begin
                            scl_cnt <= scl_cnt + 8'b1;
                            if (scl_cnt == scl_cnt_max >> 2)
                                begin
                                    sda_out <= 0;
                                    sda_oen <= 1;
                                    state <= ack1;
                                end
                            else if (scl_cnt == (scl_cnt_max >> 2) + scl_cnt_max)
                                begin
                                    state <= w_addr;
                                    sda_oen <= 0;
                                    scl_cnt <= 8'b0;
                                    bit_cnt <= 4'd7;
                                end
                            else
                                state <= ack1;
                        end
                    else
                        state <= stop;
                end

                w_addr: begin
                    // Write address
                    if (scl_negedge)
                        begin
                            bit_cnt <= bit_cnt - 4'b1;
                            wen <= save_ctrl[0]; // write operation
                            a <= {a[6:0], sync_sda_1};
                            if (bit_cnt == 4'd0)
                                begin
                                    bit_cnt <= 4'd7;
                                    state <= ack2;
                                end
                            else
                                state <= w_addr;
                        end
                    else
                        state <= w_addr;
                end

                ack2: begin
                    // Acknowledge address
                    scl_cnt <= scl_cnt + 8'b1;
                    if (scl_cnt == scl_cnt_max >> 2)
                        begin
                            sda_out <= 1'b0;
                            sda_oen <= 1'b1;
                            state <= ack2;
                        end
                    else if (scl_cnt == (scl_cnt_max >> 2) + scl_cnt_max)
                        begin
                            sda_oen <= 1'b0;
                            scl_cnt <= 8'b0;
                            if (wen == 0) // decide write or read
                                state <= w_data;
                            else
                                state <= r_start;
                        end
                    else
                        state <= ack2;
                end

                w_data: begin
                    // Write data
                    if (scl_negedge)
                        begin
                            d <= {d[6:0], sync_sda_1};
                            bit_cnt <= bit_cnt - 4'b1;
                            if (bit_cnt == 4'd0)
                                begin
                                    bit_cnt <= 4'd7;
                                    state <= ack3;
                                end
                            else
                                state <= w_data;
                        end
                    else
                        state <= w_data;
                end

                ack3: begin
                    // Acknowledge data
                    scl_cnt <= scl_cnt + 8'b1;
                    if (scl_cnt == scl_cnt_max >> 2)
                        begin
                            sda_out <= 0;
                            sda_oen <= 1'b1;
                            state <= ack3;
                        end
                    else if (scl_cnt == (scl_cnt_max >> 2) + scl_cnt_max)
                        begin
                            sda_oen <= 1'b0;
                            scl_cnt <= 8'b0;
                            state <= stop;
                        end
                    else
                        state <= ack3;
                end

                r_start: begin
                    // Read start condition
                    if (sync_scl_1 && sda_negedge)
                        begin
                            sda_oen <= 1'b0;
                            bit_cnt <= 4'd8;
                            state <= r_ctrl;
                        end
                    else
                        state <= r_start;
                end

                r_ctrl: begin
                    // Read control word
                    if (scl_negedge)
                        begin
                            bit_cnt <= bit_cnt - 4'b1;
                            save_ctrl <= {save_ctrl[6:0], sync_sda_1};
                            if (bit_cnt == 4'd0)
                                begin
                                    wen <= save_ctrl[0];
                                    bit_cnt <= 4'd7;
                                    state <= ack4;
                                end
                            else
                                state <= r_ctrl;
                        end
                    else
                        state <= r_ctrl;
                end

                ack4: begin
                    // Acknowledge control word
                    if (save_ctrl[7:1] == slave_addr)
                        begin
                            scl_cnt <= scl_cnt + 8'b1;
                            if (scl_cnt == scl_cnt_max >> 2)
                                begin
                                    sda_out <= 0;
                                    sda_oen <= 1;
                                    state <= ack4;
                                end
                            else if (scl_cnt == (scl_cnt_max >> 2) + scl_cnt_max)
                                begin
                                    sda_oen <= 1'b0;
                                    scl_cnt <= 8'b0;
                                    if (wen)
                                        begin
                                            state <= r_data;
                                            sda_oen <= 1'b1;
                                            sda_out <= sync_sda_1;
                                        end
                                    else
                                        state <= w_data;
                                end
                            else
                                state <= ack4;
                        end
                    else
                        state <= stop;
                end

                r_data: begin
                    // Read data
                    if (scl_negedge)
                        begin
                            save_q_data <= q[7:0];
                            bit_cnt <= bit_cnt - 4'b1;
                            sda_out <= save_q_data[7];
                            if (bit_cnt == 4'd0)
                                begin
                                    state <= ack5;
                                    bit_cnt <= 4'd7;
                                    sda_oen <= 0;
                                end
                            else
                                begin
                                    state <= r_data;
                                    sda_oen <= 1;
                                    save_q_data <= {save_q_data[6:0], 1'b0};
                                end
                        end
                    else
                        state <= r_data;
                end

                ack5: begin
                    // Acknowledge data
                    if (scl_posedge)
                        begin
                            if (sync_sda_1 == 1)
                                state <= stop;
                            else
                                state <= idle;
                        end
                    else
                        state <= ack5;
                end

                stop: begin
                    // Stop condition
                    if (sync_scl_1 && sda_posedge)
                        begin
                            state <= idle;
                            sda_oen <= 1'b0;
                            sda_out <= 1'b1;
                        end
                    else
                        state <= stop;
                end

                default: state <= idle;
            endcase
        end
    end
endmodule

2.3.1 状态机状态解释

当然可以!以下是优化后的代码中每个状态的作用解释:

c 复制代码
reg [3:0] state; // state transform
parameter idle       = 4'd0,
          w_start   = 4'd1,
          w_ctrl    = 4'd2,
          ack1      = 4'd3,
          w_addr    = 4'd4,
          ack2      = 4'd5,
          w_data    = 4'd6,
          ack3      = 4'd7,
          r_start   = 4'd8,
          r_ctrl    = 4'd9,
          ack4      = 4'd10,
          r_data    = 4'd11,
          ack5      = 4'd12,
          stop      = 4'd13;

状态作用解释

  1. idle (4'd0):
  • 作用: 初始状态,等待复位信号或起始条件。
  • 描述: 在这个状态下,所有信号被初始化,状态机等待复位信号 rstn 或起始条件(sync_scl_1 和 sda_negedge)。
  1. w_start (4'd1):
  • 作用: 等待起始条件。
  • 描述: 在这个状态下,状态机检测起始条件(sync_scl_1 和 sda_negedge)。如果检测到起始条件,状态机进入 w_ctrl 状态。
  1. w_ctrl (4'd2):
  • 作用: 接收控制字。
  • 描述: 在这个状态下,状态机接收控制字(save_ctrl),并将其存储在寄存器中。控制字的接收通过 scl_negedge 信号完成。当接收到完整的控制字后,状态机进入 ack1 状态。
  1. ack1 (4'd3):
  • 作用: 发送 ACK 信号。
  • 描述: 在这个状态下,状态机发送 ACK 信号(sda_out 和 sda_oen)。如果接收到的控制字匹配从设备地址(slave_addr),状态机进入 w_addr 状态。否则,状态机进入 stop 状态。
  1. w_addr (4'd4):
  • 作用: 接收地址。
  • 描述: 在这个状态下,状态机接收地址数据(a),并将其存储在寄存器中。地址的接收通过 scl_negedge 信号完成。当接收到完整的地址后,状态机进入 ack2 状态。
  1. ack2 (4'd5):
  • 作用: 发送 ACK 信号。
  • 描述: 在这个状态下,状态机发送 ACK 信号(sda_out 和 sda_oen)。根据控制字中的写入标志(wen),状态机决定进入 w_data 状态(写入数据)或 r_start 状态(读取数据)。
  1. w_data (4'd6):
  • 作用: 写入数据。
  • 描述: 在这个状态下,状态机接收数据(d),并将其存储在寄存器中。数据的接收通过 scl_negedge 信号完成。当接收到完整的数据后,状态机进入 ack3 状态。
  1. ack3 (4'd7):
  • 作用: 发送 ACK 信号。
  • 描述: 在这个状态下,状态机发送 ACK 信号(sda_out 和 sda_oen)。然后状态机进入 stop 状态。
  1. r_start (4'd8):
  • 作用: 等待读取起始条件。
  • 描述: 在这个状态下,状态机检测读取起始条件(sync_scl_1 和 sda_negedge)。如果检测到起始条件,状态机进入 r_ctrl 状态。
  1. r_ctrl (4'd9):
  • 作用: 接收控制字。
  • 描述: 在这个状态下,状态机接收控制字(save_ctrl),并将其存储在寄存器中。控制字的接收通过 scl_negedge 信号完成。当接收到完整的控制字后,状态机进入 ack4 状态。
  1. ack4 (4'd10):
  • 作用: 发送 ACK 信号。
  • 描述: 在这个状态下,状态机发送 ACK 信号(sda_out 和 sda_oen)。如果接收到的控制字匹配从设备地址(slave_addr),状态机进入 r_data 状态。否则,状态机进入 stop 状态。
  1. r_data (4'd11):
  • 作用: 读取数据。
  • 描述: 在这个状态下,状态机读取数据(q),并将其存储在寄存器中。数据的读取通过 scl_negedge 信号完成。当读取完数据后,状态机进入 ack5 状态。
    13。 ack5 (4'd12):
  • 作用: 发送 ACK 信号。
  • 描述: 在这个状态下,状态机发送 ACK 信号(sda_out 和 sda_oen)。然后状态机进入 idle 状态。
  1. stop (4'd13):
  • 作用: 停止状态。
  • 描述: 在这个状态下,状态机等待停止条件(sync_scl_1 和 sda_posedge)。如果检测到停止条件,状态机进入 idle 状态。

每个状态的作用如下:

  • idle: 初始状态,等待复位或起始条件。
  • w_start: 等待起始条件。
  • w_ctrl: 接收控制字。
  • ack1: 发送 ACK 信号,确认控制字接收。
  • w_addr: 接收地址。
  • ack2: 发送 ACK 信号,确认地址接收。
  • w_data: 写入数据。
  • ack3: 发送 ACK 信号,确认数据写入。
  • r_start: 等待读取起始条件。
  • r_ctrl: 接收控制字。
  • ack4: 发送 ACK 信号,确认控制字接收。
  • r_data: 读取数据。
  • ack5: 发送 ACK 信号,确认数据读取。
  • stop: 停止状态,等待停止条件。

这些状态共同构成了一个完整的 I2C 从设备写入和读取的有限状态机,确保数据的正确传输和接收。

2.4 ram.v

c 复制代码
`timescale 1ns/1ps
module ram (
    clk,
    rstn,
    d,
    a,
    q,
    wen
);

    // Input ports
    input clk, rstn;
    input [7:0] a;  // Address input
    input [7:0] d;  // Data input
    input wen;      // Write enable

    // Output ports
    output [7:0] q; // Data output

    // Internal registers
    reg [7:0] ram [255:0]; // RAM array
    integer i;             // Loop counter
    reg [7:0] q;           // Output data register

    // Always block for RAM operations
    always @(posedge clk or negedge rstn)
    begin
        if (!rstn) begin
            // Initialize RAM on reset
            for (i = 0; i <= 255; i = i + 1)
                ram[i] <= 8'b0;
        end else begin
            if (!wen) begin
                // Write operation: wen = 0
                ram[a] <= d;
            end else begin
                // Read operation: wen = 1
                q <= ram[a];
            end
        end
    end

endmodule

3. 波形展示

4. 建议

必看

此设计还存在一些问题,后续有时间我会完善的。

在同步的时候我建议还是使用两个寄存器缓冲,而不是使用一个,使用多个更加的稳妥一些,我这个就是使用了较少的寄存器缓冲,所以波形中有问题。(我把这段字打个红色背景)。(是因为在边沿检测的时候无法确认信号是否同步还是异步所以在设计的时候还是使用双寄存器进行消除亚稳态)

5. 资料总结

练习时的一些思路。

https://blog.csdn.net/weixin_46163885/article/details/107170689

相关推荐
知识分享小能手1 小时前
React学习教程,从入门到精通, React 属性(Props)语法知识点与案例详解(14)
前端·javascript·vue.js·学习·react.js·vue·react
茯苓gao4 小时前
STM32G4 速度环开环,电流环闭环 IF模式建模
笔记·stm32·单片机·嵌入式硬件·学习
是誰萆微了承諾4 小时前
【golang学习笔记 gin 】1.2 redis 的使用
笔记·学习·golang
DKPT5 小时前
Java内存区域与内存溢出
java·开发语言·jvm·笔记·学习
aaaweiaaaaaa5 小时前
HTML和CSS学习
前端·css·学习·html
看海天一色听风起雨落6 小时前
Python学习之装饰器
开发语言·python·学习
speop7 小时前
llm的一点学习笔记
笔记·学习
非凡ghost7 小时前
FxSound:提升音频体验,让音乐更动听
前端·学习·音视频·生活·软件需求
ue星空8 小时前
月2期学习笔记
学习·游戏·ue5
萧邀人8 小时前
第二课、熟悉Cocos Creator 编辑器界面
学习