目录
一、任务
1、PC端通过串口发送数据至FPGA,然后将数据写进EEPROM,每写1次,地址加1。
2、按下按键进行一次随机读操作,随机读的地址从地址0开始,依次读;读出来的数据通过串口发送至PC端显示,显示内容为"读数据:xx"。
二、需求分析
首先,肯定需要串口的发送和接收模块,
然后,还需要EEPROM的读写控制模块,
最后,还需要按键消抖模块。
了解了IIC通信协议(FPGA学习笔记------IIC协议简介-CSDN博客),我们可以知道,在随机读和字节写的时候都要发送一个带起始信号的写(在随机读方向位也是"0")和 一个普通的写(Word Address) **。**其余的就是:带起始信号的读,普通的读,带停止信号的读和带停止信号的写。当然,这是一种想法,另外一种想法就是分成:起始信号、写、读、停止信号,分别组合在一起,在rw_ctrl模块中传相应的命令去执行相应的读写操作(在代码中有体现)。cmd参数定义: cmd[0]:起始 信号,cmd[1]:写 ,cmd[2]:读 ,cmd[3]:停止信号。
我这里使用的IIC的时钟频率是200kHz(在100kHz~400kHz之间),这里****不用写分频,直接MAX_CNT_SCL= 50MHz**/200kHz,MAX_CNT_SCL>>1直接分完低电平和高电平。**
三、Visio图
Visio图

四、具体分析
首先,串口发送和接收模块( FPGA学习笔记------串口RS232回环实验_verilog 串口回环-CSDN博客)之前的文章里面已经讲过了,这里就不分析这个,
然后,EEPROM的读写控制模块 可以分成rw_ctrl(读写控制模块)模块和iic_intf(IIC接口模块),这样可以将读和写控制分开写,这样写我觉得思路更清晰一点,更好的描述这个两个状态。
rw_ctrl模块:这个模块的状态分为IDLE
WRITE
READ
DONE为什么分成这几个状态:
首先,在进行写操作的时候,不能进行读操作,所以将读写操作状态分开来,
写操作: 当上位机发送1byte数据过来,就从IDLE进入WRITE状态,将命令发给iic_intf模块和1byte写入EEPROM里面,
写完到DONE状态,
最后到IDLE状态,等待下一次触发。
读操作: 当按键按下,就从IDLE进入READ状态,从EEPROM里面读出1byte数据,发给上位机显示
发完到DONE状态,
最后到IDLE状态,等待下一次触发。

iic_intf模块:这个模块的状态分为
IDLE
START
WR_DATA
RD_DATA
R_ACK
S_ACK
STOP为什么分成这几个状态:
首先 ,根据rw_ctrl模块传过来 的cmd 对相应的位进行判断,先判断cmd[0] ,如果cmd[0]==1 ,则跳到START 状态,如果cmd[0]==0 ,则判断cmd[1]和cmd[2] ,哪个为1,则跳到相应的状态,在等待字节写完或读完,再判断cmd[3] ,是否跳到STOP状态。

以上就是每个模块的思路。(有可能说的不是很清楚,可以看看代码,应该就能理解了)
五、代码
sys_top.v
cpp
module sys_top(
input sys_clk ,
input sys_rst_n,
input uart_rxd ,
output uart_txd ,
input key_in ,
output iic_scl ,
inout iic_sda ,
output rtc_nrst
);
wire [7:0] rx_data ;
wire rx_data_vld;
wire ready ;
wire [7:0] tx_data ;
wire tx_data_vld;
wire key_down ;
wire sda_in ;
wire sda_out ;
wire sda_out_en ;
assign iic_sda = sda_out_en ? sda_out : 1'bz;
assign sda_in = iic_sda;
assign rtc_nrst = 1'b0;
uart_rx #( .CLOCK_FRQ(50_000_000),
.BAUD(115200),
.DATA_LENTH(8) ,
.CHECKBIT_SELECT(0),
.CHECK_TYPE(0) //偶校验:0 奇校验:1
)uart_rx_inst(
. clk (sys_clk ),
. rst_n (sys_rst_n ),
. uart_rxd (uart_rxd ),
. dout (rx_data ),
. dout_vld (rx_data_vld)
);
eeprom_ctrl u_eeprom_ctrl(
.clk (sys_clk ),
.rst_n (sys_rst_n ),
.rx_data (rx_data ),
.rx_data_vld (rx_data_vld ),
.ready (ready ),
.tx_data (tx_data ),
.tx_data_vld (tx_data_vld ),
.key_down (key_down ),
.scl (iic_scl ),
.sda_in (sda_in ),
.sda_out (sda_out ),
.sda_out_en (sda_out_en )
);
key_filter u_key_filter(
.clk (sys_clk ),
.rst_n (sys_rst_n),
.key_in (key_in ),
.key_down (key_down )
);
uart_tx #( .CLOCK_FRQ(50_000_000),
.BAUD(115200),
.DATA_LENTH(8) ,
.CHECKBIT_SELECT(0),
.CHECK_TYPE(0) //偶校验:0 奇校验:1
)uart_tx_inst(
. clk (sys_clk ),
. rst_n (sys_rst_n ),
. tx_din (tx_data ),
. tx_enable(tx_data_vld),
. uart_txd (uart_txd ),
. ready (ready ) //忙闲指示信号
);
endmodule
uart_tx
cpp
module uart_tx #(parameter CLOCK_FRQ = 50_000_000,
BAUD = 9600 ,
DATA_LENTH = 8 ,
CHECKBIT_SELECT = 0 ,
CHECK_TYPE = 0 //偶校验:0 奇校验:1
)(
input clk ,
input rst_n ,
input [DATA_LENTH-1:0] tx_din ,
input tx_enable,
output reg uart_txd ,
output reg ready //忙闲指示信号
);
//---------<参数定义>------------------------------------------------
//状态机参数定义
localparam IDLE = 5'b00001,
START = 5'b00010,
DATA = 5'b00100,
CHECK = 5'b01000,
STOP = 5'b10000;
localparam BUAD_MAX = CLOCK_FRQ/BAUD;
//---------<内部信号定义>--------------------------------------------
reg [4:0] state_c ;
reg [4:0] state_n ;
reg [15:0] cnt_baud ;//波特率计数器
wire add_cnt_baud;
wire end_cnt_baud;
reg [2:0] cnt_bit ;//数据传输的比特计数器
wire add_cnt_bit ;
wire end_cnt_bit ;
reg [7:0] tx_din_r ;
//状态转移条件定义
wire idle2start ;
wire start2data ;
wire data2check ;
wire data2stop ;
wire check2stop ;
wire stop2idle ;
//*******************************************************************
//--tx_din_r
//*******************************************************************
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
tx_din_r <= 'd0;
end
else if(tx_enable)begin
tx_din_r <= tx_din;
end
end
//*******************************************************************
//--状态机
//*******************************************************************
//第一段:同步时序描述状态转移
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
state_c <= IDLE;
end
else begin
state_c <= state_n;
end
end
//第二段:组合逻辑判断状态转移条件,描述状态转移规律
always @(*) begin
case(state_c)
IDLE : begin
if(idle2start)
state_n = START;
else
state_n = state_c;
end
START : begin
if(start2data)
state_n = DATA;
else
state_n = state_c;
end
DATA : begin
if(data2check)
state_n = CHECK;
else if(data2stop)
state_n = STOP;
else
state_n = state_c;
end
CHECK : begin
if(check2stop)
state_n = STOP;
else
state_n = state_c;
end
STOP : begin
if(stop2idle)
state_n = IDLE;
else
state_n = state_c;
end
default : state_n = IDLE;
endcase
end
assign idle2start = (state_c == IDLE ) && tx_enable;
assign start2data = (state_c == START) && end_cnt_baud;
assign data2check = (state_c == DATA ) && end_cnt_bit && CHECKBIT_SELECT;
assign data2stop = (state_c == DATA ) && end_cnt_bit && !CHECKBIT_SELECT;
assign check2stop = (state_c == CHECK) && end_cnt_baud;
assign stop2idle = (state_c == STOP ) && end_cnt_baud;
//*******************************************************************
//--cnt_baud
//*******************************************************************
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_baud <= 'd0;
end
else if(add_cnt_baud)begin
if(end_cnt_baud)begin
cnt_baud <= 'd0;
end
else begin
cnt_baud <= cnt_baud + 1'b1;
end
end
end
assign add_cnt_baud = state_c != IDLE;
assign end_cnt_baud = add_cnt_baud && cnt_baud == ((state_c == STOP) ? ((BUAD_MAX>>1)+(BUAD_MAX>>2)) : (BUAD_MAX - 1));
//*******************************************************************
//--cnt_bit
//*******************************************************************
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_bit <= 'd0;
end
else if(add_cnt_bit)begin
if(end_cnt_bit)begin
cnt_bit <= 'd0;
end
else begin
cnt_bit <= cnt_bit + 1'b1;
end
end
end
assign add_cnt_bit = state_c == DATA && end_cnt_baud;
assign end_cnt_bit = add_cnt_bit && cnt_bit == DATA_LENTH - 1;
//*******************************************************************
//--uart_txd
//*******************************************************************
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
uart_txd <= 1'b1;
end
else begin
case (state_c)
IDLE : uart_txd <= 1'b1;
START : uart_txd <= 1'b0;
DATA : uart_txd <= tx_din_r[cnt_bit];//并转串,LSB
// CHECK : uart_txd <= ^tx_din_r + CHECK_TYPE;
CHECK : uart_txd <= CHECK_TYPE ? (^tx_din_r + 1'b1) : (^tx_din_r);
STOP : uart_txd <= 1'b1;
default: uart_txd <= 1'b1;
endcase
end
end
//*******************************************************************
//--ready
//*******************************************************************
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
ready <= 'd0;
end
else begin
ready <= state_n == IDLE;
end
end
endmodule
uart_rx
cpp
module uart_rx #(parameter CLOCK_FRQ = 50_000_000,
BAUD = 9600 ,
DATA_LENTH = 8 ,
CHECKBIT_SELECT = 0 ,
CHECK_TYPE = 0 //偶校验:0 奇校验:1
)(
input clk ,
input rst_n ,
input uart_rxd ,
output reg [7:0] dout ,
output dout_vld
);
//---------<参数定义>------------------------------------------------
//状态机参数定义
localparam IDLE = 5'b00001,
START = 5'b00010,
DATA = 5'b00100,
CHECK = 5'b01000,
STOP = 5'b10000;
localparam BUAD_MAX = CLOCK_FRQ/BAUD;
//---------<内部信号定义>--------------------------------------------
reg [4:0] state_c ;
reg [4:0] state_n ;
reg [15:0] cnt_baud ;//波特率计数器
wire add_cnt_baud;
wire end_cnt_baud;
reg [2:0] cnt_bit ;//接收bit数量的计数器
wire add_cnt_bit;
wire end_cnt_bit;
reg [2:0] uart_rxd_r ;
wire rxd_n_edge ;
//状态转移条件定义
wire idle2start ;
wire start2data ;
wire data2check ;
wire data2stop ;
wire check2stop ;
wire stop2idle ;
//*******************************************************************
//--打三拍(前两拍同步,后一拍获得边沿)
//*******************************************************************
//uart_rxd_r
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
uart_rxd_r <= 3'b111;
end
else begin
uart_rxd_r <= {uart_rxd_r[1:0],uart_rxd};
end
end
//rxd_n_edge
assign rxd_n_edge = ~uart_rxd_r[1] & uart_rxd_r[2];
//*******************************************************************
//--状态机
//*******************************************************************
//第一段:同步时序描述状态转移
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
state_c <= IDLE;
end
else begin
state_c <= state_n;
end
end
//第二段:组合逻辑判断状态转移条件,描述状态转移规律
always @(*) begin
case(state_c)
IDLE : begin
if(idle2start)
state_n = START;
else
state_n = state_c;
end
START : begin
if(start2data)
state_n = DATA;
else
state_n = state_c;
end
DATA : begin
if(data2check)
state_n = CHECK;
else if(data2stop)
state_n = STOP;
else
state_n = state_c;
end
CHECK : begin
if(check2stop)
state_n = STOP;
else
state_n = state_c;
end
STOP : begin
if(stop2idle)
state_n = IDLE;
else
state_n = state_c;
end
default : state_n = IDLE;
endcase
end
assign idle2start = (state_c == IDLE ) && rxd_n_edge;
assign start2data = (state_c == START) && end_cnt_baud;
assign data2check = (state_c == DATA ) && end_cnt_bit && CHECKBIT_SELECT;
assign data2stop = (state_c == DATA ) && end_cnt_bit && !CHECKBIT_SELECT;
assign check2stop = (state_c == CHECK) && end_cnt_baud;
assign stop2idle = (state_c == STOP ) && end_cnt_baud;
//*******************************************************************
//--cnt_baud
//*******************************************************************
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_baud <= 'd0;
end
else if(add_cnt_baud)begin
if(end_cnt_baud)begin
cnt_baud <= 'd0;
end
else begin
cnt_baud <= cnt_baud + 1'b1;
end
end
end
assign add_cnt_baud = state_c != IDLE;
assign end_cnt_baud = add_cnt_baud && cnt_baud == ((state_c == STOP) ? ((BUAD_MAX>>1)+(BUAD_MAX>>2)) : (BUAD_MAX - 1));
//*******************************************************************
//--cnt_bit
//*******************************************************************
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_bit <= 'd0;
end
else if(add_cnt_bit)begin
if(end_cnt_bit)begin
cnt_bit <= 'd0;
end
else begin
cnt_bit <= cnt_bit + 1'b1;
end
end
end
assign add_cnt_bit = state_c == DATA && end_cnt_baud;
assign end_cnt_bit = add_cnt_bit && cnt_bit == DATA_LENTH - 1;
//*******************************************************************
//--dout
//*******************************************************************
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
dout <= 'd0;
end
else if(state_c == DATA && cnt_baud == (BUAD_MAX>>1) - 1)begin
dout[cnt_bit] <= uart_rxd_r[2];
// dout <= {uart_rxd_r[2],dout[7:1]};
end
end
//*******************************************************************
//--dout_vld
//*******************************************************************
assign dout_vld = stop2idle;
endmodule
param.v
cpp
//IIC命令参数定义
`define CMD_START 4'b0001
`define CMD_WRITE 4'b0010
`define CMD_READ 4'b0100
`define CMD_STOP 4'b1000
`define WR_ID 8'ha0
`define RD_ID 8'ha1
eeprom.v
cpp
module eeprom_ctrl(
input clk ,
input rst_n ,
//uart_rx
input [7:0] rx_data ,
input rx_data_vld,
//uart_tx
input ready ,
output [7:0] tx_data ,
output tx_data_vld,
//key_filter
input key_down ,
//iic slave
output scl ,
input sda_in ,
output sda_out ,
output sda_out_en
);
wire start_en ;
wire [3:0] cmd ;
wire [7:0] wr_data ;
wire [7:0] rd_data ;
wire trans_done;
rw_ctrl u_rw_ctrl(
.clk (clk ),
.rst_n (rst_n ),
.rx_data (rx_data ),
.rx_data_vld(rx_data_vld),
.ready (ready ),
.tx_data (tx_data ),
.tx_data_vld(tx_data_vld),
.key_down (key_down ),
.start_en (start_en ),
.cmd (cmd ),
.wr_data (wr_data ),
.rd_data (rd_data ),
.trans_done (trans_done )
);
iic_intf u_iic_intf(
.clk (clk ),
.rst_n (rst_n ),
.start_en (start_en ),//读/写使能信号
.cmd (cmd ),//命令信号 cmd[0]:起始 cmd[1]:写 cmd[2]:读 cmd[3]:停止信号
.wr_data (wr_data ),//写入的数据(ID、地址、数据)
.rd_data (rd_data ),//读出的数据
.trans_done(trans_done ),//一帧(字节)数据传输完成
.slave_ack (),//从机应答信号
.scl (scl ),
.sda_in (sda_in ),
.sda_out (sda_out ),
.sda_out_en(sda_out_en)
);
endmodule
rw_ctrl.v
cpp
`include "./param.v"
module rw_ctrl#(parameter WR_LENTH = 3,RD_LENTH = 4)(
input clk ,
input rst_n ,
//uart_rx
input [7:0] rx_data ,
input rx_data_vld,
//uart_tx
input ready ,
output reg [7:0] tx_data ,
output tx_data_vld,
//key_filter
input key_down ,
//iic_intf
output reg start_en ,
output reg [3:0] cmd ,
output reg [7:0] wr_data ,
input [7:0] rd_data ,
input trans_done
);
localparam IDLE = 4'h1,
WRITE = 4'h2,
READ = 4'h4,
DONE = 4'h8;
reg [3:0] state_c ;
reg [3:0] state_n ;
reg [7:0] cnt_byte ;//读/写操作传输字节数量计数器
wire add_cnt_byte;
wire end_cnt_byte;
reg [7:0] wr_addr ;
reg [7:0] rd_addr ;
reg tx_flag ;
reg [3:0] cnt_tx ;
wire add_cnt_tx ;
wire end_cnt_tx ;
wire idle2write;
wire idle2read ;
wire write2done;
wire read2done ;
//---------<State Machine>-------------------------------------------------
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
state_c <= IDLE;
end
else begin
state_c <= state_n;
end
end
always @(*) begin
case(state_c)
IDLE : state_n = idle2write ? WRITE : (idle2read ? READ : IDLE);
WRITE : state_n = write2done ? DONE : WRITE;
READ : state_n = read2done ? DONE : READ;
DONE : state_n = IDLE;
default : state_n = IDLE;
endcase
end
assign idle2write = (state_c == IDLE) && rx_data_vld;
assign idle2read = (state_c == IDLE) && key_down;
assign write2done = (state_c == WRITE) && end_cnt_byte;
assign read2done = (state_c == READ) && end_cnt_byte;
//---------<cnt_byte>-------------------------------------------------
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_byte <= 'd0;
end
else if(add_cnt_byte)begin
if(end_cnt_byte)begin
cnt_byte <= 'd0;
end
else begin
cnt_byte <= cnt_byte + 1'b1;
end
end
end
assign add_cnt_byte = trans_done && (state_c == WRITE || state_c == READ);
assign end_cnt_byte = add_cnt_byte && cnt_byte == ((state_c == WRITE) ? (WR_LENTH-1) : (RD_LENTH-1));//优先级
//---------<wr_addr>-------------------------------------------------
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
wr_addr <= 'd0;
end
else if(write2done)begin
wr_addr <= wr_addr + 1'b1;
end
end
//---------<rd_addr>-------------------------------------------------
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
rd_addr <= 'd0;
end
else if(read2done)begin
rd_addr <= rd_addr + 1'b1;
end
end
//---------<start_en cmd wr_data>-------------------------------------------------
always @(*)begin
if(state_c == WRITE)begin
case (cnt_byte)
0 : begin start_en <= 1'b1;cmd <= `CMD_START | `CMD_WRITE;wr_data <= `WR_ID; end
1 : begin start_en <= 1'b1;cmd <= `CMD_WRITE ;wr_data <= wr_addr; end
2 : begin start_en <= 1'b1;cmd <= `CMD_STOP | `CMD_WRITE;wr_data <= rx_data; end
default: begin start_en <= 1'b0;cmd <= 4'd0;wr_data <= 8'd0; end
endcase
end
else if(state_c == READ)begin
case (cnt_byte)
0 : begin start_en <= 1'b1;cmd <= `CMD_START | `CMD_WRITE;wr_data <= `WR_ID; end
1 : begin start_en <= 1'b1;cmd <= `CMD_WRITE ;wr_data <= rd_addr; end
2 : begin start_en <= 1'b1;cmd <= `CMD_START | `CMD_WRITE;wr_data <= `RD_ID; end
3 : begin start_en <= 1'b1;cmd <= `CMD_STOP | `CMD_READ ;wr_data <= 8'h00; end
default: begin start_en <= 1'b0;cmd <= 4'd0;wr_data <= 8'd0; end
endcase
end
else begin
start_en <= 1'b0;cmd <= 4'd0;wr_data <= 8'd0;
end
end
//---------<串口显示>-------------------------------------------------
//读数据:xx-->B6 C1 CA FD BE DD A3 BA x x
//tx_flag
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
tx_flag <= 'd0;
end
else if(read2done)begin
tx_flag <= 1'b1;
end
else if(end_cnt_tx)begin
tx_flag <= 1'b0;
end
end
//cnt_tx
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_tx <= 'd0;
end
else if(add_cnt_tx)begin
if(end_cnt_tx)begin
cnt_tx <= 'd0;
end
else begin
cnt_tx <= cnt_tx + 1'b1;
end
end
end
assign add_cnt_tx = tx_flag && ready;
assign end_cnt_tx = add_cnt_tx && cnt_tx == 10 - 1;
//tx_data
always @(*)begin
case (cnt_tx)
0 : tx_data = 8'hB6;
1 : tx_data = 8'hC1;
2 : tx_data = 8'hCA;
3 : tx_data = 8'hFD;
4 : tx_data = 8'hBE;
5 : tx_data = 8'hDD;
6 : tx_data = 8'hA3;
7 : tx_data = 8'hBA;
8 : tx_data = (rd_data[7:4]<8'd10) ? (rd_data[7:4] + "0") : (rd_data[7:4] + "A" - 8'd10);
9 : tx_data = (rd_data[3:0]<8'd10) ? (rd_data[3:0] + "0") : (rd_data[3:0] + "A" - 8'd10);
default: tx_data = 0;
endcase
end
//tx_data_vld
assign tx_data_vld = tx_flag && ready;
endmodule
iic_intf.v
cpp
module iic_intf #(parameter SYS_CLOCK = 50_000_000,//系统时钟频率
IIC_CLOCK = 200_000 //IIC传输速率
)(
input clk ,
input rst_n ,
//rw_ctrl
input start_en ,//读/写使能信号
input [3:0] cmd ,//命令信号 cmd[0]:起始 cmd[1]:写 cmd[2]:读 cmd[3]:停止信号
input [7:0] wr_data ,//写入的数据(ID、地址、数据)
output reg [7:0] rd_data ,//读出的数据
output trans_done,//一帧(字节)数据传输完成
// output reg slave_ack ,//从机应答信号
//iic slave
output reg scl ,
input sda_in ,
output reg sda_out ,
output reg sda_out_en
);
localparam IDLE = 7'b000_0001,
START = 7'b000_0010,
WR_DATA = 7'b000_0100,
RD_DATA = 7'b000_1000,
R_ACK = 7'b001_0000,
S_ACK = 7'b010_0000,
STOP = 7'b100_0000;
localparam CMD_START = 4'b0001,
CMD_WRITE = 4'b0010,
CMD_READ = 4'b0100,
CMD_STOP = 4'b1000;
localparam MAX_CNT_SCL = SYS_CLOCK/IIC_CLOCK,
CHANGE_TIME = (MAX_CNT_SCL>>2) ,//四分之一串行时钟周期
SAMPLE_TIME = (MAX_CNT_SCL>>1) + (MAX_CNT_SCL>>2);//四分之三串行时钟周期
reg [6:0] state_c;
reg [6:0] state_n;
reg [9:0] cnt_scl ;//串行时钟计数器
wire add_cnt_scl;
wire end_cnt_scl;
reg [2:0] cnt_bit ;//读写数据比特计数器
wire add_cnt_bit;
wire end_cnt_bit;
wire idle2start ;
wire idle2wr_data ;
wire idle2rd_data ;
wire start2wr_data;
wire start2rd_data;
wire wr_data2r_ack;
wire r_ack2idle ;
wire r_ack2stop ;
wire rd_data2s_ack;
wire s_ack2idle ;
wire s_ack2stop ;
wire stop2idle ;
//---------<State Machine>-------------------------------------------------
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
state_c <= IDLE;
end
else begin
state_c <= state_n;
end
end
always @(*) begin
case(state_c)
IDLE : begin
if(idle2start) //带起始信号的传输优先级高于其它传输
state_n = START;
else if(idle2wr_data)
state_n = WR_DATA;
else if(idle2rd_data)
state_n = RD_DATA;
else
state_n = state_c;
end
START : begin
if(start2wr_data)
state_n = WR_DATA;
else if(start2rd_data)
state_n = RD_DATA;
else
state_n = state_c;
end
WR_DATA : begin
if(wr_data2r_ack)
state_n = R_ACK;
else
state_n = state_c;
end
RD_DATA : begin
if(rd_data2s_ack)
state_n = S_ACK;
else
state_n = state_c;
end
R_ACK : begin
if(r_ack2idle)
state_n = IDLE;
else if(r_ack2stop)
state_n = STOP;
else
state_n = state_c;
end
S_ACK : begin
if(s_ack2idle)
state_n = IDLE;
else if(s_ack2stop)
state_n = STOP;
else
state_n = state_c;
end
STOP : begin
if(stop2idle)
state_n = IDLE;
else
state_n = state_c;
end
default : state_n = IDLE;
endcase
end
assign idle2start = (state_c == IDLE) && start_en && cmd[0];//(cmd & CMD_START)
assign idle2wr_data = (state_c == IDLE) && start_en && cmd[1];
assign idle2rd_data = (state_c == IDLE) && start_en && cmd[2];
assign start2wr_data = (state_c == START) && end_cnt_scl && cmd[1];
assign start2rd_data = (state_c == START) && end_cnt_scl && cmd[2];
assign wr_data2r_ack = (state_c == WR_DATA) && end_cnt_bit;
assign r_ack2idle = (state_c == R_ACK) && end_cnt_scl && cmd[3] == 1'b0;
assign r_ack2stop = (state_c == R_ACK) && end_cnt_scl && cmd[3] == 1'b1;
assign rd_data2s_ack = (state_c == RD_DATA) && end_cnt_bit;
assign s_ack2idle = (state_c == S_ACK) && end_cnt_scl && cmd[3] == 1'b0;
assign s_ack2stop = (state_c == S_ACK) && end_cnt_scl && cmd[3] == 1'b1;
assign stop2idle = (state_c == STOP) && end_cnt_scl;
//---------<cnt_scl>-------------------------------------------------
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_scl <= 'd0;
end
else if(add_cnt_scl)begin
if(end_cnt_scl)begin
cnt_scl <= 'd0;
end
else begin
cnt_scl <= cnt_scl + 1'b1;
end
end
end
assign add_cnt_scl = state_c != IDLE;
assign end_cnt_scl = add_cnt_scl && cnt_scl == MAX_CNT_SCL - 1;
//---------<cnt_bit>-------------------------------------------------
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
cnt_bit <= 'd0;
end
else if(add_cnt_bit)begin
if(end_cnt_bit)begin
cnt_bit <= 'd0;
end
else begin
cnt_bit <= cnt_bit + 1'b1;
end
end
end
assign add_cnt_bit = (state_c == WR_DATA || state_c == RD_DATA) && end_cnt_scl;
assign end_cnt_bit = add_cnt_bit && cnt_bit == 8 - 1;
//---------<scl>-------------------------------------------------
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
scl <= 1'b1;
end
else if(state_c != IDLE)begin
if(cnt_scl < (MAX_CNT_SCL>>1))
scl <= 1'b0;
else
scl <= 1'b1;
end
else begin
scl <= 1'b1;
end
end
//---------<sda_out sda_out_en rd_data>------------------------------------
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
sda_out <= 1'b1;
sda_out_en <= 1'b0;
rd_data <= 8'd0;
// slave_ack <= 1'b0;
end
else begin
case (state_c)
IDLE : begin
sda_out <= 1'b1;
sda_out_en <= 1'b0;
end
START : begin
sda_out_en <= 1'b1;
if(cnt_scl < SAMPLE_TIME)
sda_out <= 1'b1;
else
sda_out <= 1'b0;
end
WR_DATA : begin
sda_out_en <= 1'b1;
if(cnt_scl == CHANGE_TIME - 1) //并转串 MSB
sda_out <= wr_data[7-cnt_bit];
else
sda_out <= sda_out;
end
RD_DATA : begin
sda_out <= 1'b0;
sda_out_en <= 1'b0;
if(cnt_scl == SAMPLE_TIME - 1)
rd_data[7-cnt_bit] <= sda_in;
else
rd_data <= rd_data;
end
R_ACK : begin
sda_out <= 1'b0;
sda_out_en <= 1'b0;
// // slave_ack <= (cnt_scl == SAMPLE_TIME - 1) ? sda_in : slave_ack;
// if(cnt_scl == SAMPLE_TIME - 1)
// slave_ack <= sda_in;
// else
// slave_ack <= slave_ack;
end
S_ACK : begin
sda_out_en <= 1'b1;
// sda_out <= (cnt_scl == CHANGE_TIME && cmd[3]) ? 1'b1 : 1'b0;
if(cnt_scl == CHANGE_TIME)
sda_out <= cmd[3] ? 1'b1 : 1'b0;
else
sda_out <= sda_out;
end
STOP : begin
sda_out_en <= 1'b1;
if(cnt_scl == CHANGE_TIME)
sda_out <= 1'b0;
else if(cnt_scl == SAMPLE_TIME)
sda_out <= 1'b1;
end
default: begin
sda_out_en <= 1'b0;
sda_out <= 1'b0;
rd_data <= rd_data;
end
endcase
end
end
//---------<trans_done>-------------------------------------------------
assign trans_done = r_ack2idle | s_ack2idle | stop2idle;
endmodule
六、实现现象
以下是发送一个字节,并读取的出来的现象。

以上就是简单的IIC读写EEPROM实现。(如果有错误,还请大家指出来,谢谢!有什么不懂的都可以在评论区问,看到就会回复。)