I2C LCD String Module

// LCD 문자열 출력
module i2c_lcd_string (
    input clk, reset_p,
    input lcd_start,
    input lcd_row,
    input [8*16-1:0] lcd_txt,
    output scl, sda,
    output reg lcd_init_flag,
    output busy,
    output [15:0] led
    );

    integer cnt_sysclk;
    reg cnt_sysclk_e;
    // System Clock Counter
    always @(negedge clk, posedge reset_p) begin
        if (reset_p) cnt_sysclk <= 0;
        else if (cnt_sysclk_e) cnt_sysclk <= cnt_sysclk + 1;
        else cnt_sysclk <= 0;
    end

    // Edge Detection of LCD Start-bit
    wire start_pedge, start_nedge;
    edge_detector_pos start_ed (clk, reset_p, lcd_start, start_pedge, start_nedge);

    reg [7:0] send_buffer;
    reg send, rs;
    // Using Module, Byte Unit Transmission
    i2c_lcd_send_byte send_byte (clk, reset_p, 7'h27, send_buffer,
        send, rs, scl, sda, busy);

    localparam LCD_IDLE    = 4'b0001;
    localparam LCD_INIT    = 4'b0010;
    localparam MOVE_CURSOR = 4'b0100;
    localparam SEND_STRING = 4'b1000;

    // Change State in Negative Edge
    reg [3:0] state, next_state;
    always @(negedge clk, posedge reset_p) begin
        if (reset_p) state <= LCD_IDLE;
        else state <= next_state;
    end

    assign led[3:0] = state;
    assign led[4] = lcd_init_flag;

    reg [4:0] cnt_data;
    always @(posedge clk, posedge reset_p) begin
        if (reset_p) begin
            next_state <= LCD_IDLE;
            lcd_init_flag <= 0;
            send_buffer <= 0;
            send <= 0;
            rs <= 0;
            cnt_data <= 0;
        end
        else begin
            case (state)
                LCD_IDLE    : begin
                    if (lcd_init_flag) begin
                        if (start_pedge) next_state <= MOVE_CURSOR;
                    end
                    else begin
                        if (cnt_sysclk < 32'd80_000_00) begin   // Wait 80ms
                            cnt_sysclk_e <= 1;      // System Clock Count Start
                        end
                        else begin
                            next_state <= LCD_INIT; // Change State I2C_INIT
                            cnt_sysclk_e <= 0;      // System Clock Count Stop, Clear
                        end
                    end
                end
                LCD_INIT    : begin
                    if (busy) begin                 // Communicating
                        send <= 0;                  // Wait Transmission
                        if (cnt_data >= 6) begin
                            cnt_data <= 0;          // LCD Initialization 6-Step Complete
                            next_state <= LCD_IDLE; // Change State I2C_IDLE
                            lcd_init_flag <= 1;
                        end
                    end
                    else if (!send) begin
                        case (cnt_data)             // Step-by-Step Command Transmission
                            0 : send_buffer <= 8'h33;    // Function Set & Function Set
                            1 : send_buffer <= 8'h32;    // Function Set & Return Home
                            2 : send_buffer <= 8'h28;    // Function Set Data 4-bit, 2-Lines, 5×8 Dots
                            3 : send_buffer <= 8'h0C;    // Display On, Cursor Off, Blinking Cursor Off
                            4 : send_buffer <= 8'h01;    // Clear Display
                            5 : send_buffer <= 8'h06;    // Entry Mode Cursor Move Increment
                        endcase
                        send <= 1;                  // Request Transmission
                        cnt_data <= cnt_data + 1;   // Step Change
                    end
                end
                MOVE_CURSOR : begin
                    if (busy) begin                 // Communicating
                        send <= 0;                  // Wait Transmission
                        next_state <= SEND_STRING;
                    end
                    else if (!send) begin
                        rs <= 0;                    // Instruction Register Select
                        send_buffer <= {1'b1, lcd_row, 6'b00_0000}; // 저장 위치 설정
                        send <= 1;                  // Request Transmission
                    end
                end
                SEND_STRING : begin
                    if (busy) begin
                        send <= 0;
                        if (cnt_data >= 16) begin
                            cnt_data <= 0;
                            next_state <= LCD_IDLE;
                        end
                    end
                    else if (!send) begin
                        rs <= 1;                    // Data Register Select
                        send_buffer <= lcd_txt[8*(16-cnt_data)-1 -: 8]; // 왼쪽 글자부터 차례로 전송
                        send <= 1;
                        cnt_data <= cnt_data + 1;
                    end
                end
            endcase
        end
    end
endmodule

I2C LCD Send Byte Module

// Sending to LCD Using I2C Communication in Nibble(4-bit) Unit
module i2c_lcd_send_byte (
    input clk, reset_p,
    input [6:0] addr,                           // Slave Address
    input [7:0] send_buffer,                    // Transmission Data Buffer
    input send, rs,                             // Send Start-bit, Register Select
    output scl, sda,                            // Serial Clock , Serial Data
    output reg busy,                            // Communication Situation
    output [15:0] led                           // for Debugging
    );

    // Change State Using Shift
    localparam I2C_IDLE                 = 6'b00_0001;   // Standby State
    localparam SEND_HIGH_NIBBLE_DISABLE = 6'b00_0010;   // High Nibble(4-bit) Transmit in Enable Clear
    localparam SEND_HIGH_NIBBLE_ENABLE  = 6'b00_0100;   // Enable Set
    localparam SEND_LOW_NIBBLE_DISABLE  = 6'b00_1000;   // Low Nibble(4-bit) Transmit in Enable Clear
    localparam SEND_LOW_NIBBLE_ENABLE   = 6'b01_0000;   // Enable Set
    localparam SEND_DISABLE             = 6'b10_0000;   // Byte(8-bit) Transmission Complete

    reg [7:0] data;                             // 4-bit Unit Transmission, D7 ~ D4, BL, E, RW, RS
    reg comm_start;                             // Communication Start-bit

    // Clock Divide 100, 10ns x 100 = 1us
    wire clk_usec_nedge, clk_usec_pedge; // Divide Clock 1us
    clock_div_100 us_clk (.clk(clk), .reset_p(reset_p),
        .nedge_div_100(clk_usec_nedge), .pedge_div_100(clk_usec_pedge));

    // Edge Detection of Send Signal
    wire send_pedge, send_nedge;
    edge_detector_pos comm_start_ed (.clk(clk), .reset_p(reset_p),
        .cp(send), .p_edge(send_pedge), .n_edge(send_nedge));

    // us Unit Count
    reg [21:0] cnt_usec;                        // us Count
    reg cnt_usec_e;                             // us Count Enable
    always @(negedge clk, posedge reset_p) begin
        if (reset_p) cnt_usec = 0;              // Count Clear
        else if (clk_usec_nedge && cnt_usec_e) begin    // Count Start when Enable & us Negative Edge
            cnt_usec = cnt_usec + 1;            // Count During Enable
        end
        else if (!cnt_usec_e) cnt_usec = 0;     // Count Clear when Disable
    end

    // Using Module, Address & Data Transmission, I2C Communication
    i2c_master master (clk, reset_p, addr, data, 1'b0, comm_start, scl, sda);

    reg [5:0] state, next_state;                // Current & Next State
    always @(negedge clk, posedge reset_p) begin
        if (reset_p) state = I2C_IDLE;          // Basic Standby
        else state = next_state;                // Change State in Negative Edge
    end

    always @(posedge clk, posedge reset_p) begin
        if (reset_p) begin
            next_state = I2C_IDLE;              // Basic Standby
            comm_start = 0;                     // Communication Disable
            cnt_usec_e = 0;                     // us Count Disable, Clear
            data = 0;                           // Transmission Data Reset
            busy = 0;                           // Communication Available
        end
        else begin
            case (state)
                I2C_IDLE                 : begin    // Standby State
                    if (send_pedge) begin       // Send Start
                        next_state = SEND_HIGH_NIBBLE_DISABLE;  // Change State SEND_HIGH_NIBBLE_DISABLE
                        busy = 1;               // Communicating
                    end
                end
                SEND_HIGH_NIBBLE_DISABLE : begin    // High Nibble(4-bit) Transmit in Enable Clear
                    if (cnt_usec < 22'd200) begin   // About 200us Required to Complete Transmission
                        data = {send_buffer[7:4], 3'b100, rs};  // High Nibble Transmission
                        comm_start = 1;         // Communication Start
                        cnt_usec_e = 1;         // us Count Enable
                    end
                    else begin
                        cnt_usec_e = 0;         // us Count Disable, Clear
                        comm_start = 0;         // Communication Start-bit Clear
                        next_state = SEND_HIGH_NIBBLE_ENABLE;   // Change State SEND_HIGH_NIBBLE_ENABLE
                    end
                end
                SEND_HIGH_NIBBLE_ENABLE  : begin    // Enable Set
                    if (cnt_usec < 22'd200) begin   // About 200us Required to Complete Transmission
                        data = {send_buffer[7:4], 3'b110, rs};  // E Pin Enable
                        comm_start = 1;         // Communication Start
                        cnt_usec_e = 1;         // us Count Enable
                    end
                    else begin
                        cnt_usec_e = 0;         // us Count Disable, Clear
                        comm_start = 0;         // Communication Start-bit Clear
                        next_state = SEND_LOW_NIBBLE_DISABLE;   // Change State SEND_LOW_NIBBLE_DISABLE
                    end
                end
                SEND_LOW_NIBBLE_DISABLE  : begin    // Low Nibble(4-bit) Transmit in Enable Clear
                    if (cnt_usec < 22'd200) begin   // About 200us Required to Complete Transmission
                        data = {send_buffer[3:0], 3'b100, rs};  // Low Nibble Transmission
                        comm_start = 1;         // Communication Start
                        cnt_usec_e = 1;         // us Count Enable
                    end
                    else begin
                        cnt_usec_e = 0;         // us Count Disable, Clear
                        comm_start = 0;         // Communication Start-bit Clear
                        next_state = SEND_LOW_NIBBLE_ENABLE;    // Change State SEND_LOW_NIBBLE_ENABLE
                    end
                end
                SEND_LOW_NIBBLE_ENABLE   : begin    // Enable Set
                    if (cnt_usec < 22'd200) begin   // About 200us Required to Complete Transmission
                        data = {send_buffer[3:0], 3'b110, rs};  // E Pin Enable
                        comm_start = 1;         // Communication Start
                        cnt_usec_e = 1;         // us Count Enable
                    end
                    else begin
                        cnt_usec_e = 0;         // us Count Disable, Clear
                        comm_start = 0;         // Communication Start-bit Clear
                        next_state = SEND_DISABLE;  // Change State SEND_DISABLE
                    end
                end
                SEND_DISABLE             : begin    // Byte(8-bit) Transmission Complete
                    if (cnt_usec < 22'd200) begin   // About 200us Required to Complete Transmission
                        data = {send_buffer[7:4], 3'b100, rs};  // E Pin Disable
                        comm_start = 1;         // Communication Start
                        cnt_usec_e = 1;         // us Count Enable
                    end
                    else begin
                        cnt_usec_e = 0;         // us Count Disable, Clear
                        comm_start = 0;         // Communication Start-bit Clear
                        next_state = I2C_IDLE;  // Change State I2C_IDLE
                        busy = 0;               // Communication Available
                    end
                end
                default                  : begin
                    cnt_usec_e = 0;             // us Count Disable, Clear
                    comm_start = 0;             // Communication Start-bit Clear
                    next_state = I2C_IDLE;      // Basic Standby
                    busy = 0;                   // Communication Available
                end
            endcase
        end
    end
endmodule

I2C LCD Master Module

// Input Address or Data, 100㎑ I2C Communication When Start-bit High
module i2c_master (
    input clk, reset_p,
    input [6:0] addr,                           // Slave Address
    input [7:0] data,                           // Transmission Data
    input rd_wr, comm_start,                    // Read & Write Select-bit, Communication Start-bit
    output reg scl, sda,                        // Serial Clock , Serial Data
                                                // Originally SDA Input + Output, but Here only Output
    output [15:0] led                           // for Debugging
    );

    // Change State Using Shift
    localparam I2C_IDLE     = 7'b000_0001;      // Standby State
    localparam COMM_START   = 7'b000_0010;      // Communication Start
    localparam SEND_ADDR    = 7'b000_0100;      // Address Transmission
    localparam READ_ACK     = 7'b000_1000;      // Read ACK-bit, Assume Read
    localparam SEND_DATA    = 7'b001_0000;      // Data Transmission
    localparam SCL_STOP     = 7'b010_0000;      // Stop Generate Serial Clock
    localparam COMM_STOP    = 7'b100_0000;      // Communication Stop

    // Clock Divide 100, 10ns x 100 = 1us
    wire clk_usec_nedge, clk_usec_pedge; // Divide Clock 1us
    clock_div_100 us_clk (.clk(clk), .reset_p(reset_p),
        .nedge_div_100(clk_usec_nedge), .pedge_div_100(clk_usec_pedge));

    // Edge Detection of Command Signal
    wire comm_start_pedge, comm_start_nedge;
    edge_detector_pos comm_start_ed (.clk(clk), .reset_p(reset_p),
        .cp(comm_start), .p_edge(comm_start_pedge), .n_edge(comm_start_nedge));

    // Edge Detection of SCL Signal
    wire scl_pedge, scl_nedge;
    edge_detector_pos scl_ed (.clk(clk), .reset_p(reset_p),
        .cp(scl), .p_edge(scl_pedge), .n_edge(scl_nedge));

    reg [2:0] cnt_usec5;                        // 5us Count
    reg scl_e;                                  // SCL Enable
    always @(posedge clk, posedge reset_p) begin
        if (reset_p) begin
            cnt_usec5 = 0;                      // 5us Count Reset
            scl = 1;                            // SCL Reset, Pull-Up
        end
        else if (scl_e) begin                   // when SCL Enable
            if (clk_usec_nedge) begin           // when us Negative Edge
                if (cnt_usec5 >= 4) begin       // Every 5us 
                    cnt_usec5 = 0;              // 5us Count Reset
                    scl = ~scl;                 // Generate Serial Clock
                end
                else begin
                    cnt_usec5 = cnt_usec5 + 1;  // Count During Enable
                end
            end
        end
        else if (!scl_e) begin                  // SCL Disable
            cnt_usec5 = 0;                      // 5us Count Reset
            scl = 1;                            // SCL Reset, Pull-Up
        end
    end

    reg [6:0] state, next_state;                // Current & Next State
    always @(negedge clk, posedge reset_p) begin
        if (reset_p) state = I2C_IDLE;          // Basic Standby
        else state = next_state;                // Change State in Negative Edge
    end

    wire [7:0] addr_rd_wr;
    assign addr_rd_wr = {addr, rd_wr};          // Address + RW Select-bit
    reg [2:0] cnt_bit;                          // Count for Transmit 1-bit at a Time
    reg stop_flag;                              // Check Data Transmission
    always @(posedge clk, posedge reset_p) begin
        if (reset_p) begin
            next_state = I2C_IDLE;              // Basic Standby
            scl_e = 0;                          // SCL Disable
            sda = 1;                            // SDA Reset, Pull-Up
            cnt_bit = 7;                        // bit Count Reset, from Upper bit
            stop_flag = 0;                      // Before Data Transmission
        end
        else begin
            case (state)
                I2C_IDLE   : begin              // Standby State
                    scl_e = 0;                  // SCL Disable
                    sda = 1;                    // SDA Reset, Pull-Up
                    if (comm_start_pedge) begin // Communication Start
                        next_state = COMM_START;    // Change State COMM_START
                    end
                end
                COMM_START : begin              // Communication Start
                    sda = 0;                    // SDA Start
                    scl_e = 1;                  // SCL Enable
                    next_state = SEND_ADDR;     // Change State SEND_ADDR
                end
                SEND_ADDR  : begin              // Address Transmission
                    if (scl_nedge) begin        // when SCL Negative Edge
                        sda = addr_rd_wr[cnt_bit];  // Enter Address into SDA
                    end
                    if (scl_pedge) begin        // when SCL Positive Edge
                        if (cnt_bit == 0) begin // Read to LSB
                            cnt_bit = 7;        // bit Count Reset
                            next_state = READ_ACK;  // Change State READ_ACK
                        end
                        else begin
                            cnt_bit = cnt_bit - 1;  // Read from MSB
                        end
                    end
                end
                READ_ACK   : begin              // Read ACK-bit, Assume Read
                    if (scl_nedge) begin        // when SCL Negative Edge
                        sda = 'bz; // Enter Inpedance Value into SDA, Disconnect
                    end
                    else if (scl_pedge) begin   // when SCL Positive Edge
                        if (stop_flag) begin    // Complete Data Transmission
                            stop_flag = 0;      // Data Transmission Flag Clear
                            next_state = SCL_STOP;  // Change State SCL_STOP
                        end
                        else begin              // Before Data Transmission
                            stop_flag = 1;      // Data Transmission Flag Set
                            next_state = SEND_DATA; // Change State SEND_DATA
                        end
                    end
                end
                SEND_DATA  : begin              // Data Transmission
                    if (scl_nedge) begin        // when SCL Negative Edge
                        sda = data[cnt_bit];    // Enter Data into SDA
                    end
                    if (scl_pedge) begin        // when SCL Positive Edge
                        if (cnt_bit == 0) begin // Read to LSB
                            cnt_bit = 7;        // bit Count Reset
                            next_state = READ_ACK;  // Change State READ_ACK
                        end
                        else begin
                            cnt_bit = cnt_bit - 1;  // Read from MSB
                        end
                    end
                end
                SCL_STOP   : begin              // Stop Generate Serial Clock
                    if (scl_nedge) sda = 0;     // SDA Low when SCL Negative Edge
                    if (scl_pedge) next_state = COMM_STOP;  // Change State COMM_STOP when SCL Positive Edge
                end
                COMM_STOP  : begin              // Communication Stop
                    if (cnt_usec5 >= 3) begin   // Wait 4us
                        scl_e = 0;              // SCL Disable
                        sda = 1;                // SDA Stop
                        next_state = I2C_IDLE;  // Change State I2C_IDLE
                    end
                end
                default    : begin
                    scl_e = 0;                  // SCL Disable
                    sda = 1;                    // SDA Reset, Pull-Up
                    next_state = I2C_IDLE;      // Basic Standby
                end
            endcase
        end
    end
endmodule