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

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

基于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、框架设计

在这里插入图片描述

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

2、状态机设计

主体采用三段式状态机设计,状态机转移图如下
在这里插入图片描述
默认空闲状态为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;//ack
always @(*)begincase(state_c)ACK:    begin ack = (sda_in == 0) ? 1'b0 : ack; empty_m = (empty) ? 1'b1:empty_m;enddefault:   begin  ack = 1'b1;empty_m = 1'b0; endendcase
end always @(posedge clk or negedge rst_n) beginif(!rst_n)begindata <= 8'd0;endelse if(rx_vld)begindata <= data_in;endendalways@(posedge clk or negedge rst_n)beginif(!rst_n)beginw_mod <= 1'b0;endelse if((state_c == SLAVE_ADDR) && end_cnt_bit )beginw_mod <= data[0];end
endalways @(*) beginif((state_c == SLAVE_ADDR) || (state_c == W_DATA) || (state_c == R_DATA))beginmax_bit <= 8;endelse beginmax_bit <= 1;end
end//速度计数器
always @(posedge clk or negedge rst_n) beginif(!rst_n)begincnt_dp <= 0;endelse if(add_cnt_dp)beginif(end_cnt_dp)begincnt_dp <= 0;endelse begincnt_dp <= cnt_dp + 1;endendelse begincnt_dp <= cnt_dp;end
endalways @(posedge clk or negedge rst_n) beginif(!rst_n)beginadd_cnt_dp <= 0;endelse if(rx_vld && state_c != IDLE || IDLE_2_START || ACK_2_R_DATA || ACK_2_STOP || R_DATA_2_ACK)beginadd_cnt_dp <= 1;endelse if((state_c == START || state_c == ACK || state_c == R_DATA || state_c == STOP) && (end_cnt_bit))beginadd_cnt_dp <= 0;endelse beginadd_cnt_dp <= add_cnt_dp;end
endassign end_cnt_dp = add_cnt_dp && cnt_dp == depth-1; //bit计数器
always @(posedge clk or negedge rst_n) beginif(!rst_n)begincnt_bit <= 0;endelse if(add_cnt_bit)beginif(end_cnt_bit)begincnt_bit <= 0;endelse begincnt_bit <= cnt_bit + 1;endendelse begincnt_bit <= cnt_bit;end
endassign 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) beginif(!rst_n)beginstate_c <= IDLE;end// else if(rx_vld && (data == 8'h03))begin// state_c <= STOP;// endelse beginstate_c <= state_n;end
end//状态机二段
always @(*) begincase(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:        beginif(ACK_2_IDLE)beginstate_n <= IDLE;endelse if (w_mod)beginif(ACK_2_R_DATA)beginstate_n <= R_DATA;endelse if(ACK_2_STOP)beginstate_n <= STOP;endelse beginstate_n <= state_c;endendelse if(full)beginstate_n <= STOP;endelse if(ACK_2_W_DATA)beginstate_n <= W_DATA;endelse if(ACK_2_STOP)beginstate_n <= STOP;endelse beginstate_n <= state_c;endendW_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;endcase
end//描述跳转条件
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) beginif(!rst_n)beginfirst_ack <= 1'b0;endelse if((state_c == SLAVE_ADDR ) && SLAVE_ADDR_2_ACK)beginfirst_ack <= 1'b1;endelse if(end_cnt_bit)beginfirst_ack <= 1'b0;end
end//scl描述
always @(posedge clk or negedge rst_n) beginif(!rst_n)beginscl <= 1'b1;endelse begincase(state_c) IDLE :scl <= 1'b1;SLAVE_ADDR,W_DATA,R_DATA,ACK   :beginif((cnt_dp < 7'd31) || (cnt_dp > 7'd93))beginscl <= 1'b0;endelse beginscl <= 1'b1;endendSTOP :beginif(cnt_dp < 7'd31)beginscl <= 1'b0;endelse beginscl <= 1'b1;endenddefault:beginscl <= 1'b1;endendcaseend
endalways@(posedge clk or negedge rst_n)beginif(!rst_n)beginsda_out <= 1'b1;endelse begincase(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)beginsda_out <= 1'b0;endendR_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;endcaseend
end
always@(posedge clk or negedge rst_n)beginif(!rst_n)beginsda_en <= 1'b1;endelse begincase(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;endcaseend
endalways@(posedge clk or negedge rst_n)beginif(!rst_n)begindata_out <= 8'b0;endelse if((state_c == R_DATA) && (cnt_dp == (depth -1)>>1))begindata_out[7-cnt_bit]  <= sda_in;end
endassign 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;//ack
always @(*)begincase(state_c)ACK:    ack = (sda_in == 0) ? 1'b0 : ack;default:                ack = 1'b1;endcase
end always @(posedge clk or negedge rst_n) beginif(!rst_n)begindata <= 8'h0;endelse if(scl_pos)begindata[7-cnt_bit] <= sda_in;endelse if(state_c == START)begindata <= 8'h0;endelse begindata <= data;end
endalways @(posedge clk or negedge rst_n) beginif(!rst_n)beginctrl_data = 0;w_mod <= 1'b0;endelse if(state_c == SLAVE_ADDR && ((cnt_bit == 7)&&scl_neg))beginw_mod <= data[0];endelse if(state_c == SLAVE_ADDR && ((cnt_bit == 7)&&scl_pos))beginctrl_data <= data;endelse beginctrl_data <= ctrl_data;w_mod <= w_mod;end
endalways @(posedge clk or negedge rst_n) begin if(!rst_n)beginsda_r <= 2'b11;scl_r <= 2'b11;endelse beginsda_r <= {sda_r[0],sda_in};scl_r <= {scl_r[0],scl};end
endassign 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)beginstate_c <= IDLE;endelse if(sda_pos && scl == 1'b1) beginstate_c <= IDLE;endelse beginstate_c <= state_n;end
end//状态机二段
always @(*) beginif(sda_pos && scl == 1'b1)beginstate_n = IDLE;endelse begincase(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:        beginif(ACK_2_IDLE)beginstate_n <= IDLE;endelse if(w_mod)beginif(ACK_2_R_DATA)beginstate_n <= R_DATA;endelse beginstate_n <= state_c;endendelse if(ACK_2_W_DATA)beginstate_n <= W_DATA;endelse beginstate_n <= state_c;endendW_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;endcaseend
endassign 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_r
assign 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_r
assign 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)beginif(!rst_n)begincnt_bit <= 3'd0;endelse if(add_cnt_bit)beginif(end_cnt_bit)begincnt_bit <= 3'd0;endelse begincnt_bit <= cnt_bit + 3'd1;endendelse begincnt_bit <= cnt_bit;end
endassign add_cnt_bit = scl_neg;
assign end_cnt_bit = add_cnt_bit && (cnt_bit == max_bit-1);always @(*) beginif(state_c == SLAVE_ADDR || state_c == W_DATA || state_c == R_DATA)beginmax_bit <= 8;endelse beginmax_bit <= 1;end
endalways @(posedge clk or negedge rst_n) beginif(!rst_n)begindata_out <= 0;endelse if((state_c == W_DATA) && ((cnt_bit == 7)&&scl_neg))begindata_out <= data;end
endassign rx_vld = ((state_c == ACK) && scl_pos && !first_ack && (data_out != 8'h03));always @(posedge clk or negedge rst_n) beginif(!rst_n)beginsda_en <=  1'b0;sda_out <= 1'b1;endelse begincase(state_c)ACK:beginif (w_mod)beginif(first_ack)beginsda_en <= 1'b1;sda_out <= 1'b0;endelse beginsda_en <= 1'b0;endendelse beginsda_en <=  1'b0;sda_out <= 1'b0;endend R_DATA:beginsda_en <=  1'b1;sda_out <= data_in[7-cnt_bit];enddefault:beginsda_en <=  1'b0;sda_out <= 1'b0;endendcase end
endalways @(posedge clk or negedge rst_n) beginif(!rst_n)beginfirst_ack <= 1'b0;endelse if((state_c == SLAVE_ADDR ) && SLAVE_ADDR_2_ACK)beginfirst_ack <= 1'b1;endelse if(end_cnt_bit)beginfirst_ack <= 1'b0;end
endassign 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,output	reg [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;end
endalways @(posedge rd_clk or negedge rst_n) beginif(!rst_n)beginrd_addr <= 6'd0;endelse if(rden && !empty)beginrd_addr <= rd_addr + 1'b1;end
endassign 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;end
endalways @(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;end
endassign	empty = (wr_addr_gray == rd_addr_2);
assign	full  = (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;end
endalways @(posedge rd_clk ) beginif(rden && !empty)begindata_out <= fifo_mem[rd_addr[addr_width - 1 :0]];end
end
endmodule
uart_rx与uart_tx

详见uart详解

三、小结

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

http://www.dtcms.com/a/283464.html

相关文章:

  • Mybatis07-逆向工程
  • 《python语言程序设计》2018版第8章5题编写函数统计特定不重复字符串s2在s1中的出现次数
  • C#获取当前系统账户是否为管理员账户
  • 资源利用率提升50%:Serverless 驱动国诚投顾打造智能投顾新范式
  • 用Amazon Q Developer助力Python快捷软件开发
  • EMS4000/EMS3900/EMS4100/EMS3157/EMS/23157高性能高质量模拟开关芯片
  • Go语言自学笔记(2.3-2.6)
  • C++:vector(2)之vector的模拟实现
  • 使用 SeaTunnel 建立从 MySQL 到 Databend 的数据同步管道
  • [2025CVPR-图象检索方向]CCIN:用于合成图像检索的合成冲突识别和中和模型
  • OWASP Top 10 攻击场景实战
  • 简单易懂,什么是连续分配管理方式
  • Vue3+Ts实现父子组件间传值的两种方式
  • 设计模式之【观察者模式】
  • 多维动态规划题解——不同路径【LeetCode】记忆化搜索
  • ai 编程工具,简单总结
  • 16路串口光纤通信FPGA项目实现指南 - 第二部分(下)
  • Day36 Java方法和流程控制练习 计算器
  • Linux运维新手的修炼手扎之第19天
  • Linux内核设计与实现 第1章:内核简介
  • UDP和TCP的主要区别是什么?
  • --- Bean 的生命周期 ---
  • Redis键过期后会立即删除吗
  • 光环效应(HALO Effect)
  • MySQL高并发高可用架构设计与实现:主从复制与读写分离
  • x86版Ubuntu的容器中运行ARM版Ubuntu
  • 3分钟实战!用DeepSeek+墨刀AI生成智能对话APP原型图
  • Karate(Java)接口自动化测试框架
  • 代码随想录算法训练营第五十天|图论part1
  • 【图像处理基石】如何入门人体关键点检测?