当前位置: 首页 > article >正文

verilog练习:i2c slave 模块设计

文章目录

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


前言

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

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

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


1. 结构

顶层结构图在这里插入图片描述
master结构图
在这里插入图片描述
slave结构图
在这里插入图片描述

2.代码

2.1 iic_slave.v

`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

`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

`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 状态机状态解释

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

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

`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


http://www.kler.cn/a/539848.html

相关文章:

  • Django项目中创建app并快速上手(pycharm Windows)
  • Chapter2:C#基本数据类型
  • 使用WebStorm开发Vue3项目
  • 使用 Postman 进行 API 测试:从入门到精通
  • Spring Boot 的问题:“由于无须配置,报错时很难定位”,该怎么解决?
  • 【WB 深度学习实验管理】使用 PyTorch Lightning 实现高效的图像分类实验跟踪
  • 链表(LinkedList) 1
  • 基于HTML5 Canvas 和 JavaScript 实现的烟花动画效果
  • 2.6 寒假训练营补题
  • oracle-函数-concat(c1,c2)
  • Linux下Gufw防火墙安装指南
  • Java入门与进阶指南
  • 小哆啦探秘《JavaScript高级程序设计》
  • 力扣动态规划-25【算法学习day.119】
  • Versal - Petalinux 2024.2(下载与安装+VD100+安装JupyterLab+SD卡分区+SDT流程)
  • 机器学习数学公式推导笔记
  • 2025清华:DeepSeek从入门到精通.pdf(附下载)
  • vscode中使用code-runner插件运行c程序语法报错code: 1
  • MyBatis的工作流程是怎样的?
  • Spring Boot Actuator使用
  • AI时代医疗大健康微服务编程提升路径和具体架构设计
  • C++11详解(四) -- 新的类功能和包装器
  • GIT创建子模块(submodule)
  • 【共享文件夹】使用Samba服务可在Ubuntu和Windows系统之间共享一个实际的文件夹
  • 告别人工检测!casaim自动化三维激光扫描
  • sqlite 查看表结构