Notice
Recent Posts
Recent Comments
Link
관리 메뉴

거북이처럼 천천히

I2C 통신을 통한 EEPROM Read/Write (수정 중) 본문

RTL Design/Verilog 프로젝트

I2C 통신을 통한 EEPROM Read/Write (수정 중)

유로 청년 2025. 2. 26. 16:43

1. 과제 

 

 



2. 인자값으로 전달 받은 8bit 값을 I2C 통신을 통해 전송하는 모듈 설계

2.1) Input / Output 설정

  • sned_8bit_data_spi 모듈은 parameter값으로 전달 받은 8bit memory_data_in 값을 I2C 통신을 통해 전송하는 모듈이다. ( 이름은 SPI로 되어 있으나, I2C 통신을 사용한다. 최근 SPI 통신을 하다보니, SPI 라고 잘못 적었다. ㅠㅠ )
  • 해당 모듈은 다음과 같은 Input / Output 값을 갖는다.

    Input 

    → clk : FPGA System Clock (100MHz)
    → reset_p : System Positive edge reset
    → comm_go : Positive edge가 발생할 시, I2C 통신 시작하는 플래그
    → read_write : R/W Bit
    → Slave address : External Slave Module의 주소
    → memory address : EEPROM의 메모리 주소
    → memory data in : 해당 주소에 쓸 데이터 

    Output 
    → memory_data_out : 읽기 작업 수행할 시, 저장되어 있던 데이터
    → scl, sda

 

 

 

 

2.2) FSM에서 State 정의

  • 해당 EEPROM에서 Read/Write 작업을 수행하기 위해서는 다음과 같은 프로세스를 수행하게 된다.

(위) 데이터 변경 시점 정의, (아래) 시작, 끝 조건 정의

 

 

  • 이에 따라 Finite State Machine에서의 State을 다음과 같이 9단계로 정의하였다.
 // Declare parameter of state\-machine
parameter S_IDLE = 4'd0;
parameter S_START = 4'd1;
parameter S_SEND_DEVICE_ADDR = 4'd2;
parameter S_WAIT_ACK = 4'd3;
parameter S_SEND_WORD_ADDR = 4'd4;
parameter S_START_FOR_READ = 4'd5;
parameter S_SEND_DEVICE_ADDR_READ = 4'd6;
parameter S_SEND_DATA = 4'd7;
parameter S_STOP = 4'd8;
parameter S_END = 4'd9;

 

 

 

 

 

 

 

 

 

 

 

 

2. 코드

2.1) Send_8bit_data_spi

  • 해당 함수는 전달 받은 EEPROM의 주소에 접근하여 데이터를 읽기/쓰기 작업을 수행하는 모듈이다.
`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2025/02/26 08:48:52
// Design Name: 
// Module Name: send_8bit_data_spi
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module send_8bit_data_spi(
    input clk, reset_p,
    input comm_go,
    input [6:0] slave_address,
    input read_write,
    input [7:0] memory_address,
    input [7:0] memory_data_in,
    inout sda,
    output reg scl,
    output reg [7:0] memory_data_out);
    
    // Declare parameter of state\-machine
    parameter S_IDLE = 4'd0;
    parameter S_START = 4'd1;
    parameter S_SEND_DEVICE_ADDR = 4'd2;
    parameter S_WAIT_ACK = 4'd3;
    parameter S_SEND_WORD_ADDR = 4'd4;
    parameter S_START_FOR_READ = 4'd5;
    parameter S_SEND_DEVICE_ADDR_READ = 4'd6;
    parameter S_SEND_DATA = 4'd7;
    parameter S_STOP = 4'd8;
    parameter S_END = 4'd9;
    
    // Declare current state, next state register
    reg [8:0] state, next_state;
    
    // 다음 상태 전이되는가? 
    always @(posedge clk or posedge reset_p) begin
        if(reset_p) state = S_IDLE;
        else state = next_state;
    end
    
    // Detect positive/negative edge of SCL
    wire clk_1usec_nedge;
    prescale_100 prescale_100_1usc(.clk(clk), .reset_p(reset_p), .clk_div_100_nedge(clk_1usec_nedge));
    
    // Control SCL Signal & SCL Enable 
    reg [2:0] scl_5us_counter;
    reg scl_enable;
    reg [10:0] stop_bit_delay_time;
    
    always @(negedge clk or posedge reset_p) begin
        if(reset_p) begin
            scl = 1;
            scl_5us_counter = 0;
        end
        else if(scl_enable && clk_1usec_nedge) begin
            if(scl_5us_counter >= 4) begin
                scl_5us_counter = 0;
                scl = ~scl;
            end
            else scl_5us_counter = scl_5us_counter + 1;
        end
        else if(!scl_enable) begin
            scl_5us_counter = 0;
            scl = 1;
        end
    end
    
    // Get positive edge of comm_go
    wire comm_go_pedge;
    edge_detector edge_detector_comm_go (.clk(clk), .reset_p(reset_p), .cp(comm_go), .p_edge(comm_go_pedge));
    
    // Get positive/negative edge of SCL
    wire scl_pedge, scl_nedge;
    edge_detector edge_detector_scl (.clk(clk), .reset_p(reset_p), .cp(scl), .n_edge(scl_nedge), .p_edge(scl_pedge));
   
    // Declare variables
    wire [7:0] slave_addr_and_RW_bit = {slave_address, read_write};
    wire [7:0] device_address = {slave_address, 1'b0};
    reg [3:0] idx_send_data;
    reg [2:0] flag_ack;
    reg [7:0] received_data;
    
    // SDA inout이기 때문에 레지스터를 통한 접근 필요
    reg sda_instead;
    assign sda = sda_instead;
   
    // State-Machine of I2C
    always @(negedge clk or posedge reset_p) begin
        if(reset_p) begin
            sda_instead = 1;
            next_state = S_IDLE;
            scl_enable = 0;
            idx_send_data = 0;
            flag_ack = 0;
            stop_bit_delay_time = 0;
        end
        else begin
            case(state) 
                // *** State 1 : IDLE
                S_IDLE : begin
                    sda_instead = 1;
                    scl_enable = 0;
                    
                    if(comm_go_pedge) next_state = S_START;
                end
                
                // *** State 2 : Start (SDA Signal Negative edge)
                S_START : begin
                    sda_instead = 0;
                    next_state = S_SEND_DEVICE_ADDR;
                end
                
                // *** State 3 : Send the data (Slave Address + R/W bit)
                S_SEND_DEVICE_ADDR : begin
                     scl_enable = 1;
                    
                     if (scl_nedge) sda_instead = device_address[7 - idx_send_data];
                     else if (scl_pedge) begin
                            if (idx_send_data == 7) begin
                                next_state = S_WAIT_ACK;
                                idx_send_data = 0;
                            end else begin
                                idx_send_data = idx_send_data + 1; 
                            end
                    end
                end
                
                // *** State 4 : Wait ACK signal
                S_WAIT_ACK : begin
                    // Master의 SDA 값을 임피던스 상태로 변환
                    if(scl_nedge) sda_instead = 'bz;
                    else if(scl_pedge && !sda) begin
                        if(flag_ack == 3'd0) begin
                            next_state = S_SEND_WORD_ADDR;
                            flag_ack = 3'd1;
                        end
                        else if(flag_ack == 3'd1) begin
                            if(!read_write) begin
                                next_state = S_SEND_DATA; 
                                flag_ack = 3'd3;
                            end else begin
                                next_state = S_START_FOR_READ;
                                flag_ack = 3'd2;
                            end
                        end
                        else if(flag_ack == 3'd2) begin
                            next_state = S_SEND_DATA; 
                            flag_ack = 3'd3;
                        end    
                        else if(flag_ack == 3'd3) begin
                            next_state = S_STOP;
                            flag_ack = 3'd0;
                        end
                    end
                    else if(scl_pedge && sda) begin
                        next_state = S_END;
                        flag_ack = 3'd0;
                    end
                end
                
                // *** State 5 : Send Word Address
                S_SEND_WORD_ADDR : begin
                    if (scl_nedge) sda_instead = memory_address[7 - idx_send_data];
                     else if (scl_pedge) begin
                            if (idx_send_data == 7) begin
                                next_state = S_WAIT_ACK;
                                idx_send_data = 0;
                            end else begin
                                idx_send_data = idx_send_data + 1; 
                            end
                    end
                end
                
                // *** State 6 : Send Re-start signal
                S_START_FOR_READ : begin
                    if(scl_nedge) sda_instead = 1;
                    else if(scl_pedge) begin 
                        sda_instead = 0;
                        next_state = S_SEND_DEVICE_ADDR_READ;
                    end
                end
                
                // *** State 7 : Send Device Address for Read bit
                S_SEND_DEVICE_ADDR_READ : begin
                    if (scl_nedge) sda_instead = slave_addr_and_RW_bit[7 - idx_send_data];
                     else if (scl_pedge) begin
                            if (idx_send_data == 7) begin
                                next_state = S_WAIT_ACK;
                                idx_send_data = 0;
                            end else begin
                                idx_send_data = idx_send_data + 1; 
                            end
                    end
                end
                
                // *** State 8 : Send data
                S_SEND_DATA : begin
                    if(read_write) begin
                        if(scl_nedge) idx_send_data = idx_send_data + 1;
                        else if(scl_pedge) begin
                            if(idx_send_data == 9) begin
                                idx_send_data = 0;
                                next_state = S_WAIT_ACK;
                            end
                            else  received_data[8 - idx_send_data] = sda;
                        end
                    end
                    else begin
                        if (scl_nedge) sda_instead = memory_data_in[7 - idx_send_data];
                        else if (scl_pedge) begin
                                if (idx_send_data == 7) begin
                                    next_state = S_WAIT_ACK;
                                    idx_send_data = 0;
                                end else begin
                                    idx_send_data = idx_send_data + 1; 
                                end
                        end
                    end
                end
                
                // *** State 6 : Stop I2C (SDA positive edge)
                S_STOP : begin
                     if(scl_nedge) sda_instead = 0;
                     else if(scl_pedge) begin 
                        next_state = S_END;
                     end
                end
                
                // *** State 7 : End I2C : 
                S_END : begin
                    scl_enable = 0;
                    if(stop_bit_delay_time <= 999) begin
                        stop_bit_delay_time = stop_bit_delay_time + 1;
                        sda_instead = 0;
                    end
                    else begin
                        sda_instead = 1;
                        scl_enable = 0;
                        idx_send_data = 0;
                        next_state = S_IDLE;
                        stop_bit_delay_time = 0;
                        memory_data_out = received_data;
                    end
                end
            endcase
        end
    end
   

   // Declare Register variable for SDA
   // SDA는 inout 자료형이기 때문에 어떤 값이 들어 올 지 모르기 때문에 
   reg reg_sda; 
   
   always @(posedge clk or posedge reset_p) begin
        if(reset_p) reg_sda = 1;
        else reg_sda = sda;
   end
   
   // **************  ILA IP  ************** 
   ila_0 ila_instance (.clk(clk), .probe0(scl), .probe1(reg_sda));

endmodule

 

 

2.2) Top Module 

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2025/02/26 08:30:18
// Design Name: 
// Module Name: top_of_module
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module top_of_module(
    input clk, reset_p,
    inout sda,
    output scl);
    
    
   // **************  Declare variables  **************
   wire clk_out;
   wire enable, read_write;
   wire [6:0] slave_address;
   wire [7:0] memory_address, memory_data_in, memory_data_out;
   wire reset_n;
   
   assign reset_n = ~reset_p;
    
   // **************  clk_wiz IP  **************
   clk_wiz_0 clk_wiz_0_instance0 (
     // Clock out ports
        .clk_out1(clk_out),     // output clk_out1
     // Status and control signals
        .reset(reset_n), // input reset
        .locked(locked),       // output locked
    // Clock in ports
        .clk_in1(clk));      // input clk_in1
    // INST_TAG_END ------ End INSTANTIATION Template ---------
    
    
    
   // **************  vio_0 IP  **************  
   vio_0 vio_0_instance0 (
        .clk(clk_out),                // input wire clk
        .probe_in0(memory_data_out),    // input wire [7 : 0] probe_in0 (## Memory Data Output ##)
        .probe_out0(enable),  // output wire [0 : 0] probe_out0 (## Enable ##)
        .probe_out1(slave_address),  // output wire [6 : 0] probe_out1 (## Slave Address ##)
        .probe_out2(read_write),  // output wire [0 : 0] probe_out2 (## Read & Write  ##)
        .probe_out3(memory_address),  // output wire [7 : 0] probe_out3 (## Memory Address ##)
        .probe_out4(memory_data_in)  // output wire [7 : 0] probe_out4 (## Memory Data Input ##)
   );
   
   
    // **************  Instance of SPI  **************
    send_8bit_data_spi spi_send_8bit_instance_0 (.clk(clk_out), .reset_p(reset_n), .comm_go(enable),
            .slave_address(slave_address), .read_write(read_write), .memory_address(memory_address),
            .memory_data_in(memory_data_in), .scl(scl), .sda(sda), .memory_data_out(memory_data_out));
endmodule