本文参考自: 原文地址
设计两个可综合的电路模块:第一个模块(M1)接受四位并行数据,并将其转化为简化I2C传输格式。sclk为输入主钟,data[3:0]为输入的四位数据,ack为请求发送数据信号(请求后才有数据发送到data[3:0]),数据流用scl和sda两条线传输。第二个模块(M2)接收以简化I2C协议通过scl和sda传输来的数据,并转化为相应16条信号线上的高电平,若数据为1,则第一条线路为高电平,数据为n,则第N条线路为高电平。模块如下图所示。
本文引用自https://blog.csdn.net/llxxyy507/article/details/81046981
简化的I2C通信协议如下:scl为时钟信号,当scl为高电平的时候,sda从高电平变为低电平,表示串行数据流开始传输;当scl为高电平,sda从低电平变为高电平的时候,表示串行数据流结束。sda信号只能在scl为低电平的时候变化,在scl为高电平期间应该维持稳定。
上图中,sda信号在scl为高时从高变低,为数据流的开始。在scl为低电平时传输第一位数据(MSB),并在整个scl为高的期间都维持信号的稳定,接着传递剩下的数据。sda信号在scl为高时从低变高,表示数据流的结束。
模块M1的verilog代码(ptosda.v)如下:
module ptosda(sclk,rst,data,ack,scl,sda);
input sclk,rst,data;
wire [3:0]data;
output scl,sda,ack;
reg scl,ack,link_sda,sdabuf;
reg [3:0]databuf;
reg [7:0]state;
out16hi m2(.scl(scl), .sda(sda), .outhigh() ); //调用M2模块
assign sda = link_sda ? sdabuf : 1'b0; //link_sda控制sdabuf输出到串行总线上
parameter ready = 8'b0000_0000,
start = 8'b0000_0001,
bit1 = 8'b0000_0010,
bit2 = 8'b0000_0100,
bit3 = 8'b0000_1000,
bit4 = 8'b0001_0000,
bit5 = 8'b0010_0000,
stop = 8'b0100_0000,
IDLE = 8'b1000_0000;
always @(posedge sclk or negedge rst) //主钟sclk产生串行输出时钟clk
begin
if (!rst)
scl <= 1;
else
scl <= ~scl;
end
always @(posedge ack)
databuf <= data;
always @(negedge sclk or negedge rst)
if (!rst)
begin
link_sda <= 0;
state <= ready;
sdabuf <= 1;
ack <= 0;
end
else
begin
case(state)
ready : if(ack)
begin
link_sda <= 1;
state <= start;
end
else
begin
link_sda <= 0;
state <= ready;
ack <= 1;
end
start : if(scl && ack)
begin
sdabuf <= 0;
state <= bit1;
end
else
state <= start;
bit1 : if(!scl)
begin
sdabuf <= databuf[3];
state <= bit2;
ack <= 0;
end
else
state <= bit1;
bit2 : if(!scl)
begin
sdabuf <= databuf[2];
state <= bit3;
end
else
state <= bit2;
bit3 : if(!scl)
begin
sdabuf <= databuf[1];
state <= bit4;
end
else
state <= bit3;
bit4 : if(!scl)
begin
sdabuf <= databuf[0];
state <= bit5;
end
else
state <= bit4;
bit5 : if(!scl)
begin
sdabuf <= 0;
state <= stop;
end
else
state <= bit5;
stop : if(scl)
begin
sdabuf <= 1;
state <= IDLE;
end
else
state <= stop;
IDLE : begin
link_sda <= 0;
state <= ready;
end
default : begin
link_sda <= 0;
sdabuf <= 1;
state <= ready;
end
endcase
end
endmodule
模块M2(out16hi.v)verilog代码如下:
module out16hi(scl,sda,outhigh);
input scl,sda;
output [15:0]outhigh;
reg [4:0]mstate;
reg [3:0]pdata,pdatabuf;
reg [15:0]outhigh;
reg StartFlag,EndFlag; //串行数据开始和结束标志
always @(negedge sda)
begin
if (scl)
StartFlag <= 1;
else if (EndFlag)
StartFlag <= 0;
end
always @(posedge sda)
if (scl)
begin
EndFlag <= 1;
pdatabuf <= pdata;
end
else
EndFlag <= 0;
parameter sbit0 = 5'b0_0001,
sbit1 = 5'b0_0010,
sbit2 = 5'b0_0100,
sbit3 = 5'b0_1000,
sbit4 = 5'b1_0000;
always @(pdatabuf) //接受到的数据转化为相应的输出位的高电平
begin
case(pdatabuf)
4'b0001: outhigh = 16'b0000_0000_0000_0001;
4'b0010: outhigh = 16'b0000_0000_0000_0010;
4'b0011: outhigh = 16'b0000_0000_0000_0100;
4'b0100: outhigh = 16'b0000_0000_0000_1000;
4'b0101: outhigh = 16'b0000_0000_0001_0000;
4'b0110: outhigh = 16'b0000_0000_0010_0000;
4'b0111: outhigh = 16'b0000_0000_0100_0000;
4'b1000: outhigh = 16'b0000_0000_1000_0000;
4'b1001: outhigh = 16'b0000_0001_0000_0000;
4'b1010: outhigh = 16'b0000_0010_0000_0000;
4'b1011: outhigh = 16'b0000_0100_0000_0000;
4'b1100: outhigh = 16'b0000_1000_0000_0000;
4'b1101: outhigh = 16'b0001_0000_0000_0000;
4'b1110: outhigh = 16'b0010_0000_0000_0000;
4'b1111: outhigh = 16'b0100_0000_0000_0000;
4'b0000: outhigh = 16'b1000_0000_0000_0000;
endcase
end
always @(posedge scl)
if (StartFlag)
case(mstate)
sbit0 : begin
mstate <= sbit1;
pdata[3] <= sda;
end
sbit1 : begin
mstate <= sbit2;
pdata[2] <= sda;
end
sbit2 : begin
mstate <= sbit3;
pdata[1] <= sda;
end
sbit3 : begin
mstate <= sbit4;
pdata[0] <= sda;
end
sbit4 : begin
mstate <= sbit0;
end
default : mstate <= sbit0;
endcase
else mstate <= sbit0;
endmodule
testbench文件(sigdata_test.v)内容如下:
`timescale 1ns/1ns
`define halfperiod 50
module sigdata_test(rst,sclk,data,ack_for_data,sda,scl,outhigh);
output rst;
output [3:0]data;
output sclk;
input ack_for_data;
reg rst,sclk;
reg [3:0]data;
output sda,scl,outhigh;
wire sda;
wire scl;
wire [15:0]outhigh;
ptosda m1(.sclk(sclk), .rst(rst), .data(data), .ack(ack_for_data), .scl(scl), .sda(sda) );
out16hi m2(scl,sda,outhigh);
initial
begin
rst = 1;
#10 rst = 0;
#(`halfperiod*2+3) rst = 1;
end
initial
begin
sclk = 0;
data = 0;
#(`halfperiod*1000) $stop;
end
always #(`halfperiod) sclk = ~sclk;
always @(posedge ack_for_data)
begin
#(`halfperiod/2 + 3) data = data + 1;
end
endmodule
仿真的结果如下:
加入中间变量查看结果得到如下,可以看出data[3:0]数据准确的传入到了pdatabuf[3:0],并通过outhigh准确输出了对应信号线的高电平。