Semiconductor, Embedded/0. RTL, Synthesis, PnR, Simulation

RTL 설계 - I2C (Master / Slave - Simulation, Code)

잇(IT) 2024. 8. 17. 15:02

https://insoobaik.tistory.com/672

위 I2C 이론을 기반으로 RTL 코드를 설계할 것이다.


- Simulation

- Master Write / Slave Read 동작

(현재 Master와 Slave 신호를 둘 다 보기 때문에 둘 사이에 동일하게 동작하는 신호의 경우 위와 같이 동일한 2개의 신호가 나오는 것 처럼 보일 것이다.)

Master에서 Address 포함 Write 동작에 대한 전체 Simulation은 위와 같다.

 

1. Start Condition

SCL이 1인 상태에서 SDA가 0으로 떨어지는 구간은 Start Condition을 나타내고 I2C 프로토콜이 동작하기 시작하는 구간에 해당한다.

bitcount, state, pulse의 값이 동작하기 시작한다.

 

2. Address & (Master) Data write / Ack

State를 통해서도 확인할 수 있지만 위 동작을 보게되면 Start Condition 이후 처음 8bit(첫번째 파란색 선까지) {add(2)(8bit 중 상위 7bit), op(0)(마지막 bit)} => 00000010이 전달된다.

sda_en(Master, Slave가 Data를 전달할 때 사용하는 Flag)가 각자 전달 타이밍에 맞게 변하는 것을 확인할 수 있다.

처음 Addr 8bit를 전달하기 위해서는 Master가 sda를 사용하고, Addr에 대한 ACK 신호를 전달하기 위해서는 Slave가 sda를 점유해야 한다.

또한 Master에서 전달할 Data 값이 1이기 때문에 처음 8bit를 통해 Addr 및 Read, Write를 결정하고 Slave로부터 응답 신호를 전달 받으면 다음 8bit에 Data 1을 전달하게 된다.

 

3. State,  ACK, STOP

State(파란색 네모)를 보게 되면 Start Condition, Read or Write(8bit), ACK, Read or Write(8bit), ACK, Stop 순서로 변하는 것을 확인할 수 있다.
이는 I2C 프로토콜의 통신 방식과 동일하게 흘러가는 것을 확인할 수 있다.

각자 Data를 전송할 때는 sda_en(빨간색) 신호가 High를 Data를 전달 받을 때는 sda_en이 Low 신호를 전달하는 것을 볼 수 있다. 이는 sda라는 Data bus 하나를 가지고 Data를 주고 받기 때문이다.
8bit Addr, ACK1, 8bit Data, ACK2를 마치게 되면 마지막으로 scl이 1인 상태가 유지되고, sda가 0에서 1로 변하는 구간을 볼 수 있다. 이 구간이 Data I2C 프로토콜의 한 주기가 끝나는 Stop Condition(노란색) 부분에 해당하게 된다.

 

4. Data Write

w_mem(memory에 값을 입력하는 구간) High 신호일 때 Master에서 보낸 Addr 자리에 전달된 Data가 저장된 것을 확인할 수 있다.


- Master Read / Slave Send 동작

1. Start Condition

 

Start Condition의 경우 Master Write와 동일하게 신호를 전달한다.

 

2. Address & (Master) Data Read / Ack

 

 

Addr로 4의 주소를 전달하고 OP code는 Master Read이기 때문에 High 신호를 받아 처음 8bit는 {addr(0000100), op(1)} => 9의 값을 전달한다.

현재 Memory의 4번째 자리에는 04의 값이 저장되어 있고, 처음 Addr를 전달하는 8bit 이후 ACK 신호를 통해 Slave가 확인되면 4번째 자리에 있는 04의 값을 Master로 전달한다.

두번째 Data를 전달하는 8bit를 보게되면 00000100(4)의 값을 전달하고 있는 것을 확인할 수 있다.

 

3. State,  ACK, STOP

 

 

기본적으로 Start, 8bit(Addr), ACK1, 8bit(Data), ACK2, Stop은 동일하지만 Master Read의 경우 State는 물론 Flag 신호와 8bit(Data) 이후 ACK2 신호에 있어서 변화가 발생한다.

Master Read의 경우 Slave에서 sda 버스에 데이터를 Master로 전달하기 때문에 Slave의 sda_en Flag가 High 신호를 가지게 된다.

처음 ACK 신호의 경우 ADDR을 구분하기 위한 8bit이기 때문에 Slave에서 ACK신호를 전달하지만 Master Read의 경우 Slave에서 Data를 전달하기 때문에 ACK 신호를 Master에서 전달해야 한다.

ACK2는 Master에서 Slave로 전달하게 되고, 기존의 Slave에서 Master로 전달하는 ACK 신호는 Low(0)을 전달하면 응답 신호로 보지만 Master가 Slave에게 전달하는 ACK 신호의 경우 NACK로 1의 신호를 전달하게 된다.


4. Data Read

 

Data Read 의 경우 처음 Simulation에서 보았듯이 Memory에 ADDR에 해당하는 Data가 8bit를 통해 Master로 전달된 것을 확인할 수 있었다.


- Verilog Code

- Master Code

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2024/08/16 15:16:09
// Design Name: 
// Module Name: i2c_master
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module i2c_master 
//    #(parameter idle = 0, parameter start = 1, parameter write_addr = 2,
//    parameter ack_1 = 3, parameter write_data = 4, parameter read_data = 5, 
//    parameter stop = 6, parameter ack_2 = 7, parameter master_ack = 8)
    (
    input clk, rst, newd,
    input [6:0] addr,
    input op,
    inout sda,
    output scl,
    input [7:0] din,
    output [7:0] dout,
    output reg busy, ack_err, done
    );
    
    // temporary
    reg scl_t = 0;
    reg sda_t = 0;
    
    parameter sys_freq = 40000000; // 40MHz
    parameter i2c_freq = 100000; // 100k
    
    // 클럭 주기를 의미 4등분 할 것이기 때문에 4로 지정
    parameter clk_count4 = {sys_freq/i2c_freq}; // 400
    parameter clk_count1 = clk_count4/4; // 100
    
    integer count1 = 0;
    reg i2c_clk = 0;
    
    // i2c 동기화 주기를 4로 나누고 각 구간을 의미
    reg [1:0] pulse = 0;
    
    always @(posedge clk) 
    begin
        if(rst) 
        begin
            pulse <= 0;
            count1 <= 0;
        end
        // busy == 0이면 동작중이 아니다
        else if(busy == 1'b0) 
        begin
            pulse <= 0;
            count1 <= 0;
        end
        // i2c 주기의 pulse 동작 관련 코드
        else if(count1 == clk_count1 - 1) 
        begin
            pulse <= 1;
            count1 <= count1 + 1;
        end
        else if(count1 == clk_count1*2 -1) 
        begin
            pulse <= 2;
            count1 <= count1 + 1;
        end
        else if(count1 == clk_count1*3 -1) 
        begin
            pulse <= 3;
            count1 <= count1 + 1;
        end
        else if(count1 == clk_count1*4 -1) 
        begin
            pulse <= 0;
            count1 <= 0;
        end
        else 
        begin
         count1 <= count1 + 1;
        end
    end
    
    //----------
    reg [3:0] bitcount = 0;
    reg [7:0] data_addr = 0, data_tx = 0;
    reg r_ack = 0;
    reg [7:0] rx_data = 0;
    reg sda_en = 0;
    
//    reg [3:0] state;

    typedef enum logic [3:0] {idle = 0, start = 1, write_addr = 2, ack_1 = 3, write_data = 4, read_data = 5, stop = 6, ack_2 =7, master_ack = 8} state_type;
    
    state_type state = idle;
    
    always @(posedge clk)
    begin
        if(rst) 
        begin
            bitcount <= 0;
            data_addr <= 0;
            data_tx <= 0;
            scl_t <= 1;
            sda_t <= 1;
            state <= idle;
            busy <= 1'b0;
            ack_err <= 1'b0;
            done <= 1'b0;
        end
        else
        begin
            case(state)
                // --- idle
                idle : 
                begin
                    done <= 1'b0;
                    if(newd == 1'b1) 
                    begin
                        data_addr <= {addr, op};
                        data_tx <= din;
                        busy <= 1'b1;
                        state <= start;
                        ack_err <= 1'b0;
                    end
                    else 
                    begin
                        data_addr <= 0;
                        data_tx <= 0;
                        busy <= 1'b0;
                        state <= idle;
                        ack_err <= 1'b0;
                    end
                end
                // --- start
                start : 
                begin
                    sda_en <= 1'b1; // send start to slave
                    // start condition scl == 1 / sda == 1 -> 0
                    case(pulse)
                        0 : begin scl_t <= 1'b1; sda_t <= 1'b1; end
                        1 : begin scl_t <= 1'b1; sda_t <= 1'b1; end
                        2 : begin scl_t <= 1'b1; sda_t <= 1'b0; end
                        3 : begin scl_t <= 1'b1; sda_t <= 1'b0; end
                    endcase
                    
                    if(count1 == clk_count1 * 4 -1) 
                    begin
                        state <= write_addr;
                        scl_t <= 1'b0;
                    end
                    else state <= start;
                end
                
                // --- write_addr
                write_addr : 
                begin
                    sda_en <= 1'b1;
                    if(bitcount <= 7) 
                    begin
                    // data bit가 전송될 때, SDA는 SCL이 Low 상태일 때 값을 설정하고,
                    // SCL이 High 상태일 때 값이 읽힌다.
                        case(pulse)
                        0 : begin scl_t <= 1'b0; sda_t <= 1'b0; end
                        1 : begin scl_t <= 1'b0; sda_t <= data_addr[7 - bitcount]; end
                        2 : begin scl_t <= 1'b1; end
                        3 : begin scl_t <= 1'b1; end
                        endcase
                        
                        if(count1 == clk_count1*4 -1) 
                        begin
                            state <= write_addr;
                            scl_t <= 1'b0;
                            bitcount <= bitcount + 1;
                        end
                        else 
                        begin
                            state <= write_addr;
                        end
                    end
                    else
                    begin
                        state <= ack_1;
                        bitcount <= 0;
                        // Slave에서 Master로 보내는 것이기 떄문에 en = 0
                        sda_en <= 1'b0;
                    end
                end
                
               
            
            //--- write_data
            write_data :
            begin
                if(bitcount <= 7)
                begin
                    case(pulse)
                        0 : begin scl_t <= 1'b0; end
                        1 : begin scl_t <= 1'b0; sda_en <= 1'b1; sda_t <= data_tx[7-bitcount]; end
                        2 : begin scl_t <= 1'b1; end
                        3 : begin scl_t <= 1'b1; end
                    endcase
                    if(count1 == clk_count1*4 - 1)
                    begin
                        state <= write_data;
                        scl_t <= 1'b0;
                        bitcount <= bitcount + 1;
                    end
                    else
                    begin
                        state <= write_data;
                    end
                end
                else
                begin
                    state <= ack_2;
                    bitcount <= 0;
                    sda_en <= 1'b0;
                end
            end
            
            //---read data
            read_data : 
            begin
                sda_en <= 1'b0;
                if(bitcount <= 7)
                begin
                    case(pulse)
                        0 : begin scl_t <= 1'b0; sda_t <= 1'b0; end
                        1 : begin scl_t <= 1'b0; sda_t <= 1'b0; end
                        2 : begin scl_t <= 1'b1; rx_data[7:0] <= (count1 == 200) ? {rx_data[6:0], sda} : rx_data; end
                        3 : begin scl_t <= 1'b1; end
                    endcase
                    if(count1 == clk_count1*4 - 1)
                    begin
                        state <= read_data;
                        scl_t <= 1'b0;
                        bitcount <= bitcount + 1;
                    end
                    else
                    begin
                        state <= read_data;
                    end
                end
                else
                begin
                    state <= master_ack;
                    bitcount <= 0;
                    sda_en <= 1'b1;
                end
            end
            
            //---master_ack
            master_ack :
            begin
                sda_en <= 1'b1;
                case(pulse)
                    0 : begin scl_t <= 1'b0; sda_t <= 1'b1; end
                    1 : begin scl_t <= 1'b0; sda_t <= 1'b1; end
                    2 : begin scl_t <= 1'b1; sda_t <= 1'b1; end
                    3 : begin scl_t <= 1'b1; sda_t <= 1'b1; end
                endcase
                
                if(count1 == clk_count1*4 - 1)
                begin
                    sda_t <= 1'b0;
                    state <= stop;
                    sda_en <= 1'b1;
                end
                else
                begin
                    state <= master_ack;
                end
            end
            
            //---ack_2
            ack_2 :
            begin
                sda_en <= 1'b0;
                case(pulse)
                    0 : begin scl_t <= 1'b0; sda_t <= 1'b0; end
                    1 : begin scl_t <= 1'b0; sda_t <= 1'b0; end
                    2 : begin scl_t <= 1'b1; sda_t <= 1'b0; r_ack <= 1'b0; end
                    3 : begin scl_t <= 1'b1; end
                endcase
                if(count1 == clk_count1*4 - 1)
                begin
                    sda_t <= 1'b0;
                    sda_en <= 1'b1;
                    if(r_ack == 1'b0)
                    begin
                        state <= stop;
                        ack_err <= 1'b0;
                    end
                    else
                    begin
                        state <= stop;
                        ack_err <= 1'b1;
                    end
                end
                else
                begin
                    state <= ack_2;
                end
            end
            
            //---stop
            stop :
            begin
                sda_en <= 1'b1;
                case(pulse)
                    0 : begin scl_t <= 1'b1; sda_t <= 1'b0; end
                    1 : begin scl_t <= 1'b1; sda_t <= 1'b0; end
                    2 : begin scl_t <= 1'b1; sda_t <= 1'b1; end
                    3 : begin scl_t <= 1'b1; sda_t <= 1'b1; end
                endcase
                if(count1 == clk_count1*4 - 1)
                begin
                    state <= idle;
                    scl_t <= 1'b0;
                    busy <= 1'b0;
                    sda_en <= 1'b1;
                    done <= 1'b1;
                end
                else state <= stop;
            end
            
            //---default
            default : state <= idle;
            
            endcase
        end
    end
    
    assign sda = (sda_en == 1) ? (sda_t == 0) ? 1'b0 : 1'b1 : 1'bz;
    assign scl = scl_t;
    assign dout = rx_data;
    
endmodule

 

 

- Slave Code

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2024/08/17 12:26:10
// Design Name: 
// Module Name: i2c_slave
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module i2c_slave(
    input scl, clk, rst,
    inout sda,
    output reg ack_err, done
    );
    
    typedef enum logic [3:0] {idle = 0, read_addr = 1, send_ack1 = 2, send_data = 3, master_ack = 4, read_data = 5, send_ack2 = 6, wait_p = 7, detect_stop = 8} state_type;
    state_type state = idle;
    
    reg [7:0] mem[0:127];
    reg [7:0] r_addr;
    reg [6:0] addr;
    reg r_mem = 0;
    reg w_mem = 0;
    reg [7:0] dout;
    reg [7:0] din;
    reg sda_t;
    reg sda_en;
    reg [3:0] bitcnt = 0;
    
    // mem 초기화
    always @(posedge clk)
    begin
        if(rst)
        begin
            for(int i = 0; i < 128; i++)
            begin
                mem[i] = i;
            end
            dout <= 8'h0;
        end
        else if(r_mem == 1'b1)
        begin
            dout <= mem[addr];
        end
        else if(w_mem == 1'b1)
        begin
            mem[addr] <= din;
        end
    end
    
    //--- pulse
    parameter sys_freq = 40000000; // 40MHz
    parameter i2c_freq = 100000; // 100k
    
    // 클럭 주기를 의미 4등분 할 것이기 때문에 4로 지정
    parameter clk_count4 = {sys_freq/i2c_freq}; // 400
    parameter clk_count1 = clk_count4/4; // 100
    
    integer count1 = 0;
    reg i2c_clk = 0;
    
    // i2c 동기화 주기를 4로 나누고 각 구간을 의미
    reg [1:0] pulse = 0;
    reg busy;
    
    always @(posedge clk) 
    begin
        if(rst) 
        begin
            pulse <= 0;
            count1 <= 0;
        end
        // busy == 0이면 동작중이 아니다
        else if(busy == 1'b0) 
        begin
        // busy == 1'b0일 때 master, slave가 최초 연결하는 타이밍이다.
        // master에서 4개의 pulse로 나눴고, start condition이 3번째 pulse에서 동작하기 때문이다.
            pulse <= 2;
            count1 <= 202;
        end
        // i2c 주기의 pulse 동작 관련 코드
        else if(count1 == clk_count1 - 1) 
        begin
            pulse <= 1;
            count1 <= count1 + 1;
        end
        else if(count1 == clk_count1*2 -1) 
        begin
            pulse <= 2;
            count1 <= count1 + 1;
        end
        else if(count1 == clk_count1*3 -1) 
        begin
            pulse <= 3;
            count1 <= count1 + 1;
        end
        else if(count1 == clk_count1*4 -1) 
        begin
            pulse <= 0;
            count1 <= 0;
        end
        else 
        begin
         count1 <= count1 + 1;
        end
    end
    
    reg scl_t;
    wire start;
    
    // 동기화를 위해 사용한다.
    always @(posedge clk)
    begin
        scl_t <= scl;
    end
    
    //scl이 0에서 1로 변할때 start
    assign start = ~scl & scl_t;
    
    reg r_ack;
    
    always @(posedge clk)
    begin
        if(rst)
        begin
            bitcnt <= 0;
            state <= idle;
            r_addr <= 7'b0000000;
            sda_en <= 1'b0;
            sda_t <= 1'b0;
            addr <= 0;
            r_mem <= 0;
            din <= 8'h00;
            ack_err <= 0;
            done <= 1'b0;
            busy <= 1'b0;
        end
        else
        begin
            case(state)
            //---idle
            idle :
            begin
            // start condition을 만족하는지 확인
                if(scl == 1'b1 && sda == 1'b0)
                begin
                    busy <= 1'b1;
                    state <= wait_p;
                end
                else
                begin
                    state <= idle;
                end
            end
            
            //---wait_p
            wait_p :
            begin
            // start condition이 끝나는 지점
                if(pulse == 2'b11 && count1 == 399)
                begin
                    state <= read_addr;
                end
                else
                begin
                    state <= wait_p;
                end
            end
            
            //---read_addr
            // 처음 slave는 우선 master가 전달하는 address를 필수로 확인해야 한다.
            read_addr :
            begin
                sda_en <= 1'b0; //read addr to slave
                if(bitcnt <= 7)
                begin
                    case(pulse)
                        0 : begin end
                        1 : begin end
                        // 3번째 pulse scl High에서 값을 읽기 때문이다.
                        2 : begin r_addr <= (count1 == 200) ? {r_addr[6:0], sda} : r_addr; end
                        3 : begin end
                    endcase
                    if(count1 == clk_count1*4 - 1)
                    begin
                        state <= read_addr;
                        bitcnt <= bitcnt + 1;
                    end
                    else
                    begin
                        state <= read_addr;
                    end
                end
                else
                begin
                    state <= send_ack1;
                    bitcnt <= 0;
                    // ack 신호는 slave가 master에게 보내는 신호이기 때문에 sda_en을 1로 변경 ack 신호를 보내야 하기 때문이다.
                    sda_en <= 1'b1;
                    //master로부터 전달받은 8비트 중 앞에 6비트는 address bit
                    addr <= r_addr[7:1];
                end
            end
            
            
            
            //---read_data
            read_data :
            begin
                sda_en <= 1'b0;
                if(bitcnt <= 7)
                begin
                    case(pulse)
                        0 : begin end
                        1 : begin end
                        // SCL이 High일 때 sda를 통해 전달된 데이터를 Shift register로 데이터를 임시 저장소에 저장한다.
                        2 : begin din <= (count1 == 200) ? {din[6:0], sda} : din; end
                        3 : begin end
                    endcase
                    if(count1 == clk_count1*4 - 1)
                    begin
                        state <= read_data;
                        bitcnt <= bitcnt + 1;
                    end
                    else
                    begin
                        state <= read_data;
                    end
                end
                else
                begin
                    state <= send_ack2;
                    bitcnt <= 0;
                    //master로부터 data를 읽고 ack 신호를 보내기 위해 sda_en 상태를 변경한다.
                    sda_en <= 1'b1;
                    //w_mem 상태에 따라 memory에 값을 읽거나 쓴다.
                    w_mem <= 1'b1;
                end
            end
            
            //---send_ack2
            send_ack2 :
            begin
                case(pulse)
                //SCL Low일 때 값을 변경한다.
                //master에서 scl low일 때 sda를 통해 slave에서 0의 값이 들어오면 ack라는 것을 알 수 잇다.
                    0 : begin sda_t <= 1'b0; end
                    //scl low일 때 master, slave에서 값을 읽지 않기 때문에 값 전달과 관련된 상태를 변경한다.
                    1 : begin w_mem <= 1'b0; end
                    2 : begin end
                    3 : begin end
                endcase
                if(count1 == clk_count1*4 - 1)
                begin
                //Stop Condition을 만들기 위한 상태
                    state <= detect_stop;
                    sda_en <= 1'b0;
                end
                else
                begin
                    state <= send_ack2;
                end
            end
            
            //---send_data
            send_data :
            begin
                sda_en <= 1'b1;
                if(bitcnt <= 7)
                begin
                    r_mem <= 1'b0;
                    case(pulse)
                        0 : begin end
                        1 : begin sda_t <= (count1 == 100) ? dout[7-bitcnt] : sda_t; end
                        2 : begin end
                        3 : begin end
                    endcase
                    if(count1 == clk_count1*4 - 1)
                    begin
                        state <= send_data;
                        bitcnt <= bitcnt + 1;
                    end
                    else
                    begin
                        state <= send_data;
                    end
                end
                else
                begin
                    state <= master_ack;
                    bitcnt <= 0;
                    sda_en <= 1'b0;
                end
            end
            
            //---master_ack
            master_ack :
            begin
                case(pulse)
                    0 : begin end
                    1 : begin end
                    2 : begin r_ack <= (count1 == 200) ? sda : r_ack; end
                    3 : begin end
                endcase
                if(count1 == clk_count1*4 - 1)
                begin
                    if(r_ack == 1'b1)
                    begin
                        ack_err <= 1'b0;
                        state <= detect_stop;
                        sda_en <= 1'b0;
                    end
                    else
                    begin
                        ack_err <= 1'b1;
                        state <= detect_stop;
                        sda_en <= 1'b0;
                    end
                end
                else
                begin
                    state <= master_ack;
                end
            end
            
            //---detect_stop;
            detect_stop :
            begin
                if(pulse == 2'b11 && count1 == 399)
                begin
                state <= idle;
                busy <= 1'b0;
                done <= 1'b1;
                end
                else state <= detect_stop;
            end
            
            //---default
            default :
            state <= idle;
            
            endcase
        end
    end
    
    //verilog에서 z high impedance 상태는 pin이 외부로부터 들어오는 신호를 받는 경우나 외부로 신호를 내보내지 않는 경우에 사용된다.
    assign sda = (sda_en == 1'b1) ? sda_t : 1'bz;
    
endmodule

 

- Top Code

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2024/08/17 13:41:12
// Design Name: 
// Module Name: i2c_top
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////

module i2c_top(
    input clk, rst, newd, op,
    input [6:0] addr,
    input [7:0] din,
    output [7:0] dout,
    output busy, ack_err,
    output done
    );
    
    wire sda, scl;
    wire ack_errm, ack_errs;
    
    i2c_master master(clk, rst, newd, addr, op, sda, scl, din, dout, busy, ack_errm, done);
    i2c_slave slave(scl, clk, rst, sda, ack_errs,);
    
    assign ack_err = ack_errs | ack_errm;
endmodule

 

- Test Bench Code

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2024/08/17 13:44:22
// Design Name: 
// Module Name: i2c_top_tb
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module i2c_top_tb;
 
    reg clk = 0, rst = 0, newd = 0, op;
    reg [6:0] addr;
    reg [7:0] din;
    wire [7:0] dout;
    wire busy, ack_err;
    wire done;

    i2c_top dut (clk,rst, newd, op, addr, din, dout, busy, ack_err, done);
 
    always #5 clk = ~clk;
 
    initial
    begin
        rst = 1;
        repeat(5) @(posedge clk);
        rst = 0;
        repeat(40) @(posedge clk);
        //////////// write operation
 
        for(int i = 0; i < 2 ; i++)
        begin
            newd = 1;
            op = 0;
            addr = $urandom_range(1,4);
            din  = $urandom_range(1,5);
            repeat(5) @(posedge clk);
            newd <= 1'b0;
            @(posedge done);
            $display("[WR] din : %0d addr: %0d",din, addr);
            @(posedge clk);
        end 
        ////////////read operation

        for(int i = 0; i < 2 ; i++)
        begin
            newd = 1;
            op = 1;
            addr = $urandom_range(1,4);
            din = 0;
            repeat(5) @(posedge clk);
            newd <= 1'b0;  
            @(posedge done);
            $display("[RD] dout : %0d addr: %0d",dout, addr);
            @(posedge clk);
        end
 
        repeat(10) @(posedge clk);
        $stop;
    end 
 
endmodule
728x90