一、I2C协议
I2C集成电路总线是一种串行通信总线,使用多主从架构,由飞利浦1980年设计,一般用在小数量场合,传输距离短。
在物理层面I2C接口需要两条总线线路,即SCL(串行时钟线)、SDA(串行数据线),I2C是半双工,任意时刻只有一个主机,每个I2C从机器件都有唯一一个器件地址。传输速率100Kb/s,快速模式可达到400kb/s,高速模式达3.4Mbit/s,
I2C协议规定
时钟线路SCL低电平期间,数据线SDA发生改变
时钟线路SCL高电平期间,数据线SDA数据保持
时钟线路SCL高电平期间,数据线SDA高低跳变,起始信号
时钟线路SCL高电平期间,数据线SDA低高跳变,停止信号
应答,当主机把8位数据或者命令送出后,会将数据总线SDA释放,即设置为输入,等待从机应答(低电平0表示应答,1表示非应答)
数据帧格式:
IIC器件通讯的时候,首先发送“起始信号”,紧跟着七位器件地址,第八位为传输方向(0表示写,1表示读),接着等待从机的应答信号,等传输结束,结束信号由主机产生。
二、SPI通讯RTC芯片PCF8563
AC620板子上的RTC芯片 PCF8563驱动程序,使用IIC读写数据,将时间显示在数码管上。
原理图
顶层都文件Clcok_PCF8563_top.v
/*使用说明
1、下载sof程序,数码管默认显示从PCF8563中读取到的时间信息
2、按下按键S1,切换到显示日期数据上,释放S1切回显示时间
3、按下按键S0,将预设时间(2019.01.01.01 12:00:00)写入PCF8563
*/
module Clcok_PCF8563_top(
Clk, //系统时钟输出 50Mhz
Rst_n, //复位按键输入
Key, //按键输入
i2c_sclk, //I2C的时钟管脚
i2c_sdat, //I2C的数据管脚
SH_CP, //数码管显示需要
ST_CP, //数码管显示需要
DS //数码管显示需要
);
input Clk;
input Rst_n;
input [1:0]Key; //双按键,一个按键控制需要输入的初始化时钟和日期,一个按键控制显示日期/时间
output i2c_sclk; //输出时钟
inout i2c_sdat; //双向口数据线
output SH_CP; //shift clock
output ST_CP; //latch data clock
output DS; //shift serial data
wire Set_Time; //按下写入预定时间
wire [23:0]Time_to_Set; //需要写入的时间
wire Set_Date; //按下写入预设日期
wire [31:0]Date_to_Set; //需要写入的日期
wire Read;
wire [23:0]Time_Read; //从RTC中读取的时间信息
wire [31:0]Date_Read; //从RTC中读取的日期信息
wire Cmd_Done;
wire key_flag; // 按键状态
wire key_state; // 按键状态
//PCF8563驱动
PCF8563 PCF8563(
.Clk(Clk),
.Rst_n(Rst_n),
.Set_Time(Set_Time),
.Time_to_Set(Time_to_Set),
.Set_Date(Set_Date),
.Date_to_Set(Date_to_Set),
.Read(Read),
.Time_Read(Time_Read),
.Date_Read(Date_Read),
.Cmd_Done(Cmd_Done),
.i2c_sclk(i2c_sclk),
.i2c_sdat(i2c_sdat)
);
//独立按键消抖模块
key_filter key_filter0(
.Clk(Clk),
.Rst_n(Rst_n),
.key_in(Key[0]),
.key_flag(key_flag),
.key_state(key_state)
);
//数码管显示驱动
wire [31:0]disp_data;
HEX_top HEX_top(
.Clk(Clk),
.Rst_n(Rst_n),
.disp_data(disp_data),
.SH_CP(SH_CP),
.ST_CP(ST_CP),
.DS(DS)
);
assign Set_Time = key_flag & (!key_state); //按下写入预设时间
assign Set_Date = key_flag & (key_state); //释放写入预设日期
assign Time_to_Set = 24'h08_30_00; //时间为08:30:00
assign Date_to_Set = 32'h92_09_03_16; //92年09月周三16日
wire [31:0]Time_to_Disp; //需要送到数码管上显示的带格式的时间数据
wire [31:0]Date_to_Disp; //需要送到数码管上显示的带格式的日期数据
assign Time_to_Disp = {Time_Read[23:16], 4'hf, Time_Read[15:8], 4'hf, Time_Read[7:0]}; //f对应显示-
assign Date_to_Disp = {8'h19,Date_Read[31:24],Date_Read[23:16],Date_Read[7:0]};
//按下按键1可以切换到显示日期
assign disp_data = Key[1]? Time_to_Disp : Date_to_Disp;
//设定一个定时器,大概每20ms读取一次PCF8563的日期和时间寄存器
reg [19:0]cnt;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
cnt <= 0;
else
cnt <= cnt + 1'b1;
assign Read = (cnt == 20'hfffff);
endmodule
RTC芯片PCF8563模块
PCF8563.v
module PCF8563(
Clk,
Rst_n,
Set_Time,
Time_to_Set,
Set_Date,
Date_to_Set,
Read,
Time_Read,
Date_Read,
Cmd_Done,
i2c_sclk,
i2c_sdat
);
input Clk;
input Rst_n;
input Set_Time;
input [23:0]Time_to_Set;
input Set_Date;
input [31:0]Date_to_Set;
input Read;
output reg[23:0]Time_Read;
output reg[31:0]Date_Read;
output reg Cmd_Done;
output i2c_sclk;
inout i2c_sdat;
localparam PCF8563_Address_Control_Status_1 = 8'h00; //控制/状态寄存器1
localparam PCF8563_Address_Control_Status_2 = 8'h01; //控制/状态寄存器2
localparam PCF8563_Address_CLKOUT = 8'h0d; //CLKOUT频率寄存器
localparam PCF8563_Address_Timer = 8'h0e; //定时器控制寄存器
localparam PCF8563_Address_Timer_VAL = 8'h0f; //定时器倒计数寄存器
localparam PCF8563_Address_Years = 8'h08; //年
localparam PCF8563_Address_Months = 8'h07; //月
localparam PCF8563_Address_Days = 8'h05; //日
localparam PCF8563_Address_WeekDays = 8'h06; //星期
localparam PCF8563_Address_Hours = 8'h04; //小时
localparam PCF8563_Address_Minutes = 8'h03; //分钟
localparam PCF8563_Address_Seconds = 8'h02; //秒
localparam PCF8563_Alarm_Minutes = 8'h09; //分钟报警
localparam PCF8563_Alarm_Hours = 8'h0a; //小时报警
localparam PCF8563_Alarm_Days = 8'h0b; //日报警
localparam PCF8563_Alarm_WeekDays = 8'h0c; //星期报警
reg wrreg_req, rdreg_req;
reg [7:0]addr,wrdata;
wire RW_Done;
wire [7:0]rddata;
i2c_control i2c_control(
.Clk(Clk),
.Rst_n(Rst_n),
.wrreg_req(wrreg_req),
.rdreg_req(rdreg_req),
.addr(addr),
.addr_mode(0),
.wrdata(wrdata),
.rddata(rddata),
.device_id(8'ha2),
.RW_Done(RW_Done),
.ack(),
.i2c_sclk(i2c_sclk),
.i2c_sdat(i2c_sdat)
);
reg [8:0]state;
reg [3:0]cnt;
localparam
IDLE = 9'b000000001,
SET_RTC_TIME = 9'b000000010,
WAIT_TIME_SET_DONE = 9'b000000100,
SET_RTC_DATE = 9'b000001000,
WAIT_DATE_SET_DONE = 9'b000010000,
READ_RTC = 9'b000100000,
WAIT_RTC_READ_DONE = 9'b001000000,
INIT_RTC = 9'b010000000,
WAIT_RTC_INIT_DONE = 9'b100000000;
reg init_state;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
state <= IDLE;
cnt <= 0;
Cmd_Done <= 1'b0;
wrreg_req <= 1'b0;
rdreg_req <= 1'b0;
init_state <= 0;
end
else begin
case(state)
IDLE:
begin
Cmd_Done <= 1'b0;
wrreg_req <= 1'b0;
rdreg_req <= 1'b0;
cnt <= 0;
if(!init_state)
state <= INIT_RTC;
else if(Set_Time)
state <= SET_RTC_TIME;
else if(Set_Date)
state <= SET_RTC_DATE;
else if(Read)
state <= READ_RTC;
end
SET_RTC_TIME:
begin
state <= WAIT_TIME_SET_DONE;
case(cnt)
0:write_reg(PCF8563_Address_Seconds, Time_to_Set[7:0]);
1:write_reg(PCF8563_Address_Minutes, Time_to_Set[15:8]);
2:write_reg(PCF8563_Address_Hours, Time_to_Set[23:16]);
default:;
endcase
end
WAIT_TIME_SET_DONE:
begin
wrreg_req <= 1'b0;
if(RW_Done)begin
if(cnt < 2)begin
cnt <= cnt + 1'b1;
state <= SET_RTC_TIME;
end
else begin
cnt <= 0;
Cmd_Done <= 1'b1;
state <= IDLE;
end
end
else
state <= WAIT_TIME_SET_DONE;
end
SET_RTC_DATE:
begin
state <= WAIT_DATE_SET_DONE;
case(cnt)
0:write_reg(PCF8563_Address_Days, Date_to_Set[7:0]);
1:write_reg(PCF8563_Address_WeekDays, Date_to_Set[15:8]);
2:write_reg(PCF8563_Address_Months, Date_to_Set[23:16]);
3:write_reg(PCF8563_Address_Years, Date_to_Set[31:24]);
default:;
endcase
end
WAIT_DATE_SET_DONE:
begin
wrreg_req <= 1'b0;
if(RW_Done)begin
if(cnt < 3)begin
cnt <= cnt + 1'b1;
state <= SET_RTC_DATE;
end
else begin
cnt <= 0;
Cmd_Done <= 1'b1;
state <= IDLE;
end
end
else
state <= WAIT_DATE_SET_DONE;
end
READ_RTC:
begin
state <= WAIT_RTC_READ_DONE;
case(cnt)
0:read_reg(PCF8563_Address_Days);
1:read_reg(PCF8563_Address_WeekDays);
2:read_reg(PCF8563_Address_Months);
3:read_reg(PCF8563_Address_Years);
4:read_reg(PCF8563_Address_Seconds);
5:read_reg(PCF8563_Address_Minutes);
6:read_reg(PCF8563_Address_Hours);
default:;
endcase
end
WAIT_RTC_READ_DONE:
begin
rdreg_req <= 1'b0;
if(RW_Done)begin
case(cnt)
0:Date_Read[7:0] <= rddata;
1:Date_Read[15:8] <= rddata;
2:Date_Read[23:16] <= rddata;
3:Date_Read[31:24] <= rddata;
4:Time_Read[7:0] <= rddata;
5:Time_Read[15:8] <= rddata;
6:Time_Read[23:16] <= rddata;
default:;
endcase
if(cnt < 6)begin
cnt <= cnt + 1'b1;
state <= READ_RTC;
end
else begin
cnt <= 0;
Cmd_Done <= 1'b1;
state <= IDLE;
end
end
else
state <= WAIT_RTC_READ_DONE;
end
INIT_RTC:
begin
write_reg(PCF8563_Address_Control_Status_1, 0);
state <= WAIT_RTC_INIT_DONE;
end
WAIT_RTC_INIT_DONE:
begin
wrreg_req <= 1'b0;
if(RW_Done)begin
state <= IDLE;
init_state <= 1;
end
else
state <= WAIT_RTC_INIT_DONE;
end
default:;
endcase
end
task write_reg;
input [7:0]reg_addr;
input [7:0]reg_data;
begin
wrreg_req <= 1'b1;
addr = reg_addr;
wrdata = reg_data;
end
endtask
task read_reg;
input [7:0]reg_addr;
begin
rdreg_req <= 1'b1;
addr = reg_addr;
end
endtask
endmodule
PCF8563.v
module PCF8563(
Clk,
Rst_n,
Set_Time,
Time_to_Set,
Set_Date,
Date_to_Set,
Read,
Time_Read,
Date_Read,
Cmd_Done,
i2c_sclk,
i2c_sdat
);
input Clk;
input Rst_n;
input Set_Time;
input [23:0]Time_to_Set;
input Set_Date;
input [31:0]Date_to_Set;
input Read;
output reg[23:0]Time_Read;
output reg[31:0]Date_Read;
output reg Cmd_Done;
output i2c_sclk;
inout i2c_sdat;
localparam PCF8563_Address_Control_Status_1 = 8'h00; //控制/状态寄存器1
localparam PCF8563_Address_Control_Status_2 = 8'h01; //控制/状态寄存器2
localparam PCF8563_Address_CLKOUT = 8'h0d; //CLKOUT频率寄存器
localparam PCF8563_Address_Timer = 8'h0e; //定时器控制寄存器
localparam PCF8563_Address_Timer_VAL = 8'h0f; //定时器倒计数寄存器
localparam PCF8563_Address_Years = 8'h08; //年
localparam PCF8563_Address_Months = 8'h07; //月
localparam PCF8563_Address_Days = 8'h05; //日
localparam PCF8563_Address_WeekDays = 8'h06; //星期
localparam PCF8563_Address_Hours = 8'h04; //小时
localparam PCF8563_Address_Minutes = 8'h03; //分钟
localparam PCF8563_Address_Seconds = 8'h02; //秒
localparam PCF8563_Alarm_Minutes = 8'h09; //分钟报警
localparam PCF8563_Alarm_Hours = 8'h0a; //小时报警
localparam PCF8563_Alarm_Days = 8'h0b; //日报警
localparam PCF8563_Alarm_WeekDays = 8'h0c; //星期报警
reg wrreg_req, rdreg_req;
reg [7:0]addr,wrdata;
wire RW_Done;
wire [7:0]rddata;
i2c_control i2c_control(
.Clk(Clk),
.Rst_n(Rst_n),
.wrreg_req(wrreg_req),
.rdreg_req(rdreg_req),
.addr(addr),
.addr_mode(0),
.wrdata(wrdata),
.rddata(rddata),
.device_id(8'ha2),
.RW_Done(RW_Done),
.ack(),
.i2c_sclk(i2c_sclk),
.i2c_sdat(i2c_sdat)
);
reg [8:0]state;
reg [3:0]cnt;
localparam
IDLE = 9'b000000001,
SET_RTC_TIME = 9'b000000010,
WAIT_TIME_SET_DONE = 9'b000000100,
SET_RTC_DATE = 9'b000001000,
WAIT_DATE_SET_DONE = 9'b000010000,
READ_RTC = 9'b000100000,
WAIT_RTC_READ_DONE = 9'b001000000,
INIT_RTC = 9'b010000000,
WAIT_RTC_INIT_DONE = 9'b100000000;
reg init_state;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
state <= IDLE;
cnt <= 0;
Cmd_Done <= 1'b0;
wrreg_req <= 1'b0;
rdreg_req <= 1'b0;
init_state <= 0;
end
else begin
case(state)
IDLE:
begin
Cmd_Done <= 1'b0;
wrreg_req <= 1'b0;
rdreg_req <= 1'b0;
cnt <= 0;
if(!init_state)
state <= INIT_RTC;
else if(Set_Time)
state <= SET_RTC_TIME;
else if(Set_Date)
state <= SET_RTC_DATE;
else if(Read)
state <= READ_RTC;
end
SET_RTC_TIME:
begin
state <= WAIT_TIME_SET_DONE;
case(cnt)
0:write_reg(PCF8563_Address_Seconds, Time_to_Set[7:0]);
1:write_reg(PCF8563_Address_Minutes, Time_to_Set[15:8]);
2:write_reg(PCF8563_Address_Hours, Time_to_Set[23:16]);
default:;
endcase
end
WAIT_TIME_SET_DONE:
begin
wrreg_req <= 1'b0;
if(RW_Done)begin
if(cnt < 2)begin
cnt <= cnt + 1'b1;
state <= SET_RTC_TIME;
end
else begin
cnt <= 0;
Cmd_Done <= 1'b1;
state <= IDLE;
end
end
else
state <= WAIT_TIME_SET_DONE;
end
SET_RTC_DATE:
begin
state <= WAIT_DATE_SET_DONE;
case(cnt)
0:write_reg(PCF8563_Address_Days, Date_to_Set[7:0]);
1:write_reg(PCF8563_Address_WeekDays, Date_to_Set[15:8]);
2:write_reg(PCF8563_Address_Months, Date_to_Set[23:16]);
3:write_reg(PCF8563_Address_Years, Date_to_Set[31:24]);
default:;
endcase
end
WAIT_DATE_SET_DONE:
begin
wrreg_req <= 1'b0;
if(RW_Done)begin
if(cnt < 3)begin
cnt <= cnt + 1'b1;
state <= SET_RTC_DATE;
end
else begin
cnt <= 0;
Cmd_Done <= 1'b1;
state <= IDLE;
end
end
else
state <= WAIT_DATE_SET_DONE;
end
READ_RTC:
begin
state <= WAIT_RTC_READ_DONE;
case(cnt)
0:read_reg(PCF8563_Address_Days);
1:read_reg(PCF8563_Address_WeekDays);
2:read_reg(PCF8563_Address_Months);
3:read_reg(PCF8563_Address_Years);
4:read_reg(PCF8563_Address_Seconds);
5:read_reg(PCF8563_Address_Minutes);
6:read_reg(PCF8563_Address_Hours);
default:;
endcase
end
WAIT_RTC_READ_DONE:
begin
rdreg_req <= 1'b0;
if(RW_Done)begin
case(cnt)
0:Date_Read[7:0] <= rddata;
1:Date_Read[15:8] <= rddata;
2:Date_Read[23:16] <= rddata;
3:Date_Read[31:24] <= rddata;
4:Time_Read[7:0] <= rddata;
5:Time_Read[15:8] <= rddata;
6:Time_Read[23:16] <= rddata;
default:;
endcase
if(cnt < 6)begin
cnt <= cnt + 1'b1;
state <= READ_RTC;
end
else begin
cnt <= 0;
Cmd_Done <= 1'b1;
state <= IDLE;
end
end
else
state <= WAIT_RTC_READ_DONE;
end
INIT_RTC:
begin
write_reg(PCF8563_Address_Control_Status_1, 0);
state <= WAIT_RTC_INIT_DONE;
end
WAIT_RTC_INIT_DONE:
begin
wrreg_req <= 1'b0;
if(RW_Done)begin
state <= IDLE;
init_state <= 1;
end
else
state <= WAIT_RTC_INIT_DONE;
end
default:;
endcase
end
task write_reg;
input [7:0]reg_addr;
input [7:0]reg_data;
begin
wrreg_req <= 1'b1;
addr = reg_addr;
wrdata = reg_data;
end
endtask
task read_reg;
input [7:0]reg_addr;
begin
rdreg_req <= 1'b1;
addr = reg_addr;
end
endtask
endmodule
IIC控制模块
i2c_control.v
module i2c_control(
Clk,
Rst_n,
wrreg_req,
rdreg_req,
addr,
addr_mode,
wrdata,
rddata,
device_id,
RW_Done,
ack,
i2c_sclk,
i2c_sdat
);
input Clk;
input Rst_n;
input wrreg_req;
input rdreg_req;
input [15:0]addr;
input addr_mode;
input [7:0]wrdata;
output reg[7:0]rddata;
input [7:0]device_id;
output reg RW_Done;
output reg ack;
output i2c_sclk;
inout i2c_sdat;
reg [5:0]Cmd;
reg [7:0]Tx_DATA;
wire Trans_Done;
wire ack_o;
reg Go;
wire [15:0]reg_addr;
wire [7:0]Rx_DATA;
localparam
WR = 6'b000001, // 写请求
STA = 6'b000010, //起始位请求
RD = 6'b000100, //读请求
STO = 6'b001000, //停止位请求
ACK = 6'b010000, //应答位请求
NACK = 6'b100000; //无应答请求
i2c_bit_shift i2c_bit_shift(
.Clk(Clk),
.Rst_n(Rst_n),
.Cmd(Cmd),
.Go(Go),
.Rx_DATA(Rx_DATA),
.Tx_DATA(Tx_DATA),
.Trans_Done(Trans_Done),
.ack_o(ack_o),
.i2c_sclk(i2c_sclk),
.i2c_sdat(i2c_sdat)
);
reg [6:0]state;
reg [7:0]cnt;
localparam
IDLE = 7'b0000001,
WR_REG = 7'b0000010,
WAIT_WR_DONE = 7'b0000100,
WR_REG_DONE = 7'b0001000,
RD_REG = 7'b0010000,
WAIT_RD_DONE = 7'b0100000,
RD_REG_DONE = 7'b1000000;
assign reg_addr = addr_mode?addr:{addr[7:0],addr[15:8]};
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
Cmd <= 6'd0;
Tx_DATA <= 8'd0;
Go <= 1'b0;
rddata <= 0;
state <= IDLE;
ack <= 0;
end
else begin
case(state)
IDLE:
begin
cnt <= 0;
ack <= 0;
RW_Done <= 1'b0;
if(wrreg_req)
state <= WR_REG;
else if(rdreg_req)
state <= RD_REG;
else
state <= IDLE;
end
WR_REG:
begin
state <= WAIT_WR_DONE;
case(cnt)
0:write_byte(WR | STA, device_id);
1:write_byte(WR, reg_addr[15:8]);
2:write_byte(WR, reg_addr[7:0]);
3:write_byte(WR | STO, wrdata);
default:;
endcase
end
WAIT_WR_DONE:
begin
Go <= 1'b0;
if(Trans_Done)begin
ack <= ack | ack_o;
case(cnt)
0: begin cnt <= 1; state <= WR_REG;end
1:
begin
state <= WR_REG;
if(addr_mode)
cnt <= 2;
else
cnt <= 3;
end
2: begin
cnt <= 3;
state <= WR_REG;
end
3:state <= WR_REG_DONE;
default:state <= IDLE;
endcase
end
end
WR_REG_DONE:
begin
RW_Done <= 1'b1;
state <= IDLE;
end
RD_REG:
begin
state <= WAIT_RD_DONE;
case(cnt)
0:write_byte(WR | STA, device_id);
1:if(addr_mode)
write_byte(WR, reg_addr[15:8]);
else
write_byte(WR, reg_addr[15:8]);
2:write_byte(WR, reg_addr[7:0]);
3:write_byte(WR | STA, device_id | 8'd1);
4:read_byte(RD | NACK | STO);
default:;
endcase
end
WAIT_RD_DONE:
begin
Go <= 1'b0;
if(Trans_Done)begin
if(cnt <= 3)
ack <= ack | ack_o;
case(cnt)
0: begin cnt <= 1; state <= RD_REG;end
1:
begin
state <= RD_REG;
if(addr_mode)
cnt <= 2;
else
cnt <= 3;
end
2: begin
cnt <= 3;
state <= RD_REG;
end
3:begin
cnt <= 4;
state <= RD_REG;
end
4:state <= RD_REG_DONE;
default:state <= IDLE;
endcase
end
end
RD_REG_DONE:
begin
RW_Done <= 1'b1;
rddata <= Rx_DATA;
state <= IDLE;
end
default:state <= IDLE;
endcase
end
task read_byte;
input [5:0]Ctrl_Cmd;
begin
Cmd <= Ctrl_Cmd;
Go <= 1'b1;
end
endtask
task write_byte;
input [5:0]Ctrl_Cmd;
input [7:0]Wr_Byte_Data;
begin
Cmd <= Ctrl_Cmd;
Tx_DATA <= Wr_Byte_Data;
Go <= 1'b1;
end
endtask
endmodule
IIC模块
i2c_bit_shift.v
module i2c_bit_shift(
Clk,
Rst_n,
Cmd,
Go,
Rx_DATA,
Tx_DATA,
Trans_Done,
ack_o,
i2c_sclk,
i2c_sdat
);
input Clk;
input Rst_n;
input [5:0]Cmd;
input Go;
output reg[7:0]Rx_DATA;
input [7:0]Tx_DATA;
output reg Trans_Done;
output reg ack_o;
output reg i2c_sclk;
inout i2c_sdat;
reg i2c_sdat_o;
//系统时钟采用50MHz
parameter SYS_CLOCK = 50_000_000;
//SCL总线时钟采用400kHz
parameter SCL_CLOCK = 400_000;
//产生时钟SCL计数器最大值
localparam SCL_CNT_M = SYS_CLOCK/SCL_CLOCK/4 - 1;
reg i2c_sdat_oe;
localparam
WR = 6'b000001, // 写请求
STA = 6'b000010, //起始位请求
RD = 6'b000100, //读请求
STO = 6'b001000, //停止位请求
ACK = 6'b010000, //应答位请求
NACK = 6'b100000; //无应答请求
reg [19:0]div_cnt;
reg en_div_cnt;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
div_cnt <= 20'd0;
else if(en_div_cnt)begin
if(div_cnt < SCL_CNT_M)
div_cnt <= div_cnt + 1'b1;
else
div_cnt <= 0;
end
else
div_cnt <= 0;
wire sclk_plus = div_cnt == SCL_CNT_M;
assign i2c_sdat = i2c_sdat_oe?i2c_sdat_o:1'bz;
reg [7:0]state;
localparam
IDLE = 8'b00000001,
GEN_STA = 8'b00000010,
WR_DATA = 8'b00000100,
RD_DATA = 8'b00001000,
CHECK_ACK = 8'b00010000,
GEN_ACK = 8'b00100000,
GEN_STO = 8'b01000000;
reg [4:0]cnt;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
Rx_DATA <= 0;
i2c_sdat_oe <= 1'd0;
en_div_cnt <= 1'b0;
i2c_sdat_o <= 1'd1;
Trans_Done <= 1'b0;
ack_o <= 0;
state <= IDLE;
cnt <= 0;
end
else begin
case(state)
IDLE:
begin
Trans_Done <= 1'b0;
i2c_sdat_oe <= 1'd1;
if(Go)begin
en_div_cnt <= 1'b1;
if(Cmd & STA)
state <= GEN_STA;
else if(Cmd & WR)
state <= WR_DATA;
else if(Cmd & RD)
state <= RD_DATA;
else
state <= IDLE;
end
else begin
en_div_cnt <= 1'b0;
state <= IDLE;
end
end
GEN_STA:
begin
if(sclk_plus)begin
if(cnt == 3)
cnt <= 0;
else
cnt <= cnt + 1'b1;
case(cnt)
0:begin i2c_sdat_o <= 1; i2c_sdat_oe <= 1'd1;end
1:begin i2c_sclk <= 1;end
2:begin i2c_sdat_o <= 0; i2c_sclk <= 1;end
3:begin i2c_sclk <= 0;end
default:begin i2c_sdat_o <= 1; i2c_sclk <= 1;end
endcase
if(cnt == 3)begin
if(Cmd & WR)
state <= WR_DATA;
else if(Cmd & RD)
state <= RD_DATA;
end
end
end
WR_DATA:
begin
if(sclk_plus)begin
if(cnt == 31)
cnt <= 0;
else
cnt <= cnt + 1'b1;
case(cnt)
0,4,8,12,16,20,24,28:begin i2c_sdat_o <= Tx_DATA[7-cnt[4:2]]; i2c_sdat_oe <= 1'd1;end //set data;
1,5,9,13,17,21,25,29:begin i2c_sclk <= 1;end //sclk posedge
2,6,10,14,18,22,26,30:begin i2c_sclk <= 1;end //sclk keep high
3,7,11,15,19,23,27,31:begin i2c_sclk <= 0;end //sclk negedge
/*
0 :begin i2c_sdat_o <= Tx_DATA[7];end
1 :begin i2c_sclk <= 1;end //sclk posedge
2 :begin i2c_sclk <= 1;end //sclk keep high
3 :begin i2c_sclk <= 0;end //sclk negedge
4 :begin i2c_sdat_o <= Tx_DATA[6];end
5 :begin i2c_sclk <= 1;end //sclk posedge
6 :begin i2c_sclk <= 1;end //sclk keep high
7 :begin i2c_sclk <= 0;end //sclk negedge
8 :begin i2c_sdat_o <= Tx_DATA[5];end
9 :begin i2c_sclk <= 1;end //sclk posedge
10:begin i2c_sclk <= 1;end //sclk keep high
11:begin i2c_sclk <= 0;end //sclk negedge
12:begin i2c_sdat_o <= Tx_DATA[4];end
13:begin i2c_sclk <= 1;end //sclk posedge
14:begin i2c_sclk <= 1;end //sclk keep high
15:begin i2c_sclk <= 0;end //sclk negedge
16:begin i2c_sdat_o <= Tx_DATA[3];end
17:begin i2c_sclk <= 1;end //sclk posedge
18:begin i2c_sclk <= 1;end //sclk keep high
19:begin i2c_sclk <= 0;end //sclk negedge
20:begin i2c_sdat_o <= Tx_DATA[2];end
21:begin i2c_sclk <= 1;end //sclk posedge
22:begin i2c_sclk <= 1;end //sclk keep high
23:begin i2c_sclk <= 0;end //sclk negedge
24:begin i2c_sdat_o <= Tx_DATA[1];end
25:begin i2c_sclk <= 1;end //sclk posedge
26:begin i2c_sclk <= 1;end //sclk keep high
27:begin i2c_sclk <= 0;end //sclk negedge
28:begin i2c_sdat_o <= Tx_DATA[0];end
29:begin i2c_sclk <= 1;end //sclk posedge
30:begin i2c_sclk <= 1;end //sclk keep high
31:begin i2c_sclk <= 0;end //sclk negedge
*/
default:begin i2c_sdat_o <= 1; i2c_sclk <= 1;end
endcase
if(cnt == 31)begin
state <= CHECK_ACK;
end
end
end
RD_DATA:
begin
if(sclk_plus)begin
if(cnt == 31)
cnt <= 0;
else
cnt <= cnt + 1'b1;
case(cnt)
0,4,8,12,16,20,24,28:begin i2c_sdat_oe <= 1'd0; i2c_sclk <= 0;end //set data;
1,5,9,13,17,21,25,29:begin i2c_sclk <= 1;end //sclk posedge
2,6,10,14,18,22,26,30:begin i2c_sclk <= 1; Rx_DATA <= {Rx_DATA[6:0],i2c_sdat};end //sclk keep high
3,7,11,15,19,23,27,31:begin i2c_sclk <= 0;end //sclk negedge
default:begin i2c_sdat_o <= 1; i2c_sclk <= 1;end
endcase
if(cnt == 31)begin
state <= GEN_ACK;
end
end
end
CHECK_ACK:
begin
if(sclk_plus)begin
if(cnt == 3)
cnt <= 0;
else
cnt <= cnt + 1'b1;
case(cnt)
0:begin i2c_sdat_oe <= 1'd0; i2c_sclk <= 0;end
1:begin i2c_sclk <= 1;end
2:begin ack_o <= i2c_sdat; i2c_sclk <= 1;end
3:begin i2c_sclk <= 0;end
default:begin i2c_sdat_o <= 1; i2c_sclk <= 1;end
endcase
if(cnt == 3)begin
if(Cmd & STO)
state <= GEN_STO;
else begin
state <= IDLE;
Trans_Done <= 1'b1;
end
end
end
end
GEN_ACK:
begin
if(sclk_plus)begin
if(cnt == 3)
cnt <= 0;
else
cnt <= cnt + 1'b1;
case(cnt)
0:begin
i2c_sdat_oe <= 1'd1;
i2c_sclk <= 0;
if(Cmd & ACK)
i2c_sdat_o <= 1'b0;
else if(Cmd & NACK)
i2c_sdat_o <= 1'b1;
end
1:begin i2c_sclk <= 1;end
2:begin i2c_sclk <= 1;end
3:begin i2c_sclk <= 0;end
default:begin i2c_sdat_o <= 1; i2c_sclk <= 1;end
endcase
if(cnt == 3)begin
if(Cmd & STO)
state <= GEN_STO;
else begin
state <= IDLE;
Trans_Done <= 1'b1;
end
end
end
end
GEN_STO:
begin
if(sclk_plus)begin
if(cnt == 3)
cnt <= 0;
else
cnt <= cnt + 1'b1;
case(cnt)
0:begin i2c_sdat_o <= 0; i2c_sdat_oe <= 1'd1;end
1:begin i2c_sclk <= 1;end
2:begin i2c_sdat_o <= 1; i2c_sclk <= 1;end
3:begin i2c_sclk <= 1;end
default:begin i2c_sdat_o <= 1; i2c_sclk <= 1;end
endcase
if(cnt == 3)begin
Trans_Done <= 1'b1;
state <= IDLE;
end
end
end
default:state <= IDLE;
endcase
end
endmodule
数码管模块
HEX_top.v
/***************************************************
* Module Name : HEX_top
* Engineer : 小梅哥
* Target Device : EP4CE10F17C8
* Tool versions : Quartus II 13.0
* Create Date : 2017-3-31
* Revision : v1.0
* Description : 三线制数码管显示顶层设计
**************************************************/
module HEX_top(
Clk,
Rst_n,
disp_data,
SH_CP,
ST_CP,
DS
);
input Clk; //50M
input Rst_n;
output SH_CP; //shift clock
output ST_CP; //latch data clock
output DS; //shift serial data
input [31:0]disp_data;
wire [7:0] sel;//数码管位选(选择当前要显示的数码管)
wire [6:0] seg;//数码管段选(当前要显示的内容)
HEX8 HEX8(
.Clk(Clk),
.Rst_n(Rst_n),
.En(1'b1),
.disp_data(disp_data),
.sel(sel),
.seg(seg)
);
HC595_Driver HC595_Driver(
.Clk(Clk),
.Rst_n(Rst_n),
.Data({1'b1,seg,sel}),
.S_EN(1'b1),
.SH_CP(SH_CP),
.ST_CP(ST_CP),
.DS(DS)
);
endmodule
数码管模块
HEX8.v
/***************************************************
* Module Name : HEX8
* Engineer : 小梅哥
* Target Device : EP4CE10F17C8
* Tool versions : Quartus II 13.0
* Create Date : 2017-3-31
* Revision : v1.0
* Description : 8位7段数码管显示设计
**************************************************/
module HEX8(
Clk,
Rst_n,
En,
disp_data,
sel,
seg
);
input Clk; //50M
input Rst_n;
input En; //数码管显示使能,1使能,0关闭
input [31:0]disp_data;
output [7:0] sel;//数码管位选(选择当前要显示的数码管)
output reg [6:0] seg;//数码管段选(当前要显示的内容)
reg [14:0]divider_cnt;//25000-1
reg clk_1K;
reg [7:0]sel_r;
reg [3:0]data_tmp;//数据缓存
// 分频计数器计数模块
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
divider_cnt <= 15'd0;
else if(!En)
divider_cnt <= 15'd0;
else if(divider_cnt == 24999)
divider_cnt <= 15'd0;
else
divider_cnt <= divider_cnt + 1'b1;
//1K扫描时钟生成模块
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
clk_1K <= 1'b0;
else if(divider_cnt == 24999)
clk_1K <= ~clk_1K;
else
clk_1K <= clk_1K;
//8位循环移位寄存器
always@(posedge clk_1K or negedge Rst_n)
if(!Rst_n)
sel_r <= 8'b0000_0001;
else if(sel_r == 8'b1000_0000)
sel_r <= 8'b0000_0001;
else
sel_r <= sel_r << 1;
always@(*)
case(sel_r)
8'b0000_0001:data_tmp = disp_data[3:0];
8'b0000_0010:data_tmp = disp_data[7:4];
8'b0000_0100:data_tmp = disp_data[11:8];
8'b0000_1000:data_tmp = disp_data[15:12];
8'b0001_0000:data_tmp = disp_data[19:16];
8'b0010_0000:data_tmp = disp_data[23:20];
8'b0100_0000:data_tmp = disp_data[27:24];
8'b1000_0000:data_tmp = disp_data[31:28];
default:data_tmp = 4'b0000;
endcase
always@(*)
case(data_tmp)
4'h0:seg = 7'b1000000; /*0*/
4'h1:seg = 7'b1111001; /*1*/
4'h2:seg = 7'b0100100; /*2*/
4'h3:seg = 7'b0110000; /*3*/
4'h4:seg = 7'b0011001; /*4*/
4'h5:seg = 7'b0010010; /*5*/
4'h6:seg = 7'b0000010; /*6*/
4'h7:seg = 7'b1111000; /*7*/
4'h8:seg = 7'b0000000; /*8*/
4'h9:seg = 7'b0010000; /*9*/
4'ha:seg = 7'b0001000; /*a*/
4'hb:seg = 7'b0000011; /*b*/
4'hc:seg = 7'b1000110; /*c*/
4'hd:seg = 7'b0100001; /*d*/
4'he:seg = 7'b0000110; /*e*/
4'hf:seg = 7'b0111111; /*-*/
endcase
assign sel = (En)?sel_r:8'b0000_0000;
endmodule
595驱动
HC595_Driver.v
/***************************************************
* Module Name : HC595_Driver
* Engineer : 小梅哥
* Target Device : EP4CE10F17C8
* Tool versions : Quartus II 13.0
* Create Date : 2017-3-31
* Revision : v1.0
* Description : 74HC595移位寄存器驱动设计
**************************************************/
module HC595_Driver(
Clk,
Rst_n,
Data,
S_EN,
SH_CP,
ST_CP,
DS
);
parameter DATA_WIDTH = 16;
input Clk;
input Rst_n;
input [DATA_WIDTH-1 : 0] Data; //data to send
input S_EN; //send en
output reg SH_CP; //shift clock
output reg ST_CP; //latch data clock
output reg DS; //shift serial data
parameter CNT_MAX = 4;
reg [15:0] divider_cnt;//分频计数器
wire sck_pluse;
reg [4:0]SHCP_EDGE_CNT;//SH_CP EDGE counter
reg [15:0]r_data;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
r_data <= 16'd0;
else if(S_EN)
r_data <= Data;
else
r_data <= r_data;
//clock divide
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
divider_cnt <= 16'd0;
else if(divider_cnt == CNT_MAX)
divider_cnt <= 16'd0;
else
divider_cnt <= divider_cnt + 1'b1;
assign sck_pluse = (divider_cnt == CNT_MAX);
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
SHCP_EDGE_CNT <= 5'd0;
else if(sck_pluse)begin
if(SHCP_EDGE_CNT == 5'd31)
SHCP_EDGE_CNT <= 5'd0;
else
SHCP_EDGE_CNT <= SHCP_EDGE_CNT + 1'd1;
end
else
SHCP_EDGE_CNT <= SHCP_EDGE_CNT;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
SH_CP <= 1'b0;
ST_CP <= 1'b0;
DS <= 1'b0;
end
else begin
case(SHCP_EDGE_CNT)
5'd0 :begin SH_CP <= 1'b0; ST_CP <= 1'b1; DS <= r_data[15]; end
5'd1 :begin SH_CP <= 1'b1; ST_CP <= 1'b0;end
5'd2 :begin SH_CP <= 1'b0; DS <= r_data[14];end
5'd3 :begin SH_CP <= 1'b1; end
5'd4 :begin SH_CP <= 1'b0; DS <= r_data[13];end
5'd5 :begin SH_CP <= 1'b1; end
5'd6 :begin SH_CP <= 1'b0; DS <= r_data[12];end
5'd7 :begin SH_CP <= 1'b1; end
5'd8 :begin SH_CP <= 1'b0; DS <= r_data[11];end
5'd9 :begin SH_CP <= 1'b1; end
5'd10:begin SH_CP <= 1'b0; DS <= r_data[10];end
5'd11:begin SH_CP <= 1'b1; end
5'd12:begin SH_CP <= 1'b0; DS <= r_data[9];end
5'd13:begin SH_CP <= 1'b1; end
5'd14:begin SH_CP <= 1'b0; DS <= r_data[8];end
5'd15:begin SH_CP <= 1'b1; end
5'd16:begin SH_CP <= 1'b0; DS <= r_data[7];end
5'd17:begin SH_CP <= 1'b1; end
5'd18:begin SH_CP <= 1'b0; DS <= r_data[6];end
5'd19:begin SH_CP <= 1'b1; end
5'd20:begin SH_CP <= 1'b0; DS <= r_data[5];end
5'd21:begin SH_CP <= 1'b1; end
5'd22:begin SH_CP <= 1'b0; DS <= r_data[4];end
5'd23:begin SH_CP <= 1'b1; end
5'd24:begin SH_CP <= 1'b0; DS <= r_data[3];end
5'd25:begin SH_CP <= 1'b1; end
5'd26:begin SH_CP <= 1'b0; DS <= r_data[2];end
5'd27:begin SH_CP <= 1'b1; end
5'd28:begin SH_CP <= 1'b0; DS <= r_data[1];end
5'd29:begin SH_CP <= 1'b1; end
5'd30:begin SH_CP <= 1'b0; DS <= r_data[0];end
5'd31:begin SH_CP <= 1'b1; end
endcase
end
endmodule
按键模块
key_filter.v
/***************************************************
* Module Name : key_filter
* Engineer : 小梅哥
* Target Device : EP4CE10F17C8
* Tool versions : Quartus II 13.0
* Create Date : 2017-3-31
* Revision : v1.0
* Description : 单按键消抖设计
**************************************************/
module key_filter(
Clk, //50M时钟输入
Rst_n, //模块复位
key_in, //按键输入
key_flag, //按键标志信号
key_state //按键状态信号
);
input Clk;
input Rst_n;
input key_in;
output reg key_flag;
output reg key_state;
localparam
IDEL = 4'b0001,
FILTER0 = 4'b0010,
DOWN = 4'b0100,
FILTER1 = 4'b1000;
reg [3:0]state;
reg [19:0]cnt;
reg en_cnt; //使能计数寄存器
//对外部输入的异步信号进行同步处理
reg key_in_sa,key_in_sb;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
key_in_sa <= 1'b0;
key_in_sb <= 1'b0;
end
else begin
key_in_sa <= key_in;
key_in_sb <= key_in_sa;
end
reg key_tmpa,key_tmpb;
wire pedge,nedge;
reg cnt_full;//计数满标志信号
//使用D触发器存储两个相邻时钟上升沿时外部输入信号(已经同步到系统时钟域中)的电平状态
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
key_tmpa <= 1'b0;
key_tmpb <= 1'b0;
end
else begin
key_tmpa <= key_in_sb;
key_tmpb <= key_tmpa;
end
//产生跳变沿信号
assign nedge = !key_tmpa & key_tmpb;
assign pedge = key_tmpa & (!key_tmpb);
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)begin
en_cnt <= 1'b0;
state <= IDEL;
key_flag <= 1'b0;
key_state <= 1'b1;
end
else begin
case(state)
IDEL :
begin
key_flag <= 1'b0;
if(nedge)begin
state <= FILTER0;
en_cnt <= 1'b1;
end
else
state <= IDEL;
end
FILTER0:
if(cnt_full)begin
key_flag <= 1'b1;
key_state <= 1'b0;
en_cnt <= 1'b0;
state <= DOWN;
end
else if(pedge)begin
state <= IDEL;
en_cnt <= 1'b0;
end
else
state <= FILTER0;
DOWN:
begin
key_flag <= 1'b0;
if(pedge)begin
state <= FILTER1;
en_cnt <= 1'b1;
end
else
state <= DOWN;
end
FILTER1:
if(cnt_full)begin
key_flag <= 1'b1;
key_state <= 1'b1;
state <= IDEL;
en_cnt <= 1'b0;
end
else if(nedge)begin
en_cnt <= 1'b0;
state <= DOWN;
end
else
state <= FILTER1;
default:
begin
state <= IDEL;
en_cnt <= 1'b0;
key_flag <= 1'b0;
key_state <= 1'b1;
end
endcase
end
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
cnt <= 20'd0;
else if(en_cnt)
cnt <= cnt + 1'b1;
else
cnt <= 20'd0;
always@(posedge Clk or negedge Rst_n)
if(!Rst_n)
cnt_full <= 1'b0;
else if(cnt == 20'd999_999)
cnt_full <= 1'b1;
else
cnt_full <= 1'b0;
endmodule
M24LC04B模块
M24LC04B.v
// *******************************************************************************************************
// ** **
// ** 24LC04B.v - Microchip 24LC04B 4K-BIT I2C SERIAL EEPROM (VCC = +2.5V TO +5.5V) **
// ** **
// *******************************************************************************************************
// ** **
// ** This information is distributed under license from Young Engineering. **
// ** COPYRIGHT (c) 2003 YOUNG ENGINEERING **
// ** ALL RIGHTS RESERVED **
// ** **
// ** **
// ** Young Engineering provides design expertise for the digital world **
// ** Started in 1990, Young Engineering offers products and services for your electronic design **
// ** project. We have the expertise in PCB, FPGA, ASIC, firmware, and software design. **
// ** From concept to prototype to production, we can help you. **
// ** **
// ** http://www.young-engineering.com/ **
// ** **
// *******************************************************************************************************
// ** This information is provided to you for your convenience and use with Microchip products only. **
// ** Microchip disclaims all liability arising from this information and its use. **
// ** **
// ** THIS INFORMATION IS PROVIDED "AS IS." MICROCHIP MAKES NO REPRESENTATION OR WARRANTIES OF **
// ** ANY KIND WHETHER EXPRESS OR IMPLIED, WRITTEN OR ORAL, STATUTORY OR OTHERWISE, RELATED TO **
// ** THE INFORMATION PROVIDED TO YOU, INCLUDING BUT NOT LIMITED TO ITS CONDITION, QUALITY, **
// ** PERFORMANCE, MERCHANTABILITY, NON-INFRINGEMENT, OR FITNESS FOR PURPOSE. **
// ** MICROCHIP IS NOT LIABLE, UNDER ANY CIRCUMSTANCES, FOR SPECIAL, INCIDENTAL OR CONSEQUENTIAL **
// ** DAMAGES, FOR ANY REASON WHATSOEVER. **
// ** **
// ** It is your responsibility to ensure that your application meets with your specifications. **
// ** **
// *******************************************************************************************************
// ** Revision : 1.3 **
// ** Modified Date : 12/04/2006 **
// ** Revision History: **
// ** **
// ** 02/01/2003: Initial design **
// ** 07/19/2004: Fixed the timing checks and the open-drain modeling for SDA. **
// ** 01/06/2006: Changed the legal information in the header **
// ** 12/04/2006: Corrected timing checks to reference proper clock edges **
// ** Added timing check for Tbuf (bus free time) **
// ** **
// *******************************************************************************************************
// ** TABLE OF CONTENTS **
// *******************************************************************************************************
// **---------------------------------------------------------------------------------------------------**
// ** DECLARATIONS **
// **---------------------------------------------------------------------------------------------------**
// **---------------------------------------------------------------------------------------------------**
// ** INITIALIZATION **
// **---------------------------------------------------------------------------------------------------**
// **---------------------------------------------------------------------------------------------------**
// ** CORE LOGIC **
// **---------------------------------------------------------------------------------------------------**
// ** 1.01: START Bit Detection **
// ** 1.02: STOP Bit Detection **
// ** 1.03: Input Shift Register **
// ** 1.04: Input Bit Counter **
// ** 1.05: Control Byte Register **
// ** 1.06: Byte Address Register **
// ** 1.07: Write Data Buffer **
// ** 1.08: Acknowledge Generator **
// ** 1.09: Acknowledge Detect **
// ** 1.10: Write Cycle Timer **
// ** 1.11: Write Cycle Processor **
// ** 1.12: Read Data Multiplexor **
// ** 1.13: Read Data Processor **
// ** 1.14: SDA Data I/O Buffer **
// ** **
// **---------------------------------------------------------------------------------------------------**
// ** DEBUG LOGIC **
// **---------------------------------------------------------------------------------------------------**
// ** 2.01: Memory Data Bytes **
// ** 2.02: Write Data Buffer **
// ** **
// **---------------------------------------------------------------------------------------------------**
// ** TIMING CHECKS **
// **---------------------------------------------------------------------------------------------------**
// ** **
// *******************************************************************************************************
`timescale 1ns/10ps
module M24LC04B (A0, A1, A2, WP, SDA, SCL, RESET);
input A0; // unconnected pin
input A1; // unconnected pin
input A2; // unconnected pin
input WP; // write protect pin
inout SDA; // serial data I/O
input SCL; // serial data clock
input RESET; // system reset
// *******************************************************************************************************
// ** DECLARATIONS **
// *******************************************************************************************************
reg SDA_DO; // serial data - output
reg SDA_OE; // serial data - output enable
wire SDA_DriveEnable; // serial data output enable
reg SDA_DriveEnableDlyd; // serial data output enable - delayed
reg [03:00] BitCounter; // serial bit counter
reg START_Rcvd; // START bit received flag
reg STOP_Rcvd; // STOP bit received flag
reg CTRL_Rcvd; // control byte received flag
reg ADDR_Rcvd; // byte address received flag
reg MACK_Rcvd; // master acknowledge received flag
reg WrCycle; // memory write cycle
reg RdCycle; // memory read cycle
reg [07:00] ShiftRegister; // input data shift register
reg [07:00] ControlByte; // control byte register
wire BlockSelect; // memory block select
wire RdWrBit; // read/write control bit
reg [08:00] StartAddress; // memory access starting address
reg [03:00] PageAddress; // memory page address
reg [07:00] WrDataByte [0:15]; // memory write data buffer
wire [07:00] RdDataByte; // memory read data
reg [15:00] WrCounter; // write buffer counter
reg [03:00] WrPointer; // write buffer pointer
reg [08:00] RdPointer; // read address pointer
reg WriteActive; // memory write cycle active
reg [07:00] MemoryBlock0 [0:255]; // EEPROM data memory array
reg [07:00] MemoryBlock1 [0:255]; // EEPROM data memory array
integer LoopIndex; // iterative loop index
integer tAA; // timing parameter
integer tWC; // timing parameter
// *******************************************************************************************************
// ** INITIALIZATION **
// *******************************************************************************************************
//----------------------------
//------写数据间隔改动----------
initial tAA = 900; // SCL to SDA output delay
initial tWC = 500; // memory write cycle time
// initial tAA = 900; // SCL to SDA output delay
// initial tWC = 5000000; // memory write cycle time
initial begin
SDA_DO = 0;
SDA_OE = 0;
end
initial begin
START_Rcvd = 0;
STOP_Rcvd = 0;
CTRL_Rcvd = 0;
ADDR_Rcvd = 0;
MACK_Rcvd = 0;
end
initial begin
BitCounter = 0;
ControlByte = 0;
end
initial begin
WrCycle = 0;
RdCycle = 0;
WriteActive = 0;
end
// *******************************************************************************************************
// ** CORE LOGIC **
// *******************************************************************************************************
// -------------------------------------------------------------------------------------------------------
// 1.01: START Bit Detection
// -------------------------------------------------------------------------------------------------------
always @(negedge SDA) begin
if (SCL == 1) begin
START_Rcvd <= 1;
STOP_Rcvd <= 0;
CTRL_Rcvd <= 0;
ADDR_Rcvd <= 0;
MACK_Rcvd <= 0;
WrCycle <= #1 0;
RdCycle <= #1 0;
BitCounter <= 0;
end
end
// -------------------------------------------------------------------------------------------------------
// 1.02: STOP Bit Detection
// -------------------------------------------------------------------------------------------------------
always @(posedge SDA) begin
if (SCL == 1) begin
START_Rcvd <= 0;
STOP_Rcvd <= 1;
CTRL_Rcvd <= 0;
ADDR_Rcvd <= 0;
MACK_Rcvd <= 0;
WrCycle <= #1 0;
RdCycle <= #1 0;
BitCounter <= 10;
end
end
// -------------------------------------------------------------------------------------------------------
// 1.03: Input Shift Register
// -------------------------------------------------------------------------------------------------------
always @(posedge SCL) begin
ShiftRegister[00] <= SDA;
ShiftRegister[01] <= ShiftRegister[00];
ShiftRegister[02] <= ShiftRegister[01];
ShiftRegister[03] <= ShiftRegister[02];
ShiftRegister[04] <= ShiftRegister[03];
ShiftRegister[05] <= ShiftRegister[04];
ShiftRegister[06] <= ShiftRegister[05];
ShiftRegister[07] <= ShiftRegister[06];
end
// -------------------------------------------------------------------------------------------------------
// 1.04: Input Bit Counter
// -------------------------------------------------------------------------------------------------------
always @(posedge SCL) begin
if (BitCounter < 10) BitCounter <= BitCounter + 1;
end
// -------------------------------------------------------------------------------------------------------
// 1.05: Control Byte Register
// -------------------------------------------------------------------------------------------------------
always @(negedge SCL) begin
if (START_Rcvd & (BitCounter == 8)) begin
if (!WriteActive & (ShiftRegister[07:04] == 4'b1010)) begin
if (ShiftRegister[00] == 0) WrCycle <= 1;
if (ShiftRegister[00] == 1) RdCycle <= 1;
ControlByte <= ShiftRegister[07:00];
CTRL_Rcvd <= 1;
end
START_Rcvd <= 0;
end
end
assign BlockSelect = ControlByte[01];
assign RdWrBit = ControlByte[00];
// -------------------------------------------------------------------------------------------------------
// 1.06: Byte Address Register
// -------------------------------------------------------------------------------------------------------
always @(negedge SCL) begin
if (CTRL_Rcvd & (BitCounter == 8)) begin
if (RdWrBit == 0) begin
StartAddress <= {BlockSelect,ShiftRegister[07:00]};
RdPointer <= {BlockSelect,ShiftRegister[07:00]};
ADDR_Rcvd <= 1;
end
WrCounter <= 0;
WrPointer <= 0;
CTRL_Rcvd <= 0;
end
end
// -------------------------------------------------------------------------------------------------------
// 1.07: Write Data Buffer
// -------------------------------------------------------------------------------------------------------
always @(negedge SCL) begin
if (ADDR_Rcvd & (BitCounter == 8)) begin
if ((WP == 0) & (RdWrBit == 0)) begin
WrDataByte[WrPointer] <= ShiftRegister[07:00];
WrCounter <= WrCounter + 1;
WrPointer <= WrPointer + 1;
end
end
end
// -------------------------------------------------------------------------------------------------------
// 1.08: Acknowledge Generator
// -------------------------------------------------------------------------------------------------------
always @(negedge SCL) begin
if (!WriteActive) begin
if (BitCounter == 8) begin
if (WrCycle | (START_Rcvd & (ShiftRegister[07:04] == 4'b1010))) begin
SDA_DO <= 0;
SDA_OE <= 1;
end
end
if (BitCounter == 9) begin
BitCounter <= 0;
if (!RdCycle) begin
SDA_DO <= 0;
SDA_OE <= 0;
end
end
end
end
// -------------------------------------------------------------------------------------------------------
// 1.09: Acknowledge Detect
// -------------------------------------------------------------------------------------------------------
always @(posedge SCL) begin
if (RdCycle & (BitCounter == 8)) begin
if ((SDA == 0) & (SDA_OE == 0)) MACK_Rcvd <= 1;
end
end
always @(negedge SCL) MACK_Rcvd <= 0;
// -------------------------------------------------------------------------------------------------------
// 1.10: Write Cycle Timer
// -------------------------------------------------------------------------------------------------------
always @(posedge STOP_Rcvd) begin
if (WrCycle & (WP == 0) & (WrCounter > 0)) begin
WriteActive = 1;
#(tWC);
WriteActive = 0;
end
end
always @(posedge STOP_Rcvd) begin
#(1.0);
STOP_Rcvd = 0;
end
// -------------------------------------------------------------------------------------------------------
// 1.11: Write Cycle Processor
// -------------------------------------------------------------------------------------------------------
always @(negedge WriteActive) begin
for (LoopIndex = 0; LoopIndex < WrCounter; LoopIndex = LoopIndex + 1) begin
if (StartAddress[08] == 0) begin
PageAddress = StartAddress[03:00] + LoopIndex;
MemoryBlock0[{StartAddress[07:04],PageAddress[03:00]}] = WrDataByte[LoopIndex[03:00]];
end
if (StartAddress[08] == 1) begin
PageAddress = StartAddress[03:00] + LoopIndex;
MemoryBlock1[{StartAddress[07:04],PageAddress[03:00]}] = WrDataByte[LoopIndex[03:00]];
end
end
end
// -------------------------------------------------------------------------------------------------------
// 1.12: Read Data Multiplexor
// -------------------------------------------------------------------------------------------------------
always @(negedge SCL) begin
if (BitCounter == 8) begin
if (WrCycle & ADDR_Rcvd) begin
RdPointer <= StartAddress + WrPointer + 1;
end
if (RdCycle) begin
RdPointer <= RdPointer + 1;
end
end
end
assign RdDataByte = RdPointer[08] ? MemoryBlock1[RdPointer[07:00]] : MemoryBlock0[RdPointer[07:00]];
// -------------------------------------------------------------------------------------------------------
// 1.13: Read Data Processor
// -------------------------------------------------------------------------------------------------------
always @(negedge SCL) begin
if (RdCycle) begin
if (BitCounter == 8) begin
SDA_DO <= 0;
SDA_OE <= 0;
end
else if (BitCounter == 9) begin
SDA_DO <= RdDataByte[07];
if (MACK_Rcvd) SDA_OE <= 1;
end
else begin
SDA_DO <= RdDataByte[7-BitCounter];
end
end
end
// -------------------------------------------------------------------------------------------------------
// 1.14: SDA Data I/O Buffer
// -------------------------------------------------------------------------------------------------------
bufif1 (SDA, 1'b0, SDA_DriveEnableDlyd);
assign SDA_DriveEnable = !SDA_DO & SDA_OE;
always @(SDA_DriveEnable) SDA_DriveEnableDlyd <= #(tAA) SDA_DriveEnable;
// *******************************************************************************************************
// ** DEBUG LOGIC **
// *******************************************************************************************************
// -------------------------------------------------------------------------------------------------------
// 2.01: Memory Data Bytes
// -------------------------------------------------------------------------------------------------------
wire [07:00] MemoryByte0_00 = MemoryBlock0[00];
wire [07:00] MemoryByte0_01 = MemoryBlock0[01];
wire [07:00] MemoryByte0_02 = MemoryBlock0[02];
wire [07:00] MemoryByte0_03 = MemoryBlock0[03];
wire [07:00] MemoryByte0_04 = MemoryBlock0[04];
wire [07:00] MemoryByte0_05 = MemoryBlock0[05];
wire [07:00] MemoryByte0_06 = MemoryBlock0[06];
wire [07:00] MemoryByte0_07 = MemoryBlock0[07];
wire [07:00] MemoryByte0_08 = MemoryBlock0[08];
wire [07:00] MemoryByte0_09 = MemoryBlock0[09];
wire [07:00] MemoryByte0_0A = MemoryBlock0[10];
wire [07:00] MemoryByte0_0B = MemoryBlock0[11];
wire [07:00] MemoryByte0_0C = MemoryBlock0[12];
wire [07:00] MemoryByte0_0D = MemoryBlock0[13];
wire [07:00] MemoryByte0_0E = MemoryBlock0[14];
wire [07:00] MemoryByte0_0F = MemoryBlock0[15];
wire [07:00] MemoryByte1_00 = MemoryBlock1[00];
wire [07:00] MemoryByte1_01 = MemoryBlock1[01];
wire [07:00] MemoryByte1_02 = MemoryBlock1[02];
wire [07:00] MemoryByte1_03 = MemoryBlock1[03];
wire [07:00] MemoryByte1_04 = MemoryBlock1[04];
wire [07:00] MemoryByte1_05 = MemoryBlock1[05];
wire [07:00] MemoryByte1_06 = MemoryBlock1[06];
wire [07:00] MemoryByte1_07 = MemoryBlock1[07];
wire [07:00] MemoryByte1_08 = MemoryBlock1[08];
wire [07:00] MemoryByte1_09 = MemoryBlock1[09];
wire [07:00] MemoryByte1_0A = MemoryBlock1[10];
wire [07:00] MemoryByte1_0B = MemoryBlock1[11];
wire [07:00] MemoryByte1_0C = MemoryBlock1[12];
wire [07:00] MemoryByte1_0D = MemoryBlock1[13];
wire [07:00] MemoryByte1_0E = MemoryBlock1[14];
wire [07:00] MemoryByte1_0F = MemoryBlock1[15];
// -------------------------------------------------------------------------------------------------------
// 2.02: Write Data Buffer
// -------------------------------------------------------------------------------------------------------
wire [07:00] WriteData_0 = WrDataByte[00];
wire [07:00] WriteData_1 = WrDataByte[01];
wire [07:00] WriteData_2 = WrDataByte[02];
wire [07:00] WriteData_3 = WrDataByte[03];
wire [07:00] WriteData_4 = WrDataByte[04];
wire [07:00] WriteData_5 = WrDataByte[05];
wire [07:00] WriteData_6 = WrDataByte[06];
wire [07:00] WriteData_7 = WrDataByte[07];
wire [07:00] WriteData_8 = WrDataByte[08];
wire [07:00] WriteData_9 = WrDataByte[09];
wire [07:00] WriteData_A = WrDataByte[10];
wire [07:00] WriteData_B = WrDataByte[11];
wire [07:00] WriteData_C = WrDataByte[12];
wire [07:00] WriteData_D = WrDataByte[13];
wire [07:00] WriteData_E = WrDataByte[14];
wire [07:00] WriteData_F = WrDataByte[15];
// *******************************************************************************************************
// ** TIMING CHECKS **
// *******************************************************************************************************
wire TimingCheckEnable = (RESET == 0) & (SDA_OE == 0);
//--------------------------------
//-------仿真时时序约束需改动--------
//--------------------------------
specify
specparam
tHI = 600, // SCL pulse width - high
// tLO = 1300, // SCL pulse width - low
tLO = 600,
tSU_STA = 600, // SCL to SDA setup time
tHD_STA = 600, // SCL to SDA hold time
tSU_DAT = 100, // SDA to SCL setup time
tSU_STO = 600, // SCL to SDA setup time
// tBUF = 1300; // Bus free time
tBUF = 600;
$width (posedge SCL, tHI);
$width (negedge SCL, tLO);
$width (posedge SDA &&& SCL, tBUF);
$setup (posedge SCL, negedge SDA &&& TimingCheckEnable, tSU_STA);
$setup (SDA, posedge SCL &&& TimingCheckEnable, tSU_DAT);
$setup (posedge SCL, posedge SDA &&& TimingCheckEnable, tSU_STO);
$hold (negedge SDA &&& TimingCheckEnable, negedge SCL, tHD_STA);
endspecify
endmodule