diff options
-rw-r--r-- | LICENSE | 24 | ||||
-rw-r--r-- | README.md | 7 | ||||
-rw-r--r-- | README.md.gpg | bin | 0 -> 508 bytes | |||
-rw-r--r-- | src/rtl/i2c.v | 219 | ||||
-rw-r--r-- | src/rtl/i2c_core.v | 586 |
5 files changed, 836 insertions, 0 deletions
@@ -0,0 +1,24 @@ +Author: Joachim Strömbergson +Copyright (c) 2014, SUNET +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..24edcbb --- /dev/null +++ b/README.md @@ -0,0 +1,7 @@ +i2c +=== + +An I2C slave implemented in Verilog. + +The core i2c functionality is based on Bunnie Huang's i2c_slave.v from the +novena-ws2312b-fpga project (https://github.com/xobs/novena-ws2812b-fpga). diff --git a/README.md.gpg b/README.md.gpg Binary files differnew file mode 100644 index 0000000..12790ea --- /dev/null +++ b/README.md.gpg diff --git a/src/rtl/i2c.v b/src/rtl/i2c.v new file mode 100644 index 0000000..112ad70 --- /dev/null +++ b/src/rtl/i2c.v @@ -0,0 +1,219 @@ +//====================================================================== +// +// i2c.v +// ------ +// Top level wrapper for the i2c core. +// +// A simple I2C interface. +// +// +// Author: Joachim Strombergson +// Copyright (c) 2014, SUNET +// +// Redistribution and use in source and binary forms, with or +// without modification, are permitted provided that the following +// conditions are met: +// +// 1. Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// +// 2. Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in +// the documentation and/or other materials provided with the +// distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +//====================================================================== + +module i2c( + input wire clk, + input wire reset_n, + + // External interface. + input wire SCL, + input wire SDA, + output wire SDA_pd, + input wire [7:0] i2c_device_addr, + + // Internal receive interface. + output wire rxd_syn, + output [7 : 0] rxd_data, + input wire rxd_ack, + + // Internal transmit interface. + input wire txd_syn, + input wire [7 : 0] txd_data, + output wire txd_ack, + + // API interface. + input wire cs, + input wire we, + input wire [7 : 0] address, + input wire [31 : 0] write_data, + output wire [31 : 0] read_data, + output wire error, + + // Debug output. + output wire [7 : 0] debug + ); + + + //---------------------------------------------------------------- + // Internal constant and parameter definitions. + //---------------------------------------------------------------- + // API addresses. + parameter ADDR_CORE_NAME0 = 8'h00; + parameter ADDR_CORE_NAME1 = 8'h01; + parameter ADDR_CORE_TYPE = 8'h02; + parameter ADDR_CORE_VERSION = 8'h03; + + // Core ID constants. + parameter CORE_NAME0 = 32'h69326320; // "i2c " + parameter CORE_NAME1 = 32'h20202020; // " " + parameter CORE_TYPE = 32'h20202031; // " 1" + parameter CORE_VERSION = 32'h302e3031; // "0.01" + + //---------------------------------------------------------------- + // Wires. + //---------------------------------------------------------------- + + wire core_SCL; + wire core_SDA; + wire core_SDA_pd; + wire [7:0] core_i2c_device_addr; + + wire core_rxd_syn; + wire [7 : 0] core_rxd_data; + wire core_rxd_ack; + + wire core_txd_syn; + wire [7 : 0] core_txd_data; + wire core_txd_ack; + + reg [31 : 0] tmp_read_data; + reg tmp_error; + + + //---------------------------------------------------------------- + // Concurrent connectivity for ports etc. + //---------------------------------------------------------------- + assign core_SCL = SCL; + assign core_SDA = SDA; + assign SDA_pd = core_SDA_pd; + assign core_i2c_device_addr = i2c_device_addr; + + assign rxd_syn = core_rxd_syn; + assign rxd_data = core_rxd_data; + assign core_rxd_ack = rxd_ack; + + assign core_txd_syn = txd_syn; + assign core_txd_data = txd_data; + assign txd_ack = core_txd_ack; + + assign read_data = tmp_read_data; + assign error = tmp_error; + + assign debug = core_rxd_data; + + + //---------------------------------------------------------------- + // core + // + // Instantiation of the i2c core. + //---------------------------------------------------------------- + i2c_core core( + .clk(clk), + .reset(reset_n), + + // External data interface + .SCL(core_SCL), + .SDA(core_SDA), + .SDA_pd(core_SDA_pd), + .i2c_device_addr(core_i2c_device_addr), + + // Internal receive interface. + .rxd_syn(core_rxd_syn), + .rxd_data(core_rxd_data), + .rxd_ack(core_rxd_ack), + + // Internal transmit interface. + .txd_syn(core_txd_syn), + .txd_data(core_txd_data), + .txd_ack(core_txd_ack) + ); + + + //---------------------------------------------------------------- + // api + // + // The core API that allows an internal host to control the + // core functionality. + //---------------------------------------------------------------- + always @* + begin: api + // Default assignments. + tmp_read_data = 32'h00000000; + tmp_error = 0; + + if (cs) + begin + if (we) + begin + // Write operations. + case (address) + default: + begin + tmp_error = 1; + end + endcase // case (address) + end + else + begin + // Read operations. + case (address) + ADDR_CORE_NAME0: + begin + tmp_read_data = CORE_NAME0; + end + + ADDR_CORE_NAME1: + begin + tmp_read_data = CORE_NAME1; + end + + ADDR_CORE_TYPE: + begin + tmp_read_data = CORE_TYPE; + end + + ADDR_CORE_VERSION: + begin + tmp_read_data = CORE_VERSION; + end + + default: + begin + tmp_error = 1; + end + endcase // case (address) + end + end + end + +endmodule // i2c + +//====================================================================== +// EOF i2c.v +//====================================================================== diff --git a/src/rtl/i2c_core.v b/src/rtl/i2c_core.v new file mode 100644 index 0000000..3e2772a --- /dev/null +++ b/src/rtl/i2c_core.v @@ -0,0 +1,586 @@ +////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2011, Andrew "bunnie" Huang +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation and/or +// other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY +// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT +// SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +////////////////////////////////////////////////////////////////////////////// +// A simple I2C slave implementation. Oversampled for robustness. +// The slave is extended into the snoop & surpress version for the DDC bus; +// this is just a starting point for basic testing and also simple comms +// with the CPU. +// +// i2c slave module requires the top level module to implement the IOBs +// This is just to keep the tri-state easy to implemen across the hierarchy +// +// The code required on the top level is: +// IOBUF #(.DRIVE(12), .SLEW("SLOW")) IOBUF_sda (.IO(SDA), .I(1'b0), .T(!SDA_pd)); +// +/////////// +`timescale 1 ns / 1 ps + +module i2c_core ( + input wire clk, + input wire reset, + + // External data interface + input wire SCL, + input wire SDA, + output reg SDA_pd, + input wire [7:0] i2c_device_addr, + + // Internal receive interface. + output wire rxd_syn, + output [7 : 0] rxd_data, + input wire rxd_ack, + + // Internal transmit interface. + input wire txd_syn, + input wire [7 : 0] txd_data, + output wire txd_ack + ); + + /////// I2C physical layer components + /// SDA is stable when SCL is high. + /// If SDA moves while SCL is high, this is considered a start or stop condition. + /// + /// Otherwise, SDA can move around when SCL is low (this is where we suppress bits or + /// overdrive as needed). SDA is a wired-AND bus, so you only "drive" zero. + /// + /// In an oversampled implementation, a rising and falling edge de-glitcher is needed + /// for SCL and SDA. + /// + + // rise fall time cycles computation: + // At 400kHz operation, 2.5us is a cycle. "chatter" from transition should be about + // 5% of total cycle time max (just rule of thumb), so 0.125us should be the equiv + // number of cycles. + // For the demo board, a 25 MHz clock is provided, and 0.125us ~ 4 cycles + // At 100kHz operation, 10us is a cycle, so 0.5us ~ 12 cycles + parameter TRF_CYCLES = 5'd4; // number of cycles for rise/fall time + + //////////////// + ///// protocol-level state machine + //////////////// + parameter I2C_START = 14'b1 << 0; // should only pass through this state for one cycle + parameter I2C_RESTART = 14'b1 << 1; + parameter I2C_DADDR = 14'b1 << 2; + parameter I2C_ACK_DADDR = 14'b1 << 3; + parameter I2C_WR_DATA = 14'b1 << 4; + parameter I2C_RXD_SYN = 14'b1 << 5; + parameter I2C_RXD_ACK = 14'b1 << 6; + parameter I2C_ACK_WR = 14'b1 << 7; + parameter I2C_END_WR = 14'b1 << 8; + parameter I2C_TXD_SYN = 14'b1 << 9; + parameter I2C_TXD_ACK = 14'b1 << 10; + parameter I2C_RD_DATA = 14'b1 << 11; + parameter I2C_ACK_RD = 14'b1 << 12; + parameter I2C_END_RD = 14'b1 << 13; + parameter I2C_END_RD2 = 14'b1 << 14; + parameter I2C_WAITSTOP = 14'b1 << 15; + + parameter I2C_nSTATES = 16; + + reg [(I2C_nSTATES-1):0] I2C_cstate = {{(I2C_nSTATES-1){1'b0}}, 1'b1}; //current and next states + reg [(I2C_nSTATES-1):0] I2C_nstate; + +//`define SIMULATION +`ifdef SIMULATION + // synthesis translate_off + reg [8*20:1] I2C_state_ascii = "I2C_START "; + always @(I2C_cstate) begin + if (I2C_cstate == I2C_START) I2C_state_ascii <= "I2C_START "; + else if (I2C_cstate == I2C_RESTART) I2C_state_ascii <= "I2C_RESTART "; + else if (I2C_cstate == I2C_DADDR) I2C_state_ascii <= "I2C_DADDR "; + else if (I2C_cstate == I2C_ACK_DADDR) I2C_state_ascii <= "I2C_ACK_DADDR "; + else if (I2C_cstate == I2C_WR_DATA) I2C_state_ascii <= "I2C_WR_DATA "; + else if (I2C_cstate == I2C_ACK_WR) I2C_state_ascii <= "I2C_ACK_WR "; + else if (I2C_cstate == I2C_END_WR) I2C_state_ascii <= "I2C_END_WR "; + else if (I2C_cstate == I2C_RD_DATA) I2C_state_ascii <= "I2C_RD_DATA "; + else if (I2C_cstate == I2C_ACK_RD) I2C_state_ascii <= "I2C_ACK_RD "; + else if (I2C_cstate == I2C_END_RD) I2C_state_ascii <= "I2C_END_RD "; + else if (I2C_cstate == I2C_END_RD2) I2C_state_ascii <= "I2C_END_RD2 "; + else if (I2C_cstate == I2C_WAITSTOP) I2C_state_ascii <= "I2C_WAITSTOP "; + else if (I2C_cstate == I2C_RXD_SYN) I2C_state_ascii <= "I2C_RXD_SYN "; + else if (I2C_cstate == I2C_RXD_ACK) I2C_state_ascii <= "I2C_RXD_ACK "; + else if (I2C_cstate == I2C_TXD_SYN) I2C_state_ascii <= "I2C_TXD_SYN "; + else if (I2C_cstate == I2C_TXD_ACK) I2C_state_ascii <= "I2C_TXD_ACK "; + else I2C_state_ascii <= "WTF "; + end + // synthesis translate_on +`endif + + reg [3:0] I2C_bitcnt; + reg [7:0] I2C_daddr; + reg [7:0] I2C_wdata; + reg [7:0] I2C_rdata; + + reg rxd_syn_reg; + reg txd_ack_reg; + + assign rxd_data = I2C_wdata; + assign rxd_syn = rxd_syn_reg; + assign txd_ack = txd_ack_reg; + + + ////////// code begins here + always @ (posedge clk) begin + if (reset || ((SCL_cstate == SCL_HIGH) && (SDA_cstate == SDA_RISE))) // stop condition always resets + I2C_cstate <= I2C_START; + else + I2C_cstate <= I2C_nstate; + end + + always @ (*) begin + case (I2C_cstate) //synthesis parallel_case full_case + I2C_START: begin // wait for the start condition + I2C_nstate = ((SDA_cstate == SDA_FALL) && (SCL_cstate == SCL_HIGH)) ? I2C_DADDR : I2C_START; + end + I2C_RESTART: begin // repeated start moves immediately to DADDR + I2C_nstate = I2C_DADDR; + end + + // device address branch + I2C_DADDR: begin // 8 bits to get the address + I2C_nstate = ((I2C_bitcnt > 4'h7) && (SCL_cstate == SCL_FALL)) ? I2C_ACK_DADDR : I2C_DADDR; + end + I2C_ACK_DADDR: begin // depending upon W/R bit state, go to one of two branches + I2C_nstate = (SCL_cstate == SCL_FALL) ? + (I2C_daddr[7:1] == i2c_device_addr[7:1]) ? + (I2C_daddr[0] == 1'b0 ? I2C_WR_DATA : I2C_TXD_SYN) : + I2C_WAITSTOP : // !I2C_daddr match + I2C_ACK_DADDR; // !SCL_FALL + end + + // write branch + I2C_WR_DATA: begin // 8 bits to get the write data + I2C_nstate = ((SDA_cstate == SDA_FALL) && (SCL_cstate == SCL_HIGH)) ? I2C_RESTART : // repeated start + ((I2C_bitcnt > 4'h7) && (SCL_cstate == SCL_FALL)) ? I2C_RXD_SYN : I2C_WR_DATA; + end + // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + I2C_RXD_SYN: begin // put data on the coretest bus + I2C_nstate = I2C_RXD_ACK; + end + I2C_RXD_ACK: begin // wait for coretest ack + I2C_nstate = rxd_ack ? I2C_ACK_WR : I2C_RXD_ACK; + end + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + I2C_ACK_WR: begin // trigger the ack response (pull SDA low until next falling edge) + // and stay in this state until the next falling edge of SCL + I2C_nstate = (SCL_cstate == SCL_FALL) ? I2C_END_WR : I2C_ACK_WR; + end + I2C_END_WR: begin // one-cycle state to update address+1, reset SDA pulldown + I2C_nstate = I2C_WR_DATA; // SCL is now low + end + + // read branch + // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + I2C_TXD_SYN: begin // get data from the coretest bus + I2C_nstate = txd_syn ? I2C_TXD_ACK : I2C_TXD_SYN; + end + I2C_TXD_ACK: begin // send coretest ack + I2C_nstate = txd_syn ? I2C_TXD_ACK : I2C_RD_DATA; + end + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + I2C_RD_DATA: begin // 8 bits to get the read data + I2C_nstate = ((SDA_cstate == SDA_FALL) && (SCL_cstate == SCL_HIGH)) ? I2C_RESTART : // repeated start + ((I2C_bitcnt > 4'h7) && (SCL_cstate == SCL_FALL)) ? I2C_ACK_RD : I2C_RD_DATA; + end + I2C_ACK_RD: begin // wait for an (n)ack response + // need to sample (n)ack on a rising edge + I2C_nstate = (SCL_cstate == SCL_RISE) ? I2C_END_RD : I2C_ACK_RD; + end + I2C_END_RD: begin // if nack, just go to start state (don't explicitly check stop event) + // single cycle state for adr+1 update + I2C_nstate = (SDA_cstate == SDA_LOW) ? I2C_END_RD2 : I2C_START; + end + I2C_END_RD2: begin // before entering I2C_RD_DATA, we need to have seen a falling edge. + I2C_nstate = (SCL_cstate == SCL_FALL) ? I2C_RD_DATA : I2C_END_RD2; + end + + // we're not the addressed device, so we just idle until we see a stop + I2C_WAITSTOP: begin + I2C_nstate = (((SCL_cstate == SCL_HIGH) && (SDA_cstate == SDA_RISE))) ? // stop + I2C_START : + (((SCL_cstate == SCL_HIGH) && (SDA_cstate == SDA_FALL))) ? // or start + I2C_RESTART : + I2C_WAITSTOP; + end + endcase // case (cstate) + end + + always @ (posedge clk) begin + if( reset ) begin + I2C_bitcnt <= 4'b0; + I2C_daddr <= 8'b0; + I2C_wdata <= 8'b0; + SDA_pd <= 1'b0; + I2C_rdata <= 8'b0; + end else begin + case (I2C_cstate) // synthesis parallel_case full_case + I2C_START: begin // everything in reset + I2C_bitcnt <= 4'b0; + I2C_daddr <= 8'b0; + I2C_wdata <= 8'b0; + I2C_rdata <= 8'b0; + SDA_pd <= 1'b0; + end + + I2C_RESTART: begin + I2C_bitcnt <= 4'b0; + I2C_daddr <= 8'b0; + I2C_wdata <= 8'b0; + I2C_rdata <= 8'b0; + SDA_pd <= 1'b0; + end + + // get my i2c device address (am I being talked to?) + I2C_DADDR: begin // shift in the address on rising edges of clock + if( SCL_cstate == SCL_RISE ) begin + I2C_bitcnt <= I2C_bitcnt + 4'b1; + I2C_daddr[7] <= I2C_daddr[6]; + I2C_daddr[6] <= I2C_daddr[5]; + I2C_daddr[5] <= I2C_daddr[4]; + I2C_daddr[4] <= I2C_daddr[3]; + I2C_daddr[3] <= I2C_daddr[2]; + I2C_daddr[2] <= I2C_daddr[1]; + I2C_daddr[1] <= I2C_daddr[0]; + I2C_daddr[0] <= (SDA_cstate == SDA_HIGH) ? 1'b1 : 1'b0; + end else begin // we're oversampled so we need a hold-state gutter + I2C_bitcnt <= I2C_bitcnt; + I2C_daddr <= I2C_daddr; + end // else: !if( SCL_cstate == SCL_RISE ) + SDA_pd <= 1'b0; + I2C_wdata <= 8'b0; + I2C_rdata <= 8'b0; + end // case: I2C_DADDR + I2C_ACK_DADDR: begin + SDA_pd <= 1'b1; // active pull down ACK + I2C_daddr <= I2C_daddr; + I2C_bitcnt <= 4'b0; + I2C_wdata <= 8'b0; + I2C_rdata <= 8'b0; + end + + // write branch + I2C_WR_DATA: begin // shift in data on rising edges of clock + if( SCL_cstate == SCL_RISE ) begin + I2C_bitcnt <= I2C_bitcnt + 4'b1; + I2C_wdata[7] <= I2C_wdata[6]; + I2C_wdata[6] <= I2C_wdata[5]; + I2C_wdata[5] <= I2C_wdata[4]; + I2C_wdata[4] <= I2C_wdata[3]; + I2C_wdata[3] <= I2C_wdata[2]; + I2C_wdata[2] <= I2C_wdata[1]; + I2C_wdata[1] <= I2C_wdata[0]; + I2C_wdata[0] <= (SDA_cstate == SDA_HIGH) ? 1'b1 : 1'b0; + end else begin + I2C_bitcnt <= I2C_bitcnt; // hold state gutter + I2C_wdata <= I2C_wdata; + end // else: !if( SCL_cstate == SCL_RISE ) + SDA_pd <= 1'b0; + I2C_daddr <= I2C_daddr; + I2C_rdata <= I2C_rdata; + end // case: I2C_WR_DATA + // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + I2C_RXD_SYN: begin // put data on the coretest bus + rxd_syn_reg <= 1; + end + I2C_RXD_ACK: begin // wait for coretest ack + if (rxd_ack) + begin + rxd_syn_reg <= 0; + end + end + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + I2C_ACK_WR: begin + SDA_pd <= 1'b1; // active pull down ACK + I2C_daddr <= I2C_daddr; + I2C_bitcnt <= 4'b0; + I2C_wdata <= I2C_wdata; + I2C_rdata <= I2C_rdata; + end + I2C_END_WR: begin + SDA_pd <= 1'b0; // let SDA rise (host may look for this to know ack is done + I2C_bitcnt <= 4'b0; + I2C_wdata <= 8'b0; + I2C_rdata <= I2C_rdata; + I2C_daddr <= I2C_daddr; + end + + // read branch + // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv + I2C_TXD_SYN: begin // get data from the coretest bus + if (txd_syn) + begin + I2C_rdata <= txd_data; + txd_ack_reg <= 1; + end + end + I2C_TXD_ACK: begin // send coretest ack + if (!txd_syn) + begin + txd_ack_reg <= 0; + end + end + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + I2C_RD_DATA: begin // shift out data on falling edges of clock + SDA_pd <= I2C_rdata[7] ? 1'b0 : 1'b1; + if( SCL_cstate == SCL_RISE ) begin + I2C_bitcnt <= I2C_bitcnt + 4'b1; + end else begin + I2C_bitcnt <= I2C_bitcnt; // hold state gutter + end + + if( SCL_cstate == SCL_FALL ) begin + I2C_rdata[7] <= I2C_rdata[6]; + I2C_rdata[6] <= I2C_rdata[5]; + I2C_rdata[5] <= I2C_rdata[4]; + I2C_rdata[4] <= I2C_rdata[3]; + I2C_rdata[3] <= I2C_rdata[2]; + I2C_rdata[2] <= I2C_rdata[1]; + I2C_rdata[1] <= I2C_rdata[0]; + I2C_rdata[0] <= 1'b0; + end else begin + I2C_rdata <= I2C_rdata; + end // else: !if( SCL_cstate == SCL_RISE ) + I2C_daddr <= I2C_daddr; + I2C_wdata <= I2C_wdata; + end // case: I2C_RD_DATA + I2C_ACK_RD: begin + SDA_pd <= 1'b0; // in ack state don't pull down, we are listening to host + I2C_daddr <= I2C_daddr; + I2C_bitcnt <= 4'b0; + I2C_rdata <= I2C_rdata; + I2C_wdata <= I2C_wdata; + end + I2C_END_RD: begin + SDA_pd <= 1'b0; // let SDA rise (host may look for this to know ack is done + I2C_daddr <= I2C_daddr; + I2C_bitcnt <= 4'b0; + I2C_rdata <= I2C_rdata; + I2C_wdata <= I2C_wdata; + end + I2C_END_RD2: begin + SDA_pd <= 1'b0; + I2C_daddr <= 8'b0; + I2C_bitcnt <= 4'b0; + I2C_rdata <= I2C_rdata; + I2C_wdata <= I2C_wdata; + end + + I2C_WAITSTOP: begin + SDA_pd <= 1'b0; + I2C_daddr <= 8'b0; + I2C_bitcnt <= 4'b0; + I2C_rdata <= I2C_rdata; + I2C_wdata <= I2C_wdata; + end + endcase // case (cstate) + end // else: !if( reset ) + end // always @ (posedge clk or posedge reset) + + + /////////////////////////////////////////////////////////////// + /////////// low level state machines ////////////////////////// + /////////////////////////////////////////////////////////////// + + + //////////////// + ///// SCL low-level sampling state machine + //////////////// + parameter SCL_HIGH = 4'b1 << 0; // should only pass through this state for one cycle + parameter SCL_FALL = 4'b1 << 1; + parameter SCL_LOW = 4'b1 << 2; + parameter SCL_RISE = 4'b1 << 3; + parameter SCL_nSTATES = 4; + + reg [(SCL_nSTATES-1):0] SCL_cstate = {{(SCL_nSTATES-1){1'b0}}, 1'b1}; //current and next states + reg [(SCL_nSTATES-1):0] SCL_nstate; + +//`define SIMULATION +`ifdef SIMULATION + // synthesis translate_off + reg [8*20:1] SCL_state_ascii = "SCL_HIGH "; + + always @(SCL_cstate) begin + if (SCL_cstate == SCL_HIGH) SCL_state_ascii <= "SCL_HIGH "; + else if (SCL_cstate == SCL_FALL) SCL_state_ascii <= "SCL_FALL "; + else if (SCL_cstate == SCL_LOW ) SCL_state_ascii <= "SCL_LOW "; + else if (SCL_cstate == SCL_RISE) SCL_state_ascii <= "SCL_RISE "; + else SCL_state_ascii <= "WTF "; + end + // synthesis translate_on +`endif + + reg [4:0] SCL_rfcnt; + reg SCL_s, SCL_sync; + reg SDA_s, SDA_sync; + + always @ (posedge clk) begin + if (reset) + SCL_cstate <= SCL_HIGH; // always start here even if it's wrong -- easier to test + else + SCL_cstate <= SCL_nstate; + end + + always @ (*) begin + case (SCL_cstate) //synthesis parallel_case full_case + SCL_HIGH: begin + SCL_nstate = ((SCL_rfcnt > TRF_CYCLES) && (SCL_sync == 1'b0)) ? SCL_FALL : SCL_HIGH; + end + SCL_FALL: begin + SCL_nstate = SCL_LOW; + end + SCL_LOW: begin + SCL_nstate = ((SCL_rfcnt > TRF_CYCLES) && (SCL_sync == 1'b1)) ? SCL_RISE : SCL_LOW; + end + SCL_RISE: begin + SCL_nstate = SCL_HIGH; + end + endcase // case (cstate) + end // always @ (*) + + always @ (posedge clk) begin + if( reset ) begin + SCL_rfcnt <= 5'b0; + end else begin + case (SCL_cstate) // synthesis parallel_case full_case + SCL_HIGH: begin + if( SCL_sync == 1'b1 ) begin + SCL_rfcnt <= 5'b0; + end else begin + SCL_rfcnt <= SCL_rfcnt + 5'b1; + end + end + SCL_FALL: begin + SCL_rfcnt <= 5'b0; + end + SCL_LOW: begin + if( SCL_sync == 1'b0 ) begin + SCL_rfcnt <= 5'b0; + end else begin + SCL_rfcnt <= SCL_rfcnt + 5'b1; + end + end + SCL_RISE: begin + SCL_rfcnt <= 5'b0; + end + endcase // case (cstate) + end // else: !if( reset ) + end // always @ (posedge clk or posedge reset) + + + //////////////// + ///// SDA low-level sampling state machine + //////////////// + parameter SDA_HIGH = 4'b1 << 0; // should only pass through this state for one cycle + parameter SDA_FALL = 4'b1 << 1; + parameter SDA_LOW = 4'b1 << 2; + parameter SDA_RISE = 4'b1 << 3; + parameter SDA_nSTATES = 4; + + reg [(SDA_nSTATES-1):0] SDA_cstate = {{(SDA_nSTATES-1){1'b0}}, 1'b1}; //current and next states + reg [(SDA_nSTATES-1):0] SDA_nstate; + +//`define SIMULATION +`ifdef SIMULATION + // synthesis translate_off + reg [8*20:1] SDA_state_ascii = "SDA_HIGH "; + + always @(SDA_cstate) begin + if (SDA_cstate == SDA_HIGH) SDA_state_ascii <= "SDA_HIGH "; + else if (SDA_cstate == SDA_FALL) SDA_state_ascii <= "SDA_FALL "; + else if (SDA_cstate == SDA_LOW ) SDA_state_ascii <= "SDA_LOW "; + else if (SDA_cstate == SDA_RISE) SDA_state_ascii <= "SDA_RISE "; + else SDA_state_ascii <= "WTF "; + end + // synthesis translate_on +`endif + + reg [4:0] SDA_rfcnt; + + always @ (posedge clk) begin + if (reset) + SDA_cstate <= SDA_HIGH; // always start here even if it's wrong -- easier to test + else + SDA_cstate <= SDA_nstate; + end + + always @ (*) begin + case (SDA_cstate) //synthesis parallel_case full_case + SDA_HIGH: begin + SDA_nstate = ((SDA_rfcnt > TRF_CYCLES) && (SDA_sync == 1'b0)) ? SDA_FALL : SDA_HIGH; + end + SDA_FALL: begin + SDA_nstate = SDA_LOW; + end + SDA_LOW: begin + SDA_nstate = ((SDA_rfcnt > TRF_CYCLES) && (SDA_sync == 1'b1)) ? SDA_RISE : SDA_LOW; + end + SDA_RISE: begin + SDA_nstate = SDA_HIGH; + end + endcase // case (cstate) + end // always @ (*) + + always @ (posedge clk) begin + if( reset ) begin + SDA_rfcnt <= 5'b0; + end else begin + case (SDA_cstate) // synthesis parallel_case full_case + SDA_HIGH: begin + if( SDA_sync == 1'b1 ) begin + SDA_rfcnt <= 5'b0; + end else begin + SDA_rfcnt <= SDA_rfcnt + 5'b1; + end + end + SDA_FALL: begin + SDA_rfcnt <= 5'b0; + end + SDA_LOW: begin + if( SDA_sync == 1'b0 ) begin + SDA_rfcnt <= 5'b0; + end else begin + SDA_rfcnt <= SDA_rfcnt + 5'b1; + end + end + SDA_RISE: begin + SDA_rfcnt <= 5'b0; + end + endcase // case (cstate) + end // else: !if( reset ) + end // always @ (posedge clk or posedge reset) + + + + ///////////////////// + /////// synchronizers + ///////////////////// + always @ (posedge clk) begin + SCL_s <= SCL; + SCL_sync <= SCL_s; + SDA_s <= SDA; + SDA_sync <= SDA_s; + end // always @ (posedge clk or posedge reset) + +endmodule // i2c_slave |