特权同学

TestBench的IIC从机设计

0
阅读(2378)

这个testbench的编写是基于之前日志里的基于FPGA的IIC通信而做的,原来只是做了简单的主机写随机地址EEPROM, 然后主机读随机地址EEPROM,都是只有一个字节的,做的是最简单的通信。所以这次testbench设计IIC从机也是和前面的相对应 的。

    Testbench代码:

 

`timescale 1ns/1ns

module testbench();

 

// DATE:     Wed Aug 15 08:35:56 2007

// TITLE:    

// MODULE:   iic_top09

// DESIGN:   iic_top09

// FILENAME: iic_top09

// PROJECT:  top_dram

// VERSION:  Version

 

 

// Inputs

    reg clk;

    reg rst_n;

    reg sw1;

    reg sw2;

 

 

// Outputs

    wire scl;

    wire led_d2;

    wire led_d3;

    wire led_d4;

    wire led_d5;

 

 

// Bidirs

    wire sda;

 

 

// Instantiate the UUT

    iic_top09 d (

        .clk(clk),

        .rst_n(rst_n),

        .sw1(sw1),

        .sw2(sw2),

        .scl(scl),

        .sda(sda),

        .led_d2(led_d2),

        .led_d3(led_d3),

        .led_d4(led_d4),

        .led_d5(led_d5)

        );

 

    initial begin

        clk = 0;

        forever #10 clk = ~clk;//产生50MHz的时钟

    end

 

    initial begin

        rst_n = 0;  //复位

        #2000 rst_n = 1;    //复位持续2us后结束

    end

 

    initial begin

        sw1 = 1;

        sw2 = 1;

        #20000000   sw1 = 0;    //delay 20ms,iic write

        #20000000 sw1 = 1;  //delay 20ms,iic idle

        #20000000 sw2 = 0;  //delay 20ms,iic read

        #20000000 sw2 = 1;  //delay 20ms,iic idle

    end

 

    reg start;  //start=1:通信中,start=0:通信结束

    initial start = 0;

    always @ (sda)  begin

        if(scl && !sda) start = 1;  //接收到起始位

        if(scl && sda) start = 0;   //接收到结束位

    end

 

    reg sda_link;   //sda控制位

    reg sda_out;    //

    reg[7:0] device_addr;   //器件地址

    reg[7:0] eeprom_addr;   //数据地址

    reg[7:0] eeprom_data;   //数据

    reg[7:0] addr0_data,addr1_data;//0地址和1地址数据存储器

    reg lsb_data;   //

    initial begin

        device_addr = 8'd0;

        sda_link = 1'b0;

//      lsb_data = 1'b0;

    end

    always @ (posedge start) begin  //数据通信中

        repeat (8) begin    //接收器件地址

            @ (posedge scl);

            #10 device_addr = device_addr << 1;

            device_addr[0] = sda;

        end

        if(device_addr != 8'b10100000) begin

            $display ("device address ERROR!");

            $stop;

        end

        @ (posedge scl);

        sda_out = 0;    //ack      

        sda_link = 1;

        @ (negedge scl);

        sda_link = 0;

        repeat (8) begin    //接收数据地址

            @ (posedge scl);

            #10 eeprom_addr = eeprom_addr << 1;

            eeprom_addr[0] = sda;

        end

        @ (posedge scl);

        sda_out = 0;    //ack      

        sda_link = 1;

        @ (negedge scl);

        sda_link = 0;

        @ (posedge scl);

        lsb_data = sda;

        @ (negedge scl);

        if(lsb_data == sda) write_eeprom;

        else read_eeprom;              

    end

 

    task write_eeprom;

        begin

        eeprom_data[0] = lsb_data;

        repeat (7) begin

            @ (posedge scl);

            #10 eeprom_data = eeprom_data << 1;

            eeprom_data[0] = sda;

        end

        # 10;

        case (eeprom_addr) //存储数据到相应地址,本测试只仿真0地 址和1地址

            0:  addr0_data = eeprom_data;

            1: addr1_data = eeprom_data;

            default:    ;

            endcase

        @ (posedge scl);

        sda_out = 0;    //ack

        sda_link = 1;

        @ (negedge scl);

        sda_link = 0;

        end

    endtask

 

    reg[7:0] addr_data;//

    task read_eeprom;

        begin

    //      device_addr[0] = lsb_data;

            repeat (8) begin    //接收器件地址

                @ (posedge scl);

                #10 device_addr = device_addr << 1;

                device_addr[0] = sda;

            end

            if(device_addr != 8'b10100001) begin

                $display ("device address ERROR!");

                $stop;

            end

            @ (posedge scl);

            sda_out = 0;    //ack      

            sda_link = 1;      

            case (eeprom_addr)

                0: addr_data = addr0_data;

                1: addr_data = addr1_data;

                default: ;

                endcase

            repeat (8) begin

                @ (posedge scl);

                sda_out = addr_data[7];

                #10 addr_data = addr_data << 1;

            end

            @ (posedge scl);

            sda_out = 0;    //ack

            @ (negedge scl);

            sda_link = 0;  

        end

    endtask

 

    assign sda = sda_link ? sda_out : 1'bz;

 

endmodule

 

仿真波形:

    前面是主机写,后面是主机收,重点可以看应答位以及byte_data1。(byte_data1是主机 写入EEPROM的数据,byte_data2是 主机读出的写入数据)。

 


    这个图是放大了的写入数据过程(观察sda的变化):

 

 


    这个图是放大了的读出数据的过程: