Menu Close

I2C cmd层 Verilog程序结构化设计

在实现I2c PHY层设计后,可以实现4种PHY层的操作: 1)START + ADDR + WR/RD +ACK/NACK;2)WR_DATA + ACK/NACK;3)RD_DATA + ACK/NACK;4)STOP。之后就需要上层模块将PHY层组装起啦,根据用户指令,cmd模块分别发送这4种操作,完成I2c 协议。cmd 层设计结构如下:

1 cmd 层结构设计

图1

如图1所示, 在phy层基础上设计iic_cmd 层, 这层主要是用来将用户发来的数据解析为phy层所识别的4种操作,完成相应的i2c 总线协议。iic_cmd 层需要和上层应用进行数据交互(握手),同时将上层的数据翻译为phy层操作命令。 这里就需要定义和上层的接口协议了。

图2

如图2 所示, iic_cmd层和上层应用使用一个接口协议,用来确定上层数据的格式。分别为写I2C 数据格式和分别为读I2C 数据格式。

  • 写I2C 数据格式:总包长(1byte) + 完成I2c协议后等待时间(ms 单位, 1byte) + 设备地址 + 写 (1 byte) +  数据 。。。。
  • 读I2C 数据格式:总包长(1byte) + 完成I2c协议后等待时间(ms 单位, 1byte) + 设备地址 + 读 (1 byte) + 从设备种读取多收字节数据(1byte) + 设备存储地址(可选 ,1-2 bytes)

packet length : 数据包第1字节,标识当前包的总长度。

delay time : 数据包第2字节,表示当前I2c 操作后, 等待多少ms,再进行下次操作。

slave addr + op: 数据包第3字节,标识当前操作的外设地址,同时包括读写信息, OP = 0 ,表示写; OP = 1, 表示 读。

data/reg:写数据包第4字节,可以是设备存贮地址或者是写数据。

read data length:读数据包第4字节,表示准备从设备种读取多少个字节。 如果为0, 表示当前读操作无效。

data:写数据包第5 – n字节,表示有多少数据需要写入设备中。 写入数据总量可以通过 packet length – 3 计算出来。

reg0,reg1:写数据包第5 – 6字节,可选项,如果有,代表从设备的哪个存储地址开始读取数据,如果没有,表示从当前地址读取设备数据。

 

iic_cmd 层就是接收到以上包协议,根据协议,分析出控制器要对设备进行那些操作。

2 iic_cmd 模块解析

iic_cmd 模块主要是通过一个状态机解析数据包格式,得到相关的iic phy 所需要的信息,完成对phy的相关操作,最后在根据协议,是否需要延迟,延迟多长时间等。模块与上层应用的数据接口如下:

// app interface   
   input      [7:0] pkg_din,
   input            pkg_din_vld,
   output reg       pkg_din_ack = 0,
   output reg       pkg_end = 0,
   
   output reg [7:0] pkg_dout = 0,
   output reg       pkg_dout_vld = 0,

上层应用使用pkg_din_vld 发送完整数据包, pkg_din_vld 上升沿和pkg_din 同时由上层应用发出。

iic_cmd 得到pkg_din_vld 信号,抓取pkg_din 信息(数据包第1字节),返回

图3

pkg_din_vld: 数据输入包络线, 从第一个数据输入开始,知道cmd 层返回pkg_end 信号后,结束。

pkg_din: 应用层发给cmd层的数据。

pkg_din_ack:每当cmd层得到一个数据后, 就产生一个ack信号,通知上层应用程序,可以发送下一个数据了。

pkg_end:当前包结束标志,cmd层发出,通知上层应用程序,当前包处理结束。

pkg_dout: cmd模块输出,从phy层得到的数据被发送到应用层。

pkg_dout_vld: 指示pkg_dout 数据有效,只有一个时钟宽度。

状态机接口如下:

图4

 

黄色部分: 读取应用层数据,进行写操作

灰色部分:读操作,读取应用层数据, 确定读取设备中的memory 地址

绿色部分:读操作,读取设备中的数据。

iic_cmd代码部分:

`timescale 1ns / 1ps


module iic_cmd #
(
    parameter CLK_F = 100_000_000
)
(
   input            sys_clk,
// phy interface   
   output reg [1:0] op_cmd,
   output reg       op_cmd_en,
   input            op_cmd_done,

   output reg       master_ack,
   input            dev_ack,

   output reg [7:0] data_in,
   input      [7:0] data_out,
// app interface   
   input      [7:0] pkg_din,
   input            pkg_din_vld,
   output reg       pkg_din_ack = 0,
   output reg       pkg_end = 0,
   
   output reg [7:0] pkg_dout = 0,
   output reg       pkg_dout_vld = 0,
    
   output reg       i2c_error = 0,
   input            rst

);
 
localparam I2C_OP_START    = 2'b00;
localparam I2C_OP_WR_DATA  = 2'b01;
localparam I2C_OP_RD_DATA  = 2'b10;
localparam I2C_OP_STOP     = 2'b11;
//==============================================
localparam MS_CNT = CLK_F/1000;
reg ms_flag = 0;
reg [23:0] ms_cnt = 0;
always @ (posedge sys_clk)
if(pkg_end) 
begin
    ms_cnt <= 0;
    ms_flag <= 0;
end
else
begin
    ms_flag <= 0;
    
    if(ms_cnt == MS_CNT - 1)
    begin
        ms_flag <= 1;
        ms_cnt <= 0;
    end
    else ms_cnt <= ms_cnt + 1;
end


reg [7:0] reg0 = 0;
reg [7:0] reg1 = 0;

reg [7:0] pkg_len = 0;
reg [7:0] read_len = 0;
reg [7:0] pkg_delay = 0;
reg [6:0] dev_addr = 0;

reg [31:0] cnt;
reg [4:0]  st = 0;
//======================
always@(posedge sys_clk or posedge rst)
if(rst)
begin
    cnt <= 0;
    op_cmd <= 0;
    op_cmd_en <= 0;
    master_ack <= 0;
    
    pkg_din_ack <= 1'b0;
    pkg_dout_vld <= 1'b0;
    
    i2c_error <= 0;
    st <= 0;
end
else  case(st)
0: 
begin
    op_cmd <= 0;
    op_cmd_en <= 1'b0;
    i2c_error <= 0;
    
    cnt <= 0;
    
    pkg_dout_vld <= 1'b0;
    pkg_din_ack <= 1'b0;
    pkg_end <= 0;

    if(pkg_din_vld) // waiting for app start 
    begin
        pkg_len <= pkg_din; // get package length from app layer
        pkg_din_ack <= 1;
        st <= 1;
    end
end
1:
begin
    pkg_din_ack <= 0;
    if(pkg_len < 4 ) 
    begin
        pkg_end <= 1;
        st <= 21;
    end
    else st <= 2;
end
2:
begin
    pkg_delay <= pkg_din; // get delay time after the transaction finished
    pkg_din_ack <= 1;
    st <= 3;
end
3:
begin
    pkg_din_ack <= 0;
    st <= 4;
end
4:
begin
    cnt <= 3;
    
    if(pkg_din[0]) // read
    begin
        dev_addr <= pkg_din[7:1]; // get device address
        pkg_din_ack <= 1;
        st <= 6;
    end
    else    //write
    begin
        op_cmd_en <= 1'b1;
        op_cmd <= I2C_OP_START;
        data_in <= pkg_din; // device_addr + wr/rd
        pkg_din_ack <= 1;
    
        st <= 5;
    end
end
//========================= write
5:            // write data
begin
    op_cmd_en <= 1'b0; 
    pkg_din_ack <= 1'b0;

    if(op_cmd_done)
    begin
        if(!dev_ack)
        begin
            if(cnt == pkg_len)  // jump to stop if the data had been sent
                st <= 19;
            else
            begin
                pkg_din_ack <= 1'b1;
                op_cmd_en <= 1'b1;
                op_cmd <= I2C_OP_WR_DATA;
                data_in <= pkg_din; // send data to device
                cnt <= cnt + 1;
            end
        end
        else
        begin
            i2c_error <= 1'b1;
            st <= 19; // jump to stop
        end
    end
end
//=========================read
6:
begin
    pkg_din_ack <= 0;
    st <= 7;
end
7:
begin
    cnt <= 0;
    read_len <= pkg_din; // get read device length 
    pkg_din_ack <= 1;
    
    if(pkg_din == 0) // read device length = 0 means no read operation
    begin
        pkg_end <= 1;
        st <= 21; 
    end
    else st <= 8;
end
8:
begin
    pkg_din_ack <= 0;
    if(pkg_len == 4) //restart, random read device
        st <= 16;
    else st <= 9;
end                                           
9:
begin
    reg0 <= pkg_din; // device memory address 1
    pkg_din_ack <= 1;
    
    if(pkg_len == 5) 
        st <= 12;
    else
        st <= 10;
end
10:
begin
    pkg_din_ack <= 0;
    st <= 11;
end
11:
begin
    reg1 <= pkg_din; // device memory address 2
    pkg_din_ack <= 1;
    
    st <= 12;
end
12:           // start read transaction from memory address
begin
    pkg_din_ack <= 0;
    
    op_cmd_en <= 1'b1;
    op_cmd <= I2C_OP_START;                     
    data_in <= {dev_addr,1'b0};
    st <= 13;
end
13:
begin
    op_cmd_en <= 1'b0;
    
    if(op_cmd_done)
    begin
        if(!dev_ack)
        begin
            op_cmd_en <= 1'b1;
            op_cmd <= I2C_OP_WR_DATA;                     
            data_in <= reg0;
            if(pkg_len == 6)
                st <= 14;
            else
                st <= 15;
        end
        else
        begin
            i2c_error <= 1'b1;
            st <= 19;                          // 从设备ack/nack 错误跳转状态
        end
    end
end
14:
begin
    op_cmd_en <= 1'b0;
    
    if(op_cmd_done)
    begin
        if(!dev_ack)
        begin
            op_cmd_en <= 1'b1;
            op_cmd <= I2C_OP_WR_DATA;                     
            data_in <= reg1;
            st <= 15;
        end
        else
        begin
            i2c_error <= 1'b1;
            st <= 19;                          // 从设备ack/nack 错误跳转状态
        end
    end
end
15:
begin
    op_cmd_en <= 1'b0;
    
    if(op_cmd_done)
    begin
        if(!dev_ack)
            st <= 16;
        else
        begin
            i2c_error <= 1'b1;
            st <= 19;                          // 从设备ack/nack 错误跳转状态
        end
    end
end
16:          //restart
begin
    master_ack <= 1'b0;
    cnt <= 0;
    
    op_cmd_en <= 1'b1;
    op_cmd <= I2C_OP_START;                      
    data_in <= {dev_addr,1'b1};
    st <= 17;
end
17:         // read data
begin
    op_cmd_en <= 1'b0;
    pkg_dout_vld <= 1'b0;
    
    if(op_cmd_done)
    begin
        op_cmd_en <= 1'b1;
        op_cmd <= I2C_OP_RD_DATA;             

        cnt <= cnt + 1;

        if(cnt != 0)  // I2C_OP_RD_DATA respond
        begin
            pkg_dout <= data_out;
            pkg_dout_vld <= 1'b1;
        end

        if(cnt == (read_len - 1) ) // the last read transaction need a NACK
        begin
            cnt <= 0;
            master_ack <= 1'b1;
            st <= 18;
        end            
    end
end
18:
begin
    op_cmd_en <= 1'b0;
    pkg_dout_vld <= 1'b0;
    if(op_cmd_done)
    begin
        pkg_dout <= data_out;
        pkg_dout_vld <= 1'b1;
        st <= 19;
    end
end
//=========== stop
19:
begin
    pkg_dout_vld <= 1'b0;
    op_cmd_en <= 1'b1;
    op_cmd <= I2C_OP_STOP;
    i2c_error <= 1'b0;
    st <= 20;
end
20:
begin
    op_cmd_en <= 1'b0;
    cnt <= 0;
    
    if(op_cmd_done)
    begin
       pkg_end <= 1;
       st <= 21;
    end
end
21:
begin
    pkg_end <= 0;
    if(cnt == pkg_delay)
        st <= 0;
    else if(ms_flag) cnt <= cnt + 1;
end
default:st <= 0;
endcase


endmodule

 

3 仿真

图5

如图5所示,当应用层发送 0c 05 a0 00    00 01 02 03 04 05 06 07 (写设备), 当cmd层接收到应用层的数据后,cmd层发送pkg_din_ack 给应用层;当整包结束后,cmd层发送pkg_end 给应用层,应用层撤销pkg_din_vld。

图6

当应用层发送:04 01 a1 02 (随机读设备),cmd层接收到应用层的数据后,读设备,读出的数据通过pkg_dout, pkg_dout_vld 返回应用层。

Posted in FPGA, FPGA 教材教案, Verilog, 教材与教案, 文章

发表回复

相关链接