aboutsummaryrefslogtreecommitdiffstats
path: root/common
diff options
context:
space:
mode:
authorbnewbold <bryan@octopart.com>2012-01-20 19:14:58 -0500
committerbnewbold <bryan@octopart.com>2012-01-20 19:14:58 -0500
commit889a5a5395872eeb3740d5d3281b56c7afa47a6f (patch)
treec0efd6404ffdbdfe7f88a173f0836919efdf0184 /common
downloadnetv_fpga_hdmi_overlay-889a5a5395872eeb3740d5d3281b56c7afa47a6f.tar.gz
netv_fpga_hdmi_overlay-889a5a5395872eeb3740d5d3281b56c7afa47a6f.zip
initial import from NeTV archive
This repository is simply a mirror of the file fpga/hdmi_overlay_0xD_src.tgz downloaded from http://www.kosagi.com/netv_hardware/ on Jan 19th, 2011. It seems to contain all the verilog and scripts required to build FPGA firmware for the NeTV HDMI device from Chumby/Sutajio Ko-Usagi; see http://www.kosagi.com/blog/ for more information. Licensing is vague; see ip/license.txt
Diffstat (limited to 'common')
-rwxr-xr-xcommon/DRAM16XN.v51
-rwxr-xr-xcommon/boxtiming.v136
-rwxr-xr-xcommon/i2c_slave.v864
-rwxr-xr-xcommon/i2c_slave_tb.v375
-rwxr-xr-xcommon/i2c_snoop.v625
-rwxr-xr-xcommon/i2c_snoop_edid.v603
-rwxr-xr-xcommon/i2c_snoop_tb.v418
-rwxr-xr-xcommon/i2c_squash_edid.v892
-rwxr-xr-xcommon/i2c_squash_tb.v445
-rwxr-xr-xcommon/pwm.v83
-rwxr-xr-xcommon/timing_detector.v390
11 files changed, 4882 insertions, 0 deletions
diff --git a/common/DRAM16XN.v b/common/DRAM16XN.v
new file mode 100755
index 0000000..656ec8f
--- /dev/null
+++ b/common/DRAM16XN.v
@@ -0,0 +1,51 @@
+//
+// Module: DRAM16XN
+//
+// Description: Distributed SelectRAM example
+// Dual Port 16 x N-bit
+//
+// Device: Spartan-3 Family
+//---------------------------------------------------------------------------------------
+
+module DRAM16XN #(parameter data_width = 20)
+ (
+ DATA_IN,
+ ADDRESS,
+ ADDRESS_DP,
+ WRITE_EN,
+ CLK,
+ O_DATA_OUT,
+ O_DATA_OUT_DP);
+
+input [data_width-1:0]DATA_IN;
+input [3:0] ADDRESS;
+input [3:0] ADDRESS_DP;
+input WRITE_EN;
+input CLK;
+
+output [data_width-1:0]O_DATA_OUT_DP;
+output [data_width-1:0]O_DATA_OUT;
+
+genvar i;
+generate
+ for(i = 0 ; i < data_width ; i = i + 1) begin : dram16s
+ RAM16X1D i_RAM16X1D_U(
+ .D(DATA_IN[i]), //insert input signal
+ .WE(WRITE_EN), //insert Write Enable signal
+ .WCLK(CLK), //insert Write Clock signal
+ .A0(ADDRESS[0]), //insert Address 0 signal port SPO
+ .A1(ADDRESS[1]), //insert Address 1 signal port SPO
+ .A2(ADDRESS[2]), //insert Address 2 signal port SPO
+ .A3(ADDRESS[3]), //insert Address 3 signal port SPO
+ .DPRA0(ADDRESS_DP[0]), //insert Address 0 signal dual port DPO
+ .DPRA1(ADDRESS_DP[1]), //insert Address 1 signal dual port DPO
+ .DPRA2(ADDRESS_DP[2]), //insert Address 2 signal dual port DPO
+ .DPRA3(ADDRESS_DP[3]), //insert Address 3 signal dual port DPO
+ .SPO(O_DATA_OUT[i]), //insert output signal SPO
+ .DPO(O_DATA_OUT_DP[i]) //insert output signal DPO
+ );
+ end
+endgenerate
+
+endmodule
+
diff --git a/common/boxtiming.v b/common/boxtiming.v
new file mode 100755
index 0000000..c5d2995
--- /dev/null
+++ b/common/boxtiming.v
@@ -0,0 +1,136 @@
+//////////////////////////////////////////////////////////////////////////////
+// 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.
+//
+//////////////////////////////////////////////////////////////////////////////
+`timescale 1 ns / 1ps
+
+module boxtiming (
+ input wire pclk,
+ input wire rstin,
+ input wire vsync,
+ input wire hsync,
+ input wire sync_pol, // 0 means active 0, 1 means active 1
+ input wire de,
+ input wire cv,
+ input wire [11:0] hpos,
+ input wire [11:0] hsize,
+ input wire [11:0] vpos,
+ input wire [11:0] vsize,
+ output reg box_active
+ );
+
+ reg [11:0] hcount;
+ reg [11:0] vcount;
+
+ reg hsync_v; // active when high
+ reg hsync_v2;
+ reg vsync_v;
+ reg vsync_v2;
+ reg de_d;
+
+ reg active;
+
+ wire hsync_rising;
+ wire vsync_rising;
+ wire de_rising;
+ wire de_falling;
+
+ always @(posedge pclk or posedge rstin) begin
+ if( rstin ) begin
+ hsync_v <= 0;
+ vsync_v <= 0;
+
+ hsync_v2 <= 0;
+ vsync_v2 <= 0;
+
+ de_d <= 0;
+ end else begin
+ de_d <= de;
+
+ if( cv ) begin
+ hsync_v <= hsync ^ !sync_pol;
+ vsync_v <= vsync ^ !sync_pol;
+ end else begin
+ hsync_v <= hsync_v;
+ vsync_v <= vsync_v;
+ end
+
+ hsync_v2 <= hsync_v; // just a delayed version
+ vsync_v2 <= vsync_v;
+ end // else: !if( rstin )
+ end // always @ (posedge pclk or posedge rstin)
+ assign hsync_rising = hsync_v & !hsync_v2;
+ assign vsync_rising = vsync_v & !vsync_v2;
+ assign de_rising = de & !de_d;
+ assign de_falling = !de & de_d;
+
+ always @(posedge pclk or posedge rstin) begin
+ if( rstin ) begin
+ hcount <= 0;
+ end else begin
+ if( de_rising ) begin
+ hcount <= 12'b0000_0000_0000;
+ end else begin
+ if( de ) begin
+ hcount <= hcount + 12'b0000_0000_0001;
+ end else begin
+ hcount <= hcount;
+ end
+ end
+ end // else: !if( rstin )
+ end // always @ (posedge pclk or posedge rstin)
+
+ always @(posedge pclk or posedge rstin) begin
+ if( rstin ) begin
+ vcount <= 0;
+ end else begin
+ if( vsync_rising ) begin
+ vcount <= 12'b0000_0000_0000;
+ end else begin
+ if( de_falling ) begin // this may be a bug but I think it's worked around elsewhere
+ vcount <= vcount + 12'b0000_0000_0001;
+ end else begin
+ vcount <= vcount;
+ end
+ end
+ end // else: !if( rstin )
+ end // always @ (posedge pclk or posedge rstin)
+
+ always @(posedge pclk or posedge rstin) begin
+ if( rstin ) begin
+ active <= 0;
+ end else begin
+ if( (hcount >= hpos) && (hcount < (hpos + hsize)) &&
+ (vcount >= vpos) && (vcount < (vpos + vsize)) ) begin
+ active <= 1'b1;
+ end else begin
+ active <= 1'b0;
+ end
+ end
+
+ box_active <= active;
+ end // always @ (posedge pclk or posedge rstin)
+
+endmodule
+ \ No newline at end of file
diff --git a/common/i2c_slave.v b/common/i2c_slave.v
new file mode 100755
index 0000000..d8672f7
--- /dev/null
+++ b/common/i2c_slave.v
@@ -0,0 +1,864 @@
+//////////////////////////////////////////////////////////////////////////////
+// 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 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:
+// IBUF IBUF_sda (.I(SDA), .O(SDA_int));
+// IOBUF #(.DRIVE(12), .SLEW("SLOW")) IOBUF_sda (.IO(SDA), .I(1'b0), .T(!SDA_pd));
+//
+///////////
+`timescale 1 ns / 1 ps
+
+module i2c_slave (
+ // external host interface
+ input wire SCL, // the SCL pin state
+ output wire SCL_pd, // signals to IOB to pull the SCL bus low
+ input wire SDA,
+ output reg SDA_pd,
+ output wire SDA_pu, // for overriding SDA...in the future.
+
+ input wire clk, // internal FPGA clock
+ input wire reset, // internal FPGA reset
+ // i2c configuration
+ input wire [7:0] i2c_device_addr,
+
+ // internal slave interface
+ input wire [7:0] reg_addr,
+ input wire wr_stb,
+ input wire [7:0] reg_data_in,
+ output wire [7:0] reg_0,
+ output wire [7:0] reg_1,
+ output wire [7:0] reg_2,
+ output wire [7:0] reg_3,
+ output wire [7:0] reg_4,
+ output wire [7:0] reg_5,
+ output wire [7:0] reg_6,
+ output wire [7:0] reg_7,
+ output wire [7:0] reg_8,
+ output wire [7:0] reg_9,
+ output wire [7:0] reg_a,
+ output wire [7:0] reg_b,
+ output wire [7:0] reg_c,
+ output wire [7:0] reg_d,
+ output wire [7:0] reg_e,
+ output wire [7:0] reg_f,
+
+ input wire [7:0] reg_18, // note this is input now, not output
+
+ output wire [7:0] reg_19,
+ output wire [7:0] reg_1a,
+ output wire [7:0] reg_1b,
+ output wire [7:0] reg_1c,
+ output wire [7:0] reg_1d,
+ output wire [7:0] reg_1e,
+ output wire [7:0] reg_1f,
+
+ input wire [7:0] reg_10,
+
+ output wire [7:0] reg_11,
+ output wire [7:0] reg_12,
+
+ output wire [7:0] reg_13,
+ output wire [7:0] reg_14,
+ output wire [7:0] reg_15,
+ output wire [7:0] reg_16,
+ output wire [7:0] reg_17,
+
+ input wire [7:0] reg_20, // read-only bank starts here
+ input wire [7:0] reg_21,
+ input wire [7:0] reg_22,
+ input wire [7:0] reg_23,
+ input wire [7:0] reg_24,
+ input wire [7:0] reg_25,
+ input wire [7:0] reg_26,
+ input wire [7:0] reg_27,
+ input wire [7:0] reg_28,
+ input wire [7:0] reg_29,
+ input wire [7:0] reg_2a,
+ input wire [7:0] reg_2b,
+ input wire [7:0] reg_2c,
+ input wire [7:0] reg_2d,
+ input wire [7:0] reg_2e,
+ input wire [7:0] reg_2f,
+ input wire [7:0] reg_30,
+ input wire [7:0] reg_31,
+ input wire [7:0] reg_32,
+ input wire [7:0] reg_33,
+ input wire [7:0] reg_34,
+ input wire [7:0] reg_35,
+ input wire [7:0] reg_36,
+ input wire [7:0] reg_37,
+ input wire [7:0] reg_38,
+ input wire [7:0] reg_39,
+ input wire [7:0] reg_3a,
+ input wire [7:0] reg_3b,
+ input wire [7:0] reg_3c,
+ input wire [7:0] reg_3d,
+
+ input wire [7:0] reg_3e,
+ input wire [7:0] reg_3f
+ );
+
+ /////// 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
+
+ // just some tie-offs for future functionality not yet implemented...
+ assign SDA_pu = 1'b0;
+ assign SCL_pd = 1'b0;
+
+ ////////////////
+ ///// 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_ADDR = 14'b1 << 4;
+ parameter I2C_ACK_ADDR = 14'b1 << 5;
+ parameter I2C_WR_DATA = 14'b1 << 6;
+ parameter I2C_ACK_WR = 14'b1 << 7;
+ parameter I2C_END_WR = 14'b1 << 8;
+ parameter I2C_RD_DATA = 14'b1 << 9;
+ parameter I2C_ACK_RD = 14'b1 << 10;
+ parameter I2C_END_RD = 14'b1 << 11;
+ parameter I2C_END_RD2 = 14'b1 << 12;
+ parameter I2C_WAITSTOP = 14'b1 << 13;
+
+ parameter I2C_nSTATES = 14;
+
+ 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_ADDR) I2C_state_ascii <= "I2C_ADDR ";
+ else if (I2C_cstate == I2C_ACK_ADDR) I2C_state_ascii <= "I2C_ACK_ADDR ";
+ 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 I2C_state_ascii <= "WTF ";
+ end
+ // synthesis translate_on
+`endif
+
+ reg [3:0] I2C_bitcnt;
+ reg [7:0] I2C_addr;
+ reg [7:0] I2C_daddr;
+ reg [7:0] I2C_wdata;
+ reg [7:0] I2C_rdata;
+ reg I2C_reg_update;
+
+ ///// register block definitions
+ parameter RAM_WIDTH = 8;
+ parameter RAM_ADDR_BITS = 5; // note parameter width exception in reg_a* assign block below
+
+ reg [RAM_WIDTH-1:0] I2C_regblock [(2**RAM_ADDR_BITS)-1:0];
+ reg [RAM_WIDTH-1:0] I2C_regread_async;
+
+ wire [RAM_ADDR_BITS-1:0] I2C_ramaddr;
+
+ reg wr_stb_d;
+
+ ////////// 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 <=#1 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
+ 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_ADDR : I2C_RD_DATA) :
+ I2C_WAITSTOP : // !I2C_daddr match
+ I2C_ACK_DADDR; // !SCL_FALL
+ end
+
+ // device address branch
+ I2C_ADDR: begin
+ I2C_nstate = ((I2C_bitcnt > 4'h7) && (SCL_cstate == SCL_FALL)) ? I2C_ACK_ADDR : I2C_ADDR;
+ end
+ I2C_ACK_ADDR: begin
+ I2C_nstate = (SCL_cstate == SCL_FALL) ? I2C_WR_DATA : I2C_ACK_ADDR;
+ 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_ACK_WR : I2C_WR_DATA;
+ 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
+ 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 or posedge reset) begin
+ if( reset ) begin
+ I2C_bitcnt <=#1 4'b0;
+ I2C_daddr <=#1 8'b0;
+ I2C_wdata <=#1 8'b0;
+ SDA_pd <=#1 1'b0;
+ I2C_reg_update <=#1 1'b0;
+ I2C_rdata <=#1 8'b0;
+ I2C_addr <=#1 8'b0; // this persists across transactions
+ end else begin
+ case (I2C_cstate) // synthesis parallel_case full_case
+ I2C_START: begin // everything in reset
+ I2C_bitcnt <=#1 4'b0;
+ I2C_daddr <=#1 8'b0;
+ I2C_wdata <=#1 8'b0;
+ I2C_rdata <=#1 8'b0;
+ SDA_pd <=#1 1'b0;
+ I2C_reg_update <=#1 1'b0;
+ I2C_addr <=#1 I2C_addr;
+ end
+
+ I2C_RESTART: begin
+ I2C_bitcnt <=#1 4'b0;
+ I2C_daddr <=#1 8'b0;
+ I2C_wdata <=#1 8'b0;
+ I2C_rdata <=#1 8'b0;
+ SDA_pd <=#1 1'b0;
+ I2C_reg_update <=#1 1'b0;
+ I2C_addr <=#1 I2C_addr;
+ 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 <=#1 I2C_bitcnt + 4'b1;
+ I2C_daddr[7] <=#1 I2C_daddr[6];
+ I2C_daddr[6] <=#1 I2C_daddr[5];
+ I2C_daddr[5] <=#1 I2C_daddr[4];
+ I2C_daddr[4] <=#1 I2C_daddr[3];
+ I2C_daddr[3] <=#1 I2C_daddr[2];
+ I2C_daddr[2] <=#1 I2C_daddr[1];
+ I2C_daddr[1] <=#1 I2C_daddr[0];
+ I2C_daddr[0] <=#1 (SDA_cstate == SDA_HIGH) ? 1'b1 : 1'b0;
+ end else begin // we're oversampled so we need a hold-state gutter
+ I2C_bitcnt <=#1 I2C_bitcnt;
+ I2C_daddr <=#1 I2C_daddr;
+ end // else: !if( SCL_cstate == SCL_RISE )
+ SDA_pd <=#1 1'b0;
+ I2C_wdata <=#1 8'b0;
+ I2C_rdata <=#1 8'b0;
+ I2C_reg_update <=#1 1'b0;
+ I2C_addr <=#1 I2C_addr;
+ end // case: I2C_DADDR
+ I2C_ACK_DADDR: begin
+ SDA_pd <=#1 1'b1; // active pull down ACK
+ I2C_daddr <=#1 I2C_daddr;
+ I2C_bitcnt <=#1 4'b0;
+ I2C_wdata <=#1 8'b0;
+ I2C_rdata <=#1 I2C_regread_async;
+ I2C_reg_update <=#1 1'b0;
+ I2C_addr <=#1 I2C_addr;
+ end
+
+ // get my i2c "write" address (what we want to access inside me)
+ I2C_ADDR: begin
+ if( SCL_cstate == SCL_RISE ) begin
+ I2C_bitcnt <=#1 I2C_bitcnt + 4'b1;
+ I2C_addr[7] <=#1 I2C_addr[6];
+ I2C_addr[6] <=#1 I2C_addr[5];
+ I2C_addr[5] <=#1 I2C_addr[4];
+ I2C_addr[4] <=#1 I2C_addr[3];
+ I2C_addr[3] <=#1 I2C_addr[2];
+ I2C_addr[2] <=#1 I2C_addr[1];
+ I2C_addr[1] <=#1 I2C_addr[0];
+ I2C_addr[0] <=#1 (SDA_cstate == SDA_HIGH) ? 1'b1 : 1'b0;
+ end else begin // we're oversampled so we need a hold-state gutter
+ I2C_bitcnt <=#1 I2C_bitcnt;
+ I2C_addr <=#1 I2C_addr;
+ end // else: !if( SCL_cstate == SCL_RISE )
+ SDA_pd <=#1 1'b0;
+ I2C_wdata <=#1 8'b0;
+ I2C_rdata <=#1 8'b0;
+ I2C_reg_update <=#1 1'b0;
+ I2C_daddr <=#1 I2C_daddr;
+ end // case: I2C_ADDR
+ I2C_ACK_ADDR: begin
+ SDA_pd <=#1 1'b1; // active pull down ACK
+ I2C_daddr <=#1 I2C_daddr;
+ I2C_bitcnt <=#1 4'b0;
+ I2C_wdata <=#1 8'b0;
+ I2C_rdata <=#1 I2C_regread_async; // update my read data here
+ I2C_reg_update <=#1 1'b0;
+ I2C_addr <=#1 I2C_addr;
+ end
+
+
+ // write branch
+ I2C_WR_DATA: begin // shift in data on rising edges of clock
+ if( SCL_cstate == SCL_RISE ) begin
+ I2C_bitcnt <=#1 I2C_bitcnt + 4'b1;
+ I2C_wdata[7] <=#1 I2C_wdata[6];
+ I2C_wdata[6] <=#1 I2C_wdata[5];
+ I2C_wdata[5] <=#1 I2C_wdata[4];
+ I2C_wdata[4] <=#1 I2C_wdata[3];
+ I2C_wdata[3] <=#1 I2C_wdata[2];
+ I2C_wdata[2] <=#1 I2C_wdata[1];
+ I2C_wdata[1] <=#1 I2C_wdata[0];
+ I2C_wdata[0] <=#1 (SDA_cstate == SDA_HIGH) ? 1'b1 : 1'b0;
+ end else begin
+ I2C_bitcnt <=#1 I2C_bitcnt; // hold state gutter
+ I2C_wdata <=#1 I2C_wdata;
+ end // else: !if( SCL_cstate == SCL_RISE )
+ SDA_pd <=#1 1'b0;
+ I2C_daddr <=#1 I2C_daddr;
+ I2C_reg_update <=#1 1'b0;
+ I2C_rdata <=#1 I2C_rdata;
+ I2C_addr <=#1 I2C_addr;
+ end // case: I2C_WR_DATA
+ I2C_ACK_WR: begin
+ SDA_pd <=#1 1'b1; // active pull down ACK
+ I2C_daddr <=#1 I2C_daddr;
+ I2C_bitcnt <=#1 4'b0;
+ I2C_wdata <=#1 I2C_wdata;
+ I2C_reg_update <=#1 1'b1; // write the data now (over and over again while in state)
+ I2C_rdata <=#1 I2C_rdata;
+ I2C_addr <=#1 I2C_addr;
+ end
+ I2C_END_WR: begin
+ SDA_pd <=#1 1'b0; // let SDA rise (host may look for this to know ack is done
+ I2C_addr <=#1 I2C_addr + 8'b1; // this is a one-cycle state so this is safe
+ I2C_bitcnt <=#1 4'b0;
+ I2C_wdata <=#1 8'b0;
+ I2C_rdata <=#1 I2C_rdata;
+ I2C_reg_update <=#1 1'b0;
+ I2C_daddr <=#1 I2C_daddr;
+ end
+
+ // read branch
+ I2C_RD_DATA: begin // shift out data on falling edges of clock
+ SDA_pd <=#1 I2C_rdata[7] ? 1'b0 : 1'b1;
+ if( SCL_cstate == SCL_RISE ) begin
+ I2C_bitcnt <=#1 I2C_bitcnt + 4'b1;
+ end else begin
+ I2C_bitcnt <=#1 I2C_bitcnt; // hold state gutter
+ end
+
+ if( SCL_cstate == SCL_FALL ) begin
+ I2C_rdata[7] <=#1 I2C_rdata[6];
+ I2C_rdata[6] <=#1 I2C_rdata[5];
+ I2C_rdata[5] <=#1 I2C_rdata[4];
+ I2C_rdata[4] <=#1 I2C_rdata[3];
+ I2C_rdata[3] <=#1 I2C_rdata[2];
+ I2C_rdata[2] <=#1 I2C_rdata[1];
+ I2C_rdata[1] <=#1 I2C_rdata[0];
+ I2C_rdata[0] <=#1 1'b0;
+ end else begin
+ I2C_rdata <=#1 I2C_rdata;
+ end // else: !if( SCL_cstate == SCL_RISE )
+ I2C_daddr <=#1 I2C_daddr;
+ I2C_reg_update <=#1 1'b0;
+ I2C_wdata <=#1 I2C_wdata;
+ I2C_addr <=#1 I2C_addr;
+ end // case: I2C_RD_DATA
+ I2C_ACK_RD: begin
+ SDA_pd <=#1 1'b0; // in ack state don't pull down, we are listening to host
+ I2C_daddr <=#1 I2C_daddr;
+ I2C_bitcnt <=#1 4'b0;
+ I2C_rdata <=#1 I2C_rdata;
+ I2C_reg_update <=#1 1'b0;
+ I2C_wdata <=#1 I2C_wdata;
+ I2C_addr <=#1 I2C_addr;
+ end
+ I2C_END_RD: begin
+ SDA_pd <=#1 1'b0; // let SDA rise (host may look for this to know ack is done
+ I2C_addr <=#1 I2C_addr + 8'b1; // this is a one-cycle state so this is safe
+ I2C_bitcnt <=#1 4'b0;
+ I2C_rdata <=#1 I2C_rdata;
+ I2C_reg_update <=#1 1'b0;
+ I2C_wdata <=#1 I2C_wdata;
+ I2C_daddr <=#1 I2C_daddr;
+ end
+ I2C_END_RD2: begin
+ SDA_pd <=#1 1'b0;
+ I2C_daddr <=#1 8'b0;
+ I2C_bitcnt <=#1 4'b0;
+ I2C_rdata <=#1 I2C_regread_async; // update my read data here
+ I2C_reg_update <=#1 1'b0;
+ I2C_wdata <=#1 I2C_wdata;
+ I2C_addr <=#1 I2C_addr;
+ end
+
+ I2C_WAITSTOP: begin
+ SDA_pd <=#1 1'b0;
+ I2C_daddr <=#1 8'b0;
+ I2C_bitcnt <=#1 4'b0;
+ I2C_rdata <=#1 I2C_rdata;
+ I2C_reg_update <=#1 1'b0;
+ I2C_wdata <=#1 I2C_wdata;
+ I2C_addr <=#1 I2C_addr;
+ end
+ endcase // case (cstate)
+ end // else: !if( reset )
+ end // always @ (posedge clk or posedge reset)
+
+
+ ////////////////
+ ///// register bank management
+ ////////////////
+ always @(posedge clk or posedge reset) begin
+ wr_stb_d <= wr_stb;
+ if( reset ) begin
+// I2C_regblock[5'hc] <= 8'h36;
+// I2C_regblock[5'hd] <= 8'h22;
+// I2C_regblock[5'he] <= 8'h12;
+// I2C_regblock[5'h0] <= 8'h80;
+ // nothing....adding in initializations really burn a lot of resources.
+ end else if (wr_stb & !wr_stb_d) begin // only act on the rising pulse of wr_stb
+ I2C_regblock[reg_addr] <= reg_data_in; // vestigal remnant?? changes programming model
+ // slightly, need to look into this....
+ end else if (I2C_reg_update) begin // this should be multiple cycles
+ I2C_regblock[I2C_ramaddr] <= I2C_wdata;
+ end
+ end
+
+ always @(*) begin
+ case (I2C_addr)
+ 6'h10: begin
+ I2C_regread_async = reg_10;
+ end
+ 6'h02: begin
+ I2C_regread_async = reg_data_in; /// this is a vestigal remnant
+ end
+ 6'h18: begin
+ I2C_regread_async = reg_18;
+ end
+
+ 6'h20: begin
+ I2C_regread_async = reg_20;
+ end
+ 6'h21: begin
+ I2C_regread_async = reg_21;
+ end
+ 6'h22: begin
+ I2C_regread_async = reg_22;
+ end
+ 6'h23: begin
+ I2C_regread_async = reg_23;
+ end
+ 6'h24: begin
+ I2C_regread_async = reg_24;
+ end
+ 6'h25: begin
+ I2C_regread_async = reg_25;
+ end
+ 6'h26: begin
+ I2C_regread_async = reg_26;
+ end
+ 6'h27: begin
+ I2C_regread_async = reg_27;
+ end
+ 6'h28: begin
+ I2C_regread_async = reg_28;
+ end
+ 6'h29: begin
+ I2C_regread_async = reg_29;
+ end
+ 6'h2a: begin
+ I2C_regread_async = reg_2a;
+ end
+ 6'h2b: begin
+ I2C_regread_async = reg_2b;
+ end
+ 6'h2c: begin
+ I2C_regread_async = reg_2c;
+ end
+ 6'h2d: begin
+ I2C_regread_async = reg_2d;
+ end
+ 6'h2e: begin
+ I2C_regread_async = reg_2e;
+ end
+ 6'h2f: begin
+ I2C_regread_async = reg_2f;
+ end
+
+ 6'h30: begin
+ I2C_regread_async = reg_30;
+ end
+ 6'h31: begin
+ I2C_regread_async = reg_31;
+ end
+ 6'h32: begin
+ I2C_regread_async = reg_32;
+ end
+ 6'h33: begin
+ I2C_regread_async = reg_33;
+ end
+ 6'h34: begin
+ I2C_regread_async = reg_34;
+ end
+ 6'h35: begin
+ I2C_regread_async = reg_35;
+ end
+ 6'h36: begin
+ I2C_regread_async = reg_36;
+ end
+ 6'h37: begin
+ I2C_regread_async = reg_37;
+ end
+
+ 6'h38: begin
+ I2C_regread_async = reg_38;
+ end
+ 6'h39: begin
+ I2C_regread_async = reg_39;
+ end
+ 6'h3a: begin
+ I2C_regread_async = reg_3a;
+ end
+ 6'h3b: begin
+ I2C_regread_async = reg_3b;
+ end
+ 6'h3c: begin
+ I2C_regread_async = reg_3c;
+ end
+ 6'h3d: begin
+ I2C_regread_async = reg_3d;
+ end
+
+ 6'h3e: begin
+ I2C_regread_async = reg_3e;
+ end
+ 6'h3f: begin
+ I2C_regread_async = reg_3f;
+ end
+
+ default: begin
+ I2C_regread_async = I2C_regblock[I2C_ramaddr];
+ end
+ endcase // case I2C_ramaddr
+ end // always @ (*)
+
+ assign I2C_ramaddr = I2C_addr[RAM_ADDR_BITS-1:0];
+
+ ///////// ick, had to hard-code the width against RAM_ADDR_BITS which is parameterized
+ assign reg_0 = I2C_regblock[5'h0];
+ assign reg_1 = I2C_regblock[5'h1];
+ assign reg_2 = I2C_regblock[5'h2];
+ assign reg_3 = I2C_regblock[5'h3];
+ assign reg_4 = I2C_regblock[5'h4];
+ assign reg_5 = I2C_regblock[5'h5];
+ assign reg_6 = I2C_regblock[5'h6];
+ assign reg_7 = I2C_regblock[5'h7];
+ assign reg_8 = I2C_regblock[5'h8];
+ assign reg_9 = I2C_regblock[5'h9];
+ assign reg_a = I2C_regblock[5'ha];
+ assign reg_b = I2C_regblock[5'hb];
+
+ assign reg_c = I2C_regblock[5'hc];
+ assign reg_d = I2C_regblock[5'hd];
+ assign reg_e = I2C_regblock[5'he];
+ assign reg_f = I2C_regblock[5'hf];
+
+ assign reg_11 = I2C_regblock[5'h11];
+ assign reg_12 = I2C_regblock[5'h12];
+
+ assign reg_13 = I2C_regblock[5'h13];
+ assign reg_14 = I2C_regblock[5'h14];
+
+ assign reg_15 = I2C_regblock[5'h15];
+ assign reg_16 = I2C_regblock[5'h16];
+ assign reg_17 = I2C_regblock[5'h17];
+
+// assign reg_18 = I2C_regblock[5'h18];
+
+ assign reg_19 = I2C_regblock[5'h19]; // lsb of Km
+ assign reg_1a = I2C_regblock[5'h1a];
+ assign reg_1b = I2C_regblock[5'h1b];
+ assign reg_1c = I2C_regblock[5'h1c];
+ assign reg_1d = I2C_regblock[5'h1d];
+ assign reg_1e = I2C_regblock[5'h1e];
+ assign reg_1f = I2C_regblock[5'h1f]; // msb of Km
+
+ ////////////////
+ ///// 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 or posedge reset) begin
+ if (reset)
+ SCL_cstate <= SCL_HIGH; // always start here even if it's wrong -- easier to test
+ else
+ SCL_cstate <=#1 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 or posedge reset) begin
+ if( reset ) begin
+ SCL_rfcnt <=#1 5'b0;
+ end else begin
+ case (SCL_cstate) // synthesis parallel_case full_case
+ SCL_HIGH: begin
+ if( SCL_sync == 1'b1 ) begin
+ SCL_rfcnt <=#1 5'b0;
+ end else begin
+ SCL_rfcnt <=#1 SCL_rfcnt + 5'b1;
+ end
+ end
+ SCL_FALL: begin
+ SCL_rfcnt <=#1 5'b0;
+ end
+ SCL_LOW: begin
+ if( SCL_sync == 1'b0 ) begin
+ SCL_rfcnt <=#1 5'b0;
+ end else begin
+ SCL_rfcnt <=#1 SCL_rfcnt + 5'b1;
+ end
+ end
+ SCL_RISE: begin
+ SCL_rfcnt <=#1 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 or posedge reset) begin
+ if (reset)
+ SDA_cstate <= SDA_HIGH; // always start here even if it's wrong -- easier to test
+ else
+ SDA_cstate <=#1 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 or posedge reset) begin
+ if( reset ) begin
+ SDA_rfcnt <=#1 5'b0;
+ end else begin
+ case (SDA_cstate) // synthesis parallel_case full_case
+ SDA_HIGH: begin
+ if( SDA_sync == 1'b1 ) begin
+ SDA_rfcnt <=#1 5'b0;
+ end else begin
+ SDA_rfcnt <=#1 SDA_rfcnt + 5'b1;
+ end
+ end
+ SDA_FALL: begin
+ SDA_rfcnt <=#1 5'b0;
+ end
+ SDA_LOW: begin
+ if( SDA_sync == 1'b0 ) begin
+ SDA_rfcnt <=#1 5'b0;
+ end else begin
+ SDA_rfcnt <=#1 SDA_rfcnt + 5'b1;
+ end
+ end
+ SDA_RISE: begin
+ SDA_rfcnt <=#1 5'b0;
+ end
+ endcase // case (cstate)
+ end // else: !if( reset )
+ end // always @ (posedge clk or posedge reset)
+
+
+
+ /////////////////////
+ /////// synchronizers
+ /////////////////////
+ always @ (posedge clk or posedge reset) begin
+ if (reset) begin
+ SCL_s <= 0;
+ SCL_sync <= 0;
+ SDA_s <= 0;
+ SDA_sync <= 0;
+ end else begin
+ SCL_s <= SCL;
+ SCL_sync <= SCL_s;
+ SDA_s <= SDA;
+ SDA_sync <= SDA_s;
+ end // else: !if(reset)
+ end // always @ (posedge clk or posedge reset)
+
+endmodule // i2c_slave
diff --git a/common/i2c_slave_tb.v b/common/i2c_slave_tb.v
new file mode 100755
index 0000000..1713aa9
--- /dev/null
+++ b/common/i2c_slave_tb.v
@@ -0,0 +1,375 @@
+//////////////////////////////////////////////////////////////////////////////
+// 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.
+//
+//////////////////////////////////////////////////////////////////////////////
+`timescale 1ns / 1ps
+
+////////////////////////////////////////////////////////////////////////////////
+// Company:
+// Engineer:
+//
+// Create Date: 12:30:45 04/05/2011
+// Design Name: i2c_slave
+// Module Name: C:/largework/fpga/hdmi/impl4/common/i2c_slave_tb.v
+// Project Name: impl4
+// Target Device:
+// Tool versions:
+// Description:
+//
+// Verilog Test Fixture created by ISE for module: i2c_slave
+//
+// Dependencies:
+//
+// Revision:
+// Revision 0.01 - File Created
+// Additional Comments:
+//
+////////////////////////////////////////////////////////////////////////////////
+
+module i2c_slave_tb;
+
+ reg SDA; // physical wire state
+ reg SCL;
+
+ // Inputs
+ reg clk;
+ reg reset;
+ reg [7:0] i2c_device_addr;
+ reg [7:0] reg_addr;
+ reg wr_stb;
+ reg [7:0] reg_data_in;
+
+ // Outputs
+ wire SCL_pd;
+ wire SDA_pd;
+ wire SDA_pu;
+ wire [7:0] reg_a0;
+ wire [7:0] reg_a1;
+ wire [7:0] reg_a2;
+ wire [7:0] reg_a3;
+
+ // Instantiate the Unit Under Test (UUT)
+ i2c_slave uut (
+ .SCL(SCL),
+ .SCL_pd(SCL_pd),
+ .SDA(SDA),
+ .SDA_pd(SDA_pd),
+ .SDA_pu(SDA_pu),
+ .clk(clk),
+ .reset(reset),
+ .i2c_device_addr(i2c_device_addr),
+ .reg_addr(reg_addr),
+ .wr_stb(wr_stb),
+ .reg_data_in(reg_data_in),
+ .reg_0(reg_a0),
+ .reg_1(reg_a1),
+ .reg_2(reg_a2),
+ .reg_3(reg_a3)
+ );
+
+ reg sda_host; // what the host is driving to
+ reg scl_host;
+ reg ack; // what the ack state is
+ reg [7:0] readdata;
+
+// always @(SCL_pd, SDA_pd, SDA_pu, sda_host, scl_host) begin
+ always @(*) begin // lazy
+ // scl equations
+ case( {SCL_pd, scl_host} )
+ 2'b00: SCL <= 1'b0;
+ 2'b01: SCL <= 1'b1;
+ 2'b10: SCL <= 1'b0;
+ // conflict case
+ 2'b11: SCL <= 1'bX;
+ // handle tristate case
+ 2'b0Z: SCL <= 1'b1;
+ 2'b1Z: SCL <= 1'b0;
+ default: SCL <= 1'bX;
+ endcase // case ( {SCL_pd, scl_host} )
+
+ case( {SDA_pd, SDA_pu, sda_host} )
+ 3'b000: SDA <= 1'b0;
+ 3'b001: SDA <= 1'b1;
+ 3'b010: SDA <= 1'bX; // change to 1'b1 for override
+ 3'b011: SDA <= 1'b1;
+ 3'b100: SDA <= 1'b0;
+ 3'b101: SDA <= 1'bX; // change to 1'b0 for override
+ 3'b110: SDA <= 1'bX;
+ 3'b111: SDA <= 1'bX;
+
+ // tristate case
+ 3'b00Z: SDA <= 1'b1;
+ 3'b01Z: SDA <= 1'b1;
+ 3'b10Z: SDA <= 1'b0;
+ 3'b11Z: SDA <= 1'bX;
+ endcase // case ( {SDA_pd, SDA_pu, sda_host} )
+ end
+
+ parameter PERIOD = 16'd40; // 25 MHz
+ parameter I2C_PD = 16'd2464; // make it odd to try and catch non-phase synced issues
+ parameter I2C_TH = 16'd114;
+ parameter I2C_TS = 16'd217;
+
+ always begin
+ clk = 1'b0;
+ #(PERIOD/2) clk = 1'b1;
+ #(PERIOD/2);
+ end
+
+ task I2C_idle;
+ begin
+ scl_host = 1'bZ;
+ sda_host = 1'bZ;
+ #I2C_PD;
+ end
+ endtask // I2C_idle
+
+ task I2C_start;
+ begin
+ scl_host = 1'bZ;
+ sda_host = 1'bZ;
+ #(I2C_PD/2);
+ scl_host = 1'bZ;
+ sda_host = 1'b0;
+ #(I2C_PD/2);
+ end
+ endtask // I2C_start
+
+ task I2C_stop;
+ begin
+ scl_host = 1'bZ;
+ sda_host = 1'b0;
+ #(I2C_PD/2);
+ scl_host = 1'bZ;
+ sda_host = 1'bZ;
+ #(I2C_PD/2);
+ end
+ endtask // I2C_start
+
+ task I2C_tx_bit; // tx from host ( from testbench )
+ input bitval;
+
+ begin
+ scl_host = 1'b0;
+ #I2C_TH;
+ sda_host = bitval;
+ #(I2C_PD/2);
+ scl_host = 1'bZ;
+ sda_host = bitval;
+ #(I2C_PD/2);
+ end
+ endtask // I2C_tx_bit
+
+ task I2C_rx_bit; // rx to host ( to testbench )
+ output bitval;
+
+ begin
+ scl_host = 1'b0;
+ #(I2C_TH/2);
+ sda_host = 1'bz;
+ #(I2C_PD/2);
+ scl_host = 1'bZ;
+ sda_host = 1'bz;
+ #1;
+ bitval = SDA;
+ #(I2C_PD/2);
+ end
+ endtask // I2C_start
+
+ task I2C_ack_low;
+ begin
+ scl_host = 1'b0;
+ #(I2C_PD/2);
+ end
+ endtask // I2C_ack_low
+
+
+ task I2C_tx_daddr;
+ input [7:0] daddr;
+ output rack;
+
+ begin
+ I2C_tx_bit( daddr[7] );
+ I2C_tx_bit( daddr[6] );
+ I2C_tx_bit( daddr[5] );
+ I2C_tx_bit( daddr[4] );
+ I2C_tx_bit( daddr[3] );
+ I2C_tx_bit( daddr[2] );
+ I2C_tx_bit( daddr[1] );
+ I2C_tx_bit( daddr[0] );
+ I2C_rx_bit(rack);
+ I2C_ack_low();
+ end
+ endtask // I2C_TX_DADDR
+
+ task I2C_rx_daddr;
+ output [7:0] daddr;
+ input nack;
+
+ begin
+ I2C_rx_bit(daddr[7]);
+ I2C_rx_bit(daddr[6]);
+ I2C_rx_bit(daddr[5]);
+ I2C_rx_bit(daddr[4]);
+ I2C_rx_bit(daddr[3]);
+ I2C_rx_bit(daddr[2]);
+ I2C_rx_bit(daddr[1]);
+ I2C_rx_bit(daddr[0]);
+ I2C_tx_bit( nack );
+ I2C_ack_low();
+ end
+ endtask // I2C_RX_DADDR
+
+ initial begin
+ // Initialize Inputs
+ clk = 0;
+ reset = 0;
+ i2c_device_addr = 8'h72;
+ reg_addr = 0;
+ wr_stb = 0;
+ reg_data_in = 0;
+
+ $stop;
+
+ I2C_idle();
+ // run an actual reset cycle
+ #(PERIOD*4);
+ reset = 1;
+ #(PERIOD*4);
+ reset = 0;
+ #(PERIOD*4);
+
+ // now pre-set a few I2C registers
+ reg_addr = 0;
+ wr_stb = 1;
+ reg_data_in = 8'hDE;
+ #(PERIOD*4);
+
+ wr_stb = 0;
+ #(PERIOD*1);
+
+ reg_addr = 1;
+ wr_stb = 1;
+ reg_data_in = 8'hAD;
+ #(PERIOD*2);
+
+ wr_stb = 0;
+ #(PERIOD*1);
+
+ reg_addr = 2;
+ wr_stb = 1;
+ reg_data_in = 8'hBE;
+ #(PERIOD*2);
+
+ wr_stb = 0;
+ #(PERIOD*1);
+
+ reg_addr = 3;
+ wr_stb = 1;
+ reg_data_in = 8'hEF;
+ #(PERIOD*2);
+
+
+ // let it soak for a bit for good measure
+ #(PERIOD*10);
+
+ // now the real sim starts
+ I2C_idle();
+
+ // write some data
+ I2C_start();
+ I2C_tx_daddr(8'h72, ack); // write to device 72
+ I2C_tx_daddr(8'h01, ack); // address 01
+ I2C_tx_daddr(8'h33, ack); // data 55
+ I2C_stop();
+ #(I2C_PD*5);
+
+ // do a multi-cycle read
+ I2C_start();
+ I2C_tx_daddr(8'h72, ack); // dummy write to 72
+ I2C_tx_daddr(8'h00, ack); // address 00
+ I2C_start();
+ I2C_tx_daddr(8'h73, ack); // read from 72
+// #(I2C_PD*3);
+ I2C_rx_daddr(readdata, 1'b0); // data @ address 0
+// #(I2C_PD*3);
+ I2C_rx_daddr(readdata, 1'b0); // data @ address 0
+ I2C_rx_daddr(readdata, 1'b0); // data @ address 0
+ I2C_rx_daddr(readdata, 1'bz); // data @ address 0
+ I2C_stop();
+ #(I2C_PD*5);
+
+ // do a multi-cycle write
+ I2C_start();
+ I2C_tx_daddr(8'h72, ack); // write to device 70
+ I2C_tx_daddr(8'h01, ack); // address 01
+ I2C_tx_daddr(8'hFA, ack);
+ I2C_tx_daddr(8'hCE, ack);
+ I2C_tx_daddr(8'h69, ack);
+ I2C_stop();
+ #(I2C_PD*5);
+
+ // read back one address at a time
+ I2C_start();
+ I2C_tx_daddr(8'h72, ack); // dummy write to 72
+ I2C_tx_daddr(8'h00, ack); // address 00
+
+ #(I2C_PD*5);
+ I2C_start();
+ I2C_tx_daddr(8'h73, ack); // read from 72
+ I2C_rx_daddr(readdata, 1'bz); // one read
+ I2C_stop();
+
+ // this is the only questionable vector
+ // if you do an isolated read, should the address have
+ // incremeted from the previous read, or
+ // should it be the same. I have implemented it so
+ // that it increments.
+ I2C_start();
+ I2C_tx_daddr(8'h73, ack); // read from 72
+ I2C_rx_daddr(readdata, 1'bz); // one read
+ I2C_stop();
+
+ #(I2C_PD*5);
+
+ I2C_start();
+ I2C_tx_daddr(8'h73, ack); // read from 72
+ I2C_rx_daddr(readdata, 1'b0); // one read
+ I2C_rx_daddr(readdata, 1'bz); // one read
+ I2C_stop();
+
+
+ // write to another device not us
+ I2C_start();
+ I2C_tx_daddr(8'hA0, ack); // write to device a0
+ I2C_tx_daddr(8'h01, ack); // address 01
+ I2C_tx_daddr(8'h55, ack); // data 55 -- this should be ignored
+ I2C_stop();
+
+ #I2C_PD;
+ #I2C_PD;
+ end
+
+endmodule
+
diff --git a/common/i2c_snoop.v b/common/i2c_snoop.v
new file mode 100755
index 0000000..edea84e
--- /dev/null
+++ b/common/i2c_snoop.v
@@ -0,0 +1,625 @@
+//////////////////////////////////////////////////////////////////////////////
+// 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.
+//
+//////////////////////////////////////////////////////////////////////////////
+// An I2C bus snooper implementation. Oversampled for robustness.
+//
+// There are two versions, the EDID snooper and the HDCP snooper
+//
+// It's split because EDID records can be very large and the compiler should
+// infer a block ram for the large records. However, the nature of the HDCP
+// registers would cause the compiler to infer slice registers. Thus, this code
+// is identical to the HDCP snoop with the exception that the HDCP read ports
+// are removed which will allow the compiler to properly infer a LUT RAM.
+///////////
+`timescale 1 ns / 1 ps
+
+module i2c_snoop ( // HDCP snooper
+ // external host interface
+ input wire SCL, // the SCL pin state
+ input wire SDA,
+
+ input wire clk, // internal FPGA clock
+ input wire reset, // internal FPGA reset
+ // i2c configuration
+ input wire [7:0] i2c_snoop_addr,
+
+ // internal slave interface to read snooped register
+ input wire [7:0] reg_addr,
+ output wire [7:0] reg_dout,
+
+ output wire [63:0] An, // An (applies only to HDCP snooper)
+ output reg Aksv14_write // strobes on last byte of Aksv write (triggers Auth)
+ );
+
+ /////// 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_ADDR = 14'b1 << 4;
+ parameter I2C_ACK_ADDR = 14'b1 << 5;
+ parameter I2C_WR_DATA = 14'b1 << 6;
+ parameter I2C_ACK_WR = 14'b1 << 7;
+ parameter I2C_END_WR = 14'b1 << 8;
+ parameter I2C_RD_DATA = 14'b1 << 9;
+ parameter I2C_ACK_RD = 14'b1 << 10;
+ parameter I2C_END_RD = 14'b1 << 11;
+ parameter I2C_END_RD2 = 14'b1 << 12;
+ parameter I2C_WAITSTOP = 14'b1 << 13;
+
+ parameter I2C_nSTATES = 14;
+
+ 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_ADDR) I2C_state_ascii <= "I2C_ADDR ";
+ else if (I2C_cstate == I2C_ACK_ADDR) I2C_state_ascii <= "I2C_ACK_ADDR ";
+ 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 I2C_state_ascii <= "WTF ";
+ end
+ // synthesis translate_on
+`endif
+
+ reg [3:0] I2C_bitcnt;
+ reg [7:0] I2C_addr;
+ reg [7:0] I2C_daddr;
+ reg [7:0] I2C_wdata;
+ reg [7:0] I2C_rdata;
+ reg I2C_reg_update;
+
+ 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 <=#1 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
+ 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_snoop_addr[7:1]) ?
+ (I2C_daddr[0] == 1'b0 ? I2C_ADDR : I2C_RD_DATA) :
+ I2C_WAITSTOP : // !I2C_daddr match
+ I2C_ACK_DADDR; // !SCL_FALL
+ end
+
+ // device address branch
+ I2C_ADDR: begin
+ I2C_nstate = ((I2C_bitcnt > 4'h7) && (SCL_cstate == SCL_FALL)) ? I2C_ACK_ADDR : I2C_ADDR;
+ end
+ I2C_ACK_ADDR: begin
+ I2C_nstate = (SCL_cstate == SCL_FALL) ? I2C_WR_DATA : I2C_ACK_ADDR;
+ 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_ACK_WR : I2C_WR_DATA;
+ 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
+ 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 or posedge reset) begin
+ if( reset ) begin
+ I2C_bitcnt <=#1 4'b0;
+ I2C_daddr <=#1 8'b0;
+ I2C_wdata <=#1 8'b0;
+ I2C_reg_update <=#1 1'b0;
+ I2C_rdata <=#1 8'b0;
+ I2C_addr <=#1 8'b0; // this persists across transactions
+ end else begin
+ case (I2C_cstate) // synthesis parallel_case full_case
+ I2C_START: begin // everything in reset
+ I2C_bitcnt <=#1 4'b0;
+ I2C_daddr <=#1 8'b0;
+ I2C_wdata <=#1 8'b0;
+ I2C_rdata <=#1 8'b0;
+ I2C_reg_update <=#1 1'b0;
+ I2C_addr <=#1 I2C_addr;
+ end
+
+ I2C_RESTART: begin
+ I2C_bitcnt <=#1 4'b0;
+ I2C_daddr <=#1 8'b0;
+ I2C_wdata <=#1 8'b0;
+ I2C_rdata <=#1 8'b0;
+ I2C_reg_update <=#1 1'b0;
+ I2C_addr <=#1 I2C_addr;
+ 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 <=#1 I2C_bitcnt + 4'b1;
+ I2C_daddr[7] <=#1 I2C_daddr[6];
+ I2C_daddr[6] <=#1 I2C_daddr[5];
+ I2C_daddr[5] <=#1 I2C_daddr[4];
+ I2C_daddr[4] <=#1 I2C_daddr[3];
+ I2C_daddr[3] <=#1 I2C_daddr[2];
+ I2C_daddr[2] <=#1 I2C_daddr[1];
+ I2C_daddr[1] <=#1 I2C_daddr[0];
+ I2C_daddr[0] <=#1 (SDA_cstate == SDA_HIGH) ? 1'b1 : 1'b0;
+ end else begin // we're oversampled so we need a hold-state gutter
+ I2C_bitcnt <=#1 I2C_bitcnt;
+ I2C_daddr <=#1 I2C_daddr;
+ end // else: !if( SCL_cstate == SCL_RISE )
+ I2C_wdata <=#1 8'b0;
+ I2C_rdata <=#1 8'b0;
+ I2C_reg_update <=#1 1'b0;
+ I2C_addr <=#1 I2C_addr;
+ end // case: I2C_DADDR
+ I2C_ACK_DADDR: begin
+ I2C_daddr <=#1 I2C_daddr;
+ I2C_bitcnt <=#1 4'b0;
+ I2C_wdata <=#1 8'b0;
+ I2C_rdata <=#1 I2C_regread_async;
+ I2C_reg_update <=#1 1'b0;
+ I2C_addr <=#1 I2C_addr;
+ end
+
+ // get my i2c "write" address (what we want to access inside me)
+ I2C_ADDR: begin
+ if( SCL_cstate == SCL_RISE ) begin
+ I2C_bitcnt <=#1 I2C_bitcnt + 4'b1;
+ I2C_addr[7] <=#1 I2C_addr[6];
+ I2C_addr[6] <=#1 I2C_addr[5];
+ I2C_addr[5] <=#1 I2C_addr[4];
+ I2C_addr[4] <=#1 I2C_addr[3];
+ I2C_addr[3] <=#1 I2C_addr[2];
+ I2C_addr[2] <=#1 I2C_addr[1];
+ I2C_addr[1] <=#1 I2C_addr[0];
+ I2C_addr[0] <=#1 (SDA_cstate == SDA_HIGH) ? 1'b1 : 1'b0;
+ end else begin // we're oversampled so we need a hold-state gutter
+ I2C_bitcnt <=#1 I2C_bitcnt;
+ I2C_addr <=#1 I2C_addr;
+ end // else: !if( SCL_cstate == SCL_RISE )
+ I2C_wdata <=#1 8'b0;
+ I2C_rdata <=#1 8'b0;
+ I2C_reg_update <=#1 1'b0;
+ I2C_daddr <=#1 I2C_daddr;
+ end // case: I2C_ADDR
+ I2C_ACK_ADDR: begin
+ I2C_daddr <=#1 I2C_daddr;
+ I2C_bitcnt <=#1 4'b0;
+ I2C_wdata <=#1 8'b0;
+ I2C_rdata <=#1 I2C_regread_async; // update my read data here
+ I2C_reg_update <=#1 1'b0;
+ I2C_addr <=#1 I2C_addr;
+ end
+
+
+ // write branch
+ I2C_WR_DATA: begin // shift in data on rising edges of clock
+ if( SCL_cstate == SCL_RISE ) begin
+ I2C_bitcnt <=#1 I2C_bitcnt + 4'b1;
+ I2C_wdata[7] <=#1 I2C_wdata[6];
+ I2C_wdata[6] <=#1 I2C_wdata[5];
+ I2C_wdata[5] <=#1 I2C_wdata[4];
+ I2C_wdata[4] <=#1 I2C_wdata[3];
+ I2C_wdata[3] <=#1 I2C_wdata[2];
+ I2C_wdata[2] <=#1 I2C_wdata[1];
+ I2C_wdata[1] <=#1 I2C_wdata[0];
+ I2C_wdata[0] <=#1 (SDA_cstate == SDA_HIGH) ? 1'b1 : 1'b0;
+ end else begin
+ I2C_bitcnt <=#1 I2C_bitcnt; // hold state gutter
+ I2C_wdata <=#1 I2C_wdata;
+ end // else: !if( SCL_cstate == SCL_RISE )
+ I2C_daddr <=#1 I2C_daddr;
+ I2C_reg_update <=#1 1'b0;
+ I2C_rdata <=#1 I2C_rdata;
+ I2C_addr <=#1 I2C_addr;
+ end // case: I2C_WR_DATA
+ I2C_ACK_WR: begin
+ I2C_daddr <=#1 I2C_daddr;
+ I2C_bitcnt <=#1 4'b0;
+ I2C_wdata <=#1 I2C_wdata;
+ I2C_reg_update <=#1 1'b1; // write the data now (over and over again while in state)
+ I2C_rdata <=#1 I2C_rdata;
+ I2C_addr <=#1 I2C_addr;
+ end
+ I2C_END_WR: begin
+ I2C_addr <=#1 I2C_addr + 8'b1; // this is a one-cycle state so this is safe
+ I2C_bitcnt <=#1 4'b0;
+ I2C_wdata <=#1 8'b0;
+ I2C_rdata <=#1 I2C_rdata;
+ I2C_reg_update <=#1 1'b0;
+ I2C_daddr <=#1 I2C_daddr;
+ end
+
+ // read branch
+ I2C_RD_DATA: begin // shift out data on falling edges of clock
+ if( SCL_cstate == SCL_RISE ) begin
+ I2C_bitcnt <=#1 I2C_bitcnt + 4'b1;
+
+ I2C_rdata[7] <=#1 I2C_rdata[6];
+ I2C_rdata[6] <=#1 I2C_rdata[5];
+ I2C_rdata[5] <=#1 I2C_rdata[4];
+ I2C_rdata[4] <=#1 I2C_rdata[3];
+ I2C_rdata[3] <=#1 I2C_rdata[2];
+ I2C_rdata[2] <=#1 I2C_rdata[1];
+ I2C_rdata[1] <=#1 I2C_rdata[0];
+ I2C_rdata[0] <=#1 (SDA_cstate == SDA_HIGH) ? 1'b1 : 1'b0;
+ end else begin
+ I2C_bitcnt <=#1 I2C_bitcnt; // hold state gutter
+ I2C_rdata <=#1 I2C_rdata;
+ end
+ I2C_daddr <=#1 I2C_daddr;
+ I2C_reg_update <=#1 1'b0;
+ I2C_wdata <=#1 I2C_rdata; // push rdata to wdata
+ I2C_addr <=#1 I2C_addr;
+ end // case: I2C_RD_DATA
+ I2C_ACK_RD: begin
+ I2C_daddr <=#1 I2C_daddr;
+ I2C_bitcnt <=#1 4'b0;
+ I2C_rdata <=#1 I2C_rdata;
+ I2C_reg_update <=#1 1'b1; // commit reads even to our internal bank
+ I2C_wdata <=#1 I2C_rdata; // push rdata to wdata
+ I2C_addr <=#1 I2C_addr;
+ end
+ I2C_END_RD: begin
+ I2C_addr <=#1 I2C_addr + 8'b1; // this is a one-cycle state so this is safe
+ I2C_bitcnt <=#1 4'b0;
+ I2C_rdata <=#1 I2C_rdata;
+ I2C_reg_update <=#1 1'b0;
+ I2C_wdata <=#1 I2C_wdata;
+ I2C_daddr <=#1 I2C_daddr;
+ end
+ I2C_END_RD2: begin
+ I2C_daddr <=#1 8'b0;
+ I2C_bitcnt <=#1 4'b0;
+ I2C_rdata <=#1 I2C_rdata;
+ I2C_reg_update <=#1 1'b0;
+ I2C_wdata <=#1 I2C_wdata;
+ I2C_addr <=#1 I2C_addr;
+ end
+
+ I2C_WAITSTOP: begin
+ I2C_daddr <=#1 8'b0;
+ I2C_bitcnt <=#1 4'b0;
+ I2C_rdata <=#1 I2C_rdata;
+ I2C_reg_update <=#1 1'b0;
+ I2C_wdata <=#1 I2C_wdata;
+ I2C_addr <=#1 I2C_addr;
+ end
+ endcase // case (cstate)
+ end // else: !if( reset )
+ end // always @ (posedge clk or posedge reset)
+
+ always @(posedge clk) begin
+ if( reset ) begin
+ Aksv14_write <=#1 1'b0;
+ end else begin
+ if( (I2C_addr == 8'h14) && (I2C_cstate == I2C_ACK_WR ) ) begin
+ Aksv14_write <=#1 1'b1;
+ end else begin
+ Aksv14_write <=#1 1'b0;
+ end
+ end
+ end // always @ (posedge clk)
+
+ ////////////////
+ ///// register bank management
+ ////////////////
+ parameter RAM_WIDTH = 8;
+ parameter RAM_ADDR_BITS = 5; // note parameter width exception in An[*] assign block below
+
+ (* RAM_STYLE="{AUTO | DISTRIBUTED | PIPE_DISTRIBUTED}" *)
+ reg [RAM_WIDTH-1:0] I2C_regblock [(2**RAM_ADDR_BITS)-1:0];
+ wire [RAM_WIDTH-1:0] I2C_regread_async;
+
+ wire [RAM_ADDR_BITS-1:0] I2C_ramaddr;
+
+ reg wr_stb_d;
+
+ always @(posedge clk) begin
+ // added bounds check to avoid overwriting Ksv if other sections of HDCP area is checked
+ if ((I2C_reg_update && (I2C_cstate == I2C_ACK_WR) && (I2C_addr[7:0] < 8'h20)) ||
+ (I2C_reg_update && (I2C_cstate == I2C_ACK_RD) && (I2C_addr[7:0] < 8'h5)) ) begin
+ // this should be multiple cycles
+ I2C_regblock[I2C_ramaddr] <= I2C_wdata;
+ end
+ end
+
+ assign I2C_regread_async = I2C_regblock[I2C_ramaddr];
+ assign reg_dout = I2C_regblock[reg_addr[RAM_ADDR_BITS-1:0]];
+
+ assign I2C_ramaddr = I2C_addr[RAM_ADDR_BITS-1:0];
+
+ assign An[7:0] = I2C_regblock[5'h18];
+ assign An[15:8] = I2C_regblock[5'h19];
+ assign An[23:16] = I2C_regblock[5'h1a];
+ assign An[31:24] = I2C_regblock[5'h1b];
+ assign An[39:32] = I2C_regblock[5'h1c];
+ assign An[47:40] = I2C_regblock[5'h1d];
+ assign An[55:48] = I2C_regblock[5'h1e];
+ assign An[63:56] = I2C_regblock[5'h1f];
+
+ ////////////////
+ ///// 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 or posedge reset) begin
+ if (reset)
+ SCL_cstate <= SCL_HIGH; // always start here even if it's wrong -- easier to test
+ else
+ SCL_cstate <=#1 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 or posedge reset) begin
+ if( reset ) begin
+ SCL_rfcnt <=#1 5'b0;
+ end else begin
+ case (SCL_cstate) // synthesis parallel_case full_case
+ SCL_HIGH: begin
+ if( SCL_sync == 1'b1 ) begin
+ SCL_rfcnt <=#1 5'b0;
+ end else begin
+ SCL_rfcnt <=#1 SCL_rfcnt + 5'b1;
+ end
+ end
+ SCL_FALL: begin
+ SCL_rfcnt <=#1 5'b0;
+ end
+ SCL_LOW: begin
+ if( SCL_sync == 1'b0 ) begin
+ SCL_rfcnt <=#1 5'b0;
+ end else begin
+ SCL_rfcnt <=#1 SCL_rfcnt + 5'b1;
+ end
+ end
+ SCL_RISE: begin
+ SCL_rfcnt <=#1 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 or posedge reset) begin
+ if (reset)
+ SDA_cstate <= SDA_HIGH; // always start here even if it's wrong -- easier to test
+ else
+ SDA_cstate <=#1 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 or posedge reset) begin
+ if( reset ) begin
+ SDA_rfcnt <=#1 5'b0;
+ end else begin
+ case (SDA_cstate) // synthesis parallel_case full_case
+ SDA_HIGH: begin
+ if( SDA_sync == 1'b1 ) begin
+ SDA_rfcnt <=#1 5'b0;
+ end else begin
+ SDA_rfcnt <=#1 SDA_rfcnt + 5'b1;
+ end
+ end
+ SDA_FALL: begin
+ SDA_rfcnt <=#1 5'b0;
+ end
+ SDA_LOW: begin
+ if( SDA_sync == 1'b0 ) begin
+ SDA_rfcnt <=#1 5'b0;
+ end else begin
+ SDA_rfcnt <=#1 SDA_rfcnt + 5'b1;
+ end
+ end
+ SDA_RISE: begin
+ SDA_rfcnt <=#1 5'b0;
+ end
+ endcase // case (cstate)
+ end // else: !if( reset )
+ end // always @ (posedge clk or posedge reset)
+
+
+
+ /////////////////////
+ /////// synchronizers
+ /////////////////////
+ always @ (posedge clk or posedge reset) begin
+ if (reset) begin
+ SCL_s <= 0;
+ SCL_sync <= 0;
+ SDA_s <= 0;
+ SDA_sync <= 0;
+ end else begin
+ SCL_s <= SCL;
+ SCL_sync <= SCL_s;
+ SDA_s <= SDA;
+ SDA_sync <= SDA_s;
+ end // else: !if(reset)
+ end // always @ (posedge clk or posedge reset)
+
+endmodule // i2c_slave
diff --git a/common/i2c_snoop_edid.v b/common/i2c_snoop_edid.v
new file mode 100755
index 0000000..547bea0
--- /dev/null
+++ b/common/i2c_snoop_edid.v
@@ -0,0 +1,603 @@
+//////////////////////////////////////////////////////////////////////////////
+// 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.
+//
+//////////////////////////////////////////////////////////////////////////////
+// An I2C bus snooper implementation. Oversampled for robustness.
+//
+// There are two versions, the EDID snooper and the HDCP snooper
+//
+// It's split because EDID records can be very large and the compiler should
+// infer a block ram for the large records. However, the nature of the HDCP
+// registers would cause the compiler to infer slice registers. Thus, this code
+// is identical to the HDCP snoop with the exception that the HDCP read ports
+// are removed which will allow the compiler to properly infer a LUT RAM.
+///////////
+`timescale 1 ns / 1 ps
+
+module i2c_snoop_edid (
+ // external host interface
+ input wire SCL, // the SCL pin state
+ input wire SDA,
+
+ input wire clk, // internal FPGA clock
+ input wire reset, // internal FPGA reset
+ // i2c configuration
+ input wire [7:0] i2c_snoop_addr,
+
+ // internal slave interface to read snooped register
+ input wire [7:0] reg_addr,
+ output wire [7:0] reg_dout
+
+ );
+
+ /////// 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
+
+ // just some tie-offs for future functionality not yet implemented...
+ assign SDA_pu = 1'b0;
+ assign SCL_pd = 1'b0;
+
+ ////////////////
+ ///// 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_ADDR = 14'b1 << 4;
+ parameter I2C_ACK_ADDR = 14'b1 << 5;
+ parameter I2C_WR_DATA = 14'b1 << 6;
+ parameter I2C_ACK_WR = 14'b1 << 7;
+ parameter I2C_END_WR = 14'b1 << 8;
+ parameter I2C_RD_DATA = 14'b1 << 9;
+ parameter I2C_ACK_RD = 14'b1 << 10;
+ parameter I2C_END_RD = 14'b1 << 11;
+ parameter I2C_END_RD2 = 14'b1 << 12;
+ parameter I2C_WAITSTOP = 14'b1 << 13;
+
+ parameter I2C_nSTATES = 14;
+
+ 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_ADDR) I2C_state_ascii <= "I2C_ADDR ";
+ else if (I2C_cstate == I2C_ACK_ADDR) I2C_state_ascii <= "I2C_ACK_ADDR ";
+ 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 I2C_state_ascii <= "WTF ";
+ end
+ // synthesis translate_on
+`endif
+
+ reg [3:0] I2C_bitcnt;
+ reg [7:0] I2C_addr;
+ reg [7:0] I2C_daddr;
+ reg [7:0] I2C_wdata;
+ reg [7:0] I2C_rdata;
+ reg I2C_reg_update;
+
+ 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 <=#1 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
+ 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_snoop_addr[7:1]) ? // get everything *but* HDCP
+ (I2C_daddr[0] == 1'b0 ? I2C_ADDR : I2C_RD_DATA) :
+ I2C_WAITSTOP : // !I2C_daddr match
+ I2C_ACK_DADDR; // !SCL_FALL
+ end
+
+ // device address branch
+ I2C_ADDR: begin
+ I2C_nstate = ((I2C_bitcnt > 4'h7) && (SCL_cstate == SCL_FALL)) ? I2C_ACK_ADDR : I2C_ADDR;
+ end
+ I2C_ACK_ADDR: begin
+ I2C_nstate = (SCL_cstate == SCL_FALL) ? I2C_WR_DATA : I2C_ACK_ADDR;
+ 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_ACK_WR : I2C_WR_DATA;
+ 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
+ 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 or posedge reset) begin
+ if( reset ) begin
+ I2C_bitcnt <=#1 4'b0;
+ I2C_daddr <=#1 8'b0;
+ I2C_wdata <=#1 8'b0;
+ I2C_reg_update <=#1 1'b0;
+ I2C_rdata <=#1 8'b0;
+ I2C_addr <=#1 8'b0; // this persists across transactions
+ end else begin
+ case (I2C_cstate) // synthesis parallel_case full_case
+ I2C_START: begin // everything in reset
+ I2C_bitcnt <=#1 4'b0;
+ I2C_daddr <=#1 8'b0;
+ I2C_wdata <=#1 8'b0;
+ I2C_rdata <=#1 8'b0;
+ I2C_reg_update <=#1 1'b0;
+ I2C_addr <=#1 I2C_addr;
+ end
+
+ I2C_RESTART: begin
+ I2C_bitcnt <=#1 4'b0;
+ I2C_daddr <=#1 8'b0;
+ I2C_wdata <=#1 8'b0;
+ I2C_rdata <=#1 8'b0;
+ I2C_reg_update <=#1 1'b0;
+ I2C_addr <=#1 I2C_addr;
+ 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 <=#1 I2C_bitcnt + 4'b1;
+ I2C_daddr[7] <=#1 I2C_daddr[6];
+ I2C_daddr[6] <=#1 I2C_daddr[5];
+ I2C_daddr[5] <=#1 I2C_daddr[4];
+ I2C_daddr[4] <=#1 I2C_daddr[3];
+ I2C_daddr[3] <=#1 I2C_daddr[2];
+ I2C_daddr[2] <=#1 I2C_daddr[1];
+ I2C_daddr[1] <=#1 I2C_daddr[0];
+ I2C_daddr[0] <=#1 (SDA_cstate == SDA_HIGH) ? 1'b1 : 1'b0;
+ end else begin // we're oversampled so we need a hold-state gutter
+ I2C_bitcnt <=#1 I2C_bitcnt;
+ I2C_daddr <=#1 I2C_daddr;
+ end // else: !if( SCL_cstate == SCL_RISE )
+ I2C_wdata <=#1 8'b0;
+ I2C_rdata <=#1 8'b0;
+ I2C_reg_update <=#1 1'b0;
+ I2C_addr <=#1 I2C_addr;
+ end // case: I2C_DADDR
+ I2C_ACK_DADDR: begin
+ I2C_daddr <=#1 I2C_daddr;
+ I2C_bitcnt <=#1 4'b0;
+ I2C_wdata <=#1 8'b0;
+ I2C_rdata <=#1 I2C_regread_async;
+ I2C_reg_update <=#1 1'b0;
+ I2C_addr <=#1 I2C_addr;
+ end
+
+ // get my i2c "write" address (what we want to access inside me)
+ I2C_ADDR: begin
+ if( SCL_cstate == SCL_RISE ) begin
+ I2C_bitcnt <=#1 I2C_bitcnt + 4'b1;
+ I2C_addr[7] <=#1 I2C_addr[6];
+ I2C_addr[6] <=#1 I2C_addr[5];
+ I2C_addr[5] <=#1 I2C_addr[4];
+ I2C_addr[4] <=#1 I2C_addr[3];
+ I2C_addr[3] <=#1 I2C_addr[2];
+ I2C_addr[2] <=#1 I2C_addr[1];
+ I2C_addr[1] <=#1 I2C_addr[0];
+ I2C_addr[0] <=#1 (SDA_cstate == SDA_HIGH) ? 1'b1 : 1'b0;
+ end else begin // we're oversampled so we need a hold-state gutter
+ I2C_bitcnt <=#1 I2C_bitcnt;
+ I2C_addr <=#1 I2C_addr;
+ end // else: !if( SCL_cstate == SCL_RISE )
+ I2C_wdata <=#1 8'b0;
+ I2C_rdata <=#1 8'b0;
+ I2C_reg_update <=#1 1'b0;
+ I2C_daddr <=#1 I2C_daddr;
+ end // case: I2C_ADDR
+ I2C_ACK_ADDR: begin
+ I2C_daddr <=#1 I2C_daddr;
+ I2C_bitcnt <=#1 4'b0;
+ I2C_wdata <=#1 8'b0;
+ I2C_rdata <=#1 I2C_regread_async; // update my read data here
+ I2C_reg_update <=#1 1'b0;
+ I2C_addr <=#1 I2C_addr;
+ end
+
+
+ // write branch
+ I2C_WR_DATA: begin // shift in data on rising edges of clock
+ if( SCL_cstate == SCL_RISE ) begin
+ I2C_bitcnt <=#1 I2C_bitcnt + 4'b1;
+ I2C_wdata[7] <=#1 I2C_wdata[6];
+ I2C_wdata[6] <=#1 I2C_wdata[5];
+ I2C_wdata[5] <=#1 I2C_wdata[4];
+ I2C_wdata[4] <=#1 I2C_wdata[3];
+ I2C_wdata[3] <=#1 I2C_wdata[2];
+ I2C_wdata[2] <=#1 I2C_wdata[1];
+ I2C_wdata[1] <=#1 I2C_wdata[0];
+ I2C_wdata[0] <=#1 (SDA_cstate == SDA_HIGH) ? 1'b1 : 1'b0;
+ end else begin
+ I2C_bitcnt <=#1 I2C_bitcnt; // hold state gutter
+ I2C_wdata <=#1 I2C_wdata;
+ end // else: !if( SCL_cstate == SCL_RISE )
+ I2C_daddr <=#1 I2C_daddr;
+ I2C_reg_update <=#1 1'b0;
+ I2C_rdata <=#1 I2C_rdata;
+ I2C_addr <=#1 I2C_addr;
+ end // case: I2C_WR_DATA
+ I2C_ACK_WR: begin
+ I2C_daddr <=#1 I2C_daddr;
+ I2C_bitcnt <=#1 4'b0;
+ I2C_wdata <=#1 I2C_wdata;
+ I2C_reg_update <=#1 1'b1; // write the data now (over and over again while in state)
+ I2C_rdata <=#1 I2C_rdata;
+ I2C_addr <=#1 I2C_addr;
+ end
+ I2C_END_WR: begin
+ I2C_addr <=#1 I2C_addr + 8'b1; // this is a one-cycle state so this is safe
+ I2C_bitcnt <=#1 4'b0;
+ I2C_wdata <=#1 8'b0;
+ I2C_rdata <=#1 I2C_rdata;
+ I2C_reg_update <=#1 1'b0;
+ I2C_daddr <=#1 I2C_daddr;
+ end
+
+ // read branch
+ I2C_RD_DATA: begin // shift out data on falling edges of clock
+ if( SCL_cstate == SCL_RISE ) begin
+ I2C_bitcnt <=#1 I2C_bitcnt + 4'b1;
+
+ I2C_rdata[7] <=#1 I2C_rdata[6];
+ I2C_rdata[6] <=#1 I2C_rdata[5];
+ I2C_rdata[5] <=#1 I2C_rdata[4];
+ I2C_rdata[4] <=#1 I2C_rdata[3];
+ I2C_rdata[3] <=#1 I2C_rdata[2];
+ I2C_rdata[2] <=#1 I2C_rdata[1];
+ I2C_rdata[1] <=#1 I2C_rdata[0];
+ I2C_rdata[0] <=#1 (SDA_cstate == SDA_HIGH) ? 1'b1 : 1'b0;
+ end else begin
+ I2C_bitcnt <=#1 I2C_bitcnt; // hold state gutter
+ I2C_rdata <=#1 I2C_rdata;
+ end
+ I2C_daddr <=#1 I2C_daddr;
+ I2C_reg_update <=#1 1'b0;
+ I2C_wdata <=#1 I2C_rdata; // push rdata to wdata
+ I2C_addr <=#1 I2C_addr;
+ end // case: I2C_RD_DATA
+ I2C_ACK_RD: begin
+ I2C_daddr <=#1 I2C_daddr;
+ I2C_bitcnt <=#1 4'b0;
+ I2C_rdata <=#1 I2C_rdata;
+ I2C_reg_update <=#1 1'b1; // commit reads even to our internal bank
+ I2C_wdata <=#1 I2C_rdata; // push rdata to wdata
+ I2C_addr <=#1 I2C_addr;
+ end
+ I2C_END_RD: begin
+ I2C_addr <=#1 I2C_addr + 8'b1; // this is a one-cycle state so this is safe
+ I2C_bitcnt <=#1 4'b0;
+ I2C_rdata <=#1 I2C_rdata;
+ I2C_reg_update <=#1 1'b0;
+ I2C_wdata <=#1 I2C_wdata;
+ I2C_daddr <=#1 I2C_daddr;
+ end
+ I2C_END_RD2: begin
+ I2C_daddr <=#1 8'b0;
+ I2C_bitcnt <=#1 4'b0;
+ I2C_rdata <=#1 I2C_rdata;
+ I2C_reg_update <=#1 1'b0;
+ I2C_wdata <=#1 I2C_wdata;
+ I2C_addr <=#1 I2C_addr;
+ end
+
+ I2C_WAITSTOP: begin
+ I2C_daddr <=#1 8'b0;
+ I2C_bitcnt <=#1 4'b0;
+ I2C_rdata <=#1 I2C_rdata;
+ I2C_reg_update <=#1 1'b0;
+ I2C_wdata <=#1 I2C_wdata;
+ I2C_addr <=#1 I2C_addr;
+ end
+ endcase // case (cstate)
+ end // else: !if( reset )
+ end // always @ (posedge clk or posedge reset)
+
+ ////////////////
+ ///// register bank management
+ ////////////////
+ parameter RAM_WIDTH = 8;
+ parameter RAM_ADDR_BITS = 8;
+
+ (* RAM_STYLE="{AUTO | DISTRIBUTED | PIPE_DISTRIBUTED}" *)
+ reg [RAM_WIDTH-1:0] I2C_regblock [(2**RAM_ADDR_BITS)-1:0];
+ wire [RAM_WIDTH-1:0] I2C_regread_async;
+
+ wire [RAM_ADDR_BITS-1:0] I2C_ramaddr;
+
+ reg wr_stb_d;
+
+ always @(posedge clk) begin
+ if (I2C_reg_update) begin // this should be multiple cycles
+ I2C_regblock[I2C_ramaddr] <= I2C_wdata;
+ end
+ end
+
+ assign I2C_regread_async = I2C_regblock[I2C_ramaddr];
+ assign reg_dout = I2C_regblock[reg_addr[RAM_ADDR_BITS-1:0]];
+
+ assign I2C_ramaddr = I2C_addr[RAM_ADDR_BITS-1:0];
+
+ ////////////////
+ ///// 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 or posedge reset) begin
+ if (reset)
+ SCL_cstate <= SCL_HIGH; // always start here even if it's wrong -- easier to test
+ else
+ SCL_cstate <=#1 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 or posedge reset) begin
+ if( reset ) begin
+ SCL_rfcnt <=#1 5'b0;
+ end else begin
+ case (SCL_cstate) // synthesis parallel_case full_case
+ SCL_HIGH: begin
+ if( SCL_sync == 1'b1 ) begin
+ SCL_rfcnt <=#1 5'b0;
+ end else begin
+ SCL_rfcnt <=#1 SCL_rfcnt + 5'b1;
+ end
+ end
+ SCL_FALL: begin
+ SCL_rfcnt <=#1 5'b0;
+ end
+ SCL_LOW: begin
+ if( SCL_sync == 1'b0 ) begin
+ SCL_rfcnt <=#1 5'b0;
+ end else begin
+ SCL_rfcnt <=#1 SCL_rfcnt + 5'b1;
+ end
+ end
+ SCL_RISE: begin
+ SCL_rfcnt <=#1 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 or posedge reset) begin
+ if (reset)
+ SDA_cstate <= SDA_HIGH; // always start here even if it's wrong -- easier to test
+ else
+ SDA_cstate <=#1 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 or posedge reset) begin
+ if( reset ) begin
+ SDA_rfcnt <=#1 5'b0;
+ end else begin
+ case (SDA_cstate) // synthesis parallel_case full_case
+ SDA_HIGH: begin
+ if( SDA_sync == 1'b1 ) begin
+ SDA_rfcnt <=#1 5'b0;
+ end else begin
+ SDA_rfcnt <=#1 SDA_rfcnt + 5'b1;
+ end
+ end
+ SDA_FALL: begin
+ SDA_rfcnt <=#1 5'b0;
+ end
+ SDA_LOW: begin
+ if( SDA_sync == 1'b0 ) begin
+ SDA_rfcnt <=#1 5'b0;
+ end else begin
+ SDA_rfcnt <=#1 SDA_rfcnt + 5'b1;
+ end
+ end
+ SDA_RISE: begin
+ SDA_rfcnt <=#1 5'b0;
+ end
+ endcase // case (cstate)
+ end // else: !if( reset )
+ end // always @ (posedge clk or posedge reset)
+
+
+
+ /////////////////////
+ /////// synchronizers
+ /////////////////////
+ always @ (posedge clk or posedge reset) begin
+ if (reset) begin
+ SCL_s <= 0;
+ SCL_sync <= 0;
+ SDA_s <= 0;
+ SDA_sync <= 0;
+ end else begin
+ SCL_s <= SCL;
+ SCL_sync <= SCL_s;
+ SDA_s <= SDA;
+ SDA_sync <= SDA_s;
+ end // else: !if(reset)
+ end // always @ (posedge clk or posedge reset)
+
+endmodule // i2c_slave
diff --git a/common/i2c_snoop_tb.v b/common/i2c_snoop_tb.v
new file mode 100755
index 0000000..592c559
--- /dev/null
+++ b/common/i2c_snoop_tb.v
@@ -0,0 +1,418 @@
+//////////////////////////////////////////////////////////////////////////////
+// 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.
+//
+//////////////////////////////////////////////////////////////////////////////
+`timescale 1ns / 1ps
+
+////////////////////////////////////////////////////////////////////////////////
+// Company:
+// Engineer:
+//
+// Create Date: 12:30:45 04/05/2011
+// Design Name: i2c_slave
+// Module Name: C:/largework/fpga/hdmi/impl4/common/i2c_slave_tb.v
+// Project Name: impl4
+// Target Device:
+// Tool versions:
+// Description:
+//
+// Verilog Test Fixture created by ISE for module: i2c_slave
+//
+// Dependencies:
+//
+// Revision:
+// Revision 0.01 - File Created
+// Additional Comments:
+//
+////////////////////////////////////////////////////////////////////////////////
+
+module i2c_snoop_tb;
+
+ reg SDA; // physical wire state
+ reg SCL;
+
+ // Inputs
+ reg clk;
+ reg reset;
+ reg [7:0] i2c_device_addr;
+ reg [7:0] reg_addr;
+ reg wr_stb;
+ reg [7:0] reg_data_in;
+
+ // Outputs
+ wire SCL_pd;
+ wire SDA_pd;
+ wire SDA_pu;
+ wire [7:0] reg_a0;
+ wire [7:0] reg_a1;
+ wire [7:0] reg_a2;
+ wire [7:0] reg_a3;
+
+ reg [7:0] snoop_addr;
+ wire [7:0] snoop_data;
+
+ // Instantiate the Unit Under Test (UUT)
+ i2c_snoop uut (
+ .SCL(SCL),
+ .SDA(SDA),
+ .clk(clk),
+ .reset(reset),
+ .i2c_snoop_addr(8'h74),
+ .reg_addr(snoop_addr),
+ .reg_dout(snoop_data)
+ );
+
+ // Instantiate the Unit Under Test (UUT)
+ i2c_slave target (
+ .SCL(SCL),
+ .SCL_pd(SCL_pd),
+ .SDA(SDA),
+ .SDA_pd(SDA_pd),
+ .SDA_pu(SDA_pu),
+ .clk(clk),
+ .reset(reset),
+ .i2c_device_addr(i2c_device_addr),
+ .reg_addr(reg_addr),
+ .wr_stb(wr_stb),
+ .reg_data_in(reg_data_in),
+ .reg_0(reg_a0),
+ .reg_1(reg_a1),
+ .reg_2(reg_a2),
+ .reg_3(reg_a3)
+ );
+
+ reg sda_host; // what the host is driving to
+ reg scl_host;
+ reg ack; // what the ack state is
+ reg [7:0] readdata;
+
+// always @(SCL_pd, SDA_pd, SDA_pu, sda_host, scl_host) begin
+ always @(*) begin // lazy
+ // scl equations
+ case( {SCL_pd, scl_host} )
+ 2'b00: SCL <= 1'b0;
+ 2'b01: SCL <= 1'b1;
+ 2'b10: SCL <= 1'b0;
+ // conflict case
+ 2'b11: SCL <= 1'bX;
+ // handle tristate case
+ 2'b0Z: SCL <= 1'b1;
+ 2'b1Z: SCL <= 1'b0;
+ default: SCL <= 1'bX;
+ endcase // case ( {SCL_pd, scl_host} )
+
+ case( {SDA_pd, SDA_pu, sda_host} )
+ 3'b000: SDA <= 1'b0;
+ 3'b001: SDA <= 1'b1;
+ 3'b010: SDA <= 1'bX; // change to 1'b1 for override
+ 3'b011: SDA <= 1'b1;
+ 3'b100: SDA <= 1'b0;
+ 3'b101: SDA <= 1'bX; // change to 1'b0 for override
+ 3'b110: SDA <= 1'bX;
+ 3'b111: SDA <= 1'bX;
+
+ // tristate case
+ 3'b00Z: SDA <= 1'b1;
+ 3'b01Z: SDA <= 1'b1;
+ 3'b10Z: SDA <= 1'b0;
+ 3'b11Z: SDA <= 1'bX;
+ endcase // case ( {SDA_pd, SDA_pu, sda_host} )
+ end
+
+ parameter PERIOD = 16'd40; // 25 MHz
+ parameter I2C_PD = 16'd2464; // make it odd to try and catch non-phase synced issues
+ parameter I2C_TH = 16'd114;
+ parameter I2C_TS = 16'd217;
+
+ always begin
+ clk = 1'b0;
+ #(PERIOD/2) clk = 1'b1;
+ #(PERIOD/2);
+ end
+
+ task I2C_idle;
+ begin
+ scl_host = 1'bZ;
+ sda_host = 1'bZ;
+ #I2C_PD;
+ end
+ endtask // I2C_idle
+
+ task I2C_start;
+ begin
+ scl_host = 1'bZ;
+ sda_host = 1'bZ;
+ #(I2C_PD/2);
+ scl_host = 1'bZ;
+ sda_host = 1'b0;
+ #(I2C_PD/2);
+ end
+ endtask // I2C_start
+
+ task I2C_stop;
+ begin
+ scl_host = 1'bZ;
+ sda_host = 1'b0;
+ #(I2C_PD/2);
+ scl_host = 1'bZ;
+ sda_host = 1'bZ;
+ #(I2C_PD/2);
+ end
+ endtask // I2C_start
+
+ task I2C_tx_bit; // tx from host ( from testbench )
+ input bitval;
+
+ begin
+ scl_host = 1'b0;
+ #I2C_TH;
+ sda_host = bitval;
+ #(I2C_PD/2);
+ scl_host = 1'bZ;
+ sda_host = bitval;
+ #(I2C_PD/2);
+ end
+ endtask // I2C_tx_bit
+
+ task I2C_rx_bit; // rx to host ( to testbench )
+ output bitval;
+
+ begin
+ scl_host = 1'b0;
+ #(I2C_TH/2);
+ sda_host = 1'bz;
+ #(I2C_PD/2);
+ scl_host = 1'bZ;
+ sda_host = 1'bz;
+ #1;
+ bitval = SDA;
+ #(I2C_PD/2);
+ end
+ endtask // I2C_start
+
+ task I2C_ack_low;
+ begin
+ scl_host = 1'b0;
+ #(I2C_PD/2);
+ end
+ endtask // I2C_ack_low
+
+
+ task I2C_tx_daddr;
+ input [7:0] daddr;
+ output rack;
+
+ begin
+ I2C_tx_bit( daddr[7] );
+ I2C_tx_bit( daddr[6] );
+ I2C_tx_bit( daddr[5] );
+ I2C_tx_bit( daddr[4] );
+ I2C_tx_bit( daddr[3] );
+ I2C_tx_bit( daddr[2] );
+ I2C_tx_bit( daddr[1] );
+ I2C_tx_bit( daddr[0] );
+ I2C_rx_bit(rack);
+ I2C_ack_low();
+ end
+ endtask // I2C_TX_DADDR
+
+ task I2C_rx_daddr;
+ output [7:0] daddr;
+ input nack;
+
+ begin
+ I2C_rx_bit(daddr[7]);
+ I2C_rx_bit(daddr[6]);
+ I2C_rx_bit(daddr[5]);
+ I2C_rx_bit(daddr[4]);
+ I2C_rx_bit(daddr[3]);
+ I2C_rx_bit(daddr[2]);
+ I2C_rx_bit(daddr[1]);
+ I2C_rx_bit(daddr[0]);
+ I2C_tx_bit( nack );
+ I2C_ack_low();
+ end
+ endtask // I2C_RX_DADDR
+
+ initial begin
+ // Initialize Inputs
+ clk = 0;
+ reset = 0;
+ i2c_device_addr = 8'h74;
+ reg_addr = 0;
+ wr_stb = 0;
+ reg_data_in = 0;
+ snoop_addr = 0;
+
+ $stop;
+
+ I2C_idle();
+ // run an actual reset cycle
+ #(PERIOD*4);
+ reset = 1;
+ #(PERIOD*4);
+ reset = 0;
+ #(PERIOD*4);
+
+ // now pre-set a few I2C registers
+ reg_addr = 0;
+ wr_stb = 1;
+ reg_data_in = 8'hDE;
+ #(PERIOD*4);
+
+ wr_stb = 0;
+ #(PERIOD*1);
+
+ reg_addr = 1;
+ wr_stb = 1;
+ reg_data_in = 8'hAD;
+ #(PERIOD*2);
+
+ wr_stb = 0;
+ #(PERIOD*1);
+
+ reg_addr = 2;
+ wr_stb = 1;
+ reg_data_in = 8'hBE;
+ #(PERIOD*2);
+
+ wr_stb = 0;
+ #(PERIOD*1);
+
+ reg_addr = 3;
+ wr_stb = 1;
+ reg_data_in = 8'hEF;
+ #(PERIOD*2);
+
+
+ // let it soak for a bit for good measure
+ #(PERIOD*10);
+
+ // now the real sim starts
+ I2C_idle();
+
+ // write some data
+ I2C_start();
+ I2C_tx_daddr(8'h74, ack); // write to device 72
+ I2C_tx_daddr(8'h01, ack); // address 01
+ I2C_tx_daddr(8'h33, ack); // data 55
+ I2C_stop();
+ #(I2C_PD*5);
+
+ // do a multi-cycle read
+ I2C_start();
+ I2C_tx_daddr(8'h74, ack); // dummy write to 72
+ I2C_tx_daddr(8'h00, ack); // address 00
+ I2C_start();
+ I2C_tx_daddr(8'h75, ack); // read from 72
+// #(I2C_PD*3);
+ I2C_rx_daddr(readdata, 1'b0); // data @ address 0
+// #(I2C_PD*3);
+ I2C_rx_daddr(readdata, 1'b0); // data @ address 0
+ I2C_rx_daddr(readdata, 1'b0); // data @ address 0
+ I2C_rx_daddr(readdata, 1'bz); // data @ address 0
+ I2C_stop();
+ #(I2C_PD*5);
+
+ // do a multi-cycle write
+ I2C_start();
+ I2C_tx_daddr(8'h74, ack); // write to device 70
+ I2C_tx_daddr(8'h01, ack); // address 01
+ I2C_tx_daddr(8'hFA, ack);
+ I2C_tx_daddr(8'hCE, ack);
+ I2C_tx_daddr(8'h69, ack);
+ I2C_stop();
+ #(I2C_PD*5);
+
+ #I2C_PD;
+ #I2C_PD;
+ snoop_addr = 8'h01;
+ #I2C_PD;
+ snoop_addr = 8'h02;
+ #I2C_PD;
+ snoop_addr = 8'h03;
+ #I2C_PD;
+ snoop_addr = 8'h04;
+ #I2C_PD;
+ snoop_addr = 8'h05;
+ #I2C_PD;
+ #I2C_PD;
+
+ // read back one address at a time
+ I2C_start();
+ I2C_tx_daddr(8'h74, ack); // dummy write to 72
+ I2C_tx_daddr(8'h00, ack); // address 00
+
+ #(I2C_PD*5);
+ I2C_start();
+ I2C_tx_daddr(8'h75, ack); // read from 72
+ I2C_rx_daddr(readdata, 1'bz); // one read
+ I2C_stop();
+
+ // this is the only questionable vector
+ // if you do an isolated read, should the address have
+ // incremeted from the previous read, or
+ // should it be the same. I have implemented it so
+ // that it increments.
+ I2C_start();
+ I2C_tx_daddr(8'h75, ack); // read from 72
+ I2C_rx_daddr(readdata, 1'bz); // one read
+ I2C_stop();
+
+ #(I2C_PD*5);
+
+ I2C_start();
+ I2C_tx_daddr(8'h75, ack); // read from 72
+ I2C_rx_daddr(readdata, 1'b0); // one read
+ I2C_rx_daddr(readdata, 1'bz); // one read
+ I2C_stop();
+
+
+ // write to another device not us
+ I2C_start();
+ I2C_tx_daddr(8'hA0, ack); // write to device a0
+ I2C_tx_daddr(8'h01, ack); // address 01
+ I2C_tx_daddr(8'h55, ack); // data 55 -- this should be ignored
+ I2C_stop();
+
+ #I2C_PD;
+ #I2C_PD;
+ snoop_addr = 8'h01;
+ #I2C_PD;
+ snoop_addr = 8'h02;
+ #I2C_PD;
+ snoop_addr = 8'h03;
+ #I2C_PD;
+ snoop_addr = 8'h04;
+ #I2C_PD;
+ snoop_addr = 8'h05;
+ #I2C_PD;
+ #I2C_PD;
+ #I2C_PD;
+ #I2C_PD;
+
+ end
+
+endmodule
+
diff --git a/common/i2c_squash_edid.v b/common/i2c_squash_edid.v
new file mode 100755
index 0000000..f8f7317
--- /dev/null
+++ b/common/i2c_squash_edid.v
@@ -0,0 +1,892 @@
+//////////////////////////////////////////////////////////////////////////////
+// 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.
+//
+//////////////////////////////////////////////////////////////////////////////
+// An I2C bus snooper implementation. Oversampled for robustness.
+//
+// There are two versions, the EDID snooper and the HDCP snooper
+//
+// It's split because EDID records can be very large and the compiler should
+// infer a block ram for the large records. However, the nature of the HDCP
+// registers would cause the compiler to infer slice registers. Thus, this code
+// is identical to the HDCP snoop with the exception that the HDCP read ports
+// are removed which will allow the compiler to properly infer a LUT RAM.
+///////////
+`timescale 1 ns / 1 ps
+
+module i2c_squash_edid (
+ // external host interface
+ input wire SCL, // the SCL pin state
+ input wire SDA,
+ output reg SDA_pu, // overrides for SDA
+ output reg SDA_pd,
+
+ input wire clk, // internal FPGA clock
+ input wire reset, // internal FPGA reset
+ // i2c configuration
+ input wire [7:0] i2c_snoop_addr,
+
+ input wire [7:0] modeline_adr,
+ input wire [7:0] modeline_dat,
+ input wire modeline_write
+ );
+
+ wire [7:0] modeline; // change wire -> reg if using legacy hard-coded roms
+
+ /////// 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_ADDR = 14'b1 << 4;
+ parameter I2C_ACK_ADDR = 14'b1 << 5;
+ parameter I2C_WR_DATA = 14'b1 << 6;
+ parameter I2C_ACK_WR = 14'b1 << 7;
+ parameter I2C_END_WR = 14'b1 << 8;
+ parameter I2C_RD_DATA = 14'b1 << 9;
+ parameter I2C_ACK_RD = 14'b1 << 10;
+ parameter I2C_END_RD = 14'b1 << 11;
+ parameter I2C_END_RD2 = 14'b1 << 12;
+ parameter I2C_WAITSTOP = 14'b1 << 13;
+
+ parameter I2C_nSTATES = 14;
+
+ 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_ADDR) I2C_state_ascii <= "I2C_ADDR ";
+ else if (I2C_cstate == I2C_ACK_ADDR) I2C_state_ascii <= "I2C_ACK_ADDR ";
+ 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 I2C_state_ascii <= "WTF ";
+ end
+ // synthesis translate_on
+`endif
+
+ reg [3:0] I2C_bitcnt;
+ reg [7:0] I2C_addr;
+ reg [7:0] I2C_daddr;
+ reg I2C_reg_update;
+ reg [7:0] I2C_squashdata;
+ reg I2C_dosquash;
+
+ 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 <=#1 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
+ 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_snoop_addr[7:1]) ?
+ (I2C_daddr[0] == 1'b0 ? I2C_ADDR : I2C_RD_DATA) :
+ I2C_WAITSTOP : // !I2C_daddr match
+ I2C_ACK_DADDR; // !SCL_FALL
+ end
+
+ // device address branch
+ I2C_ADDR: begin
+ I2C_nstate = ((I2C_bitcnt > 4'h7) && (SCL_cstate == SCL_FALL)) ? I2C_ACK_ADDR : I2C_ADDR;
+ end
+ I2C_ACK_ADDR: begin
+ I2C_nstate = (SCL_cstate == SCL_FALL) ? I2C_WR_DATA : I2C_ACK_ADDR;
+ 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_ACK_WR : I2C_WR_DATA;
+ 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
+ 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 or posedge reset) begin
+ if( reset ) begin
+ I2C_bitcnt <=#1 4'b0;
+ I2C_daddr <=#1 8'b0;
+ I2C_reg_update <=#1 1'b0;
+ I2C_addr <=#1 8'b0; // this persists across transactions
+
+ I2C_squashdata <=#1 8'hff;
+ I2C_dosquash <=#1 0;
+ SDA_pd <=#1 0;
+ SDA_pu <=#1 0;
+ end else begin
+ case (I2C_cstate) // synthesis parallel_case full_case
+ I2C_START: begin // everything in reset
+ I2C_bitcnt <=#1 4'b0;
+ I2C_daddr <=#1 8'b0;
+ I2C_reg_update <=#1 1'b0;
+ I2C_addr <=#1 I2C_addr;
+
+ I2C_squashdata <=#1 I2C_squashdata;
+ I2C_dosquash <=#1 0;
+ SDA_pd <=#1 0;
+ SDA_pu <=#1 0;
+ end
+
+ I2C_RESTART: begin
+ I2C_bitcnt <=#1 4'b0;
+ I2C_daddr <=#1 8'b0;
+ I2C_reg_update <=#1 1'b0;
+ I2C_addr <=#1 I2C_addr;
+
+// I2C_squashdata <=#1 I2C_squashdata;
+ // on restart, I2C_addr is valid, so grab squashdata again
+ I2C_squashdata <=#1 modeline[7:0];
+ I2C_dosquash <=#1 0;
+ SDA_pd <=#1 0;
+ SDA_pu <=#1 0;
+ 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 <=#1 I2C_bitcnt + 4'b1;
+ I2C_daddr[7] <=#1 I2C_daddr[6];
+ I2C_daddr[6] <=#1 I2C_daddr[5];
+ I2C_daddr[5] <=#1 I2C_daddr[4];
+ I2C_daddr[4] <=#1 I2C_daddr[3];
+ I2C_daddr[3] <=#1 I2C_daddr[2];
+ I2C_daddr[2] <=#1 I2C_daddr[1];
+ I2C_daddr[1] <=#1 I2C_daddr[0];
+ I2C_daddr[0] <=#1 (SDA_cstate == SDA_HIGH) ? 1'b1 : 1'b0;
+ end else begin // we're oversampled so we need a hold-state gutter
+ I2C_bitcnt <=#1 I2C_bitcnt;
+ I2C_daddr <=#1 I2C_daddr;
+ end // else: !if( SCL_cstate == SCL_RISE )
+ I2C_reg_update <=#1 1'b0;
+ I2C_addr <=#1 I2C_addr;
+
+ I2C_squashdata <=#1 I2C_squashdata;
+ I2C_dosquash <=#1 0;
+ SDA_pd <=#1 0;
+ SDA_pu <=#1 0;
+ end // case: I2C_DADDR
+ I2C_ACK_DADDR: begin
+ I2C_daddr <=#1 I2C_daddr;
+ I2C_bitcnt <=#1 4'b0;
+ I2C_reg_update <=#1 1'b0;
+ I2C_addr <=#1 I2C_addr;
+
+ I2C_squashdata <=#1 I2C_squashdata;
+ I2C_dosquash <=#1 0;
+ SDA_pd <=#1 0;
+ SDA_pu <=#1 0;
+ end
+
+ // get my i2c "write" address (what we want to access inside me)
+ I2C_ADDR: begin
+ if( SCL_cstate == SCL_RISE ) begin
+ I2C_bitcnt <=#1 I2C_bitcnt + 4'b1;
+ I2C_addr[7] <=#1 I2C_addr[6];
+ I2C_addr[6] <=#1 I2C_addr[5];
+ I2C_addr[5] <=#1 I2C_addr[4];
+ I2C_addr[4] <=#1 I2C_addr[3];
+ I2C_addr[3] <=#1 I2C_addr[2];
+ I2C_addr[2] <=#1 I2C_addr[1];
+ I2C_addr[1] <=#1 I2C_addr[0];
+ I2C_addr[0] <=#1 (SDA_cstate == SDA_HIGH) ? 1'b1 : 1'b0;
+ end else begin // we're oversampled so we need a hold-state gutter
+ I2C_bitcnt <=#1 I2C_bitcnt;
+ I2C_addr <=#1 I2C_addr;
+ end // else: !if( SCL_cstate == SCL_RISE )
+ I2C_reg_update <=#1 1'b0;
+ I2C_daddr <=#1 I2C_daddr;
+
+ I2C_squashdata <=#1 I2C_squashdata;
+ I2C_dosquash <=#1 0;
+ SDA_pd <=#1 0;
+ SDA_pu <=#1 0;
+ end // case: I2C_ADDR
+ I2C_ACK_ADDR: begin
+ I2C_daddr <=#1 I2C_daddr;
+ I2C_bitcnt <=#1 4'b0;
+ I2C_reg_update <=#1 1'b0;
+ I2C_addr <=#1 I2C_addr;
+
+ // note mirror image of this on the I2C_END_RD2 branch
+`ifdef POSTERITY
+ if( I2C_addr[7] ) begin // 0x80 and above
+ I2C_squashdata <=#1 modeline[7:0];
+ I2C_dosquash <=#1 1;
+ end else if( I2C_addr[7:0] == 8'h75 ) begin
+ I2C_squashdata <=#1 8'h09; // pixclock @ 90mhz
+ I2C_dosquash <=#1 1;
+ end else if( (I2C_addr[7:0] >= 8'h36) &&
+ (I2C_addr[7:0] <= 8'h47) ) begin
+ I2C_squashdata <=#1 8'h00;
+ I2C_dosquash <=#1 1;
+ end else if( I2C_addr[7:0] == 8'h7f ) begin
+ I2C_squashdata <=#1 8'hb7; // fixup checkusm
+ I2C_dosquash <=#1 1;
+ end else begin
+ I2C_squashdata <=#1 I2C_squashdata;
+ I2C_dosquash <=#1 0;
+ end
+`endif // `ifdef POSTERITY
+ // now that bank is 256 bytes, squash everything
+ I2C_squashdata <=#1 modeline[7:0];
+ I2C_dosquash <=#1 1;
+
+ SDA_pd <=#1 0;
+ SDA_pu <=#1 0;
+ end
+
+
+ // write branch
+ I2C_WR_DATA: begin // shift in data on rising edges of clock
+ if( SCL_cstate == SCL_RISE ) begin
+ I2C_bitcnt <=#1 I2C_bitcnt + 4'b1;
+ end else begin
+ I2C_bitcnt <=#1 I2C_bitcnt; // hold state gutter
+ end // else: !if( SCL_cstate == SCL_RISE )
+ I2C_daddr <=#1 I2C_daddr;
+ I2C_reg_update <=#1 1'b0;
+ I2C_addr <=#1 I2C_addr;
+`ifdef SQUASHWRITES
+ if( SCL_cstate == SCL_FALL ) begin
+ I2C_squashdata[7:0] <=#1 {I2C_squashdata[6:0],I2C_squashdata[7]};
+ end else begin
+ I2C_squashdata <=#1 I2C_squashdata;
+ end
+
+ I2C_dosquash <=#1 I2C_dosquash;
+ if( I2C_dosquash ) begin
+ if( I2C_squashdata[7] ) begin
+ SDA_pd <=#1 0;
+ SDA_pu <=#1 1;
+ end else begin
+ SDA_pd <=#1 1;
+ SDA_pu <=#1 0;
+ end
+ end else begin
+ SDA_pd <=#1 0;
+ SDA_pu <=#1 0;
+ end // else: !if( I2C_dosquash )
+`else // !`ifdef SQUASHWRITES
+ I2C_squashdata <=#1 I2C_squashdata;
+ I2C_dosquash <=#1 0;
+ SDA_pd <=#1 0;
+ SDA_pu <=#1 0;
+`endif
+ end // case: I2C_WR_DATA
+ I2C_ACK_WR: begin
+ I2C_daddr <=#1 I2C_daddr;
+ I2C_bitcnt <=#1 4'b0;
+ I2C_reg_update <=#1 1'b1; // write the data now (over and over again while in state)
+ I2C_addr <=#1 I2C_addr;
+
+ I2C_squashdata <=#1 I2C_squashdata;
+ I2C_dosquash <=#1 0;
+ SDA_pd <=#1 0;
+ SDA_pu <=#1 0;
+ end
+ I2C_END_WR: begin
+ I2C_addr <=#1 I2C_addr + 8'b1; // this is a one-cycle state so this is safe
+ I2C_bitcnt <=#1 4'b0;
+ I2C_reg_update <=#1 1'b0;
+ I2C_daddr <=#1 I2C_daddr;
+
+ I2C_squashdata <=#1 I2C_squashdata;
+ I2C_dosquash <=#1 0;
+ SDA_pd <=#1 0;
+ SDA_pu <=#1 0;
+ end
+
+ // read branch
+ I2C_RD_DATA: begin // shift out data on falling edges of clock
+ if( SCL_cstate == SCL_RISE ) begin
+ I2C_bitcnt <=#1 I2C_bitcnt + 4'b1;
+ end else begin
+ I2C_bitcnt <=#1 I2C_bitcnt; // hold state gutter
+ end
+ I2C_daddr <=#1 I2C_daddr;
+ I2C_reg_update <=#1 1'b0;
+ I2C_addr <=#1 I2C_addr;
+
+ if( SCL_cstate == SCL_FALL ) begin
+ I2C_squashdata[7:0] <=#1 {I2C_squashdata[6:0],I2C_squashdata[7]};
+ end else begin
+ I2C_squashdata <=#1 I2C_squashdata;
+ end
+
+ I2C_dosquash <=#1 I2C_dosquash;
+ if( I2C_dosquash ) begin
+ if( I2C_squashdata[7] ) begin
+ SDA_pd <=#1 0;
+ SDA_pu <=#1 1;
+ end else begin
+ SDA_pd <=#1 1;
+ SDA_pu <=#1 0;
+ end
+ end else begin
+ SDA_pd <=#1 0;
+ SDA_pu <=#1 0;
+ end
+ end // case: I2C_RD_DATA
+ I2C_ACK_RD: begin
+ I2C_daddr <=#1 I2C_daddr;
+ I2C_bitcnt <=#1 4'b0;
+ I2C_reg_update <=#1 1'b1; // commit reads even to our internal bank
+ I2C_addr <=#1 I2C_addr;
+
+ I2C_squashdata <=#1 I2C_squashdata;
+ I2C_dosquash <=#1 0;
+ SDA_pd <=#1 0;
+ SDA_pu <=#1 0;
+ end
+ I2C_END_RD: begin
+ I2C_addr <=#1 I2C_addr + 8'b1; // this is a one-cycle state so this is safe
+ I2C_bitcnt <=#1 4'b0;
+ I2C_reg_update <=#1 1'b0;
+ I2C_daddr <=#1 I2C_daddr;
+
+ I2C_squashdata <=#1 I2C_squashdata;
+ I2C_dosquash <=#1 0;
+ SDA_pd <=#1 0;
+ SDA_pu <=#1 0;
+ end
+ I2C_END_RD2: begin
+ I2C_daddr <=#1 8'b0;
+ I2C_bitcnt <=#1 4'b0;
+ I2C_reg_update <=#1 1'b0;
+ I2C_addr <=#1 I2C_addr;
+
+`ifdef POSTERITY
+ if( I2C_addr[7] ) begin // 0x80 and above
+ I2C_squashdata <=#1 modeline[7:0];
+ I2C_dosquash <=#1 1;
+ end else if( I2C_addr[7:0] == 8'h75 ) begin
+ I2C_squashdata <=#1 8'h09; // pixclock @ 90mhz
+ I2C_dosquash <=#1 1;
+ end else if( (I2C_addr[7:0] >= 8'h36) &&
+ (I2C_addr[7:0] <= 8'h47) ) begin
+ I2C_squashdata <=#1 8'h00;
+ I2C_dosquash <=#1 1;
+ end else if( I2C_addr[7:0] == 8'h7f ) begin
+ I2C_squashdata <=#1 8'hb7; // fixup checkusm
+ I2C_dosquash <=#1 1;
+ end else begin
+ I2C_squashdata <=#1 I2C_squashdata;
+ I2C_dosquash <=#1 0;
+ end
+`endif // `ifdef POSTERITY
+ // now that bank is 256 bytes, squash everything
+ I2C_squashdata <=#1 modeline[7:0];
+ I2C_dosquash <=#1 1;
+
+ SDA_pd <=#1 0;
+ SDA_pu <=#1 0;
+ end
+
+ I2C_WAITSTOP: begin
+ I2C_daddr <=#1 8'b0;
+ I2C_bitcnt <=#1 4'b0;
+ I2C_reg_update <=#1 1'b0;
+ I2C_addr <=#1 I2C_addr;
+
+ I2C_squashdata <=#1 I2C_squashdata;
+ I2C_dosquash <=#1 0;
+ SDA_pd <=#1 0;
+ SDA_pu <=#1 0;
+ end
+ endcase // case (cstate)
+ end // else: !if( reset )
+ end // always @ (posedge clk or posedge reset)
+
+`ifdef POSTERITY
+ if( I2C_addr[7:0] == 8'h63 ) begin
+ I2C_squashdata <=#1 8'h0a;
+ I2C_dosquash <=#1 1;
+ end else if( (I2C_addr[7:0] >= 8'h36) && (I2C_addr[7:0] <= 8'h59) ) begin
+ I2C_squashdata <=#1 modeline[7:0];
+ I2C_dosquash <=#1 1;
+ end else begin
+ I2C_squashdata <=#1 I2C_squashdata;
+ I2C_dosquash <=#1 0;
+ end
+ SDA_pd <=#1 0;
+ SDA_pu <=#1 0;
+`endif
+
+ ////////////////
+ ///// 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 or posedge reset) begin
+ if (reset)
+ SCL_cstate <= SCL_HIGH; // always start here even if it's wrong -- easier to test
+ else
+ SCL_cstate <=#1 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 or posedge reset) begin
+ if( reset ) begin
+ SCL_rfcnt <=#1 5'b0;
+ end else begin
+ case (SCL_cstate) // synthesis parallel_case full_case
+ SCL_HIGH: begin
+ if( SCL_sync == 1'b1 ) begin
+ SCL_rfcnt <=#1 5'b0;
+ end else begin
+ SCL_rfcnt <=#1 SCL_rfcnt + 5'b1;
+ end
+ end
+ SCL_FALL: begin
+ SCL_rfcnt <=#1 5'b0;
+ end
+ SCL_LOW: begin
+ if( SCL_sync == 1'b0 ) begin
+ SCL_rfcnt <=#1 5'b0;
+ end else begin
+ SCL_rfcnt <=#1 SCL_rfcnt + 5'b1;
+ end
+ end
+ SCL_RISE: begin
+ SCL_rfcnt <=#1 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 or posedge reset) begin
+ if (reset)
+ SDA_cstate <= SDA_HIGH; // always start here even if it's wrong -- easier to test
+ else
+ SDA_cstate <=#1 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 or posedge reset) begin
+ if( reset ) begin
+ SDA_rfcnt <=#1 5'b0;
+ end else begin
+ case (SDA_cstate) // synthesis parallel_case full_case
+ SDA_HIGH: begin
+ if( SDA_sync == 1'b1 ) begin
+ SDA_rfcnt <=#1 5'b0;
+ end else begin
+ SDA_rfcnt <=#1 SDA_rfcnt + 5'b1;
+ end
+ end
+ SDA_FALL: begin
+ SDA_rfcnt <=#1 5'b0;
+ end
+ SDA_LOW: begin
+ if( SDA_sync == 1'b0 ) begin
+ SDA_rfcnt <=#1 5'b0;
+ end else begin
+ SDA_rfcnt <=#1 SDA_rfcnt + 5'b1;
+ end
+ end
+ SDA_RISE: begin
+ SDA_rfcnt <=#1 5'b0;
+ end
+ endcase // case (cstate)
+ end // else: !if( reset )
+ end // always @ (posedge clk or posedge reset)
+
+
+
+ /////////////////////
+ /////// synchronizers
+ /////////////////////
+ always @ (posedge clk or posedge reset) begin
+ if (reset) begin
+ SCL_s <= 0;
+ SCL_sync <= 0;
+ SDA_s <= 0;
+ SDA_sync <= 0;
+ end else begin
+ SCL_s <= SCL;
+ SCL_sync <= SCL_s;
+ SDA_s <= SDA;
+ SDA_sync <= SDA_s;
+ end // else: !if(reset)
+ end // always @ (posedge clk or posedge reset)
+
+ /////////////////////////
+ // ram that contains the override modeline
+ /////////////////////////
+ (* RAM_STYLE="{AUTO | DISTRIBUTED | PIPE_DISTRIBUTED}" *)
+ reg [7:0] moderam [255:0];
+
+// wire [7:0] modeline; // declared up top
+
+ always @(posedge clk)
+ if (modeline_write)
+ moderam[modeline_adr[7:0]] <= modeline_dat[7:0];
+
+ assign modeline[7:0] = moderam[I2C_addr[7:0]];
+
+`ifdef POSTERITY
+ wire [7:0] I2C_offset36;
+ assign I2C_offset36[7:0] = I2C_addr[7:0] - 8'h36;
+
+ always @(*) begin
+ case( I2C_offset36[4:0] )
+ 5'h00: modeline = 8'h01; // master copy
+ 5'h01: modeline = 8'h1d;
+ 5'h02: modeline = 8'h00;
+ 5'h03: modeline = 8'hbc;
+ 5'h04: modeline = 8'h52;
+ 5'h05: modeline = 8'hd0;
+ 5'h06: modeline = 8'h1e;
+ 5'h07: modeline = 8'h20;
+ 5'h08: modeline = 8'hb8;
+ 5'h09: modeline = 8'h28;
+ 5'h0a: modeline = 8'h55;
+ 5'h0b: modeline = 8'h40;
+ 5'h0c: modeline = 8'ha0;
+ 5'h0d: modeline = 8'h5a;
+ 5'h0e: modeline = 8'h00;
+ 5'h0f: modeline = 8'h00;
+ 5'h10: modeline = 8'h00;
+ 5'h11: modeline = 8'h1e;
+
+ 5'h12: modeline = 8'h01; // alias copy
+ 5'h13: modeline = 8'h1d;
+ 5'h14: modeline = 8'h00;
+ 5'h15: modeline = 8'hbc;
+ 5'h16: modeline = 8'h52;
+ 5'h17: modeline = 8'hd0;
+ 5'h18: modeline = 8'h1e;
+ 5'h19: modeline = 8'h20;
+ 5'h1a: modeline = 8'hb8;
+ 5'h1b: modeline = 8'h28;
+ 5'h1c: modeline = 8'h55;
+ 5'h1d: modeline = 8'h40;
+ 5'h1e: modeline = 8'ha0;
+ 5'h1f: modeline = 8'h5a;
+ endcase // case ( I2C_addr )
+ end // always @ (*)
+`endif // !`ifdef POSTERITY
+`ifdef POSTERITY
+ always @(*) begin
+ case( I2C_addr[6:0] )
+ 7'h00: modeline = 8'h02;
+ 7'h01: modeline = 8'h03;
+ 7'h02: modeline = 8'h1f;
+ 7'h03: modeline = 8'hf2;
+ 7'h04: modeline = 8'h4b;
+ 7'h05: modeline = 8'h93;
+ 7'h06: modeline = 8'h04;
+ 7'h07: modeline = 8'h12;
+ 7'h08: modeline = 8'h83;
+ 7'h09: modeline = 8'h14;
+ 7'h0a: modeline = 8'h05;
+ 7'h0b: modeline = 8'h20;
+ 7'h0c: modeline = 8'h00;
+ 7'h0d: modeline = 8'h00;
+ 7'h0e: modeline = 8'h00;
+ 7'h0f: modeline = 8'h00;
+ 7'h10: modeline = 8'h23;
+ 7'h11: modeline = 8'h09;
+ 7'h12: modeline = 8'h07;
+ 7'h13: modeline = 8'h07;
+ 7'h14: modeline = 8'h83;
+ 7'h15: modeline = 8'h01;
+ 7'h16: modeline = 8'h00;
+ 7'h17: modeline = 8'h00;
+ 7'h18: modeline = 8'h66;
+ 7'h19: modeline = 8'h03;
+ 7'h1a: modeline = 8'h0c;
+ 7'h1b: modeline = 8'h00;
+ 7'h1c: modeline = 8'h10;
+ 7'h1d: modeline = 8'h00;
+ 7'h1e: modeline = 8'h80;
+ 7'h1f: modeline = 8'h8c;
+ 7'h20: modeline = 8'h0a;
+ 7'h21: modeline = 8'hd0;
+ 7'h22: modeline = 8'h8a;
+ 7'h23: modeline = 8'h20;
+ 7'h24: modeline = 8'he0;
+ 7'h25: modeline = 8'h2d;
+ 7'h26: modeline = 8'h10;
+ 7'h27: modeline = 8'h10;
+ 7'h28: modeline = 8'h3e;
+ 7'h29: modeline = 8'h96;
+ 7'h2a: modeline = 8'h00;
+ 7'h2b: modeline = 8'ha0;
+ 7'h2c: modeline = 8'h5a;
+ 7'h2d: modeline = 8'h00;
+ 7'h2e: modeline = 8'h00;
+ 7'h2f: modeline = 8'h00;
+ 7'h30: modeline = 8'h18;
+ 7'h31: modeline = 8'h01;
+ 7'h32: modeline = 8'h1d;
+ 7'h33: modeline = 8'h00;
+ 7'h34: modeline = 8'h72;
+ 7'h35: modeline = 8'h51;
+ 7'h36: modeline = 8'hd0;
+ 7'h37: modeline = 8'h1e;
+ 7'h38: modeline = 8'h20;
+ 7'h39: modeline = 8'h6e;
+ 7'h3a: modeline = 8'h28;
+ 7'h3b: modeline = 8'h55;
+ 7'h3c: modeline = 8'h00;
+ 7'h3d: modeline = 8'ha0;
+ 7'h3e: modeline = 8'h5a;
+ 7'h3f: modeline = 8'h00;
+ 7'h40: modeline = 8'h00;
+ 7'h41: modeline = 8'h00;
+ 7'h42: modeline = 8'h1e;
+ 7'h43: modeline = 8'h01;
+ 7'h44: modeline = 8'h1d;
+ 7'h45: modeline = 8'h80;
+ 7'h46: modeline = 8'hd0;
+ 7'h47: modeline = 8'h72;
+ 7'h48: modeline = 8'h1c;
+ 7'h49: modeline = 8'h16;
+ 7'h4a: modeline = 8'h20;
+ 7'h4b: modeline = 8'h10;
+ 7'h4c: modeline = 8'h2c;
+ 7'h4d: modeline = 8'h25;
+ 7'h4e: modeline = 8'h80;
+ 7'h4f: modeline = 8'ha0;
+ 7'h50: modeline = 8'h5a;
+ 7'h51: modeline = 8'h00;
+ 7'h52: modeline = 8'h00;
+ 7'h53: modeline = 8'h00;
+ 7'h54: modeline = 8'h9e;
+ 7'h55: modeline = 8'h01;
+ 7'h56: modeline = 8'h1d;
+ 7'h57: modeline = 8'h80;
+ 7'h58: modeline = 8'h18;
+ 7'h59: modeline = 8'h71;
+ 7'h5a: modeline = 8'h1c;
+ 7'h5b: modeline = 8'h16;
+ 7'h5c: modeline = 8'h20;
+ 7'h5d: modeline = 8'h58;
+ 7'h5e: modeline = 8'h2c;
+ 7'h5f: modeline = 8'h25;
+ 7'h60: modeline = 8'h00;
+ 7'h61: modeline = 8'ha0;
+ 7'h62: modeline = 8'h5a;
+ 7'h63: modeline = 8'h00;
+ 7'h64: modeline = 8'h00;
+ 7'h65: modeline = 8'h00;
+ 7'h66: modeline = 8'h9e;
+ 7'h67: modeline = 8'h8c;
+ 7'h68: modeline = 8'h0a;
+ 7'h69: modeline = 8'hd0;
+ 7'h6a: modeline = 8'h90;
+ 7'h6b: modeline = 8'h20;
+ 7'h6c: modeline = 8'h40;
+ 7'h6d: modeline = 8'h31;
+ 7'h6e: modeline = 8'h20;
+ 7'h6f: modeline = 8'h0c;
+ 7'h70: modeline = 8'h40;
+ 7'h71: modeline = 8'h55;
+ 7'h72: modeline = 8'h00;
+ 7'h73: modeline = 8'ha0;
+ 7'h74: modeline = 8'h5a;
+ 7'h75: modeline = 8'h00;
+ 7'h76: modeline = 8'h00;
+ 7'h77: modeline = 8'h00;
+ 7'h78: modeline = 8'h18;
+ 7'h79: modeline = 8'h00;
+ 7'h7a: modeline = 8'h00;
+ 7'h7b: modeline = 8'h00;
+ 7'h7c: modeline = 8'h00;
+ 7'h7d: modeline = 8'h00;
+ 7'h7e: modeline = 8'h00;
+ 7'h7f: modeline = 8'ha3;
+ endcase // case ( I2C_addr )
+ end // always @ (*)
+`endif // !`ifdef POSTERITY
+
+endmodule // i2c_slave
diff --git a/common/i2c_squash_tb.v b/common/i2c_squash_tb.v
new file mode 100755
index 0000000..70c76a9
--- /dev/null
+++ b/common/i2c_squash_tb.v
@@ -0,0 +1,445 @@
+//////////////////////////////////////////////////////////////////////////////
+// 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.
+//
+//////////////////////////////////////////////////////////////////////////////
+`timescale 1ns / 1ps
+
+////////////////////////////////////////////////////////////////////////////////
+// Company:
+// Engineer:
+//
+// Create Date: 12:30:45 04/05/2011
+// Design Name: i2c_slave
+// Module Name: C:/largework/fpga/hdmi/impl4/common/i2c_slave_tb.v
+// Project Name: impl4
+// Target Device:
+// Tool versions:
+// Description:
+//
+// Verilog Test Fixture created by ISE for module: i2c_slave
+//
+// Dependencies:
+//
+// Revision:
+// Revision 0.01 - File Created
+// Additional Comments:
+//
+////////////////////////////////////////////////////////////////////////////////
+
+module i2c_squash_tb;
+
+ reg SDA; // physical wire state
+ reg SCL;
+
+ // Inputs
+ reg clk;
+ reg reset;
+ reg [7:0] i2c_device_addr;
+ reg [7:0] reg_addr;
+ reg wr_stb;
+ reg [7:0] reg_data_in;
+
+ // Outputs
+ wire SCL_pd;
+ wire SDA_pd;
+ wire SDA_pu;
+ wire [7:0] reg_a0;
+ wire [7:0] reg_a1;
+ wire [7:0] reg_a2;
+ wire [7:0] reg_a3;
+
+ reg [7:0] snoop_addr;
+ wire [7:0] snoop_data;
+
+ wire DDC_SDA_pu;
+ wire DDC_SDA_pd;
+ wire [63:0] An;
+ wire Aksv14_write;
+
+//`define FOO
+`ifdef FOO
+ // Instantiate the Unit Under Test (UUT)
+ i2c_snoop supa_snoopa (
+ .SCL(SCL),
+ .SDA(SDA),
+ .clk(clk),
+ .reset(reset),
+ .i2c_snoop_addr(8'h74),
+ .reg_addr(snoop_addr),
+ .reg_dout(snoop_data),
+ .An(An),
+ .Aksv14_write(Aksv14_write)
+ );
+`endif
+
+ // Instantiate the Unit Under Test (UUT)
+ i2c_slave target (
+ .SCL(SCL),
+ .SCL_pd(SCL_pd),
+ .SDA(SDA),
+ .SDA_pd(SDA_pd_comb),
+ .SDA_pu(SDA_pu_comb),
+ .clk(clk),
+ .reset(reset),
+ .i2c_device_addr(i2c_device_addr),
+ .reg_addr(reg_addr),
+ .wr_stb(wr_stb),
+ .reg_data_in(reg_data_in),
+ .reg_0(reg_a0),
+ .reg_1(reg_a1),
+ .reg_2(reg_a2),
+ .reg_3(reg_a3)
+ );
+ i2c_squash_edid ddc_edid_squash (
+ .SCL(SCL),
+ .SDA(SDA),
+ .clk(clk),
+ .reset(reset),
+
+ .SDA_pu(DDC_SDA_pu),
+ .SDA_pd(DDC_SDA_pd),
+
+ .i2c_snoop_addr(8'h74) // EDID address
+ );
+
+ reg sda_host; // what the host is driving to
+ reg scl_host;
+ reg ack; // what the ack state is
+ reg [7:0] readdata;
+
+ wire SDA_pu_comb;
+ wire SDA_pd_comb;
+
+ assign SDA_pu = SDA_pu_comb | DDC_SDA_pu;
+ assign SDA_pd = SDA_pd_comb | DDC_SDA_pd;
+
+// always @(SCL_pd, SDA_pd, SDA_pu, sda_host, scl_host) begin
+ always @(*) begin // lazy
+ // scl equations
+ case( {SCL_pd, scl_host} )
+ 2'b00: SCL <= 1'b0;
+ 2'b01: SCL <= 1'b1;
+ 2'b10: SCL <= 1'b0;
+ // conflict case
+ 2'b11: SCL <= 1'bX;
+ // handle tristate case
+ 2'b0Z: SCL <= 1'b1;
+ 2'b1Z: SCL <= 1'b0;
+ default: SCL <= 1'bX;
+ endcase // case ( {SCL_pd, scl_host} )
+
+ case( {SDA_pd, SDA_pu, sda_host} )
+ 3'b000: SDA <= 1'b0;
+ 3'b001: SDA <= 1'b1;
+ 3'b010: SDA <= 1'bX; // change to 1'b1 for override
+ 3'b011: SDA <= 1'b1;
+ 3'b100: SDA <= 1'b0;
+ 3'b101: SDA <= 1'bX; // change to 1'b0 for override
+ 3'b110: SDA <= 1'bX;
+ 3'b111: SDA <= 1'bX;
+
+ // tristate case
+ 3'b00Z: SDA <= 1'b1;
+ 3'b01Z: SDA <= 1'b1;
+ 3'b10Z: SDA <= 1'b0;
+ 3'b11Z: SDA <= 1'bX;
+ endcase // case ( {SDA_pd, SDA_pu, sda_host} )
+ end
+
+ parameter PERIOD = 16'd40; // 25 MHz
+ parameter I2C_PD = 16'd2464; // make it odd to try and catch non-phase synced issues
+ parameter I2C_TH = 16'd114;
+ parameter I2C_TS = 16'd217;
+
+ always begin
+ clk = 1'b0;
+ #(PERIOD/2) clk = 1'b1;
+ #(PERIOD/2);
+ end
+
+ task I2C_idle;
+ begin
+ scl_host = 1'bZ;
+ sda_host = 1'bZ;
+ #I2C_PD;
+ end
+ endtask // I2C_idle
+
+ task I2C_start;
+ begin
+ scl_host = 1'bZ;
+ sda_host = 1'bZ;
+ #(I2C_PD/2);
+ scl_host = 1'bZ;
+ sda_host = 1'b0;
+ #(I2C_PD/2);
+ end
+ endtask // I2C_start
+
+ task I2C_stop;
+ begin
+ scl_host = 1'bZ;
+ sda_host = 1'b0;
+ #(I2C_PD/2);
+ scl_host = 1'bZ;
+ sda_host = 1'bZ;
+ #(I2C_PD/2);
+ end
+ endtask // I2C_start
+
+ task I2C_tx_bit; // tx from host ( from testbench )
+ input bitval;
+
+ begin
+ scl_host = 1'b0;
+ #I2C_TH;
+ sda_host = bitval;
+ #(I2C_PD/2);
+ scl_host = 1'bZ;
+ sda_host = bitval;
+ #(I2C_PD/2);
+ end
+ endtask // I2C_tx_bit
+
+ task I2C_rx_bit; // rx to host ( to testbench )
+ output bitval;
+
+ begin
+ scl_host = 1'b0;
+ #(I2C_TH/2);
+ sda_host = 1'bz;
+ #(I2C_PD/2);
+ scl_host = 1'bZ;
+ sda_host = 1'bz;
+ #1;
+ bitval = SDA;
+ #(I2C_PD/2);
+ end
+ endtask // I2C_start
+
+ task I2C_ack_low;
+ begin
+ scl_host = 1'b0;
+ #(I2C_PD/2);
+ end
+ endtask // I2C_ack_low
+
+
+ task I2C_tx_daddr;
+ input [7:0] daddr;
+ output rack;
+
+ begin
+ I2C_tx_bit( daddr[7] );
+ I2C_tx_bit( daddr[6] );
+ I2C_tx_bit( daddr[5] );
+ I2C_tx_bit( daddr[4] );
+ I2C_tx_bit( daddr[3] );
+ I2C_tx_bit( daddr[2] );
+ I2C_tx_bit( daddr[1] );
+ I2C_tx_bit( daddr[0] );
+ I2C_rx_bit(rack);
+ I2C_ack_low();
+ end
+ endtask // I2C_TX_DADDR
+
+ task I2C_rx_daddr;
+ output [7:0] daddr;
+ input nack;
+
+ begin
+ I2C_rx_bit(daddr[7]);
+ I2C_rx_bit(daddr[6]);
+ I2C_rx_bit(daddr[5]);
+ I2C_rx_bit(daddr[4]);
+ I2C_rx_bit(daddr[3]);
+ I2C_rx_bit(daddr[2]);
+ I2C_rx_bit(daddr[1]);
+ I2C_rx_bit(daddr[0]);
+ I2C_tx_bit( nack );
+ I2C_ack_low();
+ end
+ endtask // I2C_RX_DADDR
+
+ initial begin
+ // Initialize Inputs
+ clk = 0;
+ reset = 0;
+ i2c_device_addr = 8'h74;
+ reg_addr = 0;
+ wr_stb = 0;
+ reg_data_in = 0;
+ snoop_addr = 0;
+
+ $stop;
+
+ I2C_idle();
+ // run an actual reset cycle
+ #(PERIOD*4);
+ reset = 1;
+ #(PERIOD*4);
+ reset = 0;
+ #(PERIOD*4);
+
+ // now pre-set a few I2C registers
+ reg_addr = 0;
+ wr_stb = 1;
+ reg_data_in = 8'hDE;
+ #(PERIOD*4);
+
+ wr_stb = 0;
+ #(PERIOD*1);
+
+ reg_addr = 1;
+ wr_stb = 1;
+ reg_data_in = 8'hAD;
+ #(PERIOD*2);
+
+ wr_stb = 0;
+ #(PERIOD*1);
+
+ reg_addr = 2;
+ wr_stb = 1;
+ reg_data_in = 8'hBE;
+ #(PERIOD*2);
+
+ wr_stb = 0;
+ #(PERIOD*1);
+
+ reg_addr = 3;
+ wr_stb = 1;
+ reg_data_in = 8'hEF;
+ #(PERIOD*2);
+
+
+ // let it soak for a bit for good measure
+ #(PERIOD*10);
+
+ // now the real sim starts
+ I2C_idle();
+
+ // write some data
+ I2C_start();
+ I2C_tx_daddr(8'h74, ack); // write to device 72
+ I2C_tx_daddr(8'h01, ack); // address 01
+ I2C_tx_daddr(8'h33, ack); // data 55
+ I2C_stop();
+ #(I2C_PD*5);
+
+ // do a multi-cycle read
+ I2C_start();
+ I2C_tx_daddr(8'h74, ack); // dummy write to 72
+ I2C_tx_daddr(8'h00, ack); // address 00
+ I2C_start();
+ I2C_tx_daddr(8'h75, ack); // read from 72
+// #(I2C_PD*3);
+ I2C_rx_daddr(readdata, 1'b0); // data @ address 0
+// #(I2C_PD*3);
+ I2C_rx_daddr(readdata, 1'b0); // data @ address 0
+ I2C_rx_daddr(readdata, 1'b0); // data @ address 0
+ I2C_rx_daddr(readdata, 1'bz); // data @ address 0
+ I2C_stop();
+ #(I2C_PD*5);
+
+ // do a multi-cycle write
+ I2C_start();
+ I2C_tx_daddr(8'h74, ack); // write to device 70
+ I2C_tx_daddr(8'h01, ack); // address 01
+ I2C_tx_daddr(8'hFA, ack);
+ I2C_tx_daddr(8'hCE, ack);
+ I2C_tx_daddr(8'h69, ack);
+ I2C_stop();
+ #(I2C_PD*5);
+
+ #I2C_PD;
+ #I2C_PD;
+ snoop_addr = 8'h01;
+ #I2C_PD;
+ snoop_addr = 8'h02;
+ #I2C_PD;
+ snoop_addr = 8'h03;
+ #I2C_PD;
+ snoop_addr = 8'h04;
+ #I2C_PD;
+ snoop_addr = 8'h05;
+ #I2C_PD;
+ #I2C_PD;
+
+ // read back one address at a time
+ I2C_start();
+ I2C_tx_daddr(8'h74, ack); // dummy write to 72
+ I2C_tx_daddr(8'h00, ack); // address 00
+
+ #(I2C_PD*5);
+ I2C_start();
+ I2C_tx_daddr(8'h75, ack); // read from 72
+ I2C_rx_daddr(readdata, 1'bz); // one read
+ I2C_stop();
+
+ // this is the only questionable vector
+ // if you do an isolated read, should the address have
+ // incremeted from the previous read, or
+ // should it be the same. I have implemented it so
+ // that it increments.
+ I2C_start();
+ I2C_tx_daddr(8'h75, ack); // read from 72
+ I2C_rx_daddr(readdata, 1'bz); // one read
+ I2C_stop();
+
+ #(I2C_PD*5);
+
+ I2C_start();
+ I2C_tx_daddr(8'h75, ack); // read from 72
+ I2C_rx_daddr(readdata, 1'b0); // one read
+ I2C_rx_daddr(readdata, 1'bz); // one read
+ I2C_stop();
+
+
+ // write to another device not us
+ I2C_start();
+ I2C_tx_daddr(8'hA0, ack); // write to device a0
+ I2C_tx_daddr(8'h01, ack); // address 01
+ I2C_tx_daddr(8'h55, ack); // data 55 -- this should be ignored
+ I2C_stop();
+
+ #I2C_PD;
+ #I2C_PD;
+ snoop_addr = 8'h01;
+ #I2C_PD;
+ snoop_addr = 8'h02;
+ #I2C_PD;
+ snoop_addr = 8'h03;
+ #I2C_PD;
+ snoop_addr = 8'h04;
+ #I2C_PD;
+ snoop_addr = 8'h05;
+ #I2C_PD;
+ #I2C_PD;
+ #I2C_PD;
+ #I2C_PD;
+
+ end
+
+endmodule // i2c_squash_tb
+
diff --git a/common/pwm.v b/common/pwm.v
new file mode 100755
index 0000000..bf3fc8b
--- /dev/null
+++ b/common/pwm.v
@@ -0,0 +1,83 @@
+//////////////////////////////////////////////////////////////////////////////
+// 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.
+//
+//////////////////////////////////////////////////////////////////////////////
+// this module does simple PWM modulation to create the "breathing" LED effect
+//
+
+`timescale 1 ns / 1 ps
+
+module pwm(
+ input wire clk812k, // use clock from device DNA block, 812.5kHz
+ input wire reset,
+ output reg pwmout,
+ input wire [11:0] bright,
+ input wire [11:0] dim
+ );
+
+ reg [9:0] pwm_count;
+ reg pwmreg;
+ reg [11:0] interpolate;
+ reg countdn;
+ wire [9:0] interp;
+
+ always @(posedge clk812k or posedge reset) begin
+ if( reset ) begin
+ pwm_count <= 0;
+ interpolate[11:0] <= dim[11:0];
+ countdn <= 0;
+ end else begin
+ if( interpolate[11:0] >= bright[11:0] ) begin
+ countdn <= 1;
+ end else if( interpolate[11:0] <= dim[11:0] ) begin
+ countdn <= 0;
+ end else begin
+ countdn <= countdn;
+ end
+
+ if( pwm_count[9:0] == 10'h0 ) begin
+ if( countdn == 1'b1 ) begin
+ interpolate[11:0] <= interpolate[11:0] - 12'b1;
+ end else begin
+ interpolate[11:0] <= interpolate[11:0] + 12'b1;
+ end
+ end else begin
+ interpolate[11:0] <= interpolate[11:0];
+ end
+
+ pwm_count[9:0] <= pwm_count[9:0] + 10'b1;
+ end
+
+ pwmreg <= (pwm_count[9:0] < interp[9:0]);
+
+ end // always @ (posedge clk812k or posedge reset)
+
+ assign interp[9:0] = interpolate[11:2];
+
+ always @(posedge clk812k) begin
+ // make it registered to ease up routing congestion to the edge
+ pwmout <= !pwmreg;
+ end
+
+endmodule // pwm
diff --git a/common/timing_detector.v b/common/timing_detector.v
new file mode 100755
index 0000000..bb4e872
--- /dev/null
+++ b/common/timing_detector.v
@@ -0,0 +1,390 @@
+//////////////////////////////////////////////////////////////////////////////
+// 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.
+//
+//////////////////////////////////////////////////////////////////////////////
+`timescale 1 ns / 1ps
+
+module timing_detector (
+ input wire pclk,
+ input wire rstin, // asynchronous, as it is asserted during no-clock condition
+ input wire vsync, // assume positive-polarity sync on input
+ input wire hsync, // assume positive-polarity sync on input
+ input wire de,
+ input wire refclk,
+ input wire lcd_de,
+ input wire lcd_vsync,
+
+ output reg [11:0] hactive, // in pixels
+ output reg [11:0] vactive, // in lines
+ output reg [11:0] htotal, // in pixels
+ output reg [23:0] vtotal, // ** in PIXELS ** must divide by htotal in software
+ output reg [7:0] h_frontporch, // in pixels
+ output reg [7:0] h_backporch, // in pixels
+ output reg [23:0] v_frontporch, // ** in PIXELS **
+ output reg [23:0] v_backporch, // ** in PIXELS **
+ output reg [7:0] hsync_width, // in pixels
+ output reg [23:0] vsync_width, // ** in PIXELS **
+ output reg [11:0] lcd_de_latency, // in lines
+ output reg [11:0] lcd_vsync_latency, // in lines
+
+ output reg [23:0] refclkcnt // number of refclocks in a field
+ );
+
+ reg vsync_d;
+ reg hsync_d;
+ reg de_d;
+
+ wire vsync_rising;
+ wire hsync_rising;
+ wire de_rising;
+
+ wire vsync_falling;
+ wire hsync_falling;
+ wire de_falling;
+
+ reg [11:0] hcount;
+ reg [7:0] hsync_width_cnt;
+ reg [23:0] vcount;
+ reg [11:0] de_count;
+ reg [11:0] vactive_count;
+ reg [7:0] h_fp_count;
+ reg [7:0] h_bp_count;
+ reg [23:0] v_fp_count;
+ reg [23:0] v_bp_count;
+
+ reg vsync_refclk;
+ reg vsync__refclk;
+ reg vsync___refclk;
+ reg vsync____refclk;
+ wire vsync_refclk_rising;
+ reg [23:0] refclkcounter;
+
+ reg lcd_de_pclk;
+ reg lcd_de_1pclk;
+ reg lcd_de_2pclk;
+
+ reg lcd_vsync_pclk;
+ reg lcd_vsync_1pclk;
+ reg lcd_vsync_2pclk;
+
+ reg first_de_rising;
+ reg first_lcd_de_rising;
+
+ //// vertical front/back porch machine
+ always @(posedge pclk or posedge rstin) begin
+ if( rstin ) begin
+ v_fp_count <= 0;
+ v_bp_count <= 0;
+ v_frontporch <= 0;
+ v_backporch <= 0;
+ first_de_rising <= 1;
+ end else begin
+ if( vsync_falling ) begin
+ v_fp_count <= 0;
+ first_de_rising <= 1;
+ end else begin
+ if( v_fp_count == 24'hFF_FFFF ) begin
+ v_fp_count <= v_fp_count;
+ end else begin
+ v_fp_count <= v_fp_count + 24'b1; // counting in pixels
+ end
+
+ if( de_rising ) begin
+ first_de_rising <= 0;
+ end else begin
+ first_de_rising <= first_de_rising;
+ end
+ end
+
+ if( de_rising && first_de_rising ) begin
+ v_frontporch <= v_fp_count;
+ end else begin
+ v_frontporch <= v_frontporch;
+ end
+
+ if( de_falling ) begin
+ v_bp_count <= 0;
+ end else begin
+ if( v_bp_count == 24'hFF_FFFF ) begin
+ v_bp_count <= v_bp_count;
+ end else begin
+ v_bp_count <= v_bp_count + 24'b1; // counting in pixels
+ end
+ end
+
+ if( vsync_rising ) begin
+ v_backporch <= v_bp_count;
+ end else begin
+ v_backporch <= v_backporch;
+ end
+ end // else: !if( rstin )
+ end // always @ (posedge pclk)
+
+ //// horizonal front/back porch machine
+ always @(posedge pclk or posedge rstin) begin
+ if( rstin ) begin
+ h_fp_count <= 0;
+ h_bp_count <= 0;
+ h_frontporch <= 0;
+ h_backporch <= 0;
+ end else begin
+ if( hsync_falling ) begin
+ h_fp_count <= 8'b0;
+ end else begin
+ if( h_fp_count == 8'b1111_1111 ) begin
+ h_fp_count <= h_fp_count; // saturate to catch de-only timings
+ end else begin
+ h_fp_count <= h_fp_count + 8'b1;
+ end
+ end
+
+ if( de_rising ) begin
+ h_frontporch <= h_fp_count + 8'b10; // this is a bit of a hack, why is one pixel missing?
+ end else begin
+ h_frontporch <= h_frontporch;
+ end
+
+ if( de_falling ) begin
+ h_bp_count <= 0;
+ end else begin
+ if( h_bp_count == 8'b1111_1111 ) begin
+ h_bp_count <= h_bp_count;
+ end else begin
+ h_bp_count <= h_bp_count + 8'b1;
+ end
+ end
+
+ if( hsync_rising ) begin
+ h_backporch <= h_bp_count;
+ end else begin
+ h_backporch <= h_backporch;
+ end
+ end // else: !if( rstin )
+ end // always @ (posedge pclk)
+
+ //// vsync_width machine
+ always @(posedge pclk or posedge rstin) begin
+ if( rstin ) begin
+ vsync_width <= 0;
+ end else begin
+ if( vsync_rising ) begin
+ vsync_width <= vsync_width;
+ // vcount is reset on vsync_rising as well
+ end else begin
+ if( vsync_falling ) begin
+ vsync_width[23:0] <= vcount[23:0]; // counting in pixels
+ end else begin
+ vsync_width <= vsync_width;
+ end
+ end
+ end
+ end // always @ (posedge pclk)
+
+ //// hsync_width machine
+ always @(posedge pclk or posedge rstin) begin
+ if( rstin ) begin
+ hsync_width <= 0;
+ hsync_width_cnt <= 8'b0;
+ end else begin
+ if( hsync_rising ) begin
+ hsync_width <= hsync_width;
+ hsync_width_cnt <= 8'b1;
+ end else begin
+ if( hsync_falling ) begin
+ hsync_width <= hsync_width_cnt;
+ end else begin
+ hsync_width <= hsync_width;
+ end
+ hsync_width_cnt <= hsync_width_cnt + 8'b1;
+ end
+ end
+ end // always @ (posedge pclk)
+
+ //// vactive machine
+ //// add detectors for lcd_de_latency and lcd_vsync_latency
+ always @(posedge pclk or posedge rstin) begin
+ if( rstin ) begin
+ vactive <= 0;
+ vactive_count <= 0;
+
+ lcd_de_latency <= 0;
+ lcd_vsync_latency <= 0;
+
+ lcd_de_pclk <= 0;
+ lcd_de_1pclk <= 0;
+ lcd_de_2pclk <= 0;
+
+ lcd_vsync_pclk <= 0;
+ lcd_vsync_1pclk <= 0;
+ lcd_vsync_2pclk <= 0;
+
+ first_lcd_de_rising <= 1;
+ end else begin // if ( rstin )
+ if( vsync_rising ) begin
+ vactive <= vactive_count;
+ vactive_count <= 0;
+ end else begin
+ if( de_rising ) begin // counting in lines
+ vactive_count <= vactive_count + 12'b1;
+ end else begin
+ vactive_count <= vactive_count;
+ end
+ vactive <= vactive;
+ end // else: !if( vsync_rising )
+
+ lcd_de_2pclk <= lcd_de;
+ lcd_de_1pclk <= lcd_de_2pclk;
+ lcd_de_pclk <= lcd_de_1pclk;
+
+ lcd_vsync_2pclk <= lcd_vsync;
+ lcd_vsync_1pclk <= lcd_vsync_2pclk;
+ lcd_vsync_pclk <= lcd_vsync_1pclk;
+
+ if( vsync_rising ) begin
+ first_lcd_de_rising <= 1;
+ end else begin
+ if( !lcd_de_pclk & lcd_de_1pclk ) begin // rising edge
+ first_lcd_de_rising <= 0;
+ end else begin
+ first_lcd_de_rising <= first_lcd_de_rising;
+ end
+ end
+
+ // look for the rising edge
+ if( !lcd_de_pclk & lcd_de_1pclk & first_lcd_de_rising ) begin
+ lcd_de_latency <= vactive_count;
+ end else begin
+ lcd_de_latency <= lcd_de_latency;
+ end
+
+ // look for the rising edge
+ if( !lcd_vsync_pclk & lcd_vsync_1pclk ) begin
+ lcd_vsync_latency <= vactive_count;
+ end else begin
+ lcd_vsync_latency <= lcd_vsync_latency;
+ end
+ end // else: !if( rstin )
+ end // always @ (posedge pclk)
+
+ //// hactive mcahine
+ always @(posedge pclk or posedge rstin) begin
+ if( rstin ) begin
+ de_count <= 0;
+ hactive <= 0;
+ end else begin
+ if( de_rising ) begin
+ de_count <= 12'b1; // first pixel counts
+ end else if( de ) begin
+ de_count <= de_count + 12'b1;
+ end else begin
+ de_count <= de_count;
+ end
+
+ if( de_falling ) begin
+ hactive <= de_count;
+ end else begin
+ hactive <= hactive;
+ end
+ end // else: !if( rstin )
+ end // always @ (posedge pclk)
+
+ //// htotal, vtotal machine
+ always @(posedge pclk or posedge rstin) begin
+ if( rstin ) begin
+ hcount <= 0;
+ vcount <= 0;
+
+ htotal <= 0;
+ vtotal <= 0;
+ end else begin
+ if( vsync_rising ) begin
+ vtotal <= vcount;
+ vcount <= 24'b1;
+ end else begin
+ vcount <= vcount + 24'b1; /// counting in pixels
+ vtotal <= vtotal;
+ end
+
+ if( de_rising ) begin
+ htotal <= hcount;
+ hcount <= 12'b1;
+ end else begin
+ hcount <= hcount + 12'b1;
+ htotal <= htotal;
+ end
+ end // else: !if( rstin )
+ end // always @ (posedge pclk)
+
+ //// refclock machine
+ //// lots of cross-domain madness that might break things.
+ always @(posedge refclk or posedge rstin) begin
+ if( rstin ) begin
+ refclkcnt <= 0;
+ refclkcounter <= 0;
+
+ vsync____refclk <= 0;
+ vsync___refclk <= 0;
+ vsync__refclk <= 0;
+ vsync_refclk <= 0;
+ end else begin
+ vsync_refclk <= vsync__refclk;
+ vsync__refclk <= vsync___refclk;
+ vsync___refclk <= vsync____refclk;
+ vsync____refclk <= vsync;
+
+ if( vsync_refclk_rising ) begin
+ refclkcnt <= refclkcounter;
+ refclkcounter <= 24'b1;
+ end else begin
+ refclkcnt <= refclkcnt;
+ refclkcounter <= refclkcounter + 24'b01;
+ end
+
+ end // else: !if( rstin )
+ end // always @ (posedge refclk or posedge rstin)
+ assign vsync_refclk_rising = !vsync_refclk & vsync__refclk;
+
+
+ //// rising/falling edge extraction machine
+ always @(posedge pclk or posedge rstin) begin
+ if(rstin) begin
+ vsync_d <= 0;
+ hsync_d <= 0;
+ de_d <= 0;
+ end else begin
+ vsync_d <= vsync;
+ hsync_d <= hsync;
+ de_d <= de;
+ end
+ end // always @ (posedge pclk)
+
+ assign vsync_rising = vsync & !vsync_d;
+ assign hsync_rising = hsync & !hsync_d;
+ assign de_rising = de & !de_d;
+
+ assign vsync_falling = !vsync & vsync_d;
+ assign hsync_falling = !hsync & hsync_d;
+ assign de_falling = !de & de_d;
+
+endmodule // timing_detector