参考
用vio_uart_rpc协议,测试IIC接口的AT24C64.csdn
公共module
key_debounce.v 和 axis_uart_sender.v 参考嵌入式终端AtShell的verilog版.csdn
iic_master_slot.v
verilog
`timescale 1ns / 1ps
module iic_master_slot #(
parameter P_ADDR_WIDTH = 8,
parameter P_DATA_WIDTH = 16,
parameter [P_ADDR_WIDTH-1:0] P_SLOT_BASE = 0,
parameter P_REG_COUNT = 32,
parameter [6:0] P_SLAVE_ADDR = 7'b1010000,
parameter [25:0] P_CLK_FREQ = 26'd50_000_000,
parameter [17:0] P_I2C_FREQ = 18'd250_000
)(
input wire i_clk,
input wire i_rst_n,
//==================================================
// SMMR
//==================================================
input wire i_reg_wr_en,
input wire [P_ADDR_WIDTH-1:0] i_reg_wr_addr,
input wire [P_DATA_WIDTH-1:0] i_reg_wr_data,
input wire [P_ADDR_WIDTH-1:0] i_reg_rd_addr,
output reg [P_DATA_WIDTH-1:0] o_reg_rd_data,
//==================================================
// I2C
//==================================================
output wire o_i2c_scl,
input wire i_i2c_sda_i,
output wire o_i2c_sda_o,
output wire o_i2c_sda_t
);
////////////////////////////////////////////////////////////////////////////
// IIC MASTER SLOT Register Map
////////////////////////////////////////////////////////////////////////////
//
// +0 CONTROL
//
// bit0 : EXEC
// 写1启动一次I2C操作
//
// bit1 : RW
// 0 = WRITE
// 1 = READ
//
// bit2 : ADDR16
// 0 = 8 bit内部地址
// 1 = 16 bit内部地址
//
// +1 STATUS
//
// bit0 : DONE
// I2C一次操作完成
//
// bit1 : ACK_ERROR
// 0 = ACK
// 1 = NACK
//
// bit2 : BUSY
// 0 = 空闲
// 1 = I2C操作中
//
// +2 I2C_ADDR
//
// [15:0] : I2C器件内部地址
//
// +3 DATA_WR
//
// [7:0] : I2C写数据
//
// +4 DATA_RD
//
// [7:0] : I2C读数据
//
////////////////////////////////////////////////////////////////////////////
localparam [P_ADDR_WIDTH-1:0] ADDR_CONTROL =P_SLOT_BASE + 8'd0;
localparam [P_ADDR_WIDTH-1:0] ADDR_STATUS =P_SLOT_BASE + 8'd1;
localparam [P_ADDR_WIDTH-1:0] ADDR_I2C_ADDR = P_SLOT_BASE + 8'd2;
localparam [P_ADDR_WIDTH-1:0] ADDR_DATA_WR =P_SLOT_BASE + 8'd3;
localparam [P_ADDR_WIDTH-1:0] ADDR_DATA_RD =P_SLOT_BASE + 8'd4;
////////////////////////////////////////////////////////////////////////////
// Register
////////////////////////////////////////////////////////////////////////////
reg [P_DATA_WIDTH-1:0] r_control;
reg [P_DATA_WIDTH-1:0] r_i2c_addr;
reg [P_DATA_WIDTH-1:0] r_data_wr;
////////////////////////////////////////////////////////////////////////////
// I2C control
////////////////////////////////////////////////////////////////////////////
reg r_i2c_exec;
////////////////////////////////////////////////////////////////////////////
// Latched I2C status
////////////////////////////////////////////////////////////////////////////
reg r_done;
reg r_ack_error;
reg [7:0] r_data_rd;
////////////////////////////////////////////////////////////////////////////
// I2C status
////////////////////////////////////////////////////////////////////////////
wire w_i2c_done;
wire w_i2c_ack;
wire [7:0] w_i2c_data_rd;
////////////////////////////////////////////////////////////////////////////
// BUSY
////////////////////////////////////////////////////////////////////////////
reg r_busy;
////////////////////////////////////////////////////////////////////////////
// I2C MASTER
////////////////////////////////////////////////////////////////////////////
i2c_master_dri #(
.P_SLAVE_ADDR (P_SLAVE_ADDR),
.P_CLK_FREQ (P_CLK_FREQ),
.P_I2C_FREQ (P_I2C_FREQ)
)
u_i2c_master_dri
(
.i_clk (i_clk),
.i_rst_n (i_rst_n),
//==================================================
// I2C control
//==================================================
.i_i2c_exec (r_i2c_exec),
.i_bit_ctrl (r_control[2]),
.i_i2c_rh_wl (r_control[1]),
.i_i2c_addr (r_i2c_addr),
.i_i2c_data_w (r_data_wr[7:0]),
//==================================================
// I2C result
//==================================================
.o_i2c_data_r (w_i2c_data_rd),
.o_i2c_done (w_i2c_done),
.o_i2c_ack (w_i2c_ack),
//==================================================
// I2C SCL
//==================================================
.o_scl (o_i2c_scl),
//==================================================
// I2C SDA
//==================================================
.i_sda_i (i_i2c_sda_i),
.o_sda_o (o_i2c_sda_o),
.o_sda_t (o_i2c_sda_t),
//==================================================
// Driver clock
//==================================================
.o_dri_clk ()
);
////////////////////////////////////////////////////////////////////////////
// SMMR WRITE
////////////////////////////////////////////////////////////////////////////
always @(posedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n) begin
r_control <= 0;
r_i2c_addr <= 0;
r_data_wr <= 0;
r_i2c_exec <= 1'b0;
end
else begin
// EXEC保持到driver返回done,避免1clk脉冲被低速I2C驱动时钟漏采样
if(w_i2c_done)
r_i2c_exec <= 1'b0;
if(i_reg_wr_en) begin
case(i_reg_wr_addr)
////////////////////////////////////////////////////////////
// CONTROL
////////////////////////////////////////////////////////////
ADDR_CONTROL:
begin
r_control <= i_reg_wr_data;
// 写1启动一次I2C
if(i_reg_wr_data[0])
r_i2c_exec <= 1'b1;
end
////////////////////////////////////////////////////////////
// I2C内部地址
////////////////////////////////////////////////////////////
ADDR_I2C_ADDR:
begin
r_i2c_addr <= i_reg_wr_data;
end
////////////////////////////////////////////////////////////
// 写数据
////////////////////////////////////////////////////////////
ADDR_DATA_WR:
begin
r_data_wr <= i_reg_wr_data;
end
default:
begin
end
endcase
end
end
end
////////////////////////////////////////////////////////////////////////////
// BUSY
////////////////////////////////////////////////////////////////////////////
always @(posedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n) begin
r_busy <= 1'b0;
end
else begin
// 启动
if(r_i2c_exec)
r_busy <= 1'b1;
// 完成
if(w_i2c_done)
r_busy <= 1'b0;
end
end
////////////////////////////////////////////////////////////////////////////
// DONE / ACK latch
////////////////////////////////////////////////////////////////////////////
always @(posedge i_clk or negedge i_rst_n)
begin
if(!i_rst_n) begin
r_done <= 1'b0;
r_ack_error <= 1'b0;
r_data_rd <= 8'd0;
end
else begin
// 下一次EXEC启动时清上一次结果
if(i_reg_wr_en && (i_reg_wr_addr == ADDR_CONTROL) && i_reg_wr_data[0]) begin
r_done <= 1'b0;
r_ack_error <= 1'b0;
end
// driver的done是短脉冲,这里锁存给SMMR轮询读取
if(w_i2c_done) begin
r_done <= 1'b1;
r_ack_error <= w_i2c_ack;
r_data_rd <= w_i2c_data_rd;
end
end
end
////////////////////////////////////////////////////////////////////////////
// SMMR READ
////////////////////////////////////////////////////////////////////////////
always @(*)
begin
o_reg_rd_data = 0;
if((i_reg_rd_addr >= P_SLOT_BASE) && (i_reg_rd_addr < P_SLOT_BASE + P_REG_COUNT)) begin
case(i_reg_rd_addr)
////////////////////////////////////////////////////////////////
// CONTROL
////////////////////////////////////////////////////////////////
ADDR_CONTROL:
begin
o_reg_rd_data = r_control;
// EXEC为脉冲,不读回
o_reg_rd_data[0] = 1'b0;
end
////////////////////////////////////////////////////////////////
// STATUS
////////////////////////////////////////////////////////////////
ADDR_STATUS:
begin
o_reg_rd_data = 0;
o_reg_rd_data[0] = r_done;
o_reg_rd_data[1] = r_ack_error;
o_reg_rd_data[2] = r_busy;
end
////////////////////////////////////////////////////////////////
// I2C内部地址
////////////////////////////////////////////////////////////////
ADDR_I2C_ADDR:
begin
o_reg_rd_data = r_i2c_addr;
end
////////////////////////////////////////////////////////////////
// 写数据
////////////////////////////////////////////////////////////////
ADDR_DATA_WR:
begin
o_reg_rd_data = r_data_wr;
end
////////////////////////////////////////////////////////////////
// 读数据
////////////////////////////////////////////////////////////////
ADDR_DATA_RD:
begin
o_reg_rd_data = {8'd0, r_data_rd};
end
default:
begin
o_reg_rd_data = 0;
end
endcase
end
end
endmodule
i2c_master_dri.v
verilog
module i2c_master_dri
#(
parameter P_SLAVE_ADDR = 7'b1010000 , //EEPROM从机地址
parameter P_CLK_FREQ = 26'd50_000_000, //模块输入的时钟频率
parameter P_I2C_FREQ = 18'd250_000 //IIC_SCL的时钟频率250K
)
(
input i_clk ,
input i_rst_n ,
// i2c interface (bus control)
input i_i2c_exec , //I2C触发执行信号
input i_bit_ctrl , //字地址位控制(16b/8b)
input i_i2c_rh_wl , //I2C读写控制信号 (1=read 0=write) 原名 i2c_rh_wl 保持
input [15:0] i_i2c_addr , //I2C器件内地址
input [ 7:0] i_i2c_data_w , //I2C要写的数据
output reg [ 7:0] o_i2c_data_r , //I2C读出的数据
output reg o_i2c_done , //I2C一次操作完成
output reg o_i2c_ack , //I2C应答标志 0:应答 1:未应答
output reg o_scl , //I2C的SCL时钟信号
// SDA 三端口替换: 外部通过 IOBUF 连接到 top IO
input i_sda_i, // 从引脚读回来的 SDA (IOBUF.O)
output o_sda_o, // 要驱动到引脚的 SDA 值 (IOBUF.I)
output o_sda_t, // 三态控制 (IOBUF.T) 1=高阻, 0=驱动
// user interface
output reg o_dri_clk //驱动I2C操作的驱动时钟
);
//localparam define
localparam S_IDLE = 8'b0000_0001; //空闲状态
localparam S_SLADDR = 8'b0000_0010; //发送器件地址(slave address)
localparam S_ADDR16 = 8'b0000_0100; //发送16位字地址
localparam S_ADDR8 = 8'b0000_1000; //发送8位字地址
localparam S_DATA_WR = 8'b0001_0000; //写数据(8 bit)
localparam S_ADDR_RD = 8'b0010_0000; //发送器件地址读
localparam S_DATA_RD = 8'b0100_0000; //读数据(8 bit)
localparam S_STOP = 8'b1000_0000; //结束I2C操作
//reg define
reg r_sda_dir ; //I2C数据(SDA)方向控制 (1=drive, 0=release)
reg r_sda_out ; //SDA输出信号 (内部驱动值)
reg r_st_done ; //状态结束
reg r_wr_flag ; //写标志
reg [ 6:0] r_cnt ; //计数
reg [ 7:0] r_cur_state ; //状态机当前状态
reg [ 7:0] r_next_state; //状态机下一状态
reg [15:0] r_addr_t ; //地址
reg [ 7:0] r_data_r ; //读取的数据 (临时)
reg [ 7:0] r_data_wr_t ; //I2C需写的数据的临时寄存
reg [ 9:0] r_clk_cnt ; //分频时钟计数
//wire define
wire w_sda_in ; //SDA输入信号 (来自外部 IO via IOBUF.O)
wire [8:0] w_clk_divide ; //模块驱动时钟的分频系数
//*****************************************************
//** main code
//*****************************************************
// ---- SDA 信号映射:把内部 r_sda_out/r_sda_dir 暴露为模块输出 o_sda_o/o_sda_t;w_sda_in 从外部输入
assign o_sda_o = r_sda_out; // 内部想输出到总线的值
assign o_sda_t = ~r_sda_dir; // r_sda_dir==1 表示驱动 -> T = 0; r_sda_dir==0 表示释放 -> T = 1
assign w_sda_in = i_sda_i; // 从外部引脚读回的值 (IOBUF.O)
// ---- 分频计算 (保持原逻辑)
assign w_clk_divide = (P_CLK_FREQ/P_I2C_FREQ) >> 2'd2 ; //模块驱动时钟的分频系数
//生成I2C的SCL的四倍频率的驱动时钟用于驱动i2c的操作
always @(posedge i_clk or negedge i_rst_n) begin
if(!i_rst_n) begin
o_dri_clk <= 1'b0;
r_clk_cnt <= 10'd0;
end
else if(r_clk_cnt == (w_clk_divide[8:1] - 9'd1)) begin
r_clk_cnt <= 10'd0;
o_dri_clk <= ~o_dri_clk;
end
else
r_clk_cnt <= r_clk_cnt + 10'b1;
end
// 生成时钟使能信号,用于统一时钟域
reg r_dri_clk_en;
always @(posedge i_clk or negedge i_rst_n) begin
if(!i_rst_n) begin
r_dri_clk_en <= 1'b0;
end
else if(r_clk_cnt == (w_clk_divide[8:1] - 9'd1)) begin
r_dri_clk_en <= 1'b1;
end
else
r_dri_clk_en <= 1'b0;
end
//(三段式状态机)同步时序描述状态转移
always @(posedge i_clk or negedge i_rst_n) begin
if(!i_rst_n)
r_cur_state <= S_IDLE;
else if(r_dri_clk_en)
r_cur_state <= r_next_state;
end
//组合逻辑判断状态转移条件
always @(*) begin
r_next_state = S_IDLE;
case(r_cur_state)
S_IDLE: begin //空闲状态
if(i_i2c_exec) begin
r_next_state = S_SLADDR;
end
else
r_next_state = S_IDLE;
end
S_SLADDR: begin
if(r_st_done) begin
if(i_bit_ctrl) //判断是16位还是8位字地址
r_next_state = S_ADDR16;
else
r_next_state = S_ADDR8 ;
end
else
r_next_state = S_SLADDR;
end
S_ADDR16: begin //写16位字地址
if(r_st_done) begin
r_next_state = S_ADDR8;
end
else begin
r_next_state = S_ADDR16;
end
end
S_ADDR8: begin //8位字地址
if(r_st_done) begin
if(r_wr_flag==1'b0) //读写判断 (注意原 r_wr_flag 用法)
r_next_state = S_DATA_WR;
else
r_next_state = S_ADDR_RD;
end
else begin
r_next_state = S_ADDR8;
end
end
S_DATA_WR: begin //写数据(8 bit)
if(r_st_done)
r_next_state = S_STOP;
else
r_next_state = S_DATA_WR;
end
S_ADDR_RD: begin //写地址以进行读数据
if(r_st_done) begin
r_next_state = S_DATA_RD;
end
else begin
r_next_state = S_ADDR_RD;
end
end
S_DATA_RD: begin //读取数据(8 bit)
if(r_st_done)
r_next_state = S_STOP;
else
r_next_state = S_DATA_RD;
end
S_STOP: begin //结束I2C操作
if(r_st_done)
r_next_state = S_IDLE;
else
r_next_state = S_STOP ;
end
default: r_next_state= S_IDLE;
endcase
end
//时序电路描述状态输出
always @(posedge i_clk or negedge i_rst_n) begin
//复位初始化
if(!i_rst_n) begin
o_scl <= 1'b1;
r_sda_out <= 1'b1;
r_sda_dir <= 1'b1;
o_i2c_done <= 1'b0;
o_i2c_ack <= 1'b0;
r_cnt <= 7'b0;
r_st_done <= 1'b0;
r_data_r <= 8'b0;
o_i2c_data_r<= 8'b0;
r_wr_flag <= 1'b0;
r_addr_t <= 16'b0;
r_data_wr_t <= 8'b0;
end
else if(r_dri_clk_en) begin
r_st_done <= 1'b0 ;
r_cnt <= r_cnt +7'b1 ;
case(r_cur_state)
S_IDLE: begin //空闲状态
o_scl <= 1'b1;
r_sda_out <= 1'b1;
r_sda_dir <= 1'b1;
o_i2c_done<= 1'b0;
r_cnt <= 7'b0;
if(i_i2c_exec) begin
r_wr_flag <= i_i2c_rh_wl ;
r_addr_t <= i_i2c_addr ;
r_data_wr_t <= i_i2c_data_w;
o_i2c_ack <= 1'b0;
end
end
S_SLADDR: begin //写地址(器件地址和字地址)
case(r_cnt)
7'd1 : r_sda_out <= 1'b0; //开始I2C
7'd3 : o_scl <= 1'b0;
7'd4 : r_sda_out <= P_SLAVE_ADDR[6]; //传送器件地址
7'd5 : o_scl <= 1'b1;
7'd7 : o_scl <= 1'b0;
7'd8 : r_sda_out <= P_SLAVE_ADDR[5];
7'd9 : o_scl <= 1'b1;
7'd11: o_scl <= 1'b0;
7'd12: r_sda_out <= P_SLAVE_ADDR[4];
7'd13: o_scl <= 1'b1;
7'd15: o_scl <= 1'b0;
7'd16: r_sda_out <= P_SLAVE_ADDR[3];
7'd17: o_scl <= 1'b1;
7'd19: o_scl <= 1'b0;
7'd20: r_sda_out <= P_SLAVE_ADDR[2];
7'd21: o_scl <= 1'b1;
7'd23: o_scl <= 1'b0;
7'd24: r_sda_out <= P_SLAVE_ADDR[1];
7'd25: o_scl <= 1'b1;
7'd27: o_scl <= 1'b0;
7'd28: r_sda_out <= P_SLAVE_ADDR[0];
7'd29: o_scl <= 1'b1;
7'd31: o_scl <= 1'b0;
7'd32: r_sda_out <= 1'b0; //0:写
7'd33: o_scl <= 1'b1;
7'd35: o_scl <= 1'b0;
7'd36: begin
r_sda_dir <= 1'b0;
r_sda_out <= 1'b1;
end
7'd37: o_scl <= 1'b1;
7'd38: begin //从机应答
r_st_done <= 1'b1;
if(w_sda_in == 1'b1) //高电平表示未应答
o_i2c_ack <= 1'b1; //拉高应答标志位
end
7'd39: begin
o_scl <= 1'b0;
r_cnt <= 7'b0;
end
default : ;
endcase
end
S_ADDR16: begin
case(r_cnt)
7'd0 : begin
r_sda_dir <= 1'b1 ;
r_sda_out <= r_addr_t[15]; //传送字地址
end
7'd1 : o_scl <= 1'b1;
7'd3 : o_scl <= 1'b0;
7'd4 : r_sda_out <= r_addr_t[14];
7'd5 : o_scl <= 1'b1;
7'd7 : o_scl <= 1'b0;
7'd8 : r_sda_out <= r_addr_t[13];
7'd9 : o_scl <= 1'b1;
7'd11: o_scl <= 1'b0;
7'd12: r_sda_out <= r_addr_t[12];
7'd13: o_scl <= 1'b1;
7'd15: o_scl <= 1'b0;
7'd16: r_sda_out <= r_addr_t[11];
7'd17: o_scl <= 1'b1;
7'd19: o_scl <= 1'b0;
7'd20: r_sda_out <= r_addr_t[10];
7'd21: o_scl <= 1'b1;
7'd23: o_scl <= 1'b0;
7'd24: r_sda_out <= r_addr_t[9];
7'd25: o_scl <= 1'b1;
7'd27: o_scl <= 1'b0;
7'd28: r_sda_out <= r_addr_t[8];
7'd29: o_scl <= 1'b1;
7'd31: o_scl <= 1'b0;
7'd32: begin
r_sda_dir <= 1'b0;
r_sda_out <= 1'b1;
end
7'd33: o_scl <= 1'b1;
7'd34: begin //从机应答
r_st_done <= 1'b1;
if(w_sda_in == 1'b1) //高电平表示未应答
o_i2c_ack <= 1'b1; //拉高应答标志位
end
7'd35: begin
o_scl <= 1'b0;
r_cnt <= 7'b0;
end
default : ;
endcase
end
S_ADDR8: begin
case(r_cnt)
7'd0: begin
r_sda_dir <= 1'b1 ;
r_sda_out <= r_addr_t[7]; //字地址
end
7'd1 : o_scl <= 1'b1;
7'd3 : o_scl <= 1'b0;
7'd4 : r_sda_out <= r_addr_t[6];
7'd5 : o_scl <= 1'b1;
7'd7 : o_scl <= 1'b0;
7'd8 : r_sda_out <= r_addr_t[5];
7'd9 : o_scl <= 1'b1;
7'd11: o_scl <= 1'b0;
7'd12: r_sda_out <= r_addr_t[4];
7'd13: o_scl <= 1'b1;
7'd15: o_scl <= 1'b0;
7'd16: r_sda_out <= r_addr_t[3];
7'd17: o_scl <= 1'b1;
7'd19: o_scl <= 1'b0;
7'd20: r_sda_out <= r_addr_t[2];
7'd21: o_scl <= 1'b1;
7'd23: o_scl <= 1'b0;
7'd24: r_sda_out <= r_addr_t[1];
7'd25: o_scl <= 1'b1;
7'd27: o_scl <= 1'b0;
7'd28: r_sda_out <= r_addr_t[0];
7'd29: o_scl <= 1'b1;
7'd31: o_scl <= 1'b0;
7'd32: begin
r_sda_dir <= 1'b0;
r_sda_out <= 1'b1;
end
7'd33: o_scl <= 1'b1;
7'd34: begin //从机应答
r_st_done <= 1'b1;
if(w_sda_in == 1'b1) //高电平表示未应答
o_i2c_ack <= 1'b1; //拉高应答标志位
end
7'd35: begin
o_scl <= 1'b0;
r_cnt <= 7'b0;
end
default : ;
endcase
end
S_DATA_WR: begin //写数据(8 bit)
case(r_cnt)
7'd0: begin
r_sda_dir <= 1'b1;
r_sda_out <= r_data_wr_t[7]; //I2C写8位数据
end
7'd1 : o_scl <= 1'b1;
7'd3 : o_scl <= 1'b0;
7'd4 : r_sda_out <= r_data_wr_t[6];
7'd5 : o_scl <= 1'b1;
7'd7 : o_scl <= 1'b0;
7'd8 : r_sda_out <= r_data_wr_t[5];
7'd9 : o_scl <= 1'b1;
7'd11: o_scl <= 1'b0;
7'd12: r_sda_out <= r_data_wr_t[4];
7'd13: o_scl <= 1'b1;
7'd15: o_scl <= 1'b0;
7'd16: r_sda_out <= r_data_wr_t[3];
7'd17: o_scl <= 1'b1;
7'd19: o_scl <= 1'b0;
7'd20: r_sda_out <= r_data_wr_t[2];
7'd21: o_scl <= 1'b1;
7'd23: o_scl <= 1'b0;
7'd24: r_sda_out <= r_data_wr_t[1];
7'd25: o_scl <= 1'b1;
7'd27: o_scl <= 1'b0;
7'd28: r_sda_out <= r_data_wr_t[0];
7'd29: o_scl <= 1'b1;
7'd31: o_scl <= 1'b0;
7'd32: begin
r_sda_dir <= 1'b0;
r_sda_out <= 1'b1;
end
7'd33: o_scl <= 1'b1;
7'd34: begin //从机应答
r_st_done <= 1'b1;
if(w_sda_in == 1'b1) //高电平表示未应答
o_i2c_ack <= 1'b1; //拉高应答标志位
end
7'd35: begin
o_scl <= 1'b0;
r_cnt <= 7'b0;
end
default : ;
endcase
end
S_ADDR_RD: begin //写地址以进行读数据
case(r_cnt)
7'd0 : begin
r_sda_dir <= 1'b1;
r_sda_out <= 1'b1;
end
7'd1 : o_scl <= 1'b1;
7'd2 : r_sda_out <= 1'b0; //重新开始
7'd3 : o_scl <= 1'b0;
7'd4 : r_sda_out <= P_SLAVE_ADDR[6]; //传送器件地址
7'd5 : o_scl <= 1'b1;
7'd7 : o_scl <= 1'b0;
7'd8 : r_sda_out <= P_SLAVE_ADDR[5];
7'd9 : o_scl <= 1'b1;
7'd11: o_scl <= 1'b0;
7'd12: r_sda_out <= P_SLAVE_ADDR[4];
7'd13: o_scl <= 1'b1;
7'd15: o_scl <= 1'b0;
7'd16: r_sda_out <= P_SLAVE_ADDR[3];
7'd17: o_scl <= 1'b1;
7'd19: o_scl <= 1'b0;
7'd20: r_sda_out <= P_SLAVE_ADDR[2];
7'd21: o_scl <= 1'b1;
7'd23: o_scl <= 1'b0;
7'd24: r_sda_out <= P_SLAVE_ADDR[1];
7'd25: o_scl <= 1'b1;
7'd27: o_scl <= 1'b0;
7'd28: r_sda_out <= P_SLAVE_ADDR[0];
7'd29: o_scl <= 1'b1;
7'd31: o_scl <= 1'b0;
7'd32: r_sda_out <= 1'b1; //1:读
7'd33: o_scl <= 1'b1;
7'd35: o_scl <= 1'b0;
7'd36: begin
r_sda_dir <= 1'b0;
r_sda_out <= 1'b1;
end
7'd37: o_scl <= 1'b1;
7'd38: begin //从机应答
r_st_done <= 1'b1;
if(w_sda_in == 1'b1) //高电平表示未应答
o_i2c_ack <= 1'b1; //拉高应答标志位
end
7'd39: begin
o_scl <= 1'b0;
r_cnt <= 7'b0;
end
default : ;
endcase
end
S_DATA_RD: begin //读取数据(8 bit)
case(r_cnt)
7'd0: r_sda_dir <= 1'b0;
7'd1: begin
r_data_r[7] <= w_sda_in;
o_scl <= 1'b1;
end
7'd3: o_scl <= 1'b0;
7'd5: begin
r_data_r[6] <= w_sda_in ;
o_scl <= 1'b1 ;
end
7'd7: o_scl <= 1'b0;
7'd9: begin
r_data_r[5] <= w_sda_in;
o_scl <= 1'b1 ;
end
7'd11: o_scl <= 1'b0;
7'd13: begin
r_data_r[4] <= w_sda_in;
o_scl <= 1'b1 ;
end
7'd15: o_scl <= 1'b0;
7'd17: begin
r_data_r[3] <= w_sda_in;
o_scl <= 1'b1 ;
end
7'd19: o_scl <= 1'b0;
7'd21: begin
r_data_r[2] <= w_sda_in;
o_scl <= 1'b1 ;
end
7'd23: o_scl <= 1'b0;
7'd25: begin
r_data_r[1] <= w_sda_in;
o_scl <= 1'b1 ;
end
7'd27: o_scl <= 1'b0;
7'd29: begin
r_data_r[0] <= w_sda_in;
o_scl <= 1'b1 ;
end
7'd31: o_scl <= 1'b0;
7'd32: begin
r_sda_dir <= 1'b1;
r_sda_out <= 1'b1;
end
7'd33: o_scl <= 1'b1;
7'd34: r_st_done <= 1'b1; //非应答
7'd35: begin
o_scl <= 1'b0;
r_cnt <= 7'b0;
o_i2c_data_r <= r_data_r;
end
default : ;
endcase
end
S_STOP: begin //结束I2C操作
case(r_cnt)
7'd0: begin
r_sda_dir <= 1'b1; //结束I2C
r_sda_out <= 1'b0;
end
7'd1 : o_scl <= 1'b1;
7'd3 : r_sda_out <= 1'b1;
7'd15: r_st_done <= 1'b1;
7'd16: begin
r_cnt <= 7'b0;
o_i2c_done <= 1'b1; //向上层模块传递I2C结束信号
end
default : ;
endcase
end
endcase
end
end
endmodule
ATK_FPGA_Demo_Top.v
verilog
`timescale 1ns / 1ps
module ATK_FPGA_Demo_Top
(
input wire CLOCK_XTAL_50MHz,
input wire RESET,
input wire KEY,
output wire TXD,
output wire I2C_SCL,
inout wire I2C_SDA
);
// ============================================================
// 参数
// ============================================================
localparam P_CLK_FREQ = 50_000_000;
localparam P_UART_BPS = 115200;
localparam P_I2C_FREQ = 250_000;
// AT24C64 内部写周期
// 5ms @ 50MHz
localparam [31:0] P_WRITE_DELAY = 32'd250_000;
// ============================================================
// iic_master_slot 寄存器地址
// ============================================================
localparam [7:0]
ADDR_CONTROL = 8'd0,
ADDR_STATUS = 8'd1,
ADDR_I2C_ADDR = 8'd2,
ADDR_DATA_WR = 8'd3,
ADDR_DATA_RD = 8'd4;
// ============================================================
// IIC CONTROL
//
// bit0 = start
// bit1 = read
// bit2 = 16bit address
// ============================================================
localparam [15:0]
IIC_WRITE = 16'h0005,
IIC_READ = 16'h0007;
// ============================================================
// 测试数据
// ============================================================
localparam [15:0] TEST_ADDR = 16'h0010;
localparam [7:0] TEST_DATA = 8'h55;
// ============================================================
// 状态
// ============================================================
localparam [3:0]
S_IDLE = 4'd0,
S_WRITE_ADDR = 4'd1,
S_WRITE_DATA = 4'd2,
S_WRITE_EXEC = 4'd3,
S_WRITE_WAIT = 4'd4,
S_DELAY = 4'd5,
S_READ_ADDR = 4'd6,
S_READ_EXEC = 4'd7,
S_READ_WAIT = 4'd8,
S_READ_SAVE = 4'd9,
S_UART_DATA = 4'd10,
S_DONE = 4'd11;
// ============================================================
// KEY
// ============================================================
wire w_start;
// ============================================================
// iic_master_slot SMMR
// ============================================================
reg r_reg_wr_en;
reg [7:0] r_reg_wr_addr;
reg [15:0] r_reg_wr_data;
reg [7:0] r_reg_rd_addr;
wire [15:0] w_reg_rd_data;
// ============================================================
// I2C
// ============================================================
wire w_i2c_sda_i;
wire w_i2c_sda_o;
wire w_i2c_sda_t;
// ============================================================
// 状态
// ============================================================
reg [3:0] r_state;
reg [3:0] r_state_last;
reg [31:0] r_delay_cnt;
reg [7:0] r_read_data;
// ============================================================
// UART AXIS
// ============================================================
reg r_uart_valid;
reg [127:0] r_uart_data;
reg r_uart_done;
wire w_uart_ready;
wire w_uart_tx;
wire w_state_enter;
wire w_state_uart_done;
// ============================================================
// KEY
// ============================================================
key_debounce u_key_debounce
(
.i_clk (CLOCK_XTAL_50MHz),
.i_rst_n (RESET),
.i_key (KEY),
.o_key_pulse (w_start)
);
// ============================================================
// I2C SDA
// ============================================================
IOBUF u_i2c_sda_iobuf
(
.I (w_i2c_sda_o),
.O (w_i2c_sda_i),
.T (w_i2c_sda_t),
.IO (I2C_SDA)
);
// ============================================================
// IIC MASTER SLOT
//
// AT24C64
// 7bit slave address = 0x50
// ============================================================
iic_master_slot #(
.P_ADDR_WIDTH (8),
.P_DATA_WIDTH (16),
.P_SLOT_BASE (8'd0),
.P_REG_COUNT (32),
.P_SLAVE_ADDR (7'h50),
.P_CLK_FREQ (P_CLK_FREQ),
.P_I2C_FREQ (P_I2C_FREQ)
)
u_iic_master_slot
(
.i_clk (CLOCK_XTAL_50MHz),
.i_rst_n (RESET),
.i_reg_wr_en (r_reg_wr_en),
.i_reg_wr_addr (r_reg_wr_addr),
.i_reg_wr_data (r_reg_wr_data),
.i_reg_rd_addr (r_reg_rd_addr),
.o_reg_rd_data (w_reg_rd_data),
.o_i2c_scl (I2C_SCL),
.i_i2c_sda_i (w_i2c_sda_i),
.o_i2c_sda_o (w_i2c_sda_o),
.o_i2c_sda_t (w_i2c_sda_t)
);
// ============================================================
// AXIS UART SENDER
// ============================================================
axis_uart_sender #(
.P_CLK_FREQ (P_CLK_FREQ),
.P_UART_BPS (P_UART_BPS),
.P_FIFO_DEPTH (64)
)
u_axis_uart_sender
(
.i_clk (CLOCK_XTAL_50MHz),
.i_rst_n (RESET),
.s_axis_uart_tvalid (r_uart_valid),
.s_axis_uart_tready (w_uart_ready),
.s_axis_uart_tdata (r_uart_data),
.o_uart_tx (w_uart_tx)
);
assign TXD = w_uart_tx;
assign w_state_enter = (r_state != r_state_last);
assign w_state_uart_done = (r_state == S_IDLE) ? 1'b1 :
((r_state == r_state_last) && r_uart_done);
// ============================================================
// SMMR READ
// ============================================================
always @(*) begin
case(r_state)
S_WRITE_WAIT,
S_READ_WAIT:
r_reg_rd_addr = ADDR_STATUS;
S_READ_SAVE:
r_reg_rd_addr = ADDR_DATA_RD;
default:
r_reg_rd_addr = ADDR_STATUS;
endcase
end
// ============================================================
// SMMR WRITE
// ============================================================
always @(posedge CLOCK_XTAL_50MHz or negedge RESET) begin
if(!RESET) begin
r_reg_wr_en <= 1'b0;
r_reg_wr_addr <= 8'd0;
r_reg_wr_data <= 16'd0;
end
else begin
// 默认不写
r_reg_wr_en <= 1'b0;
if(w_state_enter) begin
case(r_state)
//================================================
// 写 EEPROM 内部地址
//================================================
S_WRITE_ADDR: begin
r_reg_wr_en <= 1'b1;
r_reg_wr_addr <= ADDR_I2C_ADDR;
r_reg_wr_data <= TEST_ADDR;
end
//================================================
// 写 EEPROM 数据
//================================================
S_WRITE_DATA: begin
r_reg_wr_en <= 1'b1;
r_reg_wr_addr <= ADDR_DATA_WR;
r_reg_wr_data <= {8'd0, TEST_DATA};
end
//================================================
// 启动 WRITE
//================================================
S_WRITE_EXEC: begin
r_reg_wr_en <= 1'b1;
r_reg_wr_addr <= ADDR_CONTROL;
r_reg_wr_data <= IIC_WRITE;
end
//================================================
// 设置 READ 地址
//================================================
S_READ_ADDR: begin
r_reg_wr_en <= 1'b1;
r_reg_wr_addr <= ADDR_I2C_ADDR;
r_reg_wr_data <= TEST_ADDR;
end
//================================================
// 启动 READ
//================================================
S_READ_EXEC: begin
r_reg_wr_en <= 1'b1;
r_reg_wr_addr <= ADDR_CONTROL;
r_reg_wr_data <= IIC_READ;
end
default: begin
end
endcase
end
end
end
// ============================================================
// 主状态机
// ============================================================
always @(posedge CLOCK_XTAL_50MHz or negedge RESET) begin
if(!RESET) begin
r_state <= S_IDLE;
r_delay_cnt <= 32'd0;
r_read_data <= 8'd0;
end
else begin
case(r_state)
//================================================
// IDLE
//================================================
S_IDLE: begin
if(w_start) begin
r_state <= S_WRITE_ADDR;
end
end
//================================================
// WRITE ADDRESS
//================================================
S_WRITE_ADDR: begin
if(w_state_uart_done) begin
r_state <= S_WRITE_DATA;
end
end
//================================================
// WRITE DATA
//================================================
S_WRITE_DATA: begin
if(w_state_uart_done) begin
r_state <= S_WRITE_EXEC;
end
end
//================================================
// WRITE EXEC
//================================================
S_WRITE_EXEC: begin
if(w_state_uart_done) begin
r_state <= S_WRITE_WAIT;
end
end
//================================================
// WRITE WAIT
//================================================
S_WRITE_WAIT: begin
if(w_state_uart_done && w_reg_rd_data[0]) begin
r_delay_cnt <= 32'd0;
r_state <= S_DELAY;
end
end
//================================================
// EEPROM 写周期
//================================================
S_DELAY: begin
if(w_state_uart_done) begin
if(r_delay_cnt >= P_WRITE_DELAY - 1) begin
r_delay_cnt <= 32'd0;
r_state <= S_READ_ADDR;
end
else begin
r_delay_cnt <= r_delay_cnt + 1'b1;
end
end
end
//================================================
// READ ADDRESS
//================================================
S_READ_ADDR: begin
if(w_state_uart_done) begin
r_state <= S_READ_EXEC;
end
end
//================================================
// READ EXEC
//================================================
S_READ_EXEC: begin
if(w_state_uart_done) begin
r_state <= S_READ_WAIT;
end
end
//================================================
// READ WAIT
//================================================
S_READ_WAIT: begin
if(w_state_uart_done && w_reg_rd_data[0]) begin
r_state <= S_READ_SAVE;
end
end
//================================================
// READ DATA
//================================================
S_READ_SAVE: begin
if(w_state_uart_done) begin
r_read_data <= w_reg_rd_data[7:0];
r_state <= S_UART_DATA;
end
end
//================================================
// UART DATA
//================================================
S_UART_DATA: begin
if(w_state_uart_done) begin
r_state <= S_DONE;
end
end
//================================================
// DONE
//================================================
S_DONE: begin
if(w_state_uart_done) begin
r_state <= S_IDLE;
end
end
default: begin
r_state <= S_IDLE;
end
endcase
end
end
// ============================================================
// UART
//
// 每次状态切换:
//
// 发送:
//
// length = 1
// data = state
//
// 最终 S_UART_DATA:
//
// length = 1
// data = r_read_data
//
// ============================================================
always @(posedge CLOCK_XTAL_50MHz or negedge RESET) begin
if(!RESET) begin
r_state_last <= S_IDLE;
r_uart_valid <= 1'b0;
r_uart_data <= 128'd0;
r_uart_done <= 1'b0;
end
else begin
if(r_state == S_IDLE) begin
r_state_last <= S_IDLE;
r_uart_valid <= 1'b0;
r_uart_done <= 1'b1;
end
else if(r_state != r_state_last) begin
r_state_last <= r_state;
r_uart_valid <= 1'b0;
r_uart_done <= 1'b0;
end
else if(!r_uart_done) begin
if(r_uart_valid) begin
if(w_uart_ready) begin
r_uart_valid <= 1'b0;
r_uart_done <= 1'b1;
end
end
else begin
// S_UART_DATA 不发送状态码,发送真正读取的数据
if(r_state == S_UART_DATA) begin
r_uart_data <= {
8'd1,
r_read_data,
112'd0
};
end
else begin
r_uart_data <= {
8'd1,
{4'd0, r_state},
112'd0
};
end
r_uart_valid <= 1'b1;
end
end
else begin
r_uart_valid <= 1'b0;
end
end
end
endmodule
Pin.xdc
verilog
create_clock -period 20.000 -name CLOCK_XTAL_50MHz [get_ports CLOCK_XTAL_50MHz]
set_property -dict {PACKAGE_PIN U18 IOSTANDARD LVCMOS33} [get_ports CLOCK_XTAL_50MHz]
set_property -dict {PACKAGE_PIN N16 IOSTANDARD LVCMOS33} [get_ports RESET]
set_property -dict {PACKAGE_PIN L14 IOSTANDARD LVCMOS33} [get_ports KEY]
set_property -dict {PACKAGE_PIN T19 IOSTANDARD LVCMOS33} [get_ports RXD]
set_property -dict {PACKAGE_PIN J15 IOSTANDARD LVCMOS33} [get_ports TXD]
set_property -dict {PACKAGE_PIN E18 IOSTANDARD LVCMOS33} [get_ports I2C_SCL]
set_property -dict {PACKAGE_PIN F17 IOSTANDARD LVCMOS33} [get_ports I2C_SDA]
测试
bash
[16:25:55.471]收←◆01 02 03 04 05 06 07 08 09 55 0B
bash
01 = S_WRITE_ADDR : 设置 EEPROM 内部地址,例如 TEST_ADDR = 0x0010
02 = S_WRITE_DATA : 设置要写入 EEPROM 的数据,例如 TEST_DATA = 0x55
03 = S_WRITE_EXEC : 启动 I2C 写操作
04 = S_WRITE_WAIT : 等待 I2C 写事务完成,等待 STATUS[0] DONE = 1
05 = S_DELAY : 等待 AT24C64 内部写周期完成
06 = S_READ_ADDR : 再次设置 EEPROM 内部地址,准备从同一地址读取
07 = S_READ_EXEC : 启动 I2C 读操作
08 = S_READ_WAIT : 等待 I2C 读事务完成,等待 STATUS[0] DONE = 1
09 = S_READ_SAVE : 保存从 DATA_RD 寄存器读到的数据
55 = READ DATA : 实际从 AT24C64 读回的数据,等于写入值 0x55
0B = S_DONE : 测试完成,状态机随后回到 S_IDLE