/* * codec_fix.v * * vim: ts=4 sw=4 * * Copyright (C) 2021 Sylvain Munaut * SPDX-License-Identifier: CERN-OHL-P-2.0 */ `default_nettype none module codec_fix ( // Shared I2C + Button/LED inout wire scl_led, inout wire sda_btn, // Internal alt-function output reg btn, input wire led, // Control input wire go, output wire rdy, // Clock / Reset input wire clk, input wire rst ); // Signals // ------- // FSM // Values encode type 'state'. // [3] == 1 : Issue master command // [3:2] == 01 : Pause (timer running) localparam ST_IDLE = 0, ST_PRE_PAUSE = 4, ST_CMD_START = 8, ST_CMD_I2C_ADDR = 9, ST_CMD_REG_HI = 10, ST_CMD_REG_LO = 11, ST_CMD_VAL_HI = 12, ST_CMD_VAL_LO = 13, ST_CMD_STOP = 14, ST_POST_PAUSE = 5; reg [3:0] state; reg [3:0] state_nxt; // Timer for pauses reg [15:0] timer_cnt; wire timer_tick; wire timer_rst; // IOB wire iob_scl_oe, iob_sda_oe; wire iob_scl_o, iob_sda_o; wire iob_scl_i, iob_sda_i; // I2C IOs from master core wire i2c_io_scl_oe; wire i2c_io_sda_oe; wire i2c_io_scl_i; wire i2c_io_sda_i; // I2C Master IF reg [7:0] i2c_m_data_in; wire i2c_m_ack_in; reg [1:0] i2c_m_cmd; reg i2c_m_stb; wire i2c_m_ready; // FSM // --- // State register always @(posedge clk) if (rst) state <= ST_IDLE; else state <= state_nxt; // Next-state always @(*) begin // Default state_nxt = state; // Transitions case (state) ST_IDLE: if (go) state_nxt = ST_PRE_PAUSE; ST_PRE_PAUSE: if (timer_tick) state_nxt = ST_CMD_START; ST_CMD_START: if (i2c_m_ready & ~i2c_m_stb) state_nxt = ST_CMD_I2C_ADDR; ST_CMD_I2C_ADDR: if (i2c_m_ready & ~i2c_m_stb) state_nxt = ST_CMD_REG_HI; ST_CMD_REG_HI: if (i2c_m_ready & ~i2c_m_stb) state_nxt = ST_CMD_REG_LO; ST_CMD_REG_LO: if (i2c_m_ready & ~i2c_m_stb) state_nxt = ST_CMD_VAL_HI; ST_CMD_VAL_HI: if (i2c_m_ready & ~i2c_m_stb) state_nxt = ST_CMD_VAL_LO; ST_CMD_VAL_LO: if (i2c_m_ready & ~i2c_m_stb) state_nxt = ST_CMD_STOP; ST_CMD_STOP: if (i2c_m_ready & ~i2c_m_stb) state_nxt = ST_POST_PAUSE; ST_POST_PAUSE: if (timer_tick) state_nxt = ST_IDLE; default: state_nxt = ST_IDLE; endcase end assign rdy = (state == ST_IDLE); // Timer // ----- always @(posedge clk) if (timer_rst) timer_cnt <= 0; else timer_cnt <= timer_cnt + 1; assign timer_tick = timer_cnt[15]; assign timer_rst = timer_tick | (state[3:2] != 2'b01); // IO // -- // Instance SB_IO #( .PIN_TYPE(6'b1010_01), // PIN_OUTPUT_TRISTATE / PIN_INPUT .PULLUP(1'b0), .IO_STANDARD("SB_LVCMOS") ) i2c[1:0] ( .PACKAGE_PIN ({scl_led, sda_btn}), .OUTPUT_ENABLE ({iob_scl_oe, iob_sda_oe}), .D_OUT_0 ({iob_scl_o, iob_sda_o }), .D_IN_0 ({iob_scl_i, iob_sda_i }) ); // Muxing // Always open-drain drive assign iob_scl_o = 1'b0; assign iob_sda_o = 1'b0; // If not IDLE, driven by the master core assign iob_scl_oe = (state == ST_IDLE) ? led : i2c_io_scl_oe; assign iob_sda_oe = (state == ST_IDLE) ? 1'b0 : i2c_io_sda_oe; // We don't support clock stretch assign i2c_io_scl_i = 1'b0; // We never do reads or check ACK's assign i2c_io_sda_i = 1'b0; // Only update button state if IDLE always @(posedge clk) if (state == ST_IDLE) btn <= iob_sda_i; // I2C master // ---------- // Instance i2c_master #( .DW(3) ) master_I ( .scl_oe (i2c_io_scl_oe), .sda_oe (i2c_io_sda_oe), .scl_i (i2c_io_scl_i), .sda_i (i2c_io_sda_i), .data_in (i2c_m_data_in), .ack_in (i2c_m_ack_in), .cmd (i2c_m_cmd), .stb (i2c_m_stb), .data_out (), .ack_out (), .ready (i2c_m_ready), .clk (clk), .rst (rst) ); // Control always @(*) case (state) ST_CMD_I2C_ADDR: i2c_m_data_in = 8'b00010100; ST_CMD_REG_HI: i2c_m_data_in = 8'b00000000; ST_CMD_REG_LO: i2c_m_data_in = 8'b00110000; ST_CMD_VAL_HI: i2c_m_data_in = 8'b01000000; ST_CMD_VAL_LO: i2c_m_data_in = 8'b01100000; default: i2c_m_data_in = 8'bxxxxxxxx; endcase assign i2c_m_ack_in = 1'b0; always @(*) case (state) ST_CMD_START: i2c_m_cmd = 2'b00; ST_CMD_I2C_ADDR: i2c_m_cmd = 2'b10; ST_CMD_REG_HI: i2c_m_cmd = 2'b10; ST_CMD_REG_LO: i2c_m_cmd = 2'b10; ST_CMD_VAL_HI: i2c_m_cmd = 2'b10; ST_CMD_VAL_LO: i2c_m_cmd = 2'b10; ST_CMD_STOP: i2c_m_cmd = 2'b01; default: i2c_m_cmd = 2'bxx; endcase always @(posedge clk) i2c_m_stb <= (state_nxt != ST_IDLE) & (state_nxt != state) & state_nxt[3]; endmodule // codec_fix