用vio_uart_rpc协议,测试IIC接口的AT24C64
在 Vivado 里,I²C 的 SDA 引脚如果你在 top-level HDL 里写成 inout,综合工具会自动在 IO 口推导出 IOBUF。但如果你在 Block Design 里只拉出一个 output 或 input,Vivado 不会自动推导,必须你自己插入 IOBUF 这个 原语。
·
用vio_uart_rpc协议,测试IIC接口的AT24C64
参考
BD

IIC时序图
写时序

读时序

注意
在 Vivado 里,I²C 的 SDA 引脚如果你在 top-level HDL 里写成 inout,综合工具会自动在 IO 口推导出 IOBUF。
但如果你在 Block Design 里只拉出一个 output 或 input,Vivado 不会自动推导,必须你自己插入 IOBUF 这个 原语
测试用例
| 用例描述 | 发送帧(→) | 响应帧(←) | 说明 |
|---|---|---|---|
| 写寄存器(地址 1) | 01 01 44 33 22 11 |
01 01 44 33 22 11 |
向寄存器1写入 0x11223344 |
| 读寄存器(地址 1) | 00 01 00 00 00 00 |
00 01 44 33 22 11 |
读取寄存器1,返回上一步数据 |
| RPC:回显(方法 0) | 02 00 BE BA FE CA |
02 00 BE BA FE CA |
回显参数0 = 0xCAFEBABE |
| RPC:加法(方法 1) | 02 01 04 03 02 01 |
02 01 05 05 05 05 |
每字节加法:+1,+2,+3,+4 |
| RPC:读IIC AT24C64 | 02 02 09 00 00 00 |
02 02 89 00 00 00 |
读到地址9的数据为0x89 |
| RPC:写IIC AT24C64 | 02 03 09 34 00 00 |
02 03 09 00 00 00 |
地址9写入0x34 |
| 非法地址读取 | 00 1E 00 00 00 00 |
无响应 / 忽略 | 地址超出范围(>29)被忽略 |
Vivado 测试
源文件
vio_uart.v
module vio_uart #(
parameter P_PACK_LEN = 6, //一 帧字节数
parameter P_CLK_FREQ = 50_000_000,
parameter P_UART_BPS = 115200
)(
input i_clk ,
input i_rst_n ,
input i_uart_rxd ,
output o_uart_txd ,
output reg o_done, //整个事务完成标志
//IIC
output o_scl , //eeprom的时钟线scl
input i_sda_i, // 从引脚读回来的 SDA (IOBUF.O)
output o_sda_o, // 要驱动到引脚的 SDA 值 (IOBUF.I)
output o_sda_t, // 三态控制 (IOBUF.T) 1=高阻, 0=驱动
output o_led //led显示eeprom读写测试结果
);
// ========== RX / TX 接口 ==========
wire w_rx_done;
wire [7:0] w_rx_data;
reg r_tx_en;
reg [7:0] r_tx_data;
wire w_tx_busy;
uart_rx #(
.P_CLK_FREQ(P_CLK_FREQ),
.P_UART_BPS(P_UART_BPS)
) uart_rx_inst(
.i_clk (i_clk),
.i_rst_n (i_rst_n),
.i_uart_rxd (i_uart_rxd),
.o_uart_rx_done (w_rx_done),
.o_uart_rx_data (w_rx_data)
);
uart_tx #(
.P_CLK_FREQ(P_CLK_FREQ),
.P_UART_BPS(P_UART_BPS)
) uart_tx_inst(
.i_clk (i_clk),
.i_rst_n (i_rst_n),
.i_uart_tx_en (r_tx_en),
.i_uart_tx_data (r_tx_data),
.o_uart_tx_busy (w_tx_busy),
.o_uart_txd (o_uart_txd)
);
// ========== 内部信号 ==========
//接收缓冲区
reg [7:0] r_recv_buffer [0:P_PACK_LEN-1];
//发送缓冲区
reg [7:0] r_tx_buffer [0:P_PACK_LEN-1];
//接收计数器
reg [3:0] r_rx_cnt;
//发送计数器
reg [3:0] r_tx_cnt;
//状态
reg [2:0] r_state,r_pre_state;
reg r_wait_busy;
localparam S_IDLE = 3'd0,
S_RECV = 3'd1,
S_CMD = 3'd2,
S_RESP = 3'd3,
S_SEND = 3'd4,
S_RPC_PROCESSING = 3'd5;
reg [31:0] r_mem [0:29];
reg [31:0] r_resp_data;
reg [7:0] r_cmd_type;
reg [7:0] r_cmd_addr;
reg [31:0] r_cmd_data;
reg r_rpc_start;
// RPC 处理器输出端口连接线
wire [31:0] w_res_reg_0, w_res_reg_1, w_res_reg_2, w_res_reg_3;
wire w_rpc_busy,w_rpc_done;
integer idx;
integer i;
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) begin
r_rx_cnt <= 0;
r_tx_cnt <= 0;
r_state <= S_IDLE;
r_pre_state <= S_IDLE;
r_tx_en <= 1'b0;
r_tx_data <= 8'd0;
r_wait_busy <= 1'b0;
o_done <= 1'b0;
r_rpc_start<= 1'b0;
r_resp_data<= 32'b0;
for (i = 0; i <= 16; i = i + 1) begin
r_mem[i] <= 0;
end
for (i = 0; i < P_PACK_LEN; i = i + 1) begin
r_tx_buffer[i] <= 0;
end
for (i = 0; i < P_PACK_LEN; i = i + 1) begin
r_recv_buffer[i] <= 0;
end
end else begin
r_tx_en <= 1'b0;
o_done <= 1'b0;
r_pre_state<= r_state;
case (r_state)
S_IDLE: begin
r_rx_cnt <= 0;
r_tx_cnt <= 0;
r_wait_busy <= 0;
r_state <= S_RECV;
o_done <= 1'b0;
end
S_RECV: begin
if (w_rx_done) begin
r_recv_buffer[r_rx_cnt] <= w_rx_data;
if (r_rx_cnt == P_PACK_LEN - 1) begin
r_state <= S_CMD;
end
r_rx_cnt <= r_rx_cnt + 1;
end
end
S_CMD: begin
r_cmd_type <= r_recv_buffer[0];
r_cmd_addr <= r_recv_buffer[1];
r_cmd_data <= {r_recv_buffer[5], r_recv_buffer[4], r_recv_buffer[3], r_recv_buffer[2]};
if (r_recv_buffer[1]< 30) begin
idx = r_recv_buffer[1];
if(idx<30) begin
//写
if (r_recv_buffer[0] == 8'h01) begin
r_mem[idx] <= {r_recv_buffer[5], r_recv_buffer[4], r_recv_buffer[3], r_recv_buffer[2]};
r_state <= S_RESP;
end
//读
else if(r_recv_buffer[0] == 8'h00) begin
r_resp_data <= r_mem[idx];
r_state <= S_RESP;
end
//rpc调用
else if(r_recv_buffer[0] == 8'h02) begin
r_resp_data <= 32'b0;
r_rpc_start<= 1'b1;
r_state <= S_RPC_PROCESSING;
end
end
else begin
r_state <= S_IDLE;
end
end else begin
r_state <= S_IDLE;
end
end
S_RPC_PROCESSING: begin
//上个状态也是处理RPC,且RPC处理完成
if (r_pre_state==S_RPC_PROCESSING && w_rpc_busy==0 && w_rpc_done) begin
r_mem[6] <= w_res_reg_0;
r_mem[7] <= w_res_reg_1;
r_mem[8] <= w_res_reg_2;
r_mem[9] <= w_res_reg_3;
r_rpc_start<= 1'b0;
r_state <= S_RESP;
end
end
S_RESP: begin
r_tx_cnt<=0;
if(r_recv_buffer[0] == 8'h00 || r_recv_buffer[0] == 8'h01) begin
r_resp_data <= r_mem[idx];
r_tx_buffer[0] <= r_cmd_type;
r_tx_buffer[1] <= r_cmd_addr;
r_tx_buffer[2] <= r_mem[idx][7:0];
r_tx_buffer[3] <= r_mem[idx][15:8];
r_tx_buffer[4] <= r_mem[idx][23:16];
r_tx_buffer[5] <= r_mem[idx][31:24];
r_state <= S_SEND;
end
else begin
r_resp_data<= w_res_reg_0;
r_tx_buffer[0] <= r_cmd_type;
r_tx_buffer[1] <= r_cmd_addr;
r_tx_buffer[2] <= w_res_reg_0[7:0];
r_tx_buffer[3] <= w_res_reg_0[15:8];
r_tx_buffer[4] <= w_res_reg_0[23:16];
r_tx_buffer[5] <= w_res_reg_0[31:24];
r_state <= S_SEND;
end
end
S_SEND: begin
if (!w_tx_busy && !r_wait_busy) begin
r_tx_data <= r_tx_buffer[r_tx_cnt];
r_tx_en <= 1'b1;
r_tx_cnt <= r_tx_cnt + 1;
r_wait_busy <= 1'b1;
end else if (w_tx_busy) begin
r_wait_busy <= 1'b0;
if (r_tx_cnt == 6) begin
r_state <= S_IDLE;
o_done <= 1'b1;
end
end
end
endcase
end
end
// 实例化 RPC 处理器模块,连接输入参数和输出结果寄存器
rpc_processor u_rpc (
.i_clk (i_clk),
.i_rst_n (i_rst_n),
.i_method_reg ({24'b0,r_recv_buffer[1]}), // 功能号寄存器
.i_req_reg_0 ({r_recv_buffer[5],r_recv_buffer[4],r_recv_buffer[3],r_recv_buffer[2]}), // 参数0
.i_req_reg_1 (r_mem[3]), // 参数1
.i_req_reg_2 (r_mem[4]), // 参数2
.i_req_reg_3 (r_mem[5]), // 参数3
.o_res_reg_0 (w_res_reg_0), // 返回值0
.o_res_reg_1 (w_res_reg_1), // 返回值1
.o_res_reg_2 (w_res_reg_2), // 返回值2
.o_res_reg_3 (w_res_reg_3), // 返回值3
.i_rpc_start (r_rpc_start), // 启动标志
.i_rpc_valid (1), //RPC主机方法和参数准备好了
.o_rpc_done (w_rpc_done), // RPC处理完成(1=结果有效)
.o_rpc_busy (w_rpc_busy), // RPC正忙(处理中保持高),
.o_scl (o_scl ), //I2C的SCL时钟信号
.i_sda_i (i_sda_i ),
.o_sda_o (o_sda_o ),
.o_sda_t (o_sda_t )
);
endmodule
uart_rx.v
module uart_rx #(
parameter P_CLK_FREQ = 50_000_000,
parameter P_UART_BPS = 115200
) (
input i_clk ,
input i_rst_n ,
input i_uart_rxd ,
output reg o_uart_rx_done ,
output reg [7:0] o_uart_rx_data
);
//parameter define
localparam L_BAUD_CNT_MAX= P_CLK_FREQ/P_UART_BPS ;
//reg define
reg r_uart_rxd0 ;
reg r_uart_rxd1 ;
reg r_uart_rxd2 ;
reg r_rx_flag ; //正在接收中的标志
reg [3:0] r_bit_cnt ;
reg [15:0] r_baud_cnt ;
reg [7:0] r_rx_data_t ;
//wire define
wire w_start_en;
////////////////////////////////////////////////////////////////////
//*************************main code******************************
////////////////////////////////////////////////////////////////////
//i_uart_rxd negedge
assign w_start_en = r_uart_rxd2 & (~r_uart_rxd1) & (~r_rx_flag);
//async signal input delay
always @(posedge i_clk or negedge i_rst_n) begin
if(!i_rst_n) begin
r_uart_rxd0 <= 1'b0 ;
r_uart_rxd1 <= 1'b0 ;
r_uart_rxd2 <= 1'b0 ;
end
else begin
r_uart_rxd0 <= i_uart_rxd ;
r_uart_rxd1 <= r_uart_rxd0 ;
r_uart_rxd2 <= r_uart_rxd1 ;
end
end
//generate r_baud_cnt
always @(posedge i_clk or negedge i_rst_n) begin
if(!i_rst_n)
r_baud_cnt <= 16'd0;
else if(r_rx_flag) begin
if(r_baud_cnt == L_BAUD_CNT_MAX - 1'b1)
r_baud_cnt <= 16'd0;
else
r_baud_cnt <= r_baud_cnt + 16'b1;
end
else
r_baud_cnt <= 16'd0;
end
//generate r_bit_cnt
always @(posedge i_clk or negedge i_rst_n) begin
if(!i_rst_n) begin
r_bit_cnt <= 4'd0;
end
else if(r_rx_flag) begin
if(r_baud_cnt == L_BAUD_CNT_MAX - 1'b1)
r_bit_cnt <= r_bit_cnt + 1'b1;
else
r_bit_cnt <= r_bit_cnt;
end
else
r_bit_cnt <= 4'd0;
end
//generate r_rx_flag
always @(posedge i_clk or negedge i_rst_n) begin
if(!i_rst_n)
r_rx_flag <= 1'b0;
else if(w_start_en)
r_rx_flag <= 1'b1;
else if((r_bit_cnt == 4'd9) && (r_baud_cnt == L_BAUD_CNT_MAX/2 - 1'b1))
r_rx_flag <= 1'b0;
else
r_rx_flag <= r_rx_flag;
end
always @(posedge i_clk or negedge i_rst_n) begin
if(!i_rst_n)
r_rx_data_t <= 8'b0;
else if(r_rx_flag) begin
if(r_baud_cnt == L_BAUD_CNT_MAX/2 - 1'b1) begin
case(r_bit_cnt)
4'd1 : r_rx_data_t[0] <= r_uart_rxd2;
4'd2 : r_rx_data_t[1] <= r_uart_rxd2;
4'd3 : r_rx_data_t[2] <= r_uart_rxd2;
4'd4 : r_rx_data_t[3] <= r_uart_rxd2;
4'd5 : r_rx_data_t[4] <= r_uart_rxd2;
4'd6 : r_rx_data_t[5] <= r_uart_rxd2;
4'd7 : r_rx_data_t[6] <= r_uart_rxd2;
4'd8 : r_rx_data_t[7] <= r_uart_rxd2;
default : ;
endcase
end
else
r_rx_data_t <= r_rx_data_t;
end
else
r_rx_data_t <= 8'b0;
end
always @(posedge i_clk or negedge i_rst_n) begin
if(!i_rst_n) begin
o_uart_rx_done <= 1'b0;
o_uart_rx_data <= 8'b0;
end
else if(r_bit_cnt == 4'd9 && r_baud_cnt == L_BAUD_CNT_MAX/2 - 1'b1) begin
o_uart_rx_done <= 1'b1;
o_uart_rx_data <= r_rx_data_t;
end
else begin
o_uart_rx_done <= 1'b0;
o_uart_rx_data <= o_uart_rx_data;
end
end
endmodule
uart_tx.v
module uart_tx #(
parameter P_CLK_FREQ = 50_000_000,
parameter P_UART_BPS = 115200
) (
// from system
input i_clk ,
input i_rst_n ,
input i_uart_tx_en ,
input [7 : 0] i_uart_tx_data ,
output reg o_uart_tx_busy , // 发送中标志
// output
output reg o_uart_txd
);
// parameter define
localparam L_BAUD_CNT_MAX = P_CLK_FREQ / P_UART_BPS;
// reg define
reg [3:0] r_bit_cnt;
reg [15:0] r_baud_cnt;
reg [7 :0] r_tx_data_t;
reg r_uart_tx_en_d;
//i_uart_tx_en的上升沿
wire w_uart_tx_en_posedge;
// detect i_uart_tx_en rising edge
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n)
r_uart_tx_en_d <= 1'b0;
else
r_uart_tx_en_d <= i_uart_tx_en;
end
assign w_uart_tx_en_posedge = i_uart_tx_en && !r_uart_tx_en_d;
// baud rate counter
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n)
r_baud_cnt <= 16'd0;
else if (o_uart_tx_busy) begin
if (r_baud_cnt == L_BAUD_CNT_MAX - 1)
r_baud_cnt <= 16'd0;
else
r_baud_cnt <= r_baud_cnt + 1'b1;
end else begin
r_baud_cnt <= 16'd0;
end
end
// tx bit counter
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n)
r_bit_cnt <= 4'd0;
else if (o_uart_tx_busy && (r_baud_cnt == L_BAUD_CNT_MAX - 1))
r_bit_cnt <= r_bit_cnt + 1'b1;
else if (!o_uart_tx_busy)
r_bit_cnt <= 4'd0;
end
// control busy and latch data
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) begin
r_tx_data_t <= 8'd0;
o_uart_tx_busy <= 1'b0;
end
else if (w_uart_tx_en_posedge && !o_uart_tx_busy) begin
r_tx_data_t <= i_uart_tx_data;
o_uart_tx_busy <= 1'b1;
end
else if (o_uart_tx_busy && r_bit_cnt == 4'd9 && r_baud_cnt == L_BAUD_CNT_MAX - 1) begin
o_uart_tx_busy <= 1'b0;
end
end
// generate txd signal
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n)
o_uart_txd <= 1'b1;
else if (o_uart_tx_busy) begin
case(r_bit_cnt)
4'd0 : o_uart_txd <= 1'b0; // start bit
4'd1 : o_uart_txd <= r_tx_data_t[0];
4'd2 : o_uart_txd <= r_tx_data_t[1];
4'd3 : o_uart_txd <= r_tx_data_t[2];
4'd4 : o_uart_txd <= r_tx_data_t[3];
4'd5 : o_uart_txd <= r_tx_data_t[4];
4'd6 : o_uart_txd <= r_tx_data_t[5];
4'd7 : o_uart_txd <= r_tx_data_t[6];
4'd8 : o_uart_txd <= r_tx_data_t[7];
4'd9 : o_uart_txd <= 1'b1; // stop bit
default : o_uart_txd <= 1'b1;
endcase
end
else
o_uart_txd <= 1'b1;
end
endmodule
rpc_processor.v
`timescale 1ns/1ps
// 宏定义:RPC方法(32位功能码)
`define RPC_FUNC_ECHO 32'h00000000 // 回显功能(返回输入参数)
`define RPC_FUNC_ADD 32'h00000001 // 加法功能(参数相加)
`define RPC_FUNC_IIC_READ 32'h00000002 // 读IIC
`define RPC_FUNC_IIC_WRITE 32'h00000003 // 写IIC
module rpc_processor (
input wire i_clk, // 时钟信号
input wire i_rst_n, // 复位信号(低有效)
// 寄存器接口(直接暴露)
input wire [31:0] i_method_reg, // 方法选择寄存器
input wire [31:0] i_req_reg_0, // 请求参数0
input wire [31:0] i_req_reg_1, // 请求参数1
input wire [31:0] i_req_reg_2, // 请求参数2
input wire [31:0] i_req_reg_3, // 请求参数3
output reg [31:0] o_res_reg_0, // 响应结果0
output reg [31:0] o_res_reg_1, // 响应结果1
output reg [31:0] o_res_reg_2, // 响应结果2
output reg [31:0] o_res_reg_3, // 响应结果3
// RPC控制信号(含启动信号)
input wire i_rpc_start, // RPC启动信号(1=触发处理,上升沿有效)
output reg o_rpc_busy, // RPC处理中(处理中保持高)
input wire i_rpc_valid, // 外部数据有效
output reg o_rpc_done, // RPC处理完成(1=结果有效)
// iic 接口
output o_scl , //eeprom的时钟线scl
input i_sda_i, // 从引脚读回来的 SDA (IOBUF.O)
output o_sda_o, // 要驱动到引脚的 SDA 值 (IOBUF.I)
output o_sda_t // 三态控制 (IOBUF.T) 1=高阻, 0=驱动
);
//parameter define
parameter P_SLAVE_ADDR = 7'b1010000 ; //器件地址(P_SLAVE_ADDR)
parameter P_BIT_CTRL = 1'b1 ; //字地址位控制参数(16b/8b)
parameter P_CLK_FREQ = 26'd50_000_000 ; //i2c_dri模块的驱动时钟频率(P_CLK_FREQ)
parameter P_I2C_FREQ = 18'd250_000 ; //I2C的SCL时钟频率
parameter P_L_TIME = 17'd125_000 ; //led闪烁时间参数
parameter P_MAX_BYTE = 16'd256 ; //读写测试的字节个数
//wire define
wire w_dri_clk ; //I2C操作时钟
wire w_i2c_exec ; //I2C触发控制
wire [15:0] w_i2c_addr ; //I2C操作地址
wire [ 7:0] w_i2c_data_w; //I2C写入的数据
wire w_i2c_done ; //I2C操作结束标志
wire w_i2c_ack ; //I2C应答标志 0:应答 1:未应答
wire w_i2c_rh_wl ; //I2C读写控制
wire [ 7:0] w_i2c_data_r; //I2C读出的数据
// reg define
reg [15:0] r_i2c_addr;
reg [7:0] r_i2c_data_w;
reg r_i2c_exec;
reg r_i2c_rh_wl;
reg r_i2c_ack;
// connect
assign w_i2c_addr = r_i2c_addr;
assign w_i2c_data_w = r_i2c_data_w;
assign w_i2c_exec = r_i2c_exec;
assign w_i2c_rh_wl = r_i2c_rh_wl;
assign w_i2c_ack = r_i2c_ack;
// --------------------------
// 启动信号边沿检测(防止持续触发)
// --------------------------
reg r_rpc_start_dly;
wire w_rpc_start_posedge; // 启动信号上升沿(真正的触发点)
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) begin
r_rpc_start_dly <= 1'b0;
end else begin
r_rpc_start_dly <= i_rpc_start; // 延迟一拍用于边沿检测
end
end
assign w_rpc_start_posedge = i_rpc_start && !r_rpc_start_dly; // 上升沿检测
// --------------------------
// 内部锁存寄存器(处理期间保持参数稳定)
// --------------------------
reg [31:0] r_method_latch;
reg [31:0] r_req_latch_0, r_req_latch_1, r_req_latch_2, r_req_latch_3;
wire w_iic_method= r_method_latch==`RPC_FUNC_IIC_READ||r_method_latch==`RPC_FUNC_IIC_WRITE;
// --------------------------
// RPC处理状态机
// --------------------------
localparam S_IDLE = 2'b00;
localparam S_INIT= 2'b01;
localparam S_PROCESSING = 2'b10;
localparam S_DONE = 2'b11;
reg [1:0] r_state;
reg [15:0] r_proc_cnt; // 模拟处理延迟(0~15周期)
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) begin
r_state <= S_IDLE;
r_proc_cnt <= 16'h0;
o_rpc_busy <= 1'b0;
o_rpc_done <= 1'b0;
r_method_latch <= 32'h0;
r_req_latch_0 <= 32'h0;
r_req_latch_1 <= 32'h0;
r_req_latch_2 <= 32'h0;
r_req_latch_3 <= 32'h0;
o_res_reg_0 <= 32'h0;
o_res_reg_1 <= 32'h0;
o_res_reg_2 <= 32'h0;
o_res_reg_3 <= 32'h0;
end else begin
case (r_state)
S_IDLE: begin
// 检测到启动信号上升沿,且外部数据有效,启动处理
if (w_rpc_start_posedge && i_rpc_valid) begin
o_rpc_done <= 1'b0; // 完成标志清0
// 锁存当前寄存器值(处理期间参数不变)
r_method_latch <= i_method_reg;
r_req_latch_0 <= i_req_reg_0;
r_req_latch_1 <= i_req_reg_1;
r_req_latch_2 <= i_req_reg_2;
r_req_latch_3 <= i_req_reg_3;
o_rpc_busy <= 1'b1; // 置位请求有效
r_state <= S_INIT; // 进入初始状态
r_proc_cnt <= 16'h0; // 重置延迟计数器
r_i2c_exec <= 1'b0;
end else begin
o_rpc_busy <= 1'b0;
r_state <= S_IDLE;
end
end
S_INIT: begin
if(r_proc_cnt==16'h0) begin
if(r_method_latch==`RPC_FUNC_IIC_READ) begin
r_i2c_addr<={8'h00, r_req_latch_0[7:0]}; // 高8位补0,使用低8位作为地址
r_i2c_rh_wl<=1;
r_i2c_data_w<=8'b0;
r_i2c_exec<=1;
end
else if(r_method_latch==`RPC_FUNC_IIC_WRITE) begin
r_i2c_addr<={8'h00, r_req_latch_0[7:0]}; // 高8位补0,使用低8位作为地址
r_i2c_rh_wl<=0;
r_i2c_data_w<=r_req_latch_0[15:8];
r_i2c_exec<=1;
end
end
r_proc_cnt<=r_proc_cnt+1;
//让r_i2c_exec拉高久一些
if(r_proc_cnt==16'd52) begin
r_proc_cnt <= 16'h0;
r_i2c_exec <= 1'b0;
r_state <= S_PROCESSING;
end
end
S_PROCESSING: begin
// 模拟处理延迟(例如10个时钟周期,可修改)
if ((r_proc_cnt >= 16'd9 && !w_iic_method)||(r_proc_cnt >= 16'd4 && w_i2c_done && w_iic_method)) begin
// 根据方法号执行不同处理(示例逻辑)
case (r_method_latch)
`RPC_FUNC_ECHO: begin // 方法0:返回请求参数
o_res_reg_0 <= r_req_latch_0;
o_res_reg_1 <= r_req_latch_1;
o_res_reg_2 <= r_req_latch_2;
o_res_reg_3 <= r_req_latch_3;
end
`RPC_FUNC_ADD: begin // 方法1:参数相加
o_res_reg_0[7:0] <= r_req_latch_0[7:0]+1;
o_res_reg_0[15:8] <= r_req_latch_0[15:8]+2;
o_res_reg_0[23:16] <= r_req_latch_0[23:16]+3;
o_res_reg_0[31:24] <= r_req_latch_0[31:24]+4;
end
`RPC_FUNC_IIC_READ: begin
o_res_reg_0[7:0] <= w_i2c_data_r;
o_res_reg_0[15:8] <= 8'd0;
o_res_reg_0[23:16] <= 8'd0;
o_res_reg_0[31:24] <= 8'd0;
end
`RPC_FUNC_IIC_WRITE: begin
o_res_reg_0[7:0] <= r_req_latch_0[7:0];
o_res_reg_0[15:8] <= 8'd0;
o_res_reg_0[23:16] <= 8'd0;
o_res_reg_0[31:24] <= 8'd0;
end
default: begin
o_res_reg_0 <= 32'h0;
o_res_reg_1 <= 32'h0;
o_res_reg_2 <= 32'h0;
o_res_reg_3 <= 32'h0;
end
endcase
r_state <= S_DONE;
end else begin
r_proc_cnt <= r_proc_cnt + 1'b1;
r_state <= S_PROCESSING;
end
end
S_DONE: begin
o_rpc_busy <= 1'b0; // 清除请求有效
o_rpc_done <= 1'b1; // 置位完成标志(通知结果就绪)
r_state <= S_IDLE; // 返回空闲状态,等待下一次启动
end
default: r_state <= S_IDLE;
endcase
end
end
//i2c驱动模块
i2c_master_dri #(
.P_SLAVE_ADDR (P_SLAVE_ADDR), //EEPROM从机地址
.P_CLK_FREQ (P_CLK_FREQ ), //模块输入的时钟频率
.P_I2C_FREQ (P_I2C_FREQ ) //IIC_SCL的时钟频率
) u_i2c_master_dri(
.i_clk (i_clk ),
.i_rst_n (i_rst_n ),
//i2c interface
.i_i2c_exec (w_i2c_exec ), //I2C触发执行信号
.i_bit_ctrl (P_BIT_CTRL ), //器件地址位控制(16b/8b)
.i_i2c_rh_wl (w_i2c_rh_wl ), //I2C读写控制信号
.i_i2c_addr (w_i2c_addr ), //I2C器件内地址
.i_i2c_data_w (w_i2c_data_w), //I2C要写的数据
.o_i2c_data_r (w_i2c_data_r), //I2C读出的数据
.o_i2c_done (w_i2c_done ), //I2C一次操作完成
.o_i2c_ack (w_i2c_ack ), //I2C应答标志
.o_scl (o_scl ), //I2C的SCL时钟信号
.i_sda_i (i_sda_i ),
.o_sda_o (o_sda_o ),
.o_sda_t (o_sda_t )
);
endmodule
i2c_master_dri.v
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
iobuf_wrapper.v
module iobuf_wrapper (
input wire I,
output wire O,
inout wire IO,
input wire T
);
IOBUF u_iobuf (.I(I), .O(O), .IO(IO), .T(T));
endmodule
readme.md
set rtl_dir "D:/workspace/gitee/ant_prj/ant/src/rtl/iic"
add_files $rtl_dir/rpc_processor.v
add_files $rtl_dir/uart_rx.v
add_files $rtl_dir/uart_tx.v
add_files $rtl_dir/vio_uart.v
add_files $rtl_dir/i2c_master_dri.v
add_files $rtl_dir/iobuf_wrapper.v
pin.xdc
#时序约束
create_clock -period 20.000 -name sys_clk [get_ports sys_clk]
#IO引脚约束
#----------------------系统时钟---------------------------
set_property -dict {PACKAGE_PIN U18 IOSTANDARD LVCMOS33} [get_ports sys_clk]
#----------------------系统复位---------------------------
set_property -dict {PACKAGE_PIN N16 IOSTANDARD LVCMOS33} [get_ports sys_rst_n]
set_property -dict {PACKAGE_PIN K14 IOSTANDARD LVCMOS33} [get_ports uart_rxd]
set_property -dict {PACKAGE_PIN M15 IOSTANDARD LVCMOS33} [get_ports uart_txd]
set_property -dict {PACKAGE_PIN E18 IOSTANDARD LVCMOS33} [get_ports iic_scl]
set_property -dict {PACKAGE_PIN F17 IOSTANDARD LVCMOS33} [get_ports iic_sda]
set_property -dict {PACKAGE_PIN H15 IOSTANDARD LVCMOS33} [get_ports led]
GoWin tang_nano_1k 测试
源文件
top_e2prom.v
module top_e2prom #(
// I2C驱动模块参数
parameter P_SLAVE_ADDR = 7'b1010000, // EEPROM从机地址
parameter P_CLK_FREQ = 26'd27_000_000,// 系统时钟频率(27MHz)
parameter P_I2C_FREQ = 18'd250_000, // I2C_SCL时钟频率(250KHz)
parameter P_BIT_CTRL = 1'b1, // 字地址位控制(1:16位 0:8位)
// E2PROM读写测试参数
parameter P_MAX_BYTE = 16'd256, // 读写测试的字节总数(0~255)
// LED指示模块参数
parameter P_LED_FLASH_CNT_MAX = 17'd125_000 // LED闪烁计数阈值(250ms@50MHz,实际随dri_clk变化)
) (
// 系统信号
input i_sys_clk ,// 系统时钟输入(27MHz)
input i_sys_rst_n ,// 系统复位信号(低有效)
// EEPROM物理接口
output o_iic_scl ,// EEPROM的SCL时钟线
inout io_iic_sda ,// EEPROM的SDA数据线(双向)
// 用户接口
output o_led // LED状态输出(低亮高灭,指示测试结果)
);
// Wire define(模块间交互信号,按功能分组)
// I2C驱动模块 → 其他模块
wire w_dri_clk ;// I2C操作驱动时钟(I2C_SCL的4倍频率)
wire w_i2c_done ;// I2C单次操作完成标志
wire w_i2c_ack ;// I2C应答标志(0:应答 1:未应答)
wire [7:0] w_i2c_data_r ;// I2C读出的数据
// E2PROM读写模块 → I2C驱动模块
wire w_i2c_exec ;// I2C触发执行信号
wire w_i2c_rh_wl ;// I2C读写控制信号(0:写 1:读)
wire [15:0] w_i2c_addr ;// I2C器件内地址
wire [7:0] w_i2c_data_w ;// I2C要写入的数据
// E2PROM读写模块 → LED指示模块
wire w_rw_done ;// E2PROM读写测试完成标志
wire w_rw_result ;// E2PROM读写测试结果(0:失败 1:成功)
// *****************************************************
// ** 模块例化
// *****************************************************
// 例化E2PROM读写测试模块
e2prom_rw #(
.P_MAX_BYTE (P_MAX_BYTE) // 读写测试的字节总数
) u_e2prom_rw (
// 系统信号
.i_clk (w_dri_clk) ,// 时钟(I2C驱动时钟)
.i_rst_n (i_sys_rst_n) ,// 复位信号
// I2C控制接口(输出到I2C驱动)
.o_i2c_exec (w_i2c_exec) ,// I2C触发执行信号
.o_i2c_rh_wl (w_i2c_rh_wl) ,// I2C读写控制信号
.o_i2c_addr (w_i2c_addr) ,// I2C器件内地址
.o_i2c_data_w (w_i2c_data_w) ,// I2C要写的数据
// I2C控制接口(输入来自I2C驱动)
.i_i2c_data_r (w_i2c_data_r) ,// I2C读出的数据
.i_i2c_done (w_i2c_done) ,// I2C单次操作完成
.i_i2c_ack (w_i2c_ack) ,// I2C应答标志
// 用户接口(输出到LED模块)
.o_rw_done (w_rw_done) ,// E2PROM读写测试完成
.o_rw_result (w_rw_result) // E2PROM读写测试结果
);
// 例化I2C主机驱动模块
i2c_master_dri #(
.P_SLAVE_ADDR (P_SLAVE_ADDR) ,// EEPROM从机地址
.P_CLK_FREQ (P_CLK_FREQ) ,// 模块输入时钟频率
.P_I2C_FREQ (P_I2C_FREQ) // I2C_SCL时钟频率
) u_i2c_dri (
// 系统信号
.i_clk (i_sys_clk) ,// 系统时钟输入
.i_rst_n (i_sys_rst_n) ,// 系统复位信号
// I2C控制接口(输入来自E2PROM读写模块)
.i_i2c_exec (w_i2c_exec) ,// I2C触发执行信号
.i_bit_ctrl (P_BIT_CTRL) ,// 字地址位控制(16b/8b,直接绑定参数)
.i_i2c_rh_wl (w_i2c_rh_wl) ,// I2C读写控制信号
.i_i2c_addr (w_i2c_addr) ,// I2C器件内地址
.i_i2c_data_w (w_i2c_data_w) ,// I2C要写的数据
// I2C控制接口(输出到E2PROM读写模块)
.o_i2c_data_r (w_i2c_data_r) ,// I2C读出的数据
.o_i2c_done (w_i2c_done) ,// I2C单次操作完成标志
.o_i2c_ack (w_i2c_ack) ,// I2C应答标志
// I2C物理接口(连接到EEPROM)
.o_scl (o_iic_scl) ,// I2C的SCL时钟信号
.io_sda (io_iic_sda) ,// I2C的SDA双向信号
// 用户接口(输出到E2PROM读写模块)
.o_dri_clk (w_dri_clk) // 驱动I2C操作的时钟
);
// 例化LED测试结果指示模块
rw_result_led #(
.P_LED_FLASH_CNT_MAX (P_LED_FLASH_CNT_MAX) // LED闪烁计数阈值
) u_rw_result_led (
// 系统信号
.i_clk (w_dri_clk) ,// 时钟(I2C驱动时钟)
.i_rst_n (i_sys_rst_n) ,// 复位信号
// 输入接口(来自E2PROM读写模块)
.i_rw_done (w_rw_done) ,// E2PROM读写测试完成
.i_rw_result (w_rw_result) ,// E2PROM读写测试结果
// 输出接口(连接到LED硬件)
.o_led (o_led) // LED状态输出
);
endmodule
e2prom_rw.v
module e2prom_rw #(
parameter P_WR_WAIT_TIME = 14'd5000, // EEPROM写操作间隔时间(ms级)
parameter P_MAX_BYTE = 16'd256 // 读写测试的字节总数(0~255)
) (
// 系统信号
input i_clk ,// 系统时钟输入
input i_rst_n ,// 系统复位信号(低有效)
// I2C控制接口(与i2c_master_dri交互)
output reg o_i2c_rh_wl ,// I2C读写控制信号(0:写 1:读)
output reg o_i2c_exec ,// I2C触发执行信号
output reg [15:0] o_i2c_addr ,// I2C器件内地址
output reg [7:0] o_i2c_data_w ,// I2C要写入的数据
input [7:0] i_i2c_data_r ,// I2C读出的数据
input i_i2c_done ,// I2C单次操作完成标志
input i_i2c_ack ,// I2C应答标志(0:应答 1:未应答)
// 用户接口
output reg o_rw_done ,// E2PROM读写测试完成标志
output reg o_rw_result // E2PROM读写测试结果(0:失败 1:成功)
);
// Reg define
reg [1:0] r_flow_cnt ; // 状态流控制寄存器(0:写等待 1:写完成 2:读触发 3:读校验)
reg [13:0] r_wait_cnt ; // 写操作间隔延时计数器
// *****************************************************
// ** main code
// *****************************************************
// E2PROM读写测试逻辑:先连续写入256字节数据,再读出校验,对比写入/读出值是否一致
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) begin
// 复位初始化所有寄存器
r_flow_cnt <= 2'd0;
o_i2c_rh_wl <= 1'b0;
o_i2c_exec <= 1'b0;
o_i2c_addr <= 16'd0;
o_i2c_data_w <= 8'd0;
r_wait_cnt <= 14'd0;
o_rw_done <= 1'b0;
o_rw_result <= 1'b0;
end
else begin
// 默认值赋值(避免综合器报警)
o_i2c_exec <= 1'b0;
o_rw_done <= 1'b0;
case (r_flow_cnt)
2'd0 : begin // 状态0:写操作间隔延时
r_wait_cnt <= r_wait_cnt + 14'b1; // 延时计数递增
// 延时达到设定值,准备触发单次写操作
if (r_wait_cnt == (P_WR_WAIT_TIME - 14'b1)) begin
r_wait_cnt <= 14'd0; // 复位延时计数器
// 判断是否完成256字节写入
if (o_i2c_addr == P_MAX_BYTE) begin
o_i2c_addr <= 16'd0; // 复位地址,准备读操作
o_i2c_rh_wl <= 1'b1; // 切换为读操作
r_flow_cnt <= 2'd2; // 跳转到读触发状态
end
else begin
r_flow_cnt <= r_flow_cnt + 2'b1; // 跳转到写执行状态
o_i2c_exec <= 1'b1; // 触发I2C写操作
end
end
end
2'd1 : begin // 状态1:等待单次写操作完成
// I2C单次写操作完成
if (i_i2c_done == 1'b1) begin
r_flow_cnt <= 2'd0; // 回到写延时状态
o_i2c_addr <= o_i2c_addr + 16'b1; // 地址递增(0~255)
o_i2c_data_w <= o_i2c_data_w + 8'b1; // 数据递增(0~255)
end
end
2'd2 : begin // 状态2:触发单次读操作
r_flow_cnt <= r_flow_cnt + 2'b1; // 跳转到读校验状态
o_i2c_exec <= 1'b1; // 触发I2C读操作
end
2'd3 : begin // 状态3:校验读出数据并判断测试结果
// I2C单次读操作完成
if (i_i2c_done == 1'b1) begin
// 异常判断:读出数据与写入值不一致 或 I2C无应答 → 测试失败
if ((o_i2c_addr[7:0] != i_i2c_data_r) || (i_i2c_ack == 1'b1)) begin
o_rw_done <= 1'b1;
o_rw_result <= 1'b0;
end
// 正常结束:完成256字节读写且全部校验正确 → 测试成功
else if (o_i2c_addr == (P_MAX_BYTE - 16'b1)) begin
o_rw_done <= 1'b1;
o_rw_result <= 1'b1;
end
// 继续读下一个字节
else begin
r_flow_cnt <= 2'd2; // 回到读触发状态
o_i2c_addr <= o_i2c_addr + 16'b1; // 地址递增
end
end
end
default : ; // 默认状态(防止综合器报警)
endcase
end
end
endmodule
rw_result_led.v
module rw_result_led #(
parameter P_LED_FLASH_CNT_MAX = 17'd125_000 // LED闪烁计数阈值(对应250ms@50MHz)
) (
// 系统信号
input i_clk ,// 系统时钟输入
input i_rst_n ,// 系统复位信号(低有效)
// 输入接口(与e2prom_rw模块交互)
input i_rw_done ,// E2PROM读写测试完成标志
input i_rw_result ,// E2PROM读写测试结果(0:失败 1:成功)
// 输出接口
output reg o_led // LED输出(低电平点亮,高电平熄灭)
);
// Reg define
reg r_rw_done_flag ; // 读写测试完成锁存标志(避免单次脉冲丢失)
reg [16:0] r_led_cnt ; // LED闪烁计数器
// *****************************************************
// ** main code
// *****************************************************
// 锁存读写测试完成标志(防止i_rw_done单次脉冲被漏采)
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) begin
r_rw_done_flag <= 1'b0;
end
else if (i_rw_done) begin // 检测到读写测试完成
r_rw_done_flag <= 1'b1;
end
end
// LED状态控制逻辑:
// - 测试完成前:LED熄灭(高电平)
// - 测试成功:LED常亮(低电平)
// - 测试失败:LED以2Hz频率闪烁(500ms周期,250ms亮/250ms灭)
always @(posedge i_clk or negedge i_rst_n) begin
if (!i_rst_n) begin
r_led_cnt <= 17'd0;
o_led <= 1'b1; // 复位后LED熄灭
end
else begin
if (r_rw_done_flag) begin // 读写测试已完成
if (i_rw_result) begin // 测试成功:LED常亮
o_led <= 1'b0;
r_led_cnt <= 17'd0; // 复位计数器
end
else begin // 测试失败:LED闪烁
r_led_cnt <= r_led_cnt + 17'd1;
if (r_led_cnt == (P_LED_FLASH_CNT_MAX - 17'd1)) begin
r_led_cnt <= 17'd0;
o_led <= ~o_led; // 翻转LED状态
end
end
end
else begin // 测试未完成:LED熄灭
o_led <= 1'b1;
r_led_cnt <= 17'd0;
end
end
end
endmodule
i2c_master_dri.v
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时钟频率
) (
// 系统信号
input i_clk ,// 系统时钟
input i_rst_n ,// 系统复位(低有效)
// I2C控制接口
input i_i2c_exec ,// I2C触发执行信号
input i_bit_ctrl ,// 字地址位控制(16b/8b)
input i_i2c_rh_wl ,// I2C读写控制信号
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:未应答)
// I2C物理接口
output reg o_scl ,// I2C的SCL时钟信号
inout io_sda ,// I2C的SDA信号(双向)
// 用户接口
output reg o_dri_clk // I2C操作驱动时钟
);
// Local parameter define (状态机/分频参数)
localparam S_IDLE = 8'b0000_0001; // 空闲状态
localparam S_SLADDR = 8'b0000_0010; // 发送器件地址
localparam S_ADDR16 = 8'b0000_0100; // 发送16位字地址
localparam S_ADDR8 = 8'b0000_1000; // 发送8位字地址
localparam S_DATA_WR = 8'b0001_0000; // 写数据(8bit)
localparam S_ADDR_RD = 8'b0010_0000; // 发送读操作器件地址
localparam S_DATA_RD = 8'b0100_0000; // 读数据(8bit)
localparam S_STOP = 8'b1000_0000; // 结束I2C操作
localparam L_CLK_DIVIDE = (P_CLK_FREQ / P_I2C_FREQ) >> 2'd2; // 驱动时钟分频系数
// Reg define
reg r_sda_dir ;// SDA方向控制(1:输出 0:输入)
reg r_sda_out ;// SDA输出寄存器
reg r_st_done ;// 状态完成标志
reg r_wr_flag ;// 读写标志(1:读 0:写)
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 ;// 写数据临时寄存器
reg [9:0] r_clk_cnt ;// 分频时钟计数寄存器
// Wire define
wire w_sda_in ;// SDA输入信号
// *****************************************************
// ** main code
// *****************************************************
// SDA双向信号控制
assign io_sda = r_sda_dir ? r_sda_out : 1'bz; // 1:输出数据 0:高阻(输入)
assign w_sda_in = io_sda; // 采集SDA输入信号
// 生成I2C操作的驱动时钟(dri_clk = SCL时钟的4倍频率)
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 == (L_CLK_DIVIDE[8:1] - 9'd1)) begin
r_clk_cnt <= 10'd0;
o_dri_clk <= ~o_dri_clk;
end
else begin
r_clk_cnt <= r_clk_cnt + 10'b1;
end
end
// 三段式状态机:1.同步时序描述状态转移
always @(posedge o_dri_clk or negedge i_rst_n) begin
if (!i_rst_n) begin
r_cur_state <= S_IDLE;
end
else begin
r_cur_state <= r_next_state;
end
end
// 三段式状态机:2.组合逻辑判断状态转移条件
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 begin
r_next_state = S_IDLE;
end
end
S_SLADDR: begin // 发送器件地址
if (r_st_done) begin
if (i_bit_ctrl) begin // 16位字地址
r_next_state = S_ADDR16;
end
else begin // 8位字地址
r_next_state = S_ADDR8;
end
end
else begin
r_next_state = S_SLADDR;
end
end
S_ADDR16: begin // 发送16位字地址高8位
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) begin // 写操作
r_next_state = S_DATA_WR;
end
else begin // 读操作
r_next_state = S_ADDR_RD;
end
end
else begin
r_next_state = S_ADDR8;
end
end
S_DATA_WR: begin // 写8位数据
if (r_st_done) begin
r_next_state = S_STOP;
end
else begin
r_next_state = S_DATA_WR;
end
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位数据
if (r_st_done) begin
r_next_state = S_STOP;
end
else begin
r_next_state = S_DATA_RD;
end
end
S_STOP: begin // 停止I2C操作
if (r_st_done) begin
r_next_state = S_IDLE;
end
else begin
r_next_state = S_STOP;
end
end
default: r_next_state = S_IDLE;
endcase
end
// 三段式状态机:3.时序电路描述状态输出
always @(posedge o_dri_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'd0;
r_st_done <= 1'b0;
r_data_r <= 8'd0;
o_i2c_data_r <= 8'd0;
r_wr_flag <= 1'b0;
r_addr_t <= 16'd0;
r_data_wr_t <= 8'd0;
end
else 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'd0;
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; // 起始信号:SDA拉低
7'd3 : o_scl <= 1'b0; // SCL拉低准备发送数据
7'd4 : r_sda_out <= P_SLAVE_ADDR[6];
7'd5 : o_scl <= 1'b1; // SCL拉高,从机采样
7'd7 : o_scl <= 1'b0; // SCL拉低,准备下一位
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) begin // 无应答
o_i2c_ack <= 1'b1;
end
end
7'd39: begin // 复位计数,准备下状态
o_scl <= 1'b0;
r_cnt <= 7'd0;
end
default: ;
endcase
end
S_ADDR16: begin // 发送16位字地址高8位
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) begin
o_i2c_ack <= 1'b1;
end
end
7'd35: begin
o_scl <= 1'b0;
r_cnt <= 7'd0;
end
default: ;
endcase
end
S_ADDR8: begin // 发送字地址低8位
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) begin
o_i2c_ack <= 1'b1;
end
end
7'd35: begin
o_scl <= 1'b0;
r_cnt <= 7'd0;
end
default: ;
endcase
end
S_DATA_WR: begin // 写8位数据
case (r_cnt)
7'd0 : begin // 切换为输出,发送数据
r_sda_dir <= 1'b1;
r_sda_out <= r_data_wr_t[7];
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) begin
o_i2c_ack <= 1'b1;
end
end
7'd35: begin
o_scl <= 1'b0;
r_cnt <= 7'd0;
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) begin
o_i2c_ack <= 1'b1;
end
end
7'd39: begin
o_scl <= 1'b0;
r_cnt <= 7'd0;
end
default: ;
endcase
end
S_DATA_RD: begin // 读8位数据
case (r_cnt)
7'd0 : r_sda_dir <= 1'b0; // 切换为输入
7'd1 : begin // 采样第7位
r_data_r[7] <= w_sda_in;
o_scl <= 1'b1;
end
7'd3 : o_scl <= 1'b0;
7'd5 : begin // 采样第6位
r_data_r[6] <= w_sda_in;
o_scl <= 1'b1;
end
7'd7 : o_scl <= 1'b0;
7'd9 : begin // 采样第5位
r_data_r[5] <= w_sda_in;
o_scl <= 1'b1;
end
7'd11: o_scl <= 1'b0;
7'd13: begin // 采样第4位
r_data_r[4] <= w_sda_in;
o_scl <= 1'b1;
end
7'd15: o_scl <= 1'b0;
7'd17: begin // 采样第3位
r_data_r[3] <= w_sda_in;
o_scl <= 1'b1;
end
7'd19: o_scl <= 1'b0;
7'd21: begin // 采样第2位
r_data_r[2] <= w_sda_in;
o_scl <= 1'b1;
end
7'd23: o_scl <= 1'b0;
7'd25: begin // 采样第1位
r_data_r[1] <= w_sda_in;
o_scl <= 1'b1;
end
7'd27: o_scl <= 1'b0;
7'd29: begin // 采样第0位
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'd0;
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;
r_sda_out <= 1'b0;
end
7'd1 : o_scl <= 1'b1; // SCL拉高
7'd3 : r_sda_out <= 1'b1; // SDA拉高,停止信号
7'd15: r_st_done <= 1'b1; // 状态完成
7'd16: begin // 操作完成标志
r_cnt <= 7'd0;
o_i2c_done <= 1'b1;
end
default: ;
endcase
end
endcase
end
end
endmodule
vio_uart_prj.cst
IO_LOC "i_sys_clk" 47;
IO_PORT "i_sys_clk" IO_TYPE=LVCMOS33 PULL_MODE=UP;
IO_LOC "i_sys_rst_n" 13;
IO_PORT "i_sys_rst_n" IO_TYPE=LVCMOS33 PULL_MODE=UP;
IO_LOC "o_iic_scl" 40;
IO_PORT "o_iic_scl" IO_TYPE=LVCMOS33 PULL_MODE=UP DRIVE=8;
IO_LOC "io_iic_sda" 41;
IO_PORT "io_iic_sda" IO_TYPE=LVCMOS33 PULL_MODE=UP;
IO_LOC "o_led" 9;
IO_PORT "o_led" IO_TYPE=LVCMOS33 PULL_MODE=UP DRIVE=8;
vio_uart_prj.sdc
create_clock -name i_sys_clk -period 37.037 -waveform {0 18.518} [get_ports {i_sys_clk}]
火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。
更多推荐
所有评论(0)