基于FPGA的IIC回环读写
文章目录
一、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 @(*)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
endcase
end
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
end
always@(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];
end
end
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;
end
end
//速度计数器
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;
end
end
always @(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;
end
end
assign 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;
end
end
assign 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;
end
end
//状态机二段
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;
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) 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;
end
end
//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
end
end
always@(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
end
end
always@(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
end
end
always@(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;
end
end
assign 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 @(*)begin
case(state_c)
ACK: ack = (sda_in == 0) ? 1'b0 : ack;
default: ack = 1'b1;
endcase
end
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;
end
end
always @(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;
end
end
always @(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};
end
end
assign 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;
end
end
//状态机二段
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
end
end
assign 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)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;
end
end
assign 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;
end
end
always @(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;
end
end
assign 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
end
end
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;
end
end
assign 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) begin
if(!rst_n)begin
wr_addr <= 6'd0;
end
else if(wren && !full)begin
wr_addr <= wr_addr + 1'b1;
end
end
always @(posedge rd_clk or negedge rst_n) begin
if(!rst_n)begin
rd_addr <= 6'd0;
end
else if(rden && !empty)begin
rd_addr <= rd_addr + 1'b1;
end
end
assign 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) begin
if(!rst_n)begin
wr_addr_1 <= 6'd0;
wr_addr_2 <= 6'd0;
end
else begin
wr_addr_1 <= wr_addr_gray;
wr_addr_2 <= wr_addr_1;
end
end
always @(posedge wr_clk or negedge rst_n) begin
if(!rst_n)begin
rd_addr_1 <= 6'd0;
rd_addr_2 <= 6'd0;
end
else begin
rd_addr_1 <= rd_addr_gray;
rd_addr_2 <= rd_addr_1;
end
end
assign 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) begin
if(!rst_n)begin
for(i = 0; i < depth; i = i + 1)begin
fifo_mem[i] <= 8'b0;
end
end
else if(wren && !full)begin
fifo_mem[wr_addr[addr_width - 1 :0]] <= data_in;
end
end
always @(posedge rd_clk ) begin
if(rden && !empty)begin
data_out <= fifo_mem[rd_addr[addr_width - 1 :0]];
end
end
endmodule
uart_rx与uart_tx
详见uart详解
三、小结
本文主要为记录个人学习IIC协议的过程,以及根据自身对协议的理解编写的代码,可读性与复用性较差,还存在诸多问题,例如在写数据时会把控制结束的信号03当作数据发送给从机;还有待改进。