源码地址:http://www.opencores.org/projects/i2c/
时序图在线绘制工具:https://wavedrom.com/
绘图工具:https://app.diagrams.net/
always @(posedge clk or negedge nReset)
begin
if (!nReset) // asynchronous reset
sr <= #1 8'h0; //the"#"just for modelsim,it's not used when Analysis & Synthesis
else if (rst==1) // synchronous reset
sr <= #1 8'h0;
else if (ld) // load data
sr <= #1 din; // write data to sda
else if (shift)
sr <= #1 {sr[6:0], core_rxd}; // read data from sda
end
八位计数器
always @(posedge clk or negedge nReset)
begin
if (!nReset)
dcnt <= #1 3'h0;
else if (rst)
dcnt <= #1 3'h0;
else if (ld)
dcnt <= #1 3'h7;
else if (shift)
dcnt <= #1 dcnt - 3'h1;
end
assign cnt_done = ~(|dcnt);
对串行后的每一位进行输出,每一位的输出可分成5个阶段(a、b、c、d、i),start多了一个e,其中i是idle
开始,读写,停止的时序如下:
start
stop
read
read里的SDA是高阻态,不是1,这样slave可以控制SDA,输出数据给master
write
SDA、SCL的数据并没有直接用,而是经历了很多filter
Capture
always @(posedge clk or negedge nReset)
begin
if (!nReset)
begin
cSCL <= #1 2'b00;
cSDA <= #1 2'b00;
end
else if (rst)
begin
cSCL <= #1 2'b00;
cSDA <= #1 2'b00;
end
else
begin
cSCL <= {cSCL[0],scl_i};
cSDA <= {cSDA[0],sda_i};
end
end
Filter
always @(posedge clk or negedge nReset)//fSCL is the Operation control clock
begin
if(!nReset)
begin
fSCL <= 3'b111;
fSDA <= 3'b111;
end
else if (rst)
begin
fSCL <= 3'b111;
fSDA <= 3'b111;
end
else if (~|filter_cnt)
begin
fSCL <= {fSCL[1:0],cSCL[1]};
fSDA <= {fSDA[1:0],cSDA[1]};
end
end
Synchronized、Delayed
always @(posedge clk or negedge nReset)
begin
if (~nReset)
begin
sSCL <= #1 1'b1;
sSDA <= #1 1'b1;
dSCL <= #1 1'b1;
dSDA <= #1 1'b1;
end
else if (rst)
begin
sSCL <= #1 1'b1;
sSDA <= #1 1'b1;
dSCL <= #1 1'b1;
dSDA <= #1 1'b1;
end
else
begin
sSCL <= #1 &fSCL[2:1] | &fSCL[1:0] | (fSCL[2] & fSCL[0]);
sSDA <= #1 &fSDA[2:1] | &fSDA[1:0] | (fSDA[2] & fSDA[0]);
dSCL <= #1 sSCL;
dSDA <= #1 sSDA;
end
end
// generate dout signal (store SDA on rising edge of SCL)
always @(posedge clk)
if(sSCL & ~dSCL)
dout <= #1 sSDA;
仲裁失败(Aribitration lost)
//仲裁丢失:当检测到停止信号,没有检测到请求信号,主机设置SDA为高电平,但实际上SDA为低电平
always @(posedge clk or negedge nReset)
begin
if(~nReset)
al <= #1 1'b0;
else if(rst)
al <= #1 1'b0;
else
al <= #1 (sda_chk & ~sSDA & sda_oen)|(|c_state & sto_condition & ~cmd_stop);
end