//////////////////////////////////////////////////////////////////////////////////
// Company: 武汉芯路恒科技有限公司
// Engineer: www.corecourse.cn
// 
// Create Date: 2019/05/01 00:00:00
// Design Name: 
// Module Name: I2C_tb
// Project Name: I2C
// Target Devices: XC7A35T-2FGG484I
// Tool Versions: Vivado 2018.3
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////

`timescale 1ns/1ns
`define CLK_PERIOD 20

//仿真用例选择，选择其中一种，屏蔽另外2种进行仿真
//`define TEST_M24LC64    //单独使用24LC64模型
//`define TEST_M24LC04    //单独使用24LC04模型
`define TEST_ALL        //使用24LC04和24LC64混合模型，模拟挂载两个不同设备

`ifdef TEST_ALL
  `define TEST_M24LC64
  `define TEST_M24LC04
`endif

module I2C_tb;

	reg        clk;          //系统时钟
	reg        reset_n;      //系统复位信号
  
  reg[1:0]   addr_byte_num;
  
  reg [6:0]  device_addr;
  reg [5:0]  burst_len;
	reg [15:0] mem_addr;     //I2C器件寄存器地址

	reg        wr_en;        //I2C器件写使能
	reg [7:0]  wr_data;      //I2C器件写数据
	wire       wr_data_req;  //I2C器件写数据有效标志位
	reg        rd_en;        //I2C器件读使能
	wire[7:0]  rd_data;      //I2C器件读数据
	wire       rd_data_valid;//I2C器件读数据有效标志位
  
  wire       pro_state;
	wire       one_pro_done; //对I2C器件读写完成标识位
  
	wire       Scl;          //I2C时钟线    
	wire       Sda;          //I2C数据线

  pullup(Sda); //对Sda信号上拉
  
  I2C I2C
  (
    .clk           (clk          ),
    .reset_n       (reset_n      ),

    .addr_byte_num (addr_byte_num),	

    .device_addr   (device_addr  ),	
    .burst_len     (burst_len    ),
    .mem_addr      (mem_addr     ),

    .wr_en         (wr_en        ),
    .wr_data       (wr_data      ),
    .wr_data_req   (wr_data_req  ),

    .rd_en         (rd_en        ),
    .rd_data       (rd_data      ),
    .rd_data_valid (rd_data_valid),

    .pro_state     (pro_state    ),
    .one_pro_done  (one_pro_done ),

    .Scl           (Scl          ),
    .Sda           (Sda          )
  );

`ifdef TEST_M24LC64

	M24LC64 M24LC64(
		.A0(1'b0), 
		.A1(1'b0), 
		.A2(1'b0), 
		.WP(1'b0), 
		.SDA(Sda), 
		.SCL(Scl), 
		.RESET(!reset_n)
	);
  
`endif

`ifdef  TEST_M24LC04
	
	M24LC04B M24LC04(
		.A0(1'b1),
		.A1(1'b0),
		.A2(1'b0),
		.WP(1'b0),
		.SDA(Sda),
		.SCL(Scl),
		.RESET(!reset_n)
	);
  
`endif
	
	//系统时钟产生
	initial clk = 1'b1;
	always #(`CLK_PERIOD/2)clk = ~clk;
	
	initial
	begin
		reset_n       = 0;
    addr_byte_num = 1;
    device_addr   = {4'b1010,3'b000};
    burst_len     = 0;
		mem_addr      = 0;
		wr_en         = 0;
		wr_data       = 0;
		rd_en         = 0;

		#(`CLK_PERIOD*200 + 1)
		reset_n        = 1;
		#200;

`ifdef TEST_M24LC64   //仿真验证24LC64模型

		device_addr   = {4'b1010,3'b000};
    addr_byte_num = 2;
    burst_len     = 4;

		//写入2组数据，每组4个，数据从0开始递增，寄存器地址从13'h200开始递增
    write_data_task(2,0,13'h200);
    
		#200;
    //读出刚写入的数据，读4次，每次2个，寄存器地址从13'h200开始递增
    burst_len     = 2;
    read_data_task(4,13'h200);
    #200;
    
`endif

`ifdef TEST_M24LC04   //仿真验证24LC04模型

		device_addr   = {4'b1010,3'b001};
    addr_byte_num = 1;
    burst_len     = 4;

		//写入2组数据，每组4个，数据从10开始递增，寄存器地址从8'h20开始递增
    write_data_task(2,10,8'h20);
    
		#200;
    //读出刚写入的数据，读1次，每次8个，寄存器地址从8'h20开始递增
    burst_len     = 8;
    read_data_task(1,8'h20);
    #200;
		
`endif
	
		$stop;
	end  

  //写EEPROM数据任务
  task write_data_task;
  
    input [7:0] wr_num;
    input [7:0] wr_data_begin;
    input [15:0]wr_addr_begin;
    
    begin
      wr_data  = wr_data_begin;
      mem_addr = wr_addr_begin;
    
      repeat(wr_num)begin
        wr_en    = 1'b1;      
        #(`CLK_PERIOD);
        wr_en    = 1'b0;

        repeat(burst_len)begin   //在写数据有效前给待写入数据
          @(posedge wr_data_req)
          wr_data = wr_data + 1;
        end
        
        @(posedge one_pro_done);
        #6000000;;
        mem_addr = mem_addr + burst_len;
      end
    end
  
  endtask

  //读EEPROM数据任务
  task read_data_task;

    input [7:0] rd_num;
    input [15:0]rd_addr_begin;
  
    begin
      mem_addr = rd_addr_begin;
      
      repeat(rd_num)begin
        rd_en     = 1'b1;      
        #(`CLK_PERIOD);
        rd_en     = 1'b0;
        
        @(posedge one_pro_done);
        #6000000;;
        mem_addr = mem_addr + burst_len;
      end
    end

  endtask

endmodule 