基于SUMBus及I2C通信协议,使用vivado2017 simulation,循环执行写操作,使用Verilog HDL代码编写,代码注释非常全面,故不再使用文字描述。SUMBus是在I2C的基础上发展起来的通信协议,其读写操作根据相关specification文献可知相同。
@[TOC] sumbus/I2C module
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2021/05/17 21:44:44
// Design Name:
// Module Name: smbus_2
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
/*本模块主always块的结构是:先写器件地址,然后写寄存器地址。寄存器地址写完后,判断读写EEPROM,
如果写就直接进行写数据,
如果读,则送读器件地址,然后送所读寄存器地址,然后再进行读数据/
*/
module smbus_2(
input sys_clk, //系统时钟500kHZ(2us)
input sys_rst_n,
output reg SMBCLK, //SMBus工作时钟50KHZ
inout IO_SDA, //SMBus数据
input SMBus_en, //SMBus使能
input rd_or_wr, //读写标志:读1,写0
input [6:0] device_addr, //SMBus器件地址
input [15:0] SMBus_reg_addr, //寄存器地址
input [7:0] SMBus_reg_data, //寄存器数据
output reg [7:0] read_data, //接收数据
output reg SMBus_done = 0 //SMBus完成标志
);
//---------------------------------------------
//分频部分
reg [2:0] cnt; // cnt=0:SMBCLK上升沿,cnt=1:SMBCLK高电平中间,cnt=2:SMBCLK下降沿,cnt=3:SMBCLK低电平中间
reg [8:0] cnt_delay; //100循环计数,产生iic所需要的时钟
// reg SMBCLK_POS = 0;
// reg SMBCLK_HIG = 0;
// reg SMBCLK_NEG = 0;
// reg SMBCLK_LOW = 0;
always @ (posedge sys_clk or negedge sys_rst_n)
if( !sys_rst_n ) cnt_delay <= 9'd0;
else if( cnt_delay == 9'd99 ) cnt_delay <= 9'd0; //计数到10us为SMBCLK的周期,即100KHz
else cnt_delay <= cnt_delay+1'b1; //时钟计数
always @ (posedge sys_clk or negedge sys_rst_n) begin
if( !sys_rst_n ) cnt <= 3'd5;
else begin
case ( cnt_delay )
9'd24: cnt <= 3'd1; //cnt=1:SMBCLK高电平中间,用于数据采样
9'd49: cnt <= 3'd2; //cnt=2:SMBCLK下降沿
9'd74: cnt <= 3'd3; //cnt=3:SMBCLK低电平中间,用于数据变化
9'd99: cnt <= 3'd0; //cnt=0:SMBCLK上升沿
default: cnt <= 3'd5;
endcase
end
end
`define SMBCLK_POS (cnt==3'd0) //cnt=0:SMBCLK上升沿
`define SMBCLK_HIG_MID (cnt==3'd1) //cnt=1:SMBCLK高电平中间,用于数据采样
`define SMBCLK_NEG (cnt==3'd2) //cnt=2:SMBCLK下降沿
`define SMBCLK_LOW_MID (cnt==3'd3) //cnt=3:SMBCLK低电平中间,用于数据变化
//产生SMBus所需要的时钟
always @ (posedge sys_clk or negedge sys_rst_n)
if(!sys_rst_n) SMBCLK <= 1'b0;
else if(cnt_delay==9'd1) SMBCLK <= 1'b1;
else if(cnt==3'd0) SMBCLK <= 1'b1; //SMBCLK信号上升沿
else if(cnt==3'd2) SMBCLK <= 1'b0; //SMBCLK信号下降沿
else SMBCLK <= SMBCLK;
//---------------------------------------------
//读、写时序
parameter IDLE = 4'd0;
parameter START1 = 4'd1;
parameter DEV_ADDR = 4'd2;
parameter ACK1 = 4'd3;
parameter MEM_ADDR_1 = 4'd4;
parameter ACK2_1 = 4'd5;
parameter MEM_ADDR_2 = 4'd6;
parameter ACK2_2 = 4'd7;
parameter START2 = 4'd8;
parameter ADD3 = 4'd9;
parameter ACK3 = 4'd10;
parameter WRITE_PROC = 4'd11;
parameter ACK4 = 4'd12;
parameter STOP1 = 4'd13;
parameter STOP2 = 4'd14;
reg[7:0] BUFF_REG = 8'b0000_0000; //SMBus上传输的数据寄存器,缓存作用
reg[3:0] SMBus_State; //状态寄存器
reg IO_SDA_REG; //输出数据寄存器
reg IO_SDA_DIR; //输出数据IO_SDA信号inout方向控制位,1'b0为input.1'b1为output
reg[3:0] num; // 计数作用
assign IO_SDA = IO_SDA_DIR ? IO_SDA_REG:1'bz;
always @ (posedge sys_clk or negedge sys_rst_n) begin
if(!sys_rst_n)
begin
SMBus_State <= IDLE;//状态寄存器
num <= 4'd0;
IO_SDA_DIR <= 1'b0;//输出数据IO_SDA信号inout方向控制位 1'b0为input
IO_SDA_REG <= 1'b1;//输出数据寄存器,IO_SDA线保持高电平
read_data <= 8'b0000_0000;//接收数据
SMBus_done <= 1'b0;//IIC完成标志
end
else
case (SMBus_State)
IDLE: begin //空闲状态
IO_SDA_DIR <= 1'b0; //数据线IO_SDA为input
IO_SDA_REG <= 1'b1; //IO_SDA拉高电平
SMBus_done <= 1'b0; //smbus clear完成标志
if(SMBus_en)
begin //SMBus_en==1,开始操作
BUFF_REG <= {device_addr,1'b0}; //从设备地址(1'b1读操作)
SMBus_State <= START1;
end
else SMBus_State <= IDLE; //没有任何键被按下
end
START1: begin //Start状态
if(`SMBCLK_HIG_MID)
begin //`SMBCLK_HIG_MID == 1, SMBCLK为高电平期间
IO_SDA_DIR <= 1'b1; //数据线IO_SDA为output
IO_SDA_REG <= 1'b0; //IO_SDA_REG输出数据寄存器,拉低数据线IO_SDA,产生起始位信号
SMBus_State <= DEV_ADDR;
num <= 4'd0; //num计数清零
end
else SMBus_State <= START1; //等待SMBCLK高电平中间位置到来
end
DEV_ADDR:
begin
if(`SMBCLK_LOW_MID)
begin
if(num == 4'd7)
begin
IO_SDA_REG <= BUFF_REG[0];
num <= 4'd0; //num计数清零
SMBus_State <= ACK1;
end
else
begin
SMBus_State <= DEV_ADDR;
case (num)
4'd0: IO_SDA_REG <= BUFF_REG[7];//BUFF_REG此时存的从设备地址,SMBus先传MSB
4'd1: IO_SDA_REG <= BUFF_REG[6];
4'd2: IO_SDA_REG <= BUFF_REG[5];
4'd3: IO_SDA_REG <= BUFF_REG[4];
4'd4: IO_SDA_REG <= BUFF_REG[3];
4'd5: IO_SDA_REG <= BUFF_REG[2];
4'd6: IO_SDA_REG <= BUFF_REG[1];
default: IO_SDA_REG <= 8'b0000_0000 ;
endcase
num <= num+1'b1;
end
end
else SMBus_State <= DEV_ADDR;
end
ACK1: begin
if(`SMBCLK_LOW_MID) begin //SMBCLK低电平
IO_SDA_DIR <= 1'b0; //IO_SDA置为高阻态(input)
SMBus_State <= MEM_ADDR_1; //响应信号
IO_SDA_REG <= 1'b0; //从机返回ACK
BUFF_REG <= SMBus_reg_addr[15:8]; // 高八位寄存器地址
end
else SMBus_State <= ACK1; //等待从机响应
end
MEM_ADDR_1: begin
if(`SMBCLK_LOW_MID)
begin
if(num == 4'd7)
begin
IO_SDA_REG <= BUFF_REG[0];
num <= 4'd0; //num计数清零
SMBus_State <= ACK2_1;
end
else begin
IO_SDA_DIR <= 1'b1; //IO_SDA作为output
case (num)
4'd0: IO_SDA_REG <= BUFF_REG[7];
4'd1: IO_SDA_REG <= BUFF_REG[6];
4'd2: IO_SDA_REG <= BUFF_REG[5];
4'd3: IO_SDA_REG <= BUFF_REG[4];
4'd4: IO_SDA_REG <= BUFF_REG[3];
4'd5: IO_SDA_REG <= BUFF_REG[2];
4'd6: IO_SDA_REG <= BUFF_REG[1];
default: IO_SDA_REG <= 8'b0000_0000 ;
endcase
num <= num+1'b1;
SMBus_State <= MEM_ADDR_1;
end
end
else SMBus_State <= MEM_ADDR_1;
end
ACK2_1: begin
if(`SMBCLK_LOW_MID) begin //
SMBus_State <= MEM_ADDR_2; //从机响应信号
IO_SDA_DIR <= 1'b0; //IO_SDA置为高阻态(input)
IO_SDA_REG <= 1'b0; //返回ACK
BUFF_REG <= SMBus_reg_addr[7:0]; // 1地址
end
else SMBus_State <= ACK2_1; //等待从机响应
end
MEM_ADDR_2: begin
if(`SMBCLK_LOW_MID) begin
if(num==4'd7) begin
IO_SDA_REG <= BUFF_REG[0];
num <= 4'd0; //num计数清零
// IO_SDA_DIR <= 1'b0; //IO_SDA置为高阻态(input)
SMBus_State <= ACK2_2;
end
else begin
IO_SDA_DIR <= 1'b1; //IO_SDA作为output
num <= num+1'b1;
case (num)
4'd0: IO_SDA_REG <= BUFF_REG[7];
4'd1: IO_SDA_REG <= BUFF_REG[6];
4'd2: IO_SDA_REG <= BUFF_REG[5];
4'd3: IO_SDA_REG <= BUFF_REG[4];
4'd4: IO_SDA_REG <= BUFF_REG[3];
4'd5: IO_SDA_REG <= BUFF_REG[2];
4'd6: IO_SDA_REG <= BUFF_REG[1];
default: IO_SDA_REG <= 8'b0000_0000;
endcase
SMBus_State <= MEM_ADDR_2;
end
end
else SMBus_State <= MEM_ADDR_2;
end
ACK2_2:
begin
if(`SMBCLK_LOW_MID)
begin //从机响应信号
IO_SDA_DIR <= 1'b0; //IO_SDA置为高阻态(input)
IO_SDA_REG <= 1'b0; //返回ACK
if(rd_or_wr) //读写标志:读1,写0
begin
SMBus_State <= START2; //读操作
BUFF_REG <= {device_addr,1'b1}; //送器件地址(读操作),特定地址读需要执行该步骤以下操作
end
else
begin
SMBus_State <= WRITE_PROC; //写操作
BUFF_REG <= SMBus_reg_data; //写入的数据
end
end
else SMBus_State <= ACK2_2; //等待从机响应
end
WRITE_PROC:
begin
if(rd_or_wr)
begin //读操作
if(num<=4'd7)
begin
if(`SMBCLK_LOW_MID) begin
num <= num+1'b1;
SMBus_State <= WRITE_PROC;
IO_SDA_DIR <= 1'b1;
case (num)
4'd0: read_data[7] <= IO_SDA;//assign IO_SDA = IO_SDA_DIR ? IO_SDA_REG:1'bz;
4'd1: read_data[6] <= IO_SDA; //读操作还没设置好,不能实现同步接收
4'd2: read_data[5] <= IO_SDA;
4'd3: read_data[4] <= IO_SDA;
4'd4: read_data[3] <= IO_SDA;
4'd5: read_data[2] <= IO_SDA;
4'd6: read_data[1] <= IO_SDA;
4'd7: read_data[0] <= IO_SDA;
default: IO_SDA_REG <= 8'b0000_0000;
endcase
end
else SMBus_State <= WRITE_PROC;
end
else if((`SMBCLK_LOW_MID) && (num==4'd8)) begin
num <= 4'd0; //num计数清零
SMBus_State <= ACK4;
end
else SMBus_State <= WRITE_PROC;
end
else begin //写操作
if(num < 4'd7)
begin
SMBus_State <= WRITE_PROC;
if(`SMBCLK_LOW_MID)
begin
IO_SDA_DIR <= 1'b1; //数据线IO_SDA作为output
case (num)
4'd0: IO_SDA_REG <= BUFF_REG[7];
4'd1: IO_SDA_REG <= BUFF_REG[6];
4'd2: IO_SDA_REG <= BUFF_REG[5];
4'd3: IO_SDA_REG <= BUFF_REG[4];
4'd4: IO_SDA_REG <= BUFF_REG[3];
4'd5: IO_SDA_REG <= BUFF_REG[2];
4'd6: IO_SDA_REG <= BUFF_REG[1];
default: IO_SDA_REG <= 8'b0000_0000;
endcase
num <= num+1'b1;
end
end
else if((`SMBCLK_LOW_MID) && (num==4'd7))
begin
IO_SDA_REG <= BUFF_REG[0];
num <= 4'd0;
// IO_SDA_DIR <= 1'b0; //IO_SDA置为高阻态
SMBus_State <= ACK4;
end
else SMBus_State <= WRITE_PROC;
end
end
ACK4: begin
if(`SMBCLK_LOW_MID) begin
SMBus_State <= STOP1;
IO_SDA_DIR <= 1'b0; //IO_SDA置为高阻态(input)
IO_SDA_REG <= 1'b0;
end
else SMBus_State <= ACK4;
end
STOP1: begin
if(`SMBCLK_LOW_MID)
begin
IO_SDA_DIR <= 1'b1;
SMBus_State <= STOP2;
#10_0000 IO_SDA_REG <= 1'b1;//SMBCLK为高时,IO_SDA产生上升沿(结束信号)
end
else SMBus_State <= STOP1;
end
STOP2: begin //
if(`SMBCLK_LOW_MID)
begin
IO_SDA_DIR <= 1'b0;
SMBus_done <= 1'b1;
#20_0000 SMBus_State <= IDLE;
// SMBus_State <= SMBus_State+ 1'b1;
end
else SMBus_State <= STOP2;
end
default: SMBus_State <= IDLE;
endcase
end
endmodule
@[TOC] sumbus/I2C Testbench
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2021/05/05 09:57:24
// Design Name:
// Module Name: testbench_2
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module testbench_2(
);
reg sys_clk ;
reg sys_rst_n = 1'b0;
reg [6:0] device_addr = 7'b0010_110;
reg SMBus_en = 1'b0;
reg rd_or_wr = 0;//1读0写
reg [15:0] SMBus_reg_addr = 16'b1011_1000_1001_0010; //SMBus完成标志
reg [7:0] SMBus_reg_data = 8'b1011_0001;
wire IO_SDA;
wire SMBCLK;
smbus_2 smbus_2_init(
. sys_clk(sys_clk), // Signal of Clock Input
. sys_rst_n(sys_rst_n), // Hardware Reset Pin, Active Low
. IO_SDA(IO_SDA),
. SMBCLK(SMBCLK),
. device_addr(device_addr), //slave device address
. SMBus_en(SMBus_en),
. rd_or_wr(rd_or_wr),
. SMBus_reg_addr(SMBus_reg_addr),
. SMBus_reg_data(SMBus_reg_data)
);
initial begin
sys_clk = 1'b0;
sys_rst_n = 1'b0;
# 10
SMBus_en = 1'b1;
sys_rst_n = 1'b1;
end
always # 1000 sys_clk = ~sys_clk; //500kHz(2us)
/****Test_Vector****/
reg [6:0] R_State;
reg [7:0] WR_Data;
reg [7:0] RD_Data;
//建立新的三态门
reg SDA_R_REG;
reg SDA_SET;
wire IO_SDA_S;
assign IO_SDA_S = (SDA_SET) ? SDA_R_REG : 1'bz;
//两个三态门相连
assign IO_SDA=IO_SDA_S;
always @ (posedge SMBCLK or negedge sys_rst_n)
begin
if (~sys_rst_n)
begin
R_State<=7'd0;
WR_Data<=8'b0;
RD_Data<=8'b0101_0110;
SDA_SET<=1'b0; //默认为输入
SDA_R_REG <= 1'b1;
end
else
begin
case(R_State)
7'b0,7'd1,7'd2,7'd3,7'd4,7'd5,7'd6,7'd7: //RST状态,接收从机地址,和读写标志
R_State <= R_State + 7'd1;
7'd8: //第9个脉冲周期,延迟150000到下一个脉冲的低电平中间,返回ACK信号
begin
#150000 SDA_SET<=1'b1;//延迟一个周期,三态转输出// IO_SDA_S = (SDA_SET) ? SDA_R_REG : 1'bz;
SDA_R_REG<=1'b0;
R_State<=R_State+7'd1;
end
7'd9://恢复
begin
#150000 SDA_SET<=1'b0; //三态门转输入
SDA_R_REG<=1'b1;
R_State<=R_State+7'd1;
end
7'd10,7'd11,7'd12,7'd13,7'd14,7'd15,7'd16://接收寄存器高8位地址
R_State<=R_State+7'd1;
7'd17:
begin
#150000 SDA_SET<=1'b1;返回ACK信号
SDA_R_REG<=1'b0;
R_State<=R_State+7'd1;
end
7'd18: //恢复
begin
#150000 SDA_SET<=1'b0; //三态门转输入
SDA_R_REG<=1'b1;
R_State<=R_State+7'd1;
end
7'd19,7'd20,7'd21,7'd22,7'd23,7'd24,7'd25://接收寄存器低8位地址
R_State<=R_State+7'd1;
7'd26:
begin
#150000 SDA_SET<=1'b1;返回ACK信号
SDA_R_REG<=1'b0;
R_State<=R_State+7'd1;
end
7'd27: //恢复
begin
#150000 SDA_SET<=1'b0; //三态门转输入
SDA_R_REG<=1'b1;
R_State<=R_State+7'd1;
end
7'd28,7'd29,7'd30,7'd31,7'd32,7'd33,7'd34: //接收数据
begin
WR_Data<={WR_Data[6:0],IO_SDA};
R_State<=R_State+7'd1;
end
7'd35:
begin
WR_Data<={WR_Data[6:0],IO_SDA};
#150000 SDA_SET<=1'b1;//返回ACK信号
SDA_R_REG<=1'b0;
R_State<=R_State+7'd1;
end
7'd36: //恢复
begin
#150000 SDA_SET<=1'b0; //三态门转输入
SDA_R_REG<=1'b1;
R_State<=R_State+7'd1;
end
7'd37: //终止位
begin
#50000 SMBus_en=1'b0; //关闭模块
#20_0000 R_State<=7'd0;
#20_0000 SMBus_en=1'b1; //Start开启 进行一次完整读过程
end
default:
R_State<=6'b0;
endcase
end
end
endmodule