写在前面
FPGA实现IIC协议读写EEPROM相关文章:
IIC通信协议
【FPGA】FPGA实现IIC协议读写EEPROM(一) ----- IIC接口驱动实现
【FPGA】FPGA实现IIC协议读写EEPROM(二) -----EEPROM读写控制模块实现
【FPGA】FPGA实现IIC协议读写EEPROM(三) ----- 汇总篇
在前面几篇文章中介绍了IIC通信协议的相关时序、IIC接口驱动以及EEPROM读写控制模块的实现,在本项目中所使用的开发板型号:Cyclone IV E (EP4CE6F17C8),EEPROM型号:24LC04B,本文将对IIC协议读写EEPROM工程进行综合。
下面介绍此工程需要实现哪些功能,以此进行功能模块划分。
通过上述功能分析,我们不难把工程进行模块划分,系统框图划分如下图所示:
EEPROM读写控制状态机实现参考文章:EEPROM读写控制状态机
IIC接口驱动状态机参考文章:IIC接口驱动状态机
顶层模块verilog代码如下:
// **************************************************************
// Author: Zhang JunYi
// Create Date: 2022.11.15
// Design Name: i2c_eeprom
// Module Name: top
// Target Device: Cyclone IV E (EP4CE6F17C8), EEPROM(24LC04B)
// Tool versions: Quartus Prime 18.1
// Description: I2C读写EEPROM工程顶层设计模块
// **************************************************************
module top (input clk ,input rst_n ,// key_filterinput [1:0] key_in ,// input wr_req ,// input rd_req ,// uartinput uart_rxd ,output uart_txd ,// eeprominout sda ,output scl ,// 数码管output [5:0] seg_sel ,output [7:0] seg_dig
);wire wr_req ;wire rd_req ;wire req ;wire [7:0] wr_dout ; wire [3:0] cmd ;wire [7:0] rdout ;wire rdout_vld ;wire rw_done ;wire sda_in ;wire sda_out ;wire sda_out_en ;wire [1:0] rw_flag ;// uart_rxwire [7:0] rx_dout ;wire rx_dout_vld ;// uart_txwire [7:0] tx_din ;wire tx_din_vld ;wire ready ;assign sda = sda_out_en ? sda_out : 1'bz ;assign sda_in = sda ;key_filter u_key_filter1 (/*input */.clk (clk ),/*input */.rst_n (rst_n ),/*input */.key_in (key_in[0] ),/*output reg */.key_out (wr_req ));key_filter u_key_filter2 (/*input */.clk (clk ),/*input */.rst_n (rst_n ),/*input */.key_in (key_in[1] ),/*output reg */.key_out (rd_req ));uart_rx u_uart_rx (/*input */.clk (clk ),/*input */.rst_n (rst_n ),/*input */.uart_rx (uart_rxd ), // 接收到的串口数据
/**//*output [7:0] */.rx_dout (rx_dout ), // 串行数据转换成并行数据后输出给control模块/*output */.rx_dout_vld (rx_dout_vld));uart_tx u_uart_tx (/*input */.clk (clk ),/*input */.rst_n (rst_n ),/*// control*//*input [7:0] */.tx_din (tx_din ),/*input */.tx_din_vld (tx_din_vld),/*output */.ready (ready ), // 给control模块的握手信号,表示可以接收数据进行发送 /*// 上位机*//*output */.uart_tx (uart_txd ));eeprom_ctrl eeprom_ctrl (/*input */.clk (clk ),/*input */.rst_n (rst_n ),/*// key_filter*//*input */.wr_req (wr_req ), // 读请求/*input */.rd_req (rd_req ), // 写请求/*// uart_rx*//*input [7:0] */.rx_din (rx_dout ), // uart接收到的要发送的数据/*input */.rx_din_vld (rx_dout_vld ), // 数据有效标志/*// uart_tx*//*output [7:0] */.tx_dout (tx_din ), // 读回的数据给到串口发送模块/*output */.tx_dout_vld (tx_din_vld ), // 数据有效/*input m */.ready (ready ),/*// i2c_interface*//*input [7:0] */.rdin (rdout ), // 从I2C接口读回的数据/*input */.rdin_vld (rdout_vld ),/*input */.rw_done (rw_done ), // 读写一字节数据完成标志/*output */.req (req ), // 读写请求/*output [7:0] */.wr_dout (wr_dout ), // 要发送的数据/*output [3:0] */.cmd (cmd ),/*output [1:0] */.rw_flag (rw_flag ) // 读写标志 00:空闲 01:写 10:读 );i2c_interface u_i2c_interface (/*input */.clk (clk ),/*input */.rst_n (rst_n ),/*// eeprom_ctrl*//*input */.req (req ), // 读写请求/*input [7:0] */.wr_din (wr_dout ), // 需要发送的一字节数据/*input [3:0] */.cmd (cmd ), // 控制命令组合/*output [7:0] */.rdout (rdout ), // 读取的数据/*output */.rdout_vld (rdout_vld ), // 读取数据有效标志/*output */.rw_done (rw_done ), // 读写一字节完成标志/*// EEPROM*//*input */.sda_in (sda_in ),/*output */.sda_out (sda_out ),/*output */.sda_out_en (sda_out_en ),.scl (scl ));seg_driver u_seg_driver (/*input */.clk (clk ),/*input */.rst_n (rst_n ),/*// eeprom_ctrl*//*// input [7:0]din , () // 要显示的数据*//*// input din_vld , () // 显示数据有效标志*//*input [1:0] */.rw_flag (rw_flag ), // 读写标志/*// 数码管*//*output [5:0] */.seg_sel (seg_sel ),/*output [7:0] */.seg_dig (seg_dig ) );endmodule
EEPROM读写控制模块verilog代码如下:
// **************************************************************
// Author: Zhang JunYi
// Create Date: 2022.11.15
// Design Name: i2c_eeprom
// Module Name: eeprom_ctrl
// Target Device: Cyclone IV E (EP4CE6F17C8) , EEPROM(24LC04B)
// Tool versions: Quartus Prime 18.1
// Description: 使用I2C协议读写EEPROM驱动控制模块
// **************************************************************
module eeprom_ctrl (input clk ,input rst_n ,// key_filterinput wr_req , // 写请求input rd_req , // 读请求// uart_rxinput [7:0] rx_din , // uart接收到的要发送的数据input rx_din_vld , // 数据有效标志// uart_txoutput [7:0] tx_dout , // 读回的数据给到串口发送模块output tx_dout_vld , // 数据有效input ready ,// i2c_interfaceinput [7:0] rdin , // 从I2C接口读回的数据input rdin_vld ,input rw_done , // 读写一字节数据完成标志output req , // 读写请求output [7:0] wr_dout , // 要发送的数据output [3:0] cmd ,// seg_driveroutput [1:0] rw_flag // 读写标志 00:空闲 01:写 10:读
);// 参数定义// 状态机参数定义localparam IDLE = 7'b000_0001 ,SEND_WRREQ = 7'b000_0010 , // 发送写请求SEND_RDREQ = 7'b000_0100 , // 发送读请求WAIT_WRDONE = 7'b000_1000 , // 等待写数据完成WAIT_RDDONE = 7'b001_0000 , // 等待读数据完成WR_DONE = 7'b010_0000 , // 写数据完成RD_DONE = 7'b100_0000 ; // 读数据完成// 信号定义reg [6:0] state_c ;reg [6:0] state_n ;reg [3:0] byte_num ; // 字节数reg [3:0] cnt_byte ; // 字节计数器wire add_cnt_byte ;wire end_cnt_byte ;reg [7:0] wrdata ; // 寄存将要发送的数据reg [3:0] command ; // 寄存将要发送的命令reg rw_req ;reg [1:0] rwflag ;// 状态转移条件wire idle2sendwrreq ;wire idle2sendrdreq ;wire sendwrreq2waitwrdone ;wire sendrdreq2waitrddone ;wire waitwrdone2wrdone ;wire waitwrdone2sendwrreq ;wire waitrddone2rddone ;wire waitrddone2sendrdreq ;wire wrdone2idle ;wire rddone2idle ;reg [7:0] rxdata ; // 串口接收数据寄存reg [7:0] txdata ; // 串口发送数据寄存// wrfifo// wire wrfifo_empty ;// wire wrfifo_full ;// wire [2:0] wrfifo_usedw ;// wire [7:0] wrfifo_q ;// wire wrfifo_rdreq ;// // rdfifo// wire rdfifo_empty ;// wire rdfifo_full ;// wire [2:0] rdfifo_usedw ;// wire [7:0] rdfifo_q ;// wire rdfifo_rdreq ;// 状态机always @(posedge clk or negedge rst_n)beginif(!rst_n)beginstate_c <= IDLE ;endelse beginstate_c <= state_n ;endendalways @(*)begincase (state_c)IDLE: beginif(idle2sendwrreq)state_n = SEND_WRREQ ;else if(idle2sendrdreq)state_n = SEND_RDREQ ;elsestate_n = state_c ;endSEND_WRREQ: beginif(sendwrreq2waitwrdone)state_n = WAIT_WRDONE ;elsestate_n = state_c ;endSEND_RDREQ: beginif(sendrdreq2waitrddone)state_n = WAIT_RDDONE ;elsestate_n = state_c ;endWAIT_WRDONE: beginif(waitwrdone2wrdone)state_n = WR_DONE ;else if(waitwrdone2sendwrreq)state_n = SEND_WRREQ ;elsestate_n = state_c ;endWAIT_RDDONE: beginif(waitrddone2rddone)state_n = RD_DONE ;else if(waitrddone2sendrdreq)state_n = SEND_RDREQ ;elsestate_n = state_c ;endWR_DONE: beginif(wrdone2idle)state_n = IDLE ;elsestate_n = state_c ;endRD_DONE: beginif(rddone2idle)state_n = IDLE ;elsestate_n = state_c ;enddefault: state_n = IDLE ;endcaseend// 状态转移assign idle2sendwrreq = state_c == IDLE && wr_req ;assign idle2sendrdreq = state_c == IDLE && rd_req ; assign sendwrreq2waitwrdone= state_c == SEND_WRREQ && (1'b1) ;assign sendrdreq2waitrddone= state_c == SEND_RDREQ && (1'b1) ;assign waitwrdone2wrdone = state_c == WAIT_WRDONE && end_cnt_byte ;assign waitwrdone2sendwrreq= state_c == WAIT_WRDONE && (cnt_byte <= 2) && rw_done ;assign waitrddone2rddone = state_c == WAIT_RDDONE && end_cnt_byte ;assign waitrddone2sendrdreq= state_c == WAIT_RDDONE && (cnt_byte <= 3) && rw_done ;assign wrdone2idle = state_c == WR_DONE && (1'b1) ;assign rddone2idle = state_c == RD_DONE && (1'b1) ;// byte_numalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginbyte_num <= 0 ;endelse if(wr_req)beginbyte_num <= 3 ;endelse if(rd_req)beginbyte_num <= 4 ;end// else if(wrdone2idle | rddone2idle)begin// byte_num <= 0 ;// endend// cnt_bytealways @(posedge clk or negedge rst_n)beginif(!rst_n)begincnt_byte <= 0 ;endelse if(add_cnt_byte)beginif(end_cnt_byte)begincnt_byte <= 0 ;end else begin cnt_byte <= cnt_byte + 1 ;end endend assign add_cnt_byte = rw_done ;assign end_cnt_byte = add_cnt_byte && (cnt_byte == byte_num - 1) ;// rw_reqalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginrw_req <= 1'b0 ;endelse if(idle2sendwrreq | idle2sendrdreq | sendwrreq2waitwrdone | sendrdreq2waitrddone)beginrw_req <= 1'b1 ;endelse beginrw_req <= 1'b0 ;endend// wrdata commandalways @(*)beginif(state_c == SEND_WRREQ || state_c == WAIT_WRDONE)begincase (cnt_byte)0: begin wrdata = 8'b1010_0000 ;command = 4'b1010 ; end // 写控制字1: begin wrdata = 8'b1101_1001 ;command = 4'b1000 ; end // 写地址2: begin wrdata = rxdata ;command = 4'b1001 ; end // 写数据default: begin wrdata = 8'b0 ;command = 4'b0 ;endendcaseendelse if(state_c == SEND_RDREQ || state_c == WAIT_RDDONE)begincase (cnt_byte)0: begin wrdata = 8'b1010_0000 ;command = 4'b1010 ; end // 写控制字1: begin wrdata = 8'b1101_1001 ;command = 4'b1000 ; end // 读地址2: begin wrdata = 8'b1010_0001 ;command = 4'b1010 ; end // 写读控制字3: begin wrdata = 8'b0 ;command = 4'b0101 ; end // 读数据default: begin wrdata = 8'b0 ;command = 4'b0 ;end endcaseendelse beginwrdata = wrdata ;command = command ;endend// rwflagalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginrwflag <= 0 ;endelse if(wr_req)beginrwflag <= 2'b01 ;endelse if(rd_req)beginrwflag <= 2'b10 ;endend// rxdataalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginrxdata <= 0 ;endelse if(rx_din_vld)beginrxdata <= rx_din ;endend// txdataalways @(posedge clk or negedge rst_n)beginif(!rst_n)begintxdata <= 0 ; endelse if(rdin_vld)begintxdata <= rdin ;endend// wrfifo例化// wrfifo wrfifo_inst (// .aclr ( ~rst_n ),// .clock ( clk ),// .data ( rx_din ),// .rdreq ( wrfifo_rdreq ),// .wrreq ( rx_din_vld ),// .empty ( wrfifo_empty ),// .full ( wrfifo_full ),// .q ( wrfifo_q ),// .usedw ( wrfifo_usedw )// );// assign wrfifo_rdreq = wr_req && ~wrfifo_empty ;// // rdfifo例化// rdfifo rdfifo_inst (// .aclr ( ~rst_n ),// .clock ( clk ),// .data ( rdin ),// .rdreq ( rdfifo_rdreq ),// .wrreq ( rdin_vld ),// .empty ( rdfifo_empty ),// .full ( rdfifo_full ),// .q ( rdfifo_q ),// .usedw ( rdfifo_usedw )// );// assign rdfifo_rdreq = rddone2idle && ~rdfifo_empty && ready;// 输出assign req = rw_req ;assign cmd = command ;assign wr_dout = wrdata ;assign rw_flag = rwflag ;assign tx_dout = txdata ;assign tx_dout_vld = rddone2idle ;endmodule
EEPROM读写控制模块verilog代码如下:
// **************************************************************
// Author: Zhang JunYi
// Create Date: 2022.11.15
// Design Name: i2c_eeprom
// Module Name: i2c_interface
// Target Device: Cyclone IV E (EP4CE6F17C8), EEPROM(24LC04B)
// Tool versions: Quartus Prime 18.1
// Description: I2C接口驱动模块
// **************************************************************
`include "param.v"module i2c_interface (input clk ,input rst_n ,// eeprom_ctrlinput req , // 读写请求input [7:0] wr_din , // 需要发送的一字节数据input [3:0] cmd , // 控制命令组合output [7:0] rdout , // 读取的数据output rdout_vld , // 读取数据有效标志output rw_done , // 读写一字节完成标志// EEPROMinput sda_in ,output sda_out ,output sda_out_en ,output scl
);// 参数定义localparam IDLE = 7'b000_0001 ,START = 7'b000_0010 ,WR_DATA = 7'b000_0100 ,RD_DATA = 7'b000_1000 ,REC_ACK = 7'b001_0000 ,SEND_ACK= 7'b010_0000 ,STOP = 7'b100_0000 ;// 信号定义reg [6:0] state_c ;reg [6:0] state_n ;reg [7:0] cnt_scl ; // scl周期计数器wire add_cnt_scl ;wire end_cnt_scl ;reg [3:0] bit_num ; // bit数reg [3:0] cnt_bit ; // bit计数器wire add_cnt_bit ;wire end_cnt_bit ;reg [7:0] dout_data ; // 发送数据寄存reg [3:0] command ; // 控制命令寄存reg scl_dout ; // SDA寄存 reg sda_dout ;reg sda_dout_en ;reg [7:0] rx_data ; // 接收读回的数据reg ack_flag ; // ack响应标志// 状态转移条件wire idle2start ;wire idle2wrdata ;wire idle2rddata ;wire start2wrdata ;wire start2rddata ;wire wrdata2recack ;wire rddata2sendack ;wire recack2idle ;wire recack2stop ;wire sendack2idle ;wire sendack2stop ;wire stop2idle ;// 状态机always @(posedge clk or negedge rst_n)beginif(!rst_n)beginstate_c <= IDLE ;endelse beginstate_c <= state_n ;endendalways @(*)begincase (state_c)IDLE: beginif(idle2start)state_n = START ;else if(idle2wrdata)state_n = WR_DATA ;else if(idle2rddata)state_n = RD_DATA ;elsestate_n = state_c ;endSTART: beginif(start2wrdata)state_n = WR_DATA ;else if(start2rddata)state_n = RD_DATA ;elsestate_n = state_c ;endWR_DATA: beginif(wrdata2recack)state_n = REC_ACK ;elsestate_n = state_c ;endRD_DATA: beginif(rddata2sendack)state_n = SEND_ACK ;elsestate_n = state_c ;endREC_ACK: beginif(recack2idle)state_n = IDLE ;else if(recack2stop)state_n = STOP ;elsestate_n = state_c ;endSEND_ACK: beginif(sendack2idle)state_n = IDLE ;else if(sendack2stop)state_n = STOP ;elsestate_n = state_c ;endSTOP: beginif(stop2idle)state_n = IDLE ;elsestate_n = state_c ;enddefault: state_n = IDLE ;endcaseend// 状态转移条件assign idle2start = state_c == IDLE && req && (cmd & `STA) ;assign idle2wrdata = state_c == IDLE && req && (cmd & `WRITE) ;assign idle2rddata = state_c == IDLE && req && (cmd & `READ) ;assign start2wrdata = state_c == START && end_cnt_bit && (command & `WRITE) ; assign start2rddata = state_c == START && end_cnt_bit && (command & `READ) ; assign wrdata2recack = state_c == WR_DATA && end_cnt_bit ;assign rddata2sendack = state_c == RD_DATA && end_cnt_bit ;assign recack2idle = state_c == REC_ACK && end_cnt_bit && (command & `STO) == 0 ;assign recack2stop = state_c == REC_ACK && end_cnt_bit && (command & `STO) ;assign sendack2idle = state_c == SEND_ACK && end_cnt_bit && (command & `STO) == 0 ;assign sendack2stop = state_c == SEND_ACK && end_cnt_bit && (command & `STO) ;assign stop2idle = state_c == STOP && end_cnt_bit ;// cnt_scl 200KHz 一个SCL周期为250个系统时钟周期always @(posedge clk or negedge rst_n)beginif(!rst_n)begincnt_scl <= 0 ;endelse if(add_cnt_scl)beginif(end_cnt_scl)begincnt_scl <= 0 ;end else begin cnt_scl <= cnt_scl + 1 ;end endend assign add_cnt_scl = state_c != IDLE ;assign end_cnt_scl = add_cnt_scl && (cnt_scl == `SCL_PERIOD - 1) ;// bit_numalways @(*)beginif(state_c == RD_DATA || state_c == WR_DATA)beginbit_num = 8 ;endelse beginbit_num = 1 ;endend// cnt_bitalways @(posedge clk or negedge rst_n)beginif(!rst_n)begincnt_bit <= 0 ;endelse if(add_cnt_bit)beginif(end_cnt_bit)begincnt_bit <= 0 ;end else begin cnt_bit <= cnt_bit + 1 ;end endend assign add_cnt_bit = end_cnt_scl ;assign end_cnt_bit = add_cnt_bit && (cnt_bit == bit_num - 1) ;// dout_dataalways @(posedge clk or negedge rst_n)beginif(!rst_n)begindout_data <= 0 ;endelse if(req)begindout_data <= wr_din ;endend// commandalways @(posedge clk or negedge rst_n)beginif(!rst_n)begincommand <= 0 ;endelse if(req)begincommand <= cmd ;endend// scl_doutalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginscl_dout <= 1'b1 ;endelse if(idle2start | idle2wrdata | idle2rddata)beginscl_dout <= 1'b0 ;endelse if(add_cnt_scl && cnt_scl == `SCL_HALF)beginscl_dout <= 1'b1 ;endelse if(end_cnt_scl && ~stop2idle)beginscl_dout <= 1'b0 ;endend// sda_dout_enalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginsda_dout_en <= 1'b0 ;endelse if(idle2start || idle2wrdata || rddata2sendack || sendack2stop || recack2stop)beginsda_dout_en <= 1'b1 ;endelse if(idle2rddata || start2rddata || wrdata2recack || stop2idle)beginsda_dout_en <= 1'b0 ;endend// sda_doutalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginsda_dout <= 1'b1 ;endelse if(state_c == START)beginif(cnt_scl == `HIGH_HALF)beginsda_dout <= 1'b0 ;endelse if(cnt_scl == `LOW_HALF)beginsda_dout <= 1'b1 ;endendelse if(state_c == WR_DATA && cnt_scl == `LOW_HALF)beginsda_dout <= dout_data[7 - cnt_bit] ;endelse if(state_c == SEND_ACK && cnt_scl == `LOW_HALF)beginsda_dout <= (command & `STO) ? 1'b1 : 1'b0 ;endelse if(state_c == STOP)beginif(cnt_scl == `LOW_HALF)begin sda_dout <= 1'b0;endelse if(cnt_scl == `HIGH_HALF)begin sda_dout <= 1'b1; end endelse if(wrdata2recack | rddata2sendack)beginsda_dout <= 1'b1; endend// rx_dataalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginrx_data <= 0 ;endelse if(state_c == RD_DATA && cnt_scl == `HIGH_HALF)beginrx_data[7 - cnt_bit] <= sda_in ;endend// ack_flagalways @(posedge clk or negedge rst_n)beginif(!rst_n)beginack_flag <= 1'b0 ;endelse if(state_c == REC_ACK && cnt_scl == `HIGH_HALF)beginack_flag <= ~sda_in ;endelse beginack_flag <= 1'b0 ;endend// 输出assign rdout = rx_data ; assign rdout_vld = rddata2sendack ;assign rw_done = stop2idle | sendack2idle | recack2idle ;assign sda_out = sda_dout ; assign sda_out_en = sda_dout_en ;assign scl = scl_dout ;endmodule
参数配置文件param.v如下:
// IIC时钟参数
`define SCL_PERIOD 250
`define SCL_HALF 125
`define LOW_HALF 65
`define HIGH_HALF 190// 控制命令
`define WRITE 4'b1000 // 写
`define READ 4'b0100 // 读
`define STA 4'b0010 // 起始位
`define STO 4'b0001 // 停止位// UART参数配置文件
`define BAUD_RATE_115200`define CLK_FREQ 50_000_000`ifdef BAUD_RATE_9600`define BAUD `CLK_FREQ / 9600
`elsif BAUD_RATE_19200`define BAUD `CLK_FREQ / 19200
`elsif BAUD_RATE_38400`define BAUD `CLK_FREQ / 38400
`elsif BAUD_RATE_57600`define BAUD `CLK_FREQ / 57600
`elsif BAUD_RATE_115200`define BAUD `CLK_FREQ / 115200
`endif
按键消抖、数码管显示模块比较简单,这里就不过多赘述。
串口数据收发参考文章:UART串口通信
1、仿真文件如下:
`timescale 1ns/1psmodule test_tb ();reg tb_clk ;reg tb_rst_n ;reg [1:0] key_in;wire sda ;wire scl ;wire [7:0] seg_dig ;wire [5:0] seg_sel ;reg [7:0] data ;reg data_vld ;wire ready ;// PCwire uart_txd ;wire uart_rxd ;uart_tx uart_tx_pc (/*input */.clk (tb_clk ),/*input */.rst_n (tb_rst_n ),/*// control*//*input [7:0] */.tx_din (data ),/*input */.tx_din_vld (data_vld),/*output */.ready (ready ), // 给control模块的握手信号,表示可以接收数据进行发送 /*// 上位机*//*output */.uart_tx (uart_txd ));top u_top (/*input */.clk (tb_clk ),/*input */.rst_n (tb_rst_n ),/*// key_filter*///input */.wr_req (wr_req ),//input */.rd_req (rd_req ),/*input [1:0] */.key_in (key_in ),// uart/*input */.uart_rxd (uart_txd ),/*input */.uart_txd (uart_rxd ),/*// eeprom*//*inout */.sda (sda ),/*output */.scl (scl ),// 数码管/*output [5:0] */.seg_sel (seg_sel ),/*output [7:0] */.seg_dig (seg_dig ) );M24LC04B u_M24LC04B(.A0 (0 ),.A1 (0 ),.A2 (0 ),.WP (0 ),.SDA (sda ),.SCL (scl ),.RESET (~tb_rst_n ));parameter CYCLE = 20;defparam u_top.u_key_filter1.DELAY = 10 ;defparam u_top.u_key_filter2.DELAY = 10 ;always #(CYCLE / 2) tb_clk = ~tb_clk;initial begintb_clk = 1'b1 ;tb_rst_n = 1'b1 ;key_in = 0 ;data = 0 ;data_vld = 1'b0 ;#(CYCLE * 2);#2;tb_rst_n = 1'b0 ;#(CYCLE * 2);tb_rst_n = 1'b1 ;#(CYCLE * 20);Send(8'ha9);key_in[0] = 1'b1 ;#(CYCLE * 20) ;key_in[0] = 1'b0 ;#(CYCLE * 100 * 100 * 30) ;key_in[1] = 1'b1 ;#(CYCLE * 20) ;key_in[1] = 1'b0 ;#(CYCLE * 100 * 100 * 10) ;$stop ;endtask Send;input [7:0] send_data ;begindata = send_data ;data_vld = 1'b1 ;# CYCLE ;data_vld = 1'b0 ;// @(posedge ready)# (CYCLE*10) ;endendtaskendmodule
2、仿真结果
注意:
1. 24LC04B读数据只能读到0,无法读1,当读1的时候显示的是高阻状态。
2. 仿真的时候写操作完成后,延时5ms再开始读操作。
总结:此工程主要实现IIC单字节读写EEPROM的操作,感兴趣的小伙伴可以尝试一下页写和其他几种方式的读操作。