// // Author: lsilvest // Create Date: 10/10/2009 // // // Copyright (c) 2009 Authors // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // `include "i2c_controller_const.v" module i2c_display( input clk, // 20 MHz input clock input rst_n, // active low board reset, connected to sw1 input pacer_sda_in, // oled data in output pacer_sda_out, // oled data out output pacer_sda_oe, // oled data output enable input pacer_scl_in, // oled clock in output pacer_scl_out, // oled clock out output pacer_scl_oe, // oled clock output enable output pacer_rst, // oled reset output [3:0] addr, // address offset in temp digits/chars displays input [4:0] char_addr, // character address in pacer_font8x16 input disp_go, output disp_done ); localparam FNT_MAX = 97, INIT_MAX = 19, PG_MAX = 3; localparam pacer_slave_addr = 8'h78; localparam pacer_cntrl_cntrl = 8'h00; localparam pacer_cntrl_data = 8'h40; localparam idl = 4'b0000, w_a_s = 4'b0001, a_rcvd_s = 4'b0010, w = 4'b0011, a_rcvd_w = 4'b0100, p = 4'b0111; localparam g_idl = 5'b00000, g_init_sl = 5'b00001, g_init_cmd = 5'b00010, g_init_data = 5'b00011, g_init_done = 5'b00100, g_pg0_sl = 5'b00101, g_pg0_cmd = 5'b00110, g_pg0_data = 5'b00111, g_pg0_done = 5'b01000, g_up_sl = 5'b01001, g_up_cmd = 5'b01010, g_up_data = 5'b01011, g_up_done = 5'b01100, g_pg1_sl = 5'b01101, g_pg1_cmd = 5'b01110, g_pg1_data = 5'b01111, g_pg1_done = 5'b10000, g_lo_sl = 5'b10001, g_lo_cmd = 5'b10010, g_lo_data = 5'b10011, g_lo_done = 5'b10100, g_done = 5'b10101; wire [7:0] fnt_data; wire [7:0] init_data; wire [7:0] pg0_data; wire [7:0] pg1_data; wire go; reg pacer_rst_r; assign pacer_rst = pacer_rst_r; reg [3:0] state, next_state; reg [4:0] g_state, g_next_state; wire sdin_o; wire sdin_oe; wire sclk_o; wire sclk_oe; wire ack_o; wire ack_e; reg [2:0] cmd; wire cmd_a; wire [7:0] data_r; wire [7:0] data_w; reg [7:0] data_w_r; assign pacer_sda_out = sdin_o; assign pacer_scl_out = sclk_o; assign pacer_sda_oe = sdin_oe; assign pacer_scl_oe = sclk_oe; reg [8:0] addr_cntr_next; reg [8:0] addr_cntr; wire data_end; assign addr = addr_cntr[6:3]; pacer_font8x16 fnt( .addr({char_addr, g_state > g_lo_sl, addr_cntr[2:0]}), .data_r(fnt_data) ); pacer_init_seq init_seq( .addr(addr_cntr[4:0]), .data_r(init_data) ); page_seq #(4'b0) pg0( .addr(addr_cntr[1:0]), .data_r(pg0_data) ); page_seq #(4'b1) pg1( .addr(addr_cntr[1:0]), .data_r(pg1_data) ); i2c_controller i2c_cntrl( .clk(clk), .rst_n(rst_n), .data_i(data_w), .data_o(data_r), .sdin_i(pacer_sda_in), .sdin_o(sdin_o), .sdin_oe(sdin_oe), .sclk_i(pacer_scl_in), .sclk_o(sclk_o), .sclk_oe(sclk_oe), .cmd_i(cmd), .cmd_a(cmd_a), .ack_o(ack_o), .ack_e(ack_e) ); // pacer reset always@(posedge clk or negedge rst_n) begin if (!rst_n) pacer_rst_r <= 1'b0; else pacer_rst_r <= 1'b1; end // generate state change always@(posedge clk or negedge rst_n) begin if (!rst_n) begin state <= idl; addr_cntr <= 0; g_state <= g_idl; end else begin addr_cntr <= addr_cntr_next; state <= next_state; g_state <= g_next_state; end end // pacer i2c state transitions: always@(*) begin case (state) idl: if (go) next_state = w_a_s; else next_state = idl; w_a_s: if (ack_o) next_state = a_rcvd_s; else next_state = w_a_s; a_rcvd_s: if (cmd_a) next_state = w; else next_state = a_rcvd_s; w: if (ack_o) next_state = a_rcvd_w; else next_state = w; a_rcvd_w: if (data_end) next_state = p; else if (cmd_a) next_state = w; else next_state = a_rcvd_w; p: if (cmd_a) next_state = idl; else next_state = p; default: next_state = idl; endcase end // i2c pacer command control: always@(*) begin case (state) idl: cmd = `nop_cmd; w_a_s: cmd = `s_cmd; a_rcvd_s: cmd = `w_cmd; w: cmd = `w_cmd; a_rcvd_w: cmd = `w_cmd; p: cmd = `p_cmd; default: cmd = `nop_cmd; endcase end // global state control: always@(*) begin g_next_state = g_state; case(g_state) g_idl: if (disp_go) g_next_state = g_init_sl; g_init_sl: if (ack_o) g_next_state = g_init_cmd; g_init_cmd: if (ack_o) g_next_state = g_init_data; g_init_data: if (data_end) g_next_state = g_init_done; else g_next_state = g_init_data; g_init_done: if (disp_go) g_next_state = g_pg0_sl; g_pg0_sl: if (ack_o) g_next_state = g_pg0_cmd; g_pg0_cmd: if (ack_o) g_next_state = g_pg0_data; g_pg0_data: if (data_end) g_next_state = g_pg0_done; else g_next_state = g_pg0_data; g_pg0_done: g_next_state = g_up_sl; g_up_sl: if (ack_o) g_next_state = g_up_cmd; g_up_cmd: if (ack_o) g_next_state = g_up_data; g_up_data: if (data_end) g_next_state = g_up_done; else g_next_state = g_up_data; g_up_done: g_next_state = g_pg1_sl; g_pg1_sl: if (ack_o) g_next_state = g_pg1_cmd; g_pg1_cmd: if (ack_o) g_next_state = g_pg1_data; g_pg1_data: if (data_end) g_next_state = g_pg1_done; else g_next_state = g_pg1_data; g_pg1_done: g_next_state = g_lo_sl; g_lo_sl: if (ack_o) g_next_state = g_lo_cmd; g_lo_cmd: if (ack_o) g_next_state = g_lo_data; g_lo_data: if (data_end) g_next_state = g_done; else g_next_state = g_lo_data; g_done: g_next_state = g_init_done; endcase end // data control: always@(*) begin data_w_r = 0; case(g_state) g_init_sl: data_w_r = pacer_slave_addr; g_init_cmd: data_w_r = pacer_cntrl_cntrl; g_init_data: data_w_r = init_data; g_pg0_sl: data_w_r = pacer_slave_addr; g_pg0_cmd: data_w_r = pacer_cntrl_cntrl; g_pg0_data: data_w_r = pg0_data; g_up_sl: data_w_r = pacer_slave_addr; g_up_cmd: data_w_r = pacer_cntrl_data; g_up_data: data_w_r = fnt_data; g_pg1_sl: data_w_r = pacer_slave_addr; g_pg1_cmd: data_w_r = pacer_cntrl_cntrl; g_pg1_data: data_w_r = pg1_data; g_lo_sl: data_w_r = pacer_slave_addr; g_lo_cmd: data_w_r = pacer_cntrl_data; g_lo_data: data_w_r = fnt_data; endcase end assign data_w = data_w_r; assign data_end = (g_state == g_up_data && addr_cntr == FNT_MAX) || (g_state == g_lo_data && addr_cntr == FNT_MAX) || (g_state == g_init_data && addr_cntr == INIT_MAX) || (g_state == g_pg0_data && addr_cntr == PG_MAX) || (g_state == g_pg1_data && addr_cntr == PG_MAX); // address control: always@(*) begin if ((g_state == g_init_data || g_state == g_up_data || g_state == g_lo_data || g_state == g_pg0_data || g_state == g_pg1_data) && ack_o) addr_cntr_next = addr_cntr + 1; else if (g_state == g_init_cmd || g_state == g_up_cmd || g_state == g_lo_cmd || g_state == g_pg0_cmd || g_state == g_pg1_cmd) addr_cntr_next = 0; else addr_cntr_next = addr_cntr; end assign go = g_state == g_init_sl || g_state == g_up_sl || g_state == g_lo_sl || g_state == g_pg0_sl || g_state == g_pg1_sl; assign disp_done = g_state == g_init_done || g_state == g_done; endmodule