> 技术文档 > 基于FPGA的IIC控制EEPROM读写(1)_fpga中i2c

基于FPGA的IIC控制EEPROM读写(1)_fpga中i2c


基于FPGA的IIC回环读写

文章目录

  • 基于FPGA的IIC回环读写
    • 一、IIC简介
    • 二、个人理解——代码实现IIC回环
      • 1、框架设计
      • 2、状态机设计
      • 3、仿真实现情况
      • 4、上板验证
      • 5、代码
        • top.v
        • iic_master.v
        • iic_slave.v
        • ip_fifo_test.v
        • uart_rx与uart_tx
    • 三、小结

一、IIC简介

采用串行总线可以简化系统硬件结构、减小系统体积、提高系统可靠性。常 用的串行总线有单总线(1-Wire Bus)、IIC(Inter-Integrated Circuit)、SPI(Serial Peripheral Interface)等。 IIC 总线是 Phlips 公司推出的一种同步串行总线,是一种支持多主机多从机 系统、总线仲裁以及高低速器件同步功能的高性能串行总线技术。 IIC 总线只有两根双向信号线:一根是数据线 SDA,一根是时钟线 SCL。
在这里插入图片描述
多主机多从机----在 IIC 总线系统上可以挂载多个主机设备与从机设备;
总线仲裁----对于总线上挂载有多主机设备的情况下,为避免多个主机同 时向总线发起通信,需要有相应的总线仲裁机制,避免出现总线冲突;
高低速器件同步–通过 IIC 总线连接的两个器件的传输速度不一定相同, 因此需要通过从机的时钟延展(clock stretching)功能保证两者的通信速度一致。
即:如果果从机跟不上主机的速率,IIC 协议规定从机可以通过将 SCL 时钟线拉 低来暂停传输直到从机释放掉 SCL 线,传输才能继续进行。

传输速率
标准模式:100Kbit/s
快速模式:400kbit/s
高速模式:3.4Mbit/s

工作原理
每一个连接到 IIC 总线上的器件都有一个唯一的器件地址(ID),主机通过 ID 建立多机通信机制,因此 IIC 总线节省了外围器件的片选信号线。虽然 IIC 总 线上可以连接多个主机和多个从机,但是同一时刻,只能有一个主机向总线上发起传输,如果有多个主机同时发起传输,则会根据 IIC 总线的仲裁机制,最终 只给一个主机授权,没有得到仲裁的主机则会停止传输。而且,IIC 总线上只能 由主机发起传输,从机无法主动发起传输,这也就意味着从机和从机之间也是无法直接交换数据的。
在这里插入图片描述

IIC 总线在传输数据时必须遵循规定的数据传输时序,一次完整的数据传输 过程中共有四类信号:起始信号、数据信号、应答信号和停止信号。
在这里插入图片描述
起始信号:在 SCL 为高电平时,SDA 的电平由高电平跳变为低电平,称为 I2C 总线的起始信号,标志着一次数据传输开始。起始信号由主机主动产生,在 产生起始信号之前 I2C 总线必须处于空闲状态。

停止信号:在 SCL 为高电平时,SDA 由低电平跳变为高电平,称为 I2C 总 线的停止信号,标志着一次数据传输终止。停止信号由主机主动产生,产生停止 信号之后 I2C 总线将返回空闲状态。

数据信号:IIC 总线传输数据时,在 SCL 为高电平时,SDA 必须保持稳定的 逻辑电平,高电平表示数据 1,低电平表示数据 0;只有在 SCL 为低电平时才允 许 SDA 上的电平状态改变。

应答信号:IIC 总线传输数据时,每传输一个字节后都必须有一个 SCL 周期 的应答信号;与应答信号相对应的时钟由主机产生,发送端必须释放 SDA 使其 处于高电平状态,以便接收端在这一个 SCL 周期发出应答信号。

应答信号有两种状态:应答(ACK)、非应答(NACK),接收端在应答位期 间输出低电平则为应答,输出高电平则为非应答。

当由于某种原因,从机设备不产生应答时,如从机在进行其它处理而无法接 收总线上的数据时,必须释放 SDA 总线,然后由主机产生一个停止信号以终止 数据传输。
当主机在接收数据时,接收到最后一个字节后,必须给从机发送一个非应答 信号,使从机释放总线以便主机可以发送停止信号,终止数据传输。

需要注意的是:在某些情况下,从机在收到一个完整的字节后,有可能因为 需要忙于其它工作(如处理内部中断服务)而无法立刻接收下一个字节,这时从 机需要将 SCL 拉为低电平,从而强制主机处于等待状态,直到从机准备好接收 下一个字节时,再释放 SCL 为高电平状态,从而可以继续传输数据。这种现象 称为时钟拉伸(Clock Stretching)。

数据地址
IIC 总线协议规定:起始信号表明一次传输开始,其后为寻址字节(高 7 位 为从机地址、最低 1 位为方向位,方向位表明主机与从机之间的数据传输方向, 0–主机发送数据给从机,1–从机发送数据给主机);在寻址字节后是对应的读、 写操作的数据字节和应答位;在数据传输完成后主机必须发送停止信号。
IIC 总线的数据传输方式有许多读、写组合方式:主机写操作、主机读操作、 主机读写操作。
在这里插入图片描述
在这里插入图片描述

二、个人理解——代码实现IIC回环

1、框架设计

基于FPGA的IIC控制EEPROM读写(1)_fpga中i2c

电脑上位机通过UART串口向FPGA发送串行数据,FPGA内rx模块将串行数据转为并行数据发给iic主机,iic主机将数据转为串行数据通过sda线发给iic从机,至此,用到了uart和iic两种协议,由于二者速度不相同,uart传输速率慢于iic所以要用到一个FIFO寄存数据;从机接收完数据,将数据返回给主机,再通过tx发送回PC,完成回环操作。

2、状态机设计

主体采用三段式状态机设计,状态机转移图如下
基于FPGA的IIC控制EEPROM读写(1)_fpga中i2c
默认空闲状态为IDLE,当收到PC发来的’02‘命令,跳转到START状态,进行相应的操作;
发送结束,跳转到SLAVE_ADDR,发送地址信息(也为PC发送来的)+读写命令,发送完成跳转到ACK应答状态;
再此状态下判断读写模式,收到应答信号,再跳转到相应的状态(写数据or读数据);
后续进行读数据或者写数据,若检测到发来的数据为03.则在ACK应答(写数据)或发送NACK(读数据)后,跳转到STOP,
发送完STOP信号后跳转到IDLE。

3、仿真实现情况

发送起始02之前发送干扰数据,状态无跳转。

在这里插入图片描述
发送02,状态由IDLE跳转到START,进行发送起始信号操作(SCL高电平期间,SDA由高电平跳为低电平)
在这里插入图片描述
进入发送地址状态“2”,数据有效进行发送,(读写位为0,是写操作)SCL高电平期间数据有效,低电平进行改变,数据相对应,检查正确。发送完成跳转到ACK应答状态,由于是仿真,应答通过sda_in发送给主机,且通过tb文件产生,一直为0,应答有效,计1bit后跳转到发送数据状态。
在这里插入图片描述
写数据流程与写地址流程相同,但数据通过从机写入到FIFO中
在这里插入图片描述
重复发送数据6次之后发送03停止信号,计数完成跳回IDLE。
在这里插入图片描述
发送02,e9进行读操作
在这里插入图片描述
所读出的通过tx发送给PC的数据与写进FIFO的一致,仿真验证成功
在这里插入图片描述

4、上板验证

在这里插入图片描述
依次发送02(开始信号)e8(地址+写操作)12 13 14 (写的数据)03(停止信号)12(干扰数据)02(开始信号)e9(地址+读操作)03(结束)。FPGA发回写的12 13 14。上板验证成功。

5、代码

top.v
module top ( input clk, input rst_n, input rx, output tx, // output rx_data, // input sda_in, // output sda_out, // output scl, input [1:0] sw);wire [7:0] rx_data;wire rx_done;wire sda_in;wire sda_out;wire tx_done;wire [7:0] data_out;wire rx_vld_s;wire tx_vld;wire [7:0] data_in;wire sda;wire scl;wire empty;wire empty_s;wire full;wire [7:0]data_out_m;wire tx_vld_m;wire [7:0] data_in_s;wire tx_vld_s;wire [7:0] data_out_s;uart_rx inst_uart_rx ( .clk(clk), .rst_n(rst_n), .rx(rx), .sw(sw), .rx_data(rx_data), .rx_done(rx_done));iic_master inst_iic_master ( .clk (clk) , .rst_n (rst_n) , .empty (empty_s) , .full (full) , .data_in (rx_data) , .rx_vld (rx_done) , .sda_in (sda_in) , .sda_out (sda_out) , .data_out (data_out_m) , .tx_vld (tx_vld_m) , // .sda (sda) , .tx_done (tx_done), .scl (scl));iic_slave inst_iic_slave ( .clk (clk) , .rst_n (rst_n) , .data_in (data_in_s) , .tx_vld (tx_vld_s) , .sda_in (sda_out) , .sda_out (sda_in) , // .sda (sda) , .scl (scl) , .data_out (data_out_s) , .tx_done (tx_done), .empty (empty), .empty_s (empty_s), .rx_vld (rx_vld_s));uart_tx inst_uart_tx ( .clk (clk), .rst_n (rst_n), .tx_data (data_out_m), .tx_start (tx_vld_m), .sw (sw), .tx (tx), .tx_done (tx_done));ip_fifo_test inst_ip_fifo_test ( .wr_clk (clk), .rd_clk (clk), .rst_n (rst_n), .wren (rx_vld_s), .rden (tx_vld_s), .empty (empty), .full (full), .data_in (data_out_s), .data_out (data_in_s));endmodule
iic_master.v
module iic_master ( input clk, input rst_n, input [7:0] data_in, input rx_vld, input empty, input full, input sda_in, input tx_done, output reg sda_out, // inout sda, output reg [7:0] data_out, output tx_vld, output reg scl);//三态门数据// wire sda_in;// reg sda_out;reg sda_en;// assign sda_in = sda;assign sda = sda_en ? sda_out : 1\'bz;localparam  IDLE = 3\'d0, START = 3\'d1, SLAVE_ADDR = 3\'d2, ACK = 3\'d3, W_DATA = 3\'d4, R_DATA = 3\'d5, STOP = 3\'d6;reg [2:0] state_c; //现态reg [2:0] state_n; //次态//数据缓存reg [7:0] data; wire  IDLE_2_START , START_2_SLAVE_ADDR , SLAVE_ADDR_2_ACK , ACK_2_W_DATA , W_DATA_2_ACK , ACK_2_R_DATA , R_DATA_2_ACK , ACK_2_IDLE  , ACK_2_STOP  , STOP_2_IDLE ;reg  w_mod;//400_000深度reg  [6:0] cnt_dp  ;reg  add_cnt_dp  ;wire  end_cnt_dp  ;parameter  depth = 7\'d125;//bit计数reg  [2:0] cnt_bit  ;wire  add_cnt_bit ;wire  end_cnt_bit ;reg [7:0]  max_bit;reg  first_ack;reg ack;reg empty_m;//ackalways @(*)begin case(state_c) ACK: begin ack = (sda_in == 0) ? 1\'b0 : ack;  empty_m = (empty) ? 1\'b1:empty_m; end default: begin ack = 1\'b1;empty_m = 1\'b0; end endcaseend always @(posedge clk or negedge rst_n) begin if(!rst_n)begin data <= 8\'d0; end else if(rx_vld)begin data <= data_in; end endalways@(posedge clk or negedge rst_n)begin if(!rst_n)begin w_mod <= 1\'b0; end else if((state_c == SLAVE_ADDR) && end_cnt_bit )begin w_mod <= data[0]; endendalways @(*) begin if((state_c == SLAVE_ADDR) || (state_c == W_DATA) || (state_c == R_DATA))begin max_bit <= 8; end else begin max_bit <= 1; endend//速度计数器always @(posedge clk or negedge rst_n) begin if(!rst_n)begin cnt_dp <= 0; end else if(add_cnt_dp)begin if(end_cnt_dp)begin cnt_dp <= 0; end else begin cnt_dp <= cnt_dp + 1; end end else begin cnt_dp <= cnt_dp; endendalways @(posedge clk or negedge rst_n) begin if(!rst_n)begin add_cnt_dp <= 0; end else if(rx_vld && state_c != IDLE || IDLE_2_START || ACK_2_R_DATA || ACK_2_STOP || R_DATA_2_ACK)begin add_cnt_dp <= 1; end else if((state_c == START || state_c == ACK || state_c == R_DATA || state_c == STOP) && (end_cnt_bit))begin add_cnt_dp <= 0; end else begin add_cnt_dp <= add_cnt_dp; endendassign end_cnt_dp = add_cnt_dp && cnt_dp == depth-1; //bit计数器always @(posedge clk or negedge rst_n) begin if(!rst_n)begin cnt_bit <= 0; end else if(add_cnt_bit)begin if(end_cnt_bit)begin cnt_bit <= 0; end else begin cnt_bit <= cnt_bit + 1; end end else begin cnt_bit <= cnt_bit; endendassign add_cnt_bit = end_cnt_dp && (state_c != IDLE);assign end_cnt_bit = add_cnt_bit && cnt_bit == (max_bit-1);//状态机一段always @(posedge clk or negedge rst_n) begin if(!rst_n)begin state_c <= IDLE; end // else if(rx_vld && (data == 8\'h03))begin // state_c <= STOP; // end else begin state_c <= state_n; endend//状态机二段always @(*) begin case(state_c) IDLE: state_n <= (IDLE_2_START) ? START : state_c; START: state_n <= (START_2_SLAVE_ADDR) ? SLAVE_ADDR : state_c; SLAVE_ADDR: state_n <= (SLAVE_ADDR_2_ACK) ? ACK : state_c; ACK: begin  if(ACK_2_IDLE)begin state_n <= IDLE;  end  else if (w_mod)begin if(ACK_2_R_DATA)begin state_n <= R_DATA; end else if(ACK_2_STOP)begin state_n <= STOP; end else begin state_n <= state_c; end  end  else if(full)begin state_n <= STOP;  end  else if(ACK_2_W_DATA)begin state_n <= W_DATA;  end  else if(ACK_2_STOP)begin state_n <= STOP;  end  else begin state_n <= state_c;  end end W_DATA: state_n <= (W_DATA_2_ACK) ? ACK : state_c; R_DATA: state_n <= (R_DATA_2_ACK) ? ACK : state_c; STOP: state_n <= (STOP_2_IDLE) ? IDLE : state_c; default: state_n <= IDLE; endcaseend//描述跳转条件assign IDLE_2_START = (state_c == IDLE ) && (data== 8\'h02) ;assign START_2_SLAVE_ADDR = (state_c == START ) && (end_cnt_bit)  ;assign SLAVE_ADDR_2_ACK = (state_c == SLAVE_ADDR) && (end_cnt_bit)  ;assign ACK_2_W_DATA = (state_c == ACK ) && (end_cnt_bit) && (!ack ) && (data != 8\'h03) && (!w_mod); assign ACK_2_R_DATA = (state_c == ACK ) && (end_cnt_bit) && (!ack && first_ack || (!first_ack)) && (w_mod && (!empty_m)) ;assign W_DATA_2_ACK = (state_c == W_DATA ) && (end_cnt_bit)  ;assign R_DATA_2_ACK = (state_c == R_DATA ) && tx_done  ;assign ACK_2_IDLE  = (state_c == ACK ) && (end_cnt_bit) && (!w_mod || first_ack) && ack ;assign ACK_2_STOP  = (state_c == ACK ) && (end_cnt_bit) && (((!ack ) && (data == 8\'h03)) && (!w_mod) || (w_mod) && empty_m);assign STOP_2_IDLE = (state_c == STOP ) && (end_cnt_bit)  ;always @(posedge clk or negedge rst_n) begin if(!rst_n)begin first_ack <= 1\'b0; end else if((state_c == SLAVE_ADDR ) && SLAVE_ADDR_2_ACK)begin first_ack <= 1\'b1; end else if(end_cnt_bit)begin first_ack <= 1\'b0; endend//scl描述always @(posedge clk or negedge rst_n) begin if(!rst_n)begin scl <= 1\'b1; end else begin case(state_c) IDLE :scl <= 1\'b1; SLAVE_ADDR,W_DATA,R_DATA,ACK :begin if((cnt_dp < 7\'d31) || (cnt_dp > 7\'d93))begin  scl <= 1\'b0; end else begin  scl <= 1\'b1; end end STOP :begin if(cnt_dp < 7\'d31)begin  scl <= 1\'b0; end else begin  scl <= 1\'b1; end end default:begin scl <= 1\'b1; end endcase endendalways@(posedge clk or negedge rst_n)begin if(!rst_n)begin sda_out <= 1\'b1; end else begin case(state_c) IDLE: sda_out <= 1\'b1; START: sda_out <= (cnt_dp <= ((depth-1)>>1) && add_cnt_dp) ? 1\'b1 : 1\'b0; SLAVE_ADDR,W_DATA: begin sda_out <= data[7-cnt_bit]; if(cnt_dp == 0)begin  sda_out <= 1\'b0; end end R_DATA: sda_out <= 1\'b0; ACK: sda_out <= 1\'b0; STOP: sda_out <= (cnt_dp <= ((depth-1)>>1)) ? 1\'b0 : 1\'b1; default: sda_out <= 1\'b1; endcase endendalways@(posedge clk or negedge rst_n)begin if(!rst_n)begin sda_en <= 1\'b1; end else begin case(state_c) IDLE: sda_en <= 1\'b0; START,SLAVE_ADDR,W_DATA,STOP: sda_en <= 1\'b1; ACK: sda_en <= (!w_mod) ? 1\'b0 : (first_ack) ? 1\'b0 :1\'b1; R_DATA: sda_en <= 1\'b0; default: sda_en <= 1\'b1; endcase endendalways@(posedge clk or negedge rst_n)begin if(!rst_n)begin data_out <= 8\'b0; end else if((state_c == R_DATA) && (cnt_dp == (depth -1)>>1))begin data_out[7-cnt_bit] <= sda_in; endendassign tx_vld = (state_c == R_DATA)&& end_cnt_bit && w_mod;//(state_c == ACK) && end_cnt_bit && (!first_ack) && w_mod;endmodule
iic_slave.v
module iic_slave( input clk, input rst_n, input [7:0] data_in, output tx_vld, input sda_in, output reg sda_out,// inout sda, input scl, input tx_done, input empty, output empty_s, output reg [7:0] data_out, output rx_vld); //三态门// wire sda_in;// reg sda_out;reg sda_en;// assign sda_in = sda;assign sda = sda_en ? sda_out : 1\'bz;//速度定义parameter  speed = 7\'d125;localparam device_addr = 8\'h74;//地址localparam  IDLE = 3\'d0, START = 3\'d1, SLAVE_ADDR = 3\'d2, ACK = 3\'d3, W_DATA = 3\'d4, R_DATA = 3\'d5; //STOP = 3\'d6;reg [2:0] state_c; //现态reg [2:0] state_n; //次态 wire  IDLE_2_START , START_2_SLAVE_ADDR , SLAVE_ADDR_2_ACK , SLAVE_ADDR_2_IDLE , ACK_2_W_DATA , W_DATA_2_ACK , ACK_2_R_DATA , R_DATA_2_ACK , ACK_2_IDLE  , STOP_2_IDLE ;//bit计数reg [2:0] cnt_bit;wire add_cnt_bit;wire end_cnt_bit;reg [3:0] max_bit;//边沿检测reg [1:0] sda_r;reg [1:0] scl_r;wire sda_pos;wire sda_neg;wire scl_pos;wire scl_neg;//缓存reg [7:0] data;reg [7:0] ctrl_data;reg w_mod;reg first_ack;reg ack;//ackalways @(*)begin case(state_c) ACK: ack = (sda_in == 0) ? 1\'b0 : ack; default: ack = 1\'b1; endcaseend always @(posedge clk or negedge rst_n) begin if(!rst_n)begin data <= 8\'h0; end else if(scl_pos)begin data[7-cnt_bit] <= sda_in; end else if(state_c == START)begin data <= 8\'h0; end else begin data <= data; endendalways @(posedge clk or negedge rst_n) begin if(!rst_n)begin ctrl_data = 0; w_mod <= 1\'b0; end else if(state_c == SLAVE_ADDR && ((cnt_bit == 7)&&scl_neg))begin w_mod <= data[0]; end else if(state_c == SLAVE_ADDR && ((cnt_bit == 7)&&scl_pos))begin ctrl_data <= data; end else begin ctrl_data <= ctrl_data; w_mod <= w_mod; endendalways @(posedge clk or negedge rst_n) begin if(!rst_n)begin sda_r <= 2\'b11; scl_r <= 2\'b11; end else begin sda_r <= {sda_r[0],sda_in}; scl_r <= {scl_r[0],scl}; endendassign sda_pos = ~sda_r[1] & sda_r[0];assign sda_neg = sda_r[1] & ~sda_r[0];assign scl_pos = ~scl_r[1] & scl_r[0];assign scl_neg = scl_r[1] & ~scl_r[0];//状态机一段always @(posedge clk or negedge rst_n) begin if(!rst_n)begin state_c <= IDLE; end else if(sda_pos && scl == 1\'b1) begin state_c <= IDLE; end else begin state_c <= state_n; endend//状态机二段always @(*) begin if(sda_pos && scl == 1\'b1)begin state_n = IDLE; end else begin case(state_c) IDLE: state_n <= (IDLE_2_START) ? START : state_c; START: state_n <= (START_2_SLAVE_ADDR) ? SLAVE_ADDR : state_c; SLAVE_ADDR: state_n <= (SLAVE_ADDR_2_IDLE) ? IDLE : ((SLAVE_ADDR_2_ACK) ? ACK : state_c); ACK: begin  if(ACK_2_IDLE)begin state_n <= IDLE;  end  else if(w_mod)begin if(ACK_2_R_DATA)begin state_n <= R_DATA; end else begin state_n <= state_c; end  end  else if(ACK_2_W_DATA)begin state_n <= W_DATA;  end  else begin state_n <= state_c;  end end W_DATA: state_n <= (W_DATA_2_ACK) ? ACK : state_c; R_DATA: state_n <= (R_DATA_2_ACK) ? ACK : state_c; default: state_n <= IDLE; endcase endendassign IDLE_2_START = (state_c == IDLE) && (scl & sda_neg);assign START_2_SLAVE_ADDR = (state_c == START) && end_cnt_bit;assign SLAVE_ADDR_2_ACK = (state_c == SLAVE_ADDR) && end_cnt_bit && (ctrl_data[7:1] == device_addr);//ctrl_data改为data_rassign ACK_2_W_DATA = (state_c == ACK) && end_cnt_bit && (!w_mod);assign SLAVE_ADDR_2_IDLE = (state_c == SLAVE_ADDR) && end_cnt_bit && (ctrl_data[7:1] != device_addr);//ctrl_data改为data_rassign ACK_2_R_DATA = (state_c == ACK) && end_cnt_bit && (first_ack || (!first_ack && !ack)) && (w_mod);assign ACK_2_IDLE  = (state_c == ACK) && end_cnt_bit && (ack && !first_ack) && (w_mod);assign R_DATA_2_ACK = (state_c == R_DATA) && tx_done ;assign W_DATA_2_ACK = (state_c == W_DATA) && end_cnt_bit;//数据描述always @(posedge clk or negedge rst_n)begin if(!rst_n)begin cnt_bit <= 3\'d0; end else if(add_cnt_bit)begin if(end_cnt_bit)begin cnt_bit <= 3\'d0; end else begin cnt_bit <= cnt_bit + 3\'d1; end end else begin cnt_bit <= cnt_bit; endendassign add_cnt_bit = scl_neg;assign end_cnt_bit = add_cnt_bit && (cnt_bit == max_bit-1);always @(*) begin if(state_c == SLAVE_ADDR || state_c == W_DATA || state_c == R_DATA)begin max_bit <= 8; end else begin max_bit <= 1; endendalways @(posedge clk or negedge rst_n) begin if(!rst_n)begin data_out <= 0; end else if((state_c == W_DATA) && ((cnt_bit == 7)&&scl_neg))begin data_out <= data; endendassign rx_vld = ((state_c == ACK) && scl_pos && !first_ack && (data_out != 8\'h03));always @(posedge clk or negedge rst_n) begin if(!rst_n)begin sda_en <= 1\'b0; sda_out <= 1\'b1; end else begin case(state_c) ACK:begin  if (w_mod)begin if(first_ack)begin sda_en <= 1\'b1; sda_out <= 1\'b0; end else begin sda_en <= 1\'b0; end  end  else begin sda_en <= 1\'b0; sda_out <= 1\'b0;  end end  R_DATA:begin  sda_en <= 1\'b1;  sda_out <= data_in[7-cnt_bit]; end default:begin  sda_en <= 1\'b0;  sda_out <= 1\'b0; end endcase endendalways @(posedge clk or negedge rst_n) begin if(!rst_n)begin first_ack <= 1\'b0; end else if((state_c == SLAVE_ADDR ) && SLAVE_ADDR_2_ACK)begin first_ack <= 1\'b1; end else if(end_cnt_bit)begin first_ack <= 1\'b0; endendassign empty_s = (ACK_2_R_DATA && empty) ? 1\'b1 : 1\'b0;assign tx_vld = ACK_2_R_DATA ;endmodule
ip_fifo_test.v
module ip_fifo_test ( input  wr_clk,inout  rd_clk, input  rst_n,input  wren,input  rden,input [7:0] data_in,output empty,output full, outputreg [7:0] data_out);integer i;parameter depth = 32;parameter addr_width = 5;// wire empty;// wire full;reg [addr_width:0] wr_addr;reg [addr_width:0] rd_addr;wire[addr_width:0] wr_addr_gray;wire[addr_width:0] rd_addr_gray;reg[addr_width:0] wr_addr_1;reg[addr_width:0] rd_addr_1;reg[addr_width:0] wr_addr_2;reg[addr_width:0] rd_addr_2;// reg [7:0] data_in;reg [7:0]fifo_mem[depth-1:0];always @(posedge wr_clk or negedge rst_n) beginif(!rst_n)beginwr_addr <= 6\'d0;endelse if(wren && !full)beginwr_addr <= wr_addr + 1\'b1;endendalways @(posedge rd_clk or negedge rst_n) beginif(!rst_n)beginrd_addr <= 6\'d0;endelse if(rden && !empty)beginrd_addr <= rd_addr + 1\'b1;endendassign wr_addr_gray = wr_addr ^ (wr_addr >> 1);assign rd_addr_gray = rd_addr ^ (rd_addr >> 1);always @(posedge rd_clk or negedge rst_n) beginif(!rst_n)beginwr_addr_1 <= 6\'d0;wr_addr_2 <= 6\'d0;endelse beginwr_addr_1 <= wr_addr_gray;wr_addr_2 <= wr_addr_1;endendalways @(posedge wr_clk or negedge rst_n) beginif(!rst_n)beginrd_addr_1 <= 6\'d0;rd_addr_2 <= 6\'d0;endelse beginrd_addr_1 <= rd_addr_gray;rd_addr_2 <= rd_addr_1;endendassignempty = (wr_addr_gray == rd_addr_2);assignfull = (wr_addr_gray == {~rd_addr_2[addr_width:addr_width-1],rd_addr_2[addr_width-2:0]});always @(posedge wr_clk or negedge rst_n) beginif(!rst_n)beginfor(i = 0; i < depth; i = i + 1)beginfifo_mem[i] <= 8\'b0;endendelse if(wren && !full)beginfifo_mem[wr_addr[addr_width - 1 :0]] <= data_in;endendalways @(posedge rd_clk ) beginif(rden && !empty)begindata_out <= fifo_mem[rd_addr[addr_width - 1 :0]];endendendmodule
uart_rx与uart_tx

详见uart详解

三、小结

本文主要为记录个人学习IIC协议的过程,以及根据自身对协议的理解编写的代码,可读性与复用性较差,还存在诸多问题,例如在写数据时会把控制结束的信号03当作数据发送给从机;还有待改进。