From 889a5a5395872eeb3740d5d3281b56c7afa47a6f Mon Sep 17 00:00:00 2001 From: bnewbold Date: Fri, 20 Jan 2012 19:14:58 -0500 Subject: 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 --- common/DRAM16XN.v | 51 + common/boxtiming.v | 136 ++ common/i2c_slave.v | 864 +++++++++ common/i2c_slave_tb.v | 375 ++++ common/i2c_snoop.v | 625 ++++++ common/i2c_snoop_edid.v | 603 ++++++ common/i2c_snoop_tb.v | 418 ++++ common/i2c_squash_edid.v | 892 +++++++++ common/i2c_squash_tb.v | 445 +++++ common/pwm.v | 83 + common/timing_detector.v | 390 ++++ hdcp/diff_network.v | 41 + hdcp/hdcp_block.v | 728 +++++++ hdcp/hdcp_cipher.v | 411 ++++ hdcp/hdcp_lfsr.v | 196 ++ hdcp/shuffle_network.v | 59 + hdmi_overlay.v | 1991 ++++++++++++++++++++ ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.asy | 21 + ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.ejp | 245 +++ ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.gise | 31 + ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.v | 133 ++ ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.veo | 78 + ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.v~ | 133 ++ ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.xco | 256 +++ ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.xise | 405 ++++ ip/clk_wiz_v3_1_0/clkgendcm_720p60hz_flist.txt | 33 + ip/clk_wiz_v3_1_0/clkgendcm_720p60hz_xmdf.tcl | 144 ++ ip/clk_wiz_v3_1_0/coregen.cgc | 860 +++++++++ ip/clk_wiz_v3_1_0/coregen.cgp | 22 + ip/clk_wiz_v3_1_0/coregen.log | 42 + ip/clk_wiz_v3_1_0/pa_cg_bom.xml | 61 + ip/clk_wiz_v3_1_0/pa_cg_config_core_invoke.tcl | 23 + ip/clk_wiz_v3_1_0/pa_cg_gen_core_invoke.tcl | 17 + ip/fifo_generator_v7_2_0/.lso | 1 + ip/fifo_generator_v7_2_0/coregen.cgc | 528 ++++++ ip/fifo_generator_v7_2_0/coregen.cgp | 22 + ip/fifo_generator_v7_2_0/coregen.log | 44 + ip/fifo_generator_v7_2_0/fifo_2kx18.asy | 41 + ip/fifo_generator_v7_2_0/fifo_2kx18.gise | 32 + ip/fifo_generator_v7_2_0/fifo_2kx18.ngc | 3 + ip/fifo_generator_v7_2_0/fifo_2kx18.v | 498 +++++ ip/fifo_generator_v7_2_0/fifo_2kx18.veo | 70 + ip/fifo_generator_v7_2_0/fifo_2kx18.xco | 203 ++ ip/fifo_generator_v7_2_0/fifo_2kx18.xise | 399 ++++ ip/fifo_generator_v7_2_0/fifo_2kx18_flist.txt | 12 + ip/fifo_generator_v7_2_0/fifo_2kx18_xmdf.tcl | 76 + ip/fifo_generator_v7_2_0/fifo_generator_readme.txt | 185 ++ ip/fifo_generator_v7_2_0/pa_cg_bom.xml | 39 + ip/fifo_generator_v7_2_0/pa_cg_gen_core_invoke.tcl | 17 + .../pa_cg_migrate_project_invoke.tcl | 25 + .../pa_cg_reconfig_core_invoke.tcl | 21 + ip/license.txt | 11 + lcd_input_v4.v | 853 +++++++++ release1.ucf | 357 ++++ rx/chnlbond.v | 170 ++ rx/decode.v | 271 +++ rx/decode_terc4.v | 66 + rx/decodeb.v | 319 ++++ rx/decodeg.v | 319 ++++ rx/decoder.v | 318 ++++ rx/dvi_decoder.v | 634 +++++++ rx/phsaligner.v | 301 +++ rx/serdes_1_to_5_diff_data.v | 405 ++++ tx/convert_30to15_fifo.v | 168 ++ tx/dvi_encoder_top.v | 311 +++ tx/encode.v | 233 +++ tx/encode_terc4.v | 86 + tx/encodeb.v | 231 +++ tx/encodeg.v | 231 +++ tx/encoder.v | 231 +++ tx/gbgen.v | 106 ++ tx/serdes_n_to_1.v | 150 ++ 72 files changed, 18799 insertions(+) create mode 100755 common/DRAM16XN.v create mode 100755 common/boxtiming.v create mode 100755 common/i2c_slave.v create mode 100755 common/i2c_slave_tb.v create mode 100755 common/i2c_snoop.v create mode 100755 common/i2c_snoop_edid.v create mode 100755 common/i2c_snoop_tb.v create mode 100755 common/i2c_squash_edid.v create mode 100755 common/i2c_squash_tb.v create mode 100755 common/pwm.v create mode 100755 common/timing_detector.v create mode 100755 hdcp/diff_network.v create mode 100755 hdcp/hdcp_block.v create mode 100755 hdcp/hdcp_cipher.v create mode 100755 hdcp/hdcp_lfsr.v create mode 100755 hdcp/shuffle_network.v create mode 100755 hdmi_overlay.v create mode 100755 ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.asy create mode 100755 ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.ejp create mode 100755 ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.gise create mode 100755 ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.v create mode 100755 ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.veo create mode 100755 ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.v~ create mode 100755 ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.xco create mode 100755 ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.xise create mode 100755 ip/clk_wiz_v3_1_0/clkgendcm_720p60hz_flist.txt create mode 100755 ip/clk_wiz_v3_1_0/clkgendcm_720p60hz_xmdf.tcl create mode 100755 ip/clk_wiz_v3_1_0/coregen.cgc create mode 100755 ip/clk_wiz_v3_1_0/coregen.cgp create mode 100755 ip/clk_wiz_v3_1_0/coregen.log create mode 100755 ip/clk_wiz_v3_1_0/pa_cg_bom.xml create mode 100755 ip/clk_wiz_v3_1_0/pa_cg_config_core_invoke.tcl create mode 100755 ip/clk_wiz_v3_1_0/pa_cg_gen_core_invoke.tcl create mode 100755 ip/fifo_generator_v7_2_0/.lso create mode 100755 ip/fifo_generator_v7_2_0/coregen.cgc create mode 100755 ip/fifo_generator_v7_2_0/coregen.cgp create mode 100755 ip/fifo_generator_v7_2_0/coregen.log create mode 100755 ip/fifo_generator_v7_2_0/fifo_2kx18.asy create mode 100755 ip/fifo_generator_v7_2_0/fifo_2kx18.gise create mode 100755 ip/fifo_generator_v7_2_0/fifo_2kx18.ngc create mode 100755 ip/fifo_generator_v7_2_0/fifo_2kx18.v create mode 100755 ip/fifo_generator_v7_2_0/fifo_2kx18.veo create mode 100755 ip/fifo_generator_v7_2_0/fifo_2kx18.xco create mode 100755 ip/fifo_generator_v7_2_0/fifo_2kx18.xise create mode 100755 ip/fifo_generator_v7_2_0/fifo_2kx18_flist.txt create mode 100755 ip/fifo_generator_v7_2_0/fifo_2kx18_xmdf.tcl create mode 100755 ip/fifo_generator_v7_2_0/fifo_generator_readme.txt create mode 100755 ip/fifo_generator_v7_2_0/pa_cg_bom.xml create mode 100755 ip/fifo_generator_v7_2_0/pa_cg_gen_core_invoke.tcl create mode 100755 ip/fifo_generator_v7_2_0/pa_cg_migrate_project_invoke.tcl create mode 100755 ip/fifo_generator_v7_2_0/pa_cg_reconfig_core_invoke.tcl create mode 100755 ip/license.txt create mode 100755 lcd_input_v4.v create mode 100755 release1.ucf create mode 100755 rx/chnlbond.v create mode 100755 rx/decode.v create mode 100755 rx/decode_terc4.v create mode 100755 rx/decodeb.v create mode 100755 rx/decodeg.v create mode 100755 rx/decoder.v create mode 100755 rx/dvi_decoder.v create mode 100755 rx/phsaligner.v create mode 100755 rx/serdes_1_to_5_diff_data.v create mode 100755 tx/convert_30to15_fifo.v create mode 100755 tx/dvi_encoder_top.v create mode 100755 tx/encode.v create mode 100755 tx/encode_terc4.v create mode 100755 tx/encodeb.v create mode 100755 tx/encodeg.v create mode 100755 tx/encoder.v create mode 100755 tx/gbgen.v create mode 100755 tx/serdes_n_to_1.v 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 diff --git a/hdcp/diff_network.v b/hdcp/diff_network.v new file mode 100755 index 0000000..1763a30 --- /dev/null +++ b/hdcp/diff_network.v @@ -0,0 +1,41 @@ +////////////////////////////////////////////////////////////////////////////// +// 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. +// +////////////////////////////////////////////////////////////////////////////// + +module diff_network ( + input wire [6:0] i, + output wire [6:0] o, + input wire [6:0] k + ); + + assign o[0] = k[0] ^ ^ i[1] ^ i[2] ^ i[3] ^ i[4] ^ i[5] ^ i[6]; + assign o[1] = k[1] ^ i[0] ^ ^ i[2] ^ i[3] ^ i[4] ^ i[5] ^ i[6]; + assign o[2] = k[2] ^ i[0] ^ i[1] ^ ^ i[3] ^ i[4] ^ i[5] ^ i[6]; + assign o[3] = k[3] ^ i[0] ^ i[1] ^ i[2] ^ ^ i[4] ^ i[5] ^ i[6]; + assign o[4] = k[4] ^ i[0] ^ i[1] ^ i[2] ^ i[3] ^ ^ i[5] ^ i[6]; + assign o[5] = k[5] ^ i[0] ^ i[1] ^ i[2] ^ i[3] ^ i[4] ^ ^ i[6]; + assign o[6] = k[6] ^ i[0] ^ i[1] ^ i[2] ^ i[3] ^ i[4] ^ i[5] ^ i[6]; + +endmodule // diff_network diff --git a/hdcp/hdcp_block.v b/hdcp/hdcp_block.v new file mode 100755 index 0000000..5f81279 --- /dev/null +++ b/hdcp/hdcp_block.v @@ -0,0 +1,728 @@ +////////////////////////////////////////////////////////////////////////////// +// 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. +// +////////////////////////////////////////////////////////////////////////////// + +module hdcp_block ( + input wire clk, + input wire reset, + + input wire load, + input wire [83:0] B, + input wire [83:0] K, + + output wire [83:0] Bo, + input wire rekey, + input wire lfsr_in, + + output reg [23:0] ostream, + + input wire advance + ); + + reg [27:0] Bx; + reg [27:0] By; + reg [27:0] Bz; + wire [27:0] o_Bx; + wire [27:0] o_By; + wire [27:0] o_Bz; + + reg [27:0] Kx; + reg [27:0] Ky; + reg [27:0] Kz; + wire [27:0] o_Kx; + wire [27:0] o_Ky; + wire o_Ky13; // bit 13 comes from lfsr_in when rekey is active + wire [27:0] o_Kz; + + wire [23:0] ostream_r; + + // semi-auto generated with a perl script + wire [3:0] SK0_in; + wire [3:0] SK1_in; + wire [3:0] SK2_in; + wire [3:0] SK3_in; + wire [3:0] SK4_in; + wire [3:0] SK5_in; + wire [3:0] SK6_in; + + reg [3:0] SK0; + reg [3:0] SK1; + reg [3:0] SK2; + reg [3:0] SK3; + reg [3:0] SK4; + reg [3:0] SK5; + reg [3:0] SK6; + + wire [3:0] SB0_in; + wire [3:0] SB1_in; + wire [3:0] SB2_in; + wire [3:0] SB3_in; + wire [3:0] SB4_in; + wire [3:0] SB5_in; + wire [3:0] SB6_in; + + reg [3:0] SB0; + reg [3:0] SB1; + reg [3:0] SB2; + reg [3:0] SB3; + reg [3:0] SB4; + reg [3:0] SB5; + reg [3:0] SB6; + + assign Bo = {Bz[27:0],By[27:0],Bx[27:0]}; + always @(posedge clk or posedge reset) begin + if( reset ) begin + Bx <= 28'b0; + By <= 28'b0; + Bz <= 28'b0; + Kx <= 28'b0; + Ky <= 28'b0; + Kz <= 28'b0; + ostream <= 24'b0; + end else begin + if( load ) begin +// Bz <= B[83:56]; + Bz <= {19'b0,1'b0,B[63:56]}; // repeater is fixed to zero + By <= B[55:28]; + Bx <= B[27:0]; + + Kz <= K[83:56]; + Ky <= K[55:28]; + Kx <= K[27:0]; + ostream <= 24'b0; + end else if( advance ) begin + Bx <= o_Bx; + By <= o_By; + Bz <= o_Bz; + + Kx <= o_Kx; + Ky <= o_Ky; + Kz <= o_Kz; + ostream <= ostream_r; + end else begin + Bx <= Bx; + By <= By; + Bz <= Bz; + + Kx <= Kx; + Ky <= Ky; + Kz <= Kz; + ostream <= ostream; + end + end // else: !if( reset ) + end // always @ (posedge clk or posedge reset) + + + //////////// + // bround linear transformation + // generated using perl script makebround.pl bround.csv > bround_code.txt + //////////// + diff_network bround1 ( .o({o_Bx[24],o_Bx[20],o_Bx[16],o_Bx[12],o_Bx[8],o_Bx[4],o_Bx[0]}), + .k({Ky[6],Ky[5],Ky[4],Ky[3],Ky[2],Ky[1],Ky[0]}), + .i({Bz[6],Bz[5],Bz[4],Bz[3],Bz[2],Bz[1],Bz[0]}) ); + + diff_network bround2 ( .o({o_By[24],o_By[20],o_By[16],o_By[12],o_By[8],o_By[4],o_By[0]}), + .k({1'b0,1'b0,1'b0,1'b0,1'b0,1'b0,1'b0}), + .i({By[12],By[2],By[1],By[0],Bz[9],Bz[8],Bz[7]}) ); + + diff_network bround3 ( .o({o_By[25],o_By[21],o_By[17],o_By[13],o_By[9],o_By[5],o_By[1]}), + .k({1'b0,1'b0,1'b0,1'b0,1'b0,1'b0,1'b0}), + .i({By[13],By[5],By[4],By[3],Bz[12],Bz[11],Bz[10]}) ); + + diff_network bround4 ( .o({o_By[26],o_By[22],o_By[18],o_By[14],o_By[10],o_By[6],o_By[2]}), + .k({1'b0,1'b0,1'b0,1'b0,1'b0,1'b0,1'b0}), + .i({By[14],By[8],By[7],By[6],Bz[15],Bz[14],Bz[13]}) ); + + diff_network bround5 ( .o({o_By[27],o_By[23],o_By[19],o_By[15],o_By[11],o_By[7],o_By[3]}), + .k({1'b0,1'b0,1'b0,1'b0,1'b0,1'b0,1'b0}), + .i({By[15],By[11],By[10],By[9],Bz[18],Bz[17],Bz[16]}) ); + + diff_network bround6 ( .o({o_Bx[25],o_Bx[21],o_Bx[17],o_Bx[13],o_Bx[9],o_Bx[5],o_Bx[1]}), + .k({Ky[13],Ky[12],Ky[11],Ky[10],Ky[9],Ky[8],Ky[7]}), + .i({Bz[21],Bz[20],Bz[19],By[19],By[18],By[17],By[16]}) ); + + diff_network bround7 ( .o({o_Bx[26],o_Bx[22],o_Bx[18],o_Bx[14],o_Bx[10],o_Bx[6],o_Bx[2]}), + .k({Ky[20],Ky[19],Ky[18],Ky[17],Ky[16],Ky[15],Ky[14]}), + .i({Bz[24],Bz[23],Bz[22],By[23],By[22],By[21],By[20]}) ); + + diff_network bround8 ( .o({o_Bx[27],o_Bx[23],o_Bx[19],o_Bx[15],o_Bx[11],o_Bx[7],o_Bx[3]}), + .k({Ky[27],Ky[26],Ky[25],Ky[24],Ky[23],Ky[22],Ky[21]}), + .i({Bz[27],Bz[26],Bz[25],By[27],By[26],By[25],By[24]}) ); + + + //////////// + // kround linear transform + // generated using perl script makelinx.pl kround.csv > kround + //////////// + + diff_network kround1 ( .o({o_Kx[24],o_Kx[20],o_Kx[16],o_Kx[12],o_Kx[8],o_Kx[4],o_Kx[0]}), + .i({Kz[6],Kz[5],Kz[4],Kz[3],Kz[2],Kz[1],Kz[0]}), + .k(7'b0) ); + + diff_network kround2 ( .o({o_Ky[24],o_Ky[20],o_Ky[16],o_Ky[12],o_Ky[8],o_Ky[4],o_Ky[0]}), + .i({Ky[12],Ky[2],Ky[1],Ky[0],Kz[9],Kz[8],Kz[7]}), + .k(7'b0) ); + + diff_network kround3 ( .o({o_Ky[25],o_Ky[21],o_Ky[17],o_Ky13,o_Ky[9],o_Ky[5],o_Ky[1]}), + .i({Ky[13],Ky[5],Ky[4],Ky[3],Kz[12],Kz[11],Kz[10]}), + .k(7'b0) ); + assign o_Ky[13] = rekey ? lfsr_in : o_Ky13; + + diff_network kround4 ( .o({o_Ky[26],o_Ky[22],o_Ky[18],o_Ky[14],o_Ky[10],o_Ky[6],o_Ky[2]}), + .i({Ky[14],Ky[8],Ky[7],Ky[6],Kz[15],Kz[14],Kz[13]}), + .k(7'b0) ); + + diff_network kround5 ( .o({o_Ky[27],o_Ky[23],o_Ky[19],o_Ky[15],o_Ky[11],o_Ky[7],o_Ky[3]}), + .i({Ky[15],Ky[11],Ky[10],Ky[9],Kz[18],Kz[17],Kz[16]}), + .k(7'b0) ); + + diff_network kround6 ( .o({o_Kx[25],o_Kx[21],o_Kx[17],o_Kx[13],o_Kx[9],o_Kx[5],o_Kx[1]}), + .i({Kz[21],Kz[20],Kz[19],Ky[19],Ky[18],Ky[17],Ky[16]}), + .k(7'b0) ); + + diff_network kround7 ( .o({o_Kx[26],o_Kx[22],o_Kx[18],o_Kx[14],o_Kx[10],o_Kx[6],o_Kx[2]}), + .i({Kz[24],Kz[23],Kz[22],Ky[23],Ky[22],Ky[21],Ky[20]}), + .k(7'b0) ); + + diff_network kround8 ( .o({o_Kx[27],o_Kx[23],o_Kx[19],o_Kx[15],o_Kx[11],o_Kx[7],o_Kx[3]}), + .i({Kz[27],Kz[26],Kz[25],Ky[27],Ky[26],Ky[25],Ky[24]}), + .k(7'b0) ); + + ///////////// + // sboxes + // generated using script makeblock.pl sbox_src.txt > sbox_code.txt + ///////////// + always @(SK0_in[3:0]) begin + case (SK0_in[3:0]) + 4'h0: SK0 = 4'd8; + 4'h1: SK0 = 4'd14; + 4'h2: SK0 = 4'd5; + 4'h3: SK0 = 4'd9; + 4'h4: SK0 = 4'd3; + 4'h5: SK0 = 4'd0; + 4'h6: SK0 = 4'd12; + 4'h7: SK0 = 4'd6; + 4'h8: SK0 = 4'd1; + 4'h9: SK0 = 4'd11; + 4'hA: SK0 = 4'd15; + 4'hB: SK0 = 4'd2; + 4'hC: SK0 = 4'd4; + 4'hD: SK0 = 4'd7; + 4'hE: SK0 = 4'd10; + 4'hF: SK0 = 4'd13; + endcase // case (SK0_in[3:0]) + end // always @ (SK0_in[3:0]) + + always @(SK0_in[3:0]) begin + case (SK0_in[3:0]) + 4'h0: SK0 = 4'd8; + 4'h1: SK0 = 4'd14; + 4'h2: SK0 = 4'd5; + 4'h3: SK0 = 4'd9; + 4'h4: SK0 = 4'd3; + 4'h5: SK0 = 4'd0; + 4'h6: SK0 = 4'd12; + 4'h7: SK0 = 4'd6; + 4'h8: SK0 = 4'd1; + 4'h9: SK0 = 4'd11; + 4'hA: SK0 = 4'd15; + 4'hB: SK0 = 4'd2; + 4'hC: SK0 = 4'd4; + 4'hD: SK0 = 4'd7; + 4'hE: SK0 = 4'd10; + 4'hF: SK0 = 4'd13; + endcase // case (SK0_in[3:0]) + end // always @ (SK0_in[3:0]) + + always @(SK1_in[3:0]) begin + case (SK1_in[3:0]) + 4'h0: SK1 = 4'd1; + 4'h1: SK1 = 4'd6; + 4'h2: SK1 = 4'd4; + 4'h3: SK1 = 4'd15; + 4'h4: SK1 = 4'd8; + 4'h5: SK1 = 4'd3; + 4'h6: SK1 = 4'd11; + 4'h7: SK1 = 4'd5; + 4'h8: SK1 = 4'd10; + 4'h9: SK1 = 4'd0; + 4'hA: SK1 = 4'd9; + 4'hB: SK1 = 4'd12; + 4'hC: SK1 = 4'd7; + 4'hD: SK1 = 4'd13; + 4'hE: SK1 = 4'd14; + 4'hF: SK1 = 4'd2; + endcase // case (SK1_in[3:0]) + end // always @ (SK1_in[3:0]) + + always @(SK2_in[3:0]) begin + case (SK2_in[3:0]) + 4'h0: SK2 = 4'd13; + 4'h1: SK2 = 4'd11; + 4'h2: SK2 = 4'd8; + 4'h3: SK2 = 4'd6; + 4'h4: SK2 = 4'd7; + 4'h5: SK2 = 4'd4; + 4'h6: SK2 = 4'd2; + 4'h7: SK2 = 4'd15; + 4'h8: SK2 = 4'd1; + 4'h9: SK2 = 4'd12; + 4'hA: SK2 = 4'd14; + 4'hB: SK2 = 4'd0; + 4'hC: SK2 = 4'd10; + 4'hD: SK2 = 4'd3; + 4'hE: SK2 = 4'd9; + 4'hF: SK2 = 4'd5; + endcase // case (SK2_in[3:0]) + end // always @ (SK2_in[3:0]) + + always @(SK3_in[3:0]) begin + case (SK3_in[3:0]) + 4'h0: SK3 = 4'd0; + 4'h1: SK3 = 4'd14; + 4'h2: SK3 = 4'd11; + 4'h3: SK3 = 4'd7; + 4'h4: SK3 = 4'd12; + 4'h5: SK3 = 4'd3; + 4'h6: SK3 = 4'd2; + 4'h7: SK3 = 4'd13; + 4'h8: SK3 = 4'd15; + 4'h9: SK3 = 4'd4; + 4'hA: SK3 = 4'd8; + 4'hB: SK3 = 4'd1; + 4'hC: SK3 = 4'd9; + 4'hD: SK3 = 4'd10; + 4'hE: SK3 = 4'd5; + 4'hF: SK3 = 4'd6; + endcase // case (SK3_in[3:0]) + end // always @ (SK3_in[3:0]) + + always @(SK4_in[3:0]) begin + case (SK4_in[3:0]) + 4'h0: SK4 = 4'd12; + 4'h1: SK4 = 4'd7; + 4'h2: SK4 = 4'd15; + 4'h3: SK4 = 4'd8; + 4'h4: SK4 = 4'd11; + 4'h5: SK4 = 4'd14; + 4'h6: SK4 = 4'd1; + 4'h7: SK4 = 4'd4; + 4'h8: SK4 = 4'd6; + 4'h9: SK4 = 4'd10; + 4'hA: SK4 = 4'd3; + 4'hB: SK4 = 4'd5; + 4'hC: SK4 = 4'd0; + 4'hD: SK4 = 4'd9; + 4'hE: SK4 = 4'd13; + 4'hF: SK4 = 4'd2; + endcase // case (SK4_in[3:0]) + end // always @ (SK4_in[3:0]) + + always @(SK5_in[3:0]) begin + case (SK5_in[3:0]) + 4'h0: SK5 = 4'd1; + 4'h1: SK5 = 4'd12; + 4'h2: SK5 = 4'd7; + 4'h3: SK5 = 4'd2; + 4'h4: SK5 = 4'd8; + 4'h5: SK5 = 4'd3; + 4'h6: SK5 = 4'd4; + 4'h7: SK5 = 4'd14; + 4'h8: SK5 = 4'd11; + 4'h9: SK5 = 4'd5; + 4'hA: SK5 = 4'd0; + 4'hB: SK5 = 4'd15; + 4'hC: SK5 = 4'd13; + 4'hD: SK5 = 4'd6; + 4'hE: SK5 = 4'd10; + 4'hF: SK5 = 4'd9; + endcase // case (SK5_in[3:0]) + end // always @ (SK5_in[3:0]) + + always @(SK6_in[3:0]) begin + case (SK6_in[3:0]) + 4'h0: SK6 = 4'd10; + 4'h1: SK6 = 4'd7; + 4'h2: SK6 = 4'd6; + 4'h3: SK6 = 4'd1; + 4'h4: SK6 = 4'd0; + 4'h5: SK6 = 4'd14; + 4'h6: SK6 = 4'd3; + 4'h7: SK6 = 4'd13; + 4'h8: SK6 = 4'd12; + 4'h9: SK6 = 4'd9; + 4'hA: SK6 = 4'd11; + 4'hB: SK6 = 4'd2; + 4'hC: SK6 = 4'd15; + 4'hD: SK6 = 4'd5; + 4'hE: SK6 = 4'd4; + 4'hF: SK6 = 4'd8; + endcase // case (SK6_in[3:0]) + end // always @ (SK6_in[3:0]) + + always @(SB0_in[3:0]) begin + case (SB0_in[3:0]) + 4'h0: SB0 = 4'd12; + 4'h1: SB0 = 4'd9; + 4'h2: SB0 = 4'd3; + 4'h3: SB0 = 4'd0; + 4'h4: SB0 = 4'd11; + 4'h5: SB0 = 4'd5; + 4'h6: SB0 = 4'd13; + 4'h7: SB0 = 4'd6; + 4'h8: SB0 = 4'd2; + 4'h9: SB0 = 4'd4; + 4'hA: SB0 = 4'd14; + 4'hB: SB0 = 4'd7; + 4'hC: SB0 = 4'd8; + 4'hD: SB0 = 4'd15; + 4'hE: SB0 = 4'd1; + 4'hF: SB0 = 4'd10; + endcase // case (SB0_in[3:0]) + end // always @ (SB0_in[3:0]) + + always @(SB1_in[3:0]) begin + case (SB1_in[3:0]) + 4'h0: SB1 = 4'd3; + 4'h1: SB1 = 4'd8; + 4'h2: SB1 = 4'd14; + 4'h3: SB1 = 4'd1; + 4'h4: SB1 = 4'd5; + 4'h5: SB1 = 4'd2; + 4'h6: SB1 = 4'd11; + 4'h7: SB1 = 4'd13; + 4'h8: SB1 = 4'd10; + 4'h9: SB1 = 4'd4; + 4'hA: SB1 = 4'd9; + 4'hB: SB1 = 4'd7; + 4'hC: SB1 = 4'd6; + 4'hD: SB1 = 4'd15; + 4'hE: SB1 = 4'd12; + 4'hF: SB1 = 4'd0; + endcase // case (SB1_in[3:0]) + end // always @ (SB1_in[3:0]) + + always @(SB2_in[3:0]) begin + case (SB2_in[3:0]) + 4'h0: SB2 = 4'd7; + 4'h1: SB2 = 4'd4; + 4'h2: SB2 = 4'd1; + 4'h3: SB2 = 4'd10; + 4'h4: SB2 = 4'd11; + 4'h5: SB2 = 4'd13; + 4'h6: SB2 = 4'd14; + 4'h7: SB2 = 4'd3; + 4'h8: SB2 = 4'd12; + 4'h9: SB2 = 4'd15; + 4'hA: SB2 = 4'd6; + 4'hB: SB2 = 4'd0; + 4'hC: SB2 = 4'd2; + 4'hD: SB2 = 4'd8; + 4'hE: SB2 = 4'd9; + 4'hF: SB2 = 4'd5; + endcase // case (SB2_in[3:0]) + end // always @ (SB2_in[3:0]) + + always @(SB3_in[3:0]) begin + case (SB3_in[3:0]) + 4'h0: SB3 = 4'd6; + 4'h1: SB3 = 4'd3; + 4'h2: SB3 = 4'd1; + 4'h3: SB3 = 4'd4; + 4'h4: SB3 = 4'd10; + 4'h5: SB3 = 4'd12; + 4'h6: SB3 = 4'd15; + 4'h7: SB3 = 4'd2; + 4'h8: SB3 = 4'd5; + 4'h9: SB3 = 4'd14; + 4'hA: SB3 = 4'd11; + 4'hB: SB3 = 4'd8; + 4'hC: SB3 = 4'd9; + 4'hD: SB3 = 4'd7; + 4'hE: SB3 = 4'd0; + 4'hF: SB3 = 4'd13; + endcase // case (SB3_in[3:0]) + end // always @ (SB3_in[3:0]) + + always @(SB4_in[3:0]) begin + case (SB4_in[3:0]) + 4'h0: SB4 = 4'd3; + 4'h1: SB4 = 4'd6; + 4'h2: SB4 = 4'd15; + 4'h3: SB4 = 4'd12; + 4'h4: SB4 = 4'd4; + 4'h5: SB4 = 4'd1; + 4'h6: SB4 = 4'd9; + 4'h7: SB4 = 4'd2; + 4'h8: SB4 = 4'd5; + 4'h9: SB4 = 4'd8; + 4'hA: SB4 = 4'd10; + 4'hB: SB4 = 4'd7; + 4'hC: SB4 = 4'd11; + 4'hD: SB4 = 4'd13; + 4'hE: SB4 = 4'd0; + 4'hF: SB4 = 4'd14; + endcase // case (SB4_in[3:0]) + end // always @ (SB4_in[3:0]) + + always @(SB5_in[3:0]) begin + case (SB5_in[3:0]) + 4'h0: SB5 = 4'd11; + 4'h1: SB5 = 4'd14; + 4'h2: SB5 = 4'd6; + 4'h3: SB5 = 4'd8; + 4'h4: SB5 = 4'd5; + 4'h5: SB5 = 4'd2; + 4'h6: SB5 = 4'd12; + 4'h7: SB5 = 4'd7; + 4'h8: SB5 = 4'd1; + 4'h9: SB5 = 4'd4; + 4'hA: SB5 = 4'd15; + 4'hB: SB5 = 4'd3; + 4'hC: SB5 = 4'd10; + 4'hD: SB5 = 4'd13; + 4'hE: SB5 = 4'd9; + 4'hF: SB5 = 4'd0; + endcase // case (SB5_in[3:0]) + end // always @ (SB5_in[3:0]) + + always @(SB6_in[3:0]) begin + case (SB6_in[3:0]) + 4'h0: SB6 = 4'd1; + 4'h1: SB6 = 4'd11; + 4'h2: SB6 = 4'd7; + 4'h3: SB6 = 4'd4; + 4'h4: SB6 = 4'd2; + 4'h5: SB6 = 4'd5; + 4'h6: SB6 = 4'd12; + 4'h7: SB6 = 4'd9; + 4'h8: SB6 = 4'd13; + 4'h9: SB6 = 4'd6; + 4'hA: SB6 = 4'd8; + 4'hB: SB6 = 4'd15; + 4'hC: SB6 = 4'd14; + 4'hD: SB6 = 4'd0; + 4'hE: SB6 = 4'd3; + 4'hF: SB6 = 4'd10; + endcase // case (SB6_in[3:0]) + end // always @ (SB6_in[3:0]) + + ////// + /// Sbox wiring + /// generated by perl script ./make_sboxwires.pl + ////// + assign SB0_in[0] = Bx[0]; + assign SB0_in[1] = Bx[7]; + assign SB0_in[2] = Bx[14]; + assign SB0_in[3] = Bx[21]; + + assign SK0_in[0] = Kx[0]; + assign SK0_in[1] = Kx[7]; + assign SK0_in[2] = Kx[14]; + assign SK0_in[3] = Kx[21]; + + assign o_Bz[0] = SB0[0]; + assign o_Bz[7] = SB0[1]; + assign o_Bz[14] = SB0[2]; + assign o_Bz[21] = SB0[3]; + + assign o_Kz[0] = SK0[0]; + assign o_Kz[7] = SK0[1]; + assign o_Kz[14] = SK0[2]; + assign o_Kz[21] = SK0[3]; + + assign SB1_in[0] = Bx[1]; + assign SB1_in[1] = Bx[8]; + assign SB1_in[2] = Bx[15]; + assign SB1_in[3] = Bx[22]; + + assign SK1_in[0] = Kx[1]; + assign SK1_in[1] = Kx[8]; + assign SK1_in[2] = Kx[15]; + assign SK1_in[3] = Kx[22]; + + assign o_Bz[1] = SB1[0]; + assign o_Bz[8] = SB1[1]; + assign o_Bz[15] = SB1[2]; + assign o_Bz[22] = SB1[3]; + + assign o_Kz[1] = SK1[0]; + assign o_Kz[8] = SK1[1]; + assign o_Kz[15] = SK1[2]; + assign o_Kz[22] = SK1[3]; + + assign SB2_in[0] = Bx[2]; + assign SB2_in[1] = Bx[9]; + assign SB2_in[2] = Bx[16]; + assign SB2_in[3] = Bx[23]; + + assign SK2_in[0] = Kx[2]; + assign SK2_in[1] = Kx[9]; + assign SK2_in[2] = Kx[16]; + assign SK2_in[3] = Kx[23]; + + assign o_Bz[2] = SB2[0]; + assign o_Bz[9] = SB2[1]; + assign o_Bz[16] = SB2[2]; + assign o_Bz[23] = SB2[3]; + + assign o_Kz[2] = SK2[0]; + assign o_Kz[9] = SK2[1]; + assign o_Kz[16] = SK2[2]; + assign o_Kz[23] = SK2[3]; + + assign SB3_in[0] = Bx[3]; + assign SB3_in[1] = Bx[10]; + assign SB3_in[2] = Bx[17]; + assign SB3_in[3] = Bx[24]; + + assign SK3_in[0] = Kx[3]; + assign SK3_in[1] = Kx[10]; + assign SK3_in[2] = Kx[17]; + assign SK3_in[3] = Kx[24]; + + assign o_Bz[3] = SB3[0]; + assign o_Bz[10] = SB3[1]; + assign o_Bz[17] = SB3[2]; + assign o_Bz[24] = SB3[3]; + + assign o_Kz[3] = SK3[0]; + assign o_Kz[10] = SK3[1]; + assign o_Kz[17] = SK3[2]; + assign o_Kz[24] = SK3[3]; + + assign SB4_in[0] = Bx[4]; + assign SB4_in[1] = Bx[11]; + assign SB4_in[2] = Bx[18]; + assign SB4_in[3] = Bx[25]; + + assign SK4_in[0] = Kx[4]; + assign SK4_in[1] = Kx[11]; + assign SK4_in[2] = Kx[18]; + assign SK4_in[3] = Kx[25]; + + assign o_Bz[4] = SB4[0]; + assign o_Bz[11] = SB4[1]; + assign o_Bz[18] = SB4[2]; + assign o_Bz[25] = SB4[3]; + + assign o_Kz[4] = SK4[0]; + assign o_Kz[11] = SK4[1]; + assign o_Kz[18] = SK4[2]; + assign o_Kz[25] = SK4[3]; + + assign SB5_in[0] = Bx[5]; + assign SB5_in[1] = Bx[12]; + assign SB5_in[2] = Bx[19]; + assign SB5_in[3] = Bx[26]; + + assign SK5_in[0] = Kx[5]; + assign SK5_in[1] = Kx[12]; + assign SK5_in[2] = Kx[19]; + assign SK5_in[3] = Kx[26]; + + assign o_Bz[5] = SB5[0]; + assign o_Bz[12] = SB5[1]; + assign o_Bz[19] = SB5[2]; + assign o_Bz[26] = SB5[3]; + + assign o_Kz[5] = SK5[0]; + assign o_Kz[12] = SK5[1]; + assign o_Kz[19] = SK5[2]; + assign o_Kz[26] = SK5[3]; + + assign SB6_in[0] = Bx[6]; + assign SB6_in[1] = Bx[13]; + assign SB6_in[2] = Bx[20]; + assign SB6_in[3] = Bx[27]; + + assign SK6_in[0] = Kx[6]; + assign SK6_in[1] = Kx[13]; + assign SK6_in[2] = Kx[20]; + assign SK6_in[3] = Kx[27]; + + assign o_Bz[6] = SB6[0]; + assign o_Bz[13] = SB6[1]; + assign o_Bz[20] = SB6[2]; + assign o_Bz[27] = SB6[3]; + + assign o_Kz[6] = SK6[0]; + assign o_Kz[13] = SK6[1]; + assign o_Kz[20] = SK6[2]; + assign o_Kz[27] = SK6[3]; + + ////// + // output function + // generated by perl script ./makeofunc.pl ofunc.txt > ostream_code.txt + ////// +assign ostream_r[0] = (Bz[17] & Kz[3]) ^ (Bz[26] & Kz[6]) ^ (Bz[22] & Kz[0]) ^ (Bz[27] & Kz[9]) ^ (Bz[21] & Kz[4]) ^ (Bz[18] & Kz[22]) ^ (Bz[2] & Kz[5]) ^ By[5] ^ Ky[10]; + +assign ostream_r[1] = (Bz[5] & Kz[20]) ^ (Bz[20] & Kz[18]) ^ (Bz[15] & Kz[7]) ^ (Bz[24] & Kz[23]) ^ (Bz[2] & Kz[15]) ^ (Bz[25] & Kz[5]) ^ (Bz[0] & Kz[3]) ^ By[16] ^ Ky[25]; + +assign ostream_r[2] = (Bz[22] & Kz[7]) ^ (Bz[5] & Kz[19]) ^ (Bz[14] & Kz[2]) ^ (Bz[16] & Kz[10]) ^ (Bz[25] & Kz[22]) ^ (Bz[17] & Kz[4]) ^ (Bz[20] & Kz[13]) ^ By[11] ^ Ky[21]; + +assign ostream_r[3] = (Bz[19] & Kz[6]) ^ (Bz[3] & Kz[14]) ^ (Bz[15] & Kz[9]) ^ (Bz[11] & Kz[8]) ^ (Bz[21] & Kz[17]) ^ (Bz[16] & Kz[18]) ^ (Bz[27] & Kz[12]) ^ By[1] ^ Ky[24]; + +assign ostream_r[4] = (Bz[19] & Kz[25]) ^ (Bz[6] & Kz[6]) ^ (Bz[17] & Kz[5]) ^ (Bz[18] & Kz[2]) ^ (Bz[22] & Kz[10]) ^ (Bz[7] & Kz[15]) ^ (Bz[9] & Kz[21]) ^ By[12] ^ Ky[8]; + +assign ostream_r[5] = (Bz[3] & Kz[27]) ^ (Bz[7] & Kz[14]) ^ (Bz[4] & Kz[2]) ^ (Bz[8] & Kz[4]) ^ (Bz[16] & Kz[24]) ^ (Bz[6] & Kz[19]) ^ (Bz[5] & Kz[1]) ^ By[17] ^ Ky[12]; + +assign ostream_r[6] = (Bz[8] & Kz[17]) ^ (Bz[21] & Kz[26]) ^ (Bz[27] & Kz[4]) ^ (Bz[2] & Kz[16]) ^ (Bz[11] & Kz[27]) ^ (Bz[24] & Kz[7]) ^ (Bz[12] & Kz[22]) ^ By[3] ^ Ky[11]; + +assign ostream_r[7] = (Bz[9] & Kz[9]) ^ (Bz[5] & Kz[10]) ^ (Bz[7] & Kz[19]) ^ (Bz[4] & Kz[11]) ^ (Bz[8] & Kz[7]) ^ (Bz[13] & Kz[6]) ^ (Bz[3] & Kz[8]) ^ By[15] ^ Ky[23]; + +assign ostream_r[8] = (Bz[26] & Kz[13]) ^ (Bz[13] & Kz[12]) ^ (Bz[23] & Kz[18]) ^ (Bz[10] & Kz[24]) ^ (Bz[11] & Kz[15]) ^ (Bz[7] & Kz[23]) ^ (Bz[15] & Kz[7]) ^ By[19] ^ Ky[16]; + +assign ostream_r[9] = (Bz[1] & Kz[0]) ^ (Bz[0] & Kz[5]) ^ (Bz[19] & Kz[20]) ^ (Bz[11] & Kz[25]) ^ (Bz[13] & Kz[1]) ^ (Bz[16] & Kz[24]) ^ (Bz[24] & Kz[9]) ^ By[18] ^ Ky[27]; + +assign ostream_r[10] = (Bz[26] & Kz[14]) ^ (Bz[13] & Kz[23]) ^ (Bz[9] & Kz[27]) ^ (Bz[14] & Kz[25]) ^ (Bz[10] & Kz[17]) ^ (Bz[4] & Kz[19]) ^ (Bz[1] & Kz[1]) ^ By[2] ^ Ky[22]; + +assign ostream_r[11] = (Bz[21] & Kz[6]) ^ (Bz[15] & Kz[21]) ^ (Bz[5] & Kz[17]) ^ (Bz[3] & Kz[15]) ^ (Bz[13] & Kz[26]) ^ (Bz[25] & Kz[11]) ^ (Bz[16] & Kz[16]) ^ By[27] ^ Ky[7]; + +assign ostream_r[12] = (Bz[20] & Kz[11]) ^ (Bz[7] & Kz[22]) ^ (Bz[18] & Kz[20]) ^ (Bz[12] & Kz[0]) ^ (Bz[17] & Kz[26]) ^ (Bz[1] & Kz[23]) ^ (Bz[16] & Kz[17]) ^ By[0] ^ Ky[2]; + +assign ostream_r[13] = (Bz[14] & Kz[8]) ^ (Bz[23] & Kz[4]) ^ (Bz[1] & Kz[3]) ^ (Bz[12] & Kz[14]) ^ (Bz[24] & Kz[20]) ^ (Bz[6] & Kz[26]) ^ (Bz[18] & Kz[23]) ^ By[9] ^ Ky[15]; + +assign ostream_r[14] = (Bz[19] & Kz[19]) ^ (Bz[6] & Kz[0]) ^ (Bz[21] & Kz[18]) ^ (Bz[25] & Kz[2]) ^ (Bz[23] & Kz[13]) ^ (Bz[1] & Kz[8]) ^ (Bz[10] & Kz[24]) ^ By[8] ^ Ky[14]; + +assign ostream_r[15] = (Bz[3] & Kz[16]) ^ (Bz[0] & Kz[21]) ^ (Bz[27] & Kz[24]) ^ (Bz[23] & Kz[25]) ^ (Bz[19] & Kz[12]) ^ (Bz[8] & Kz[27]) ^ (Bz[4] & Kz[15]) ^ By[7] ^ Ky[18]; + +assign ostream_r[16] = (Bz[6] & Kz[3]) ^ (Bz[5] & Kz[5]) ^ (Bz[14] & Kz[8]) ^ (Bz[22] & Kz[25]) ^ (Bz[24] & Kz[7]) ^ (Bz[18] & Kz[27]) ^ (Bz[2] & Kz[2]) ^ By[21] ^ Ky[26]; + +assign ostream_r[17] = (Bz[3] & Kz[11]) ^ (Bz[4] & Kz[14]) ^ (Bz[2] & Kz[23]) ^ (Bz[6] & Kz[17]) ^ (Bz[22] & Kz[22]) ^ (Bz[14] & Kz[13]) ^ (Bz[12] & Kz[19]) ^ By[26] ^ Ky[4]; + +assign ostream_r[18] = (Bz[25] & Kz[1]) ^ (Bz[21] & Kz[16]) ^ (Bz[19] & Kz[14]) ^ (Bz[9] & Kz[11]) ^ (Bz[10] & Kz[12]) ^ (Bz[15] & Kz[6]) ^ (Bz[13] & Kz[10]) ^ By[22] ^ Ky[19]; + +assign ostream_r[19] = (Bz[23] & Kz[21]) ^ (Bz[11] & Kz[1]) ^ (Bz[10] & Kz[10]) ^ (Bz[20] & Kz[20]) ^ (Bz[1] & Kz[18]) ^ (Bz[12] & Kz[26]) ^ (Bz[14] & Kz[9]) ^ By[4] ^ Ky[13]; + +assign ostream_r[20] = (Bz[11] & Kz[20]) ^ (Bz[26] & Kz[21]) ^ (Bz[20] & Kz[9]) ^ (Bz[17] & Kz[25]) ^ (Bz[8] & Kz[12]) ^ (Bz[23] & Kz[3]) ^ (Bz[0] & Kz[15]) ^ By[24] ^ Ky[0]; + +assign ostream_r[21] = (Bz[9] & Kz[18]) ^ (Bz[17] & Kz[12]) ^ (Bz[26] & Kz[21]) ^ (Bz[4] & Kz[27]) ^ (Bz[27] & Kz[1]) ^ (Bz[0] & Kz[16]) ^ (Bz[15] & Kz[24]) ^ By[6] ^ Ky[20]; + +assign ostream_r[22] = (Bz[22] & Kz[13]) ^ (Bz[12] & Kz[0]) ^ (Bz[2] & Kz[3]) ^ (Bz[10] & Kz[16]) ^ (Bz[7] & Kz[22]) ^ (Bz[20] & Kz[11]) ^ (Bz[25] & Kz[26]) ^ By[13] ^ Ky[9]; + +assign ostream_r[23] = (Bz[27] & Kz[2]) ^ (Bz[24] & Kz[0]) ^ (Bz[26] & Kz[13]) ^ (Bz[8] & Kz[5]) ^ (Bz[0] & Kz[4]) ^ (Bz[9] & Kz[8]) ^ (Bz[18] & Kz[10]) ^ By[23] ^ Ky[3]; + +endmodule // hdcp_block diff --git a/hdcp/hdcp_cipher.v b/hdcp/hdcp_cipher.v new file mode 100755 index 0000000..9804d10 --- /dev/null +++ b/hdcp/hdcp_cipher.v @@ -0,0 +1,411 @@ +////////////////////////////////////////////////////////////////////////////// +// 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 / 1 ps + +// generates a stream of hdcp cipher data +module hdcp_cipher( + input wire clk, + input reset, + + input [55:0] Km, // shared secret value + input [63:0] An, // session random number + + input hdcpBlockCipher_init, // pulsed only one cycle + input authentication, // pulsed one cycle same time as above + input hdcpRekeyCipher, // pulsed one cycle to initiate rekey + input hdcpStreamCipher,// advance cipher state one clock + output [23:0] pr_data, // pseudorandom data output + output reg stream_ready // asserted when stream is ready (after init seq) + ); + + wire lfsr_out; + wire [23:0] ostream; + wire [83:0] Bo_wire; + + parameter INIT = 12'b1 << 0; + parameter BLOCK_1 = 12'b1 << 1; + parameter BLOCK_2 = 12'b1 << 2; + parameter BLOCK_3 = 12'b1 << 3; + parameter BLOCK_4 = 12'b1 << 4; + parameter BLOCK_5 = 12'b1 << 5; + parameter BLOCK_6 = 12'b1 << 6; + parameter BLOCK_7 = 12'b1 << 7; + parameter BLOCK_8 = 12'b1 << 8; + parameter BLOCK_9 = 12'b1 << 9; + parameter GET_M = 12'b1 << 10; + parameter STREAM = 12'b1 << 11; + parameter REKEY = 12'b1 << 12; + parameter nSTATES = 13; + + reg [(nSTATES-1):0] cstate = {{(nSTATES-1){1'b0}},1'b1}; + reg [(nSTATES-1):0] nstate; + +// `define SIMULATION 1 +`ifdef SIMULATION + // synthesis translate_off + reg [8*20:1] state_ascii = "INIT "; + always @(cstate) begin + if (cstate == INIT ) state_ascii <= "INIT "; + else if (cstate == BLOCK_1 ) state_ascii <= "BLOCK_1 "; + else if (cstate == BLOCK_2 ) state_ascii <= "BLOCK_2 "; + else if (cstate == BLOCK_3 ) state_ascii <= "BLOCK_3 "; + else if (cstate == BLOCK_4 ) state_ascii <= "BLOCK_4 "; + else if (cstate == BLOCK_5 ) state_ascii <= "BLOCK_5 "; + else if (cstate == BLOCK_6 ) state_ascii <= "BLOCK_6 "; + else if (cstate == BLOCK_7 ) state_ascii <= "BLOCK_7 "; + else if (cstate == BLOCK_8 ) state_ascii <= "BLOCK_8 "; + else if (cstate == BLOCK_9 ) state_ascii <= "BLOCK_9 "; + else if (cstate == GET_M ) state_ascii <= "GET_M "; + else if (cstate == STREAM ) state_ascii <= "STREAM "; + else if (cstate == REKEY ) state_ascii <= "REKEY "; + else state_ascii <= "WTF "; + end + // synthesis translate_on +`endif // `ifdef SIMULATION + + reg [5:0] statecnt; + reg rekey; + reg load_block; + reg load_56; // load 56 or 80 bits... + reg load_lfsr; + reg advance_lfsr; + reg advance_block; + reg [83:0] Ks; + reg [83:0] Ki; + reg [63:0] Mi; + reg load_ks; + reg auth_mode; + reg [83:0] Kmod; + + always @ (posedge clk or posedge reset) begin + if (reset) + cstate <= INIT; + else + cstate <=#1 nstate; + end + + hdcp_lfsr lfsr( .clk(clk), .reset(reset), + .iv(auth_mode ? Ks[55:0] : Ki[55:0]), + .init_iv(load_lfsr), + .advance(advance_lfsr), + .onebit(lfsr_out)); + + hdcp_block block( .clk(clk), .reset(reset), + + .load(load_block), + .B(auth_mode ? {20'b0,An} : {20'b0,Mi}), + .K(Kmod), + .Bo(Bo_wire), + .rekey(rekey), + .lfsr_in(lfsr_out), + .ostream(ostream), + .advance(advance_block)); + assign pr_data = ostream; + + always @ (*) begin + case ({auth_mode,load_56}) //synthesis parallel_case full_case + 2'b00: begin // not auth mode, load 84 bits + Kmod = Ki; + end + 2'b01: begin // not auth mode, but load 56 bits only + Kmod = {28'b0,Ks[55:0]}; + end + 2'b10: begin // auth mode, load 84 bits + Kmod = Ks; + end + 2'b11: begin // auth mode, load only 56 bits + Kmod = {28'b0,Km[55:0]}; + end + endcase // case ({auth_mode,load_56}) + end // always @ (*) + + + always @ (*) begin + case (cstate) //synthesis parallel_case full_case + INIT: begin + nstate = hdcpBlockCipher_init ? BLOCK_1 : INIT; + end + BLOCK_1: begin + nstate = BLOCK_2; + end + BLOCK_2: begin + nstate = (statecnt >= 6'd47) ? BLOCK_3: BLOCK_2; + end + BLOCK_3: begin + nstate = BLOCK_4; + end + BLOCK_4: begin + nstate = BLOCK_5; + end + BLOCK_5: begin + nstate = BLOCK_6; + end + BLOCK_6: begin + nstate = BLOCK_7; + end + BLOCK_7: begin + nstate = BLOCK_8; + end + BLOCK_8: begin + nstate = (statecnt >= 6'd55) ? BLOCK_9: BLOCK_8; + end + BLOCK_9: begin + nstate = GET_M; + end + GET_M: begin + nstate = STREAM; + end + STREAM: begin + if( hdcpBlockCipher_init ) begin + nstate = BLOCK_1; + end else if( hdcpRekeyCipher ) begin + nstate = REKEY; + end else begin + nstate = STREAM; + end + end + REKEY: begin + if( hdcpBlockCipher_init ) begin + nstate = BLOCK_1; + end else begin + nstate = (statecnt >= 6'd55) ? STREAM : REKEY; + end + end + endcase // case (cstate) + end + + always @ (posedge clk or posedge reset) begin + if( reset ) begin + statecnt <=#1 6'b0; + rekey <=#1 1'b0; + load_block <=#1 1'b0; + advance_lfsr <=#1 1'b0; + advance_block <=#1 1'b0; + load_ks <=#1 1'b0; + auth_mode <=#1 1'b0; + stream_ready <=#1 1'b0; + load_56 <=#1 1'b0; + end else begin + case (cstate) // synthesis parallel_case full_case + INIT: begin + statecnt <=#1 6'b0; + rekey <=#1 1'b0; + load_block <=#1 1'b0; + advance_lfsr <=#1 1'b0; + advance_block <=#1 1'b0; + load_ks <=#1 1'b0; + auth_mode <=#1 authentication; + load_lfsr <=#1 1'b0; + stream_ready <=#1 1'b0; + load_56 <=#1 1'b0; + end + BLOCK_1: begin + statecnt <=#1 6'b0; + rekey <=#1 1'b0; + load_block <=#1 1'b1; // load B & K regs of block module + advance_lfsr <=#1 1'b0; + advance_block <=#1 1'b0; + load_ks <=#1 1'b0; + auth_mode <=#1 auth_mode; + load_lfsr <=#1 1'b0; + stream_ready <=#1 1'b0; + load_56 <=#1 1'b1; + end + BLOCK_2: begin + statecnt <=#1 statecnt + 1; // 48 clocks + rekey <=#1 1'b0; + load_block <=#1 1'b0; + advance_lfsr <=#1 1'b0; + advance_block <=#1 1'b1; + load_ks <=#1 1'b0; + auth_mode <=#1 auth_mode; + load_lfsr <=#1 1'b0; + stream_ready <=#1 1'b0; + load_56 <=#1 1'b0; + end + BLOCK_3: begin + statecnt <=#1 0; + rekey <=#1 1'b0; + load_block <=#1 1'b0; + advance_lfsr <=#1 1'b0; + advance_block <=#1 1'b0; + load_ks <=#1 1'b1; // save Ks, Ki, and B=>K + auth_mode <=#1 auth_mode; + load_lfsr <=#1 1'b0; + stream_ready <=#1 1'b0; + load_56 <=#1 1'b0; + end + BLOCK_4: begin + statecnt <=#1 0; + rekey <=#1 1'b0; + load_block <=#1 1'b0; + advance_lfsr <=#1 1'b0; + advance_block <=#1 1'b0; + load_ks <=#1 1'b1; // dup of above + auth_mode <=#1 auth_mode; + load_lfsr <=#1 1'b0; + stream_ready <=#1 1'b0; + load_56 <=#1 1'b0; + end + BLOCK_5: begin + statecnt <=#1 0; + rekey <=#1 1'b0; + load_block <=#1 1'b1; // reload block cipher + advance_lfsr <=#1 1'b0; + advance_block <=#1 1'b0; + load_ks <=#1 1'b0; + auth_mode <=#1 auth_mode; + load_lfsr <=#1 1'b0; + stream_ready <=#1 1'b0; + load_56 <=#1 1'b0; + end + BLOCK_6: begin + statecnt <=#1 0; + rekey <=#1 1'b0; + load_block <=#1 1'b0; + advance_lfsr <=#1 1'b0; + advance_block <=#1 1'b0; + load_ks <=#1 1'b0; + auth_mode <=#1 auth_mode; + load_lfsr <=#1 1'b1; // init lfsr + stream_ready <=#1 1'b0; + load_56 <=#1 1'b0; + end + BLOCK_7: begin + statecnt <=#1 0; + rekey <=#1 1'b1; // assert rekey + load_block <=#1 1'b0; + advance_lfsr <=#1 1'b0; + advance_block <=#1 1'b0; + load_ks <=#1 1'b0; + auth_mode <=#1 auth_mode; + load_lfsr <=#1 1'b0; + stream_ready <=#1 1'b0; + load_56 <=#1 1'b0; + end + BLOCK_8: begin + statecnt <=#1 statecnt + 1; // 56 clocks + rekey <=#1 1'b1; + load_block <=#1 1'b0; + advance_lfsr <=#1 1'b1; + advance_block <=#1 1'b1; + load_ks <=#1 1'b0; + auth_mode <=#1 auth_mode; + load_lfsr <=#1 1'b0; + stream_ready <=#1 1'b0; + load_56 <=#1 1'b0; + end + BLOCK_9: begin + statecnt <=#1 0; + rekey <=#1 1'b0; // de-assert rekey + load_block <=#1 1'b0; + advance_lfsr <=#1 1'b0; + advance_block <=#1 1'b0; + load_ks <=#1 1'b0; + auth_mode <=#1 auth_mode; + load_lfsr <=#1 1'b0; + stream_ready <=#1 1'b0; + load_56 <=#1 1'b0; + end // case: BLOCK_9 + GET_M: begin // one cycle wait to get M register loaded properly + statecnt <=#1 0; + rekey <=#1 1'b0; + load_block <=#1 1'b0; + advance_lfsr <=#1 1'b0; + advance_block <=#1 1'b0; + load_ks <=#1 1'b0; + auth_mode <=#1 1'b0; + load_lfsr <=#1 1'b0; + stream_ready <=#1 1'b0; + load_56 <=#1 1'b0; + end + STREAM: begin + statecnt <=#1 0; + rekey <=#1 1'b0; + load_block <=#1 1'b0; + advance_lfsr <=#1 hdcpStreamCipher; + advance_block <=#1 hdcpStreamCipher; + load_ks <=#1 1'b0; + auth_mode <=#1 authentication; + load_lfsr <=#1 1'b0; + stream_ready <=#1 1'b1; + load_56 <=#1 1'b0; + end + REKEY: begin + statecnt <=#1 statecnt + 1; + rekey <=#1 1'b1; + load_block <=#1 1'b0; + advance_lfsr <=#1 1'b1; + advance_block <=#1 1'b1; + load_ks <=#1 1'b0; + auth_mode <=#1 auth_mode; + load_lfsr <=#1 1'b0; + stream_ready <=#1 1'b0; + load_56 <=#1 1'b0; + end + endcase // case (cstate) + end + end // always @ (posedge clk or posedge reset) + + always @(posedge clk or posedge reset) begin + if( reset ) begin + Ks <= 80'b0; + Ki <= 80'b0; + end else begin +// if( hdcpBlockCipher_init ) begin +// Ks <= (authentication | auth_mode) ? {28'b0,Km} : Ks; +// Ki <= 80'b0; +// end else if( load_ks && auth_mode ) begin + if( load_ks && auth_mode ) begin + Ks <= Bo_wire; + Ki <= 80'b0; + end else if( load_ks && !auth_mode ) begin + Ks <= Ks; + Ki <= Bo_wire; + end else begin + Ks <= Ks; + Ki <= Ki; + end + end + end // always @ (posedge clk or posedge reset) + + always @(posedge clk or posedge reset) begin + if( reset ) begin + Mi <= 80'b0; + end else begin + if( (cstate == BLOCK_8) || (cstate == BLOCK_9) || (cstate == GET_M) ) begin + Mi[15:0] <= ostream[15:0]; + Mi[31:16] <= Mi[15:0]; + Mi[47:32] <= Mi[31:16]; + Mi[63:48] <= Mi[47:32]; + end else begin + Mi <= Mi; + end + end // else: !if( reset ) + end // always @ (posedge clk or posedge reset) + +endmodule // hdcp_cipher diff --git a/hdcp/hdcp_lfsr.v b/hdcp/hdcp_lfsr.v new file mode 100755 index 0000000..a85d753 --- /dev/null +++ b/hdcp/hdcp_lfsr.v @@ -0,0 +1,196 @@ +////////////////////////////////////////////////////////////////////////////// +// 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 / 1 ps + +// lfsr module for hdcp cipher +module hdcp_lfsr( + input wire clk, + input wire reset, // this is just a low-level reset + + input wire [55:0] iv, // initial values + + input wire init_iv, // load initial values + input wire advance, // advance state one cycle + output wire onebit // my one-bit output + ); + + reg [12:0] lfsr0; + reg [13:0] lfsr1; + reg [15:0] lfsr2; + reg [16:0] lfsr3; + + wire [3:0] comb_tap0; + wire [3:0] comb_tap1; + wire [3:0] comb_tap2; + + always @(posedge clk or posedge reset) begin + if( reset == 1 ) begin + lfsr0 <= 13'b0; + lfsr1 <= 14'b0; + lfsr2 <= 16'b0; + lfsr3 <= 17'b0; + end else begin + if( init_iv ) begin + // assume bit-offsets start from 0 + lfsr0[12:0] <= {~iv[6],iv[11:0]}; + lfsr1[13:0] <= {~iv[18],iv[24:12]}; + lfsr2[15:0] <= {~iv[32],iv[39:25]}; + lfsr3[16:0] <= {~iv[47],iv[55:40]}; + end else if( advance ) begin + // 13 11 9 5 + // 12 10 8 4 + lfsr0[0 ] <= lfsr0[4] ^ lfsr0[8] ^ lfsr0[10] ^ lfsr0[12]; + lfsr0[1 ] <= lfsr0[0 ]; + lfsr0[2 ] <= lfsr0[1 ]; + lfsr0[3 ] <= lfsr0[2 ]; + lfsr0[4 ] <= lfsr0[3 ]; + lfsr0[5 ] <= lfsr0[4 ]; + lfsr0[6 ] <= lfsr0[5 ]; + lfsr0[7 ] <= lfsr0[6 ]; + lfsr0[8 ] <= lfsr0[7 ]; + lfsr0[9 ] <= lfsr0[8 ]; + lfsr0[10] <= lfsr0[9 ]; + lfsr0[11] <= lfsr0[10]; + lfsr0[12] <= lfsr0[11]; + + //4 6 7 10 11 14 + //3 5 6 9 10 13 + lfsr1[0 ] <= lfsr1[3] ^ lfsr1[5] ^ lfsr1[6] ^ lfsr1[9] ^ lfsr1[10] ^ lfsr1[13]; + lfsr1[1 ] <= lfsr1[0 ]; + lfsr1[2 ] <= lfsr1[1 ]; + lfsr1[3 ] <= lfsr1[2 ]; + lfsr1[4 ] <= lfsr1[3 ]; + lfsr1[5 ] <= lfsr1[4 ]; + lfsr1[6 ] <= lfsr1[5 ]; + lfsr1[7 ] <= lfsr1[6 ]; + lfsr1[8 ] <= lfsr1[7 ]; + lfsr1[9 ] <= lfsr1[8 ]; + lfsr1[10] <= lfsr1[9 ]; + lfsr1[11] <= lfsr1[10]; + lfsr1[12] <= lfsr1[11]; + lfsr1[13] <= lfsr1[12]; + + //5 7 8 12 15 16 + //4 6 7 11 14 15 + lfsr2[0 ] <= lfsr2[4] ^ lfsr2[6] ^ lfsr2[7] ^ lfsr2[11] ^ lfsr2[14] ^ lfsr2[15]; + lfsr2[1 ] <= lfsr2[0 ]; + lfsr2[2 ] <= lfsr2[1 ]; + lfsr2[3 ] <= lfsr2[2 ]; + lfsr2[4 ] <= lfsr2[3 ]; + lfsr2[5 ] <= lfsr2[4 ]; + lfsr2[6 ] <= lfsr2[5 ]; + lfsr2[7 ] <= lfsr2[6 ]; + lfsr2[8 ] <= lfsr2[7 ]; + lfsr2[9 ] <= lfsr2[8 ]; + lfsr2[10] <= lfsr2[9 ]; + lfsr2[11] <= lfsr2[10]; + lfsr2[12] <= lfsr2[11]; + lfsr2[13] <= lfsr2[12]; + lfsr2[14] <= lfsr2[13]; + lfsr2[15] <= lfsr2[14]; + + //5 11 15 17 + //4 10 14 16 + lfsr3[0 ] <= lfsr3[4] ^ lfsr3[10] ^ lfsr3[14] ^ lfsr3[16]; + lfsr3[1 ] <= lfsr3[0 ]; + lfsr3[2 ] <= lfsr3[1 ]; + lfsr3[3 ] <= lfsr3[2 ]; + lfsr3[4 ] <= lfsr3[3 ]; + lfsr3[5 ] <= lfsr3[4 ]; + lfsr3[6 ] <= lfsr3[5 ]; + lfsr3[7 ] <= lfsr3[6 ]; + lfsr3[8 ] <= lfsr3[7 ]; + lfsr3[9 ] <= lfsr3[8 ]; + lfsr3[10] <= lfsr3[9 ]; + lfsr3[11] <= lfsr3[10]; + lfsr3[12] <= lfsr3[11]; + lfsr3[13] <= lfsr3[12]; + lfsr3[14] <= lfsr3[13]; + lfsr3[15] <= lfsr3[14]; + lfsr3[16] <= lfsr3[15]; + end else begin + // hold state + lfsr0 <= lfsr0; + lfsr1 <= lfsr1; + lfsr2 <= lfsr2; + lfsr3 <= lfsr3; + end + end // else: !if( reset == 1 ) + end // always @ (posedge clk or posedge reset) + + assign comb_tap0[0] = lfsr0[3]; + assign comb_tap0[1] = lfsr1[4]; + assign comb_tap0[2] = lfsr2[5]; + assign comb_tap0[3] = lfsr3[5]; + + assign comb_tap1[0] = lfsr0[7]; + assign comb_tap1[1] = lfsr1[8]; + assign comb_tap1[2] = lfsr2[9]; + assign comb_tap1[3] = lfsr3[11]; + + assign comb_tap2[0] = lfsr0[12]; + assign comb_tap2[1] = lfsr1[13]; + assign comb_tap2[2] = lfsr2[15]; + assign comb_tap2[3] = lfsr3[16]; + + wire [3:0] sh_dout; + shuffle_network sh0 ( .clk(clk), .reset(reset), + .din(comb_tap0[0] ^ comb_tap0[1] ^ comb_tap0[2] ^ comb_tap0[3]), + .sel(comb_tap1[0]), + .advance(advance), + .init_iv(init_iv), + .dout(sh_dout[0]) + ); + + shuffle_network sh1 ( .clk(clk), .reset(reset), + .din(sh_dout[0]), + .sel(comb_tap1[1]), + .advance(advance), + .init_iv(init_iv), + .dout(sh_dout[1]) + ); + + shuffle_network sh2 ( .clk(clk), .reset(reset), + .din(sh_dout[1]), + .sel(comb_tap1[2]), + .advance(advance), + .init_iv(init_iv), + .dout(sh_dout[2]) + ); + + shuffle_network sh3 ( .clk(clk), .reset(reset), + .din(sh_dout[2]), + .sel(comb_tap1[3]), + .advance(advance), + .init_iv(init_iv), + .dout(sh_dout[3]) + ); + + assign onebit = sh_dout[3] ^ comb_tap2[0] ^ comb_tap2[1] ^ comb_tap2[2] ^ comb_tap2[3]; + +endmodule // hdcp_lfsr + diff --git a/hdcp/shuffle_network.v b/hdcp/shuffle_network.v new file mode 100755 index 0000000..f0c5315 --- /dev/null +++ b/hdcp/shuffle_network.v @@ -0,0 +1,59 @@ +////////////////////////////////////////////////////////////////////////////// +// 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. +// +////////////////////////////////////////////////////////////////////////////// + +module shuffle_network ( + input wire clk, + input wire reset, + input wire din, + input wire sel, + input wire advance, + input wire init_iv, + output wire dout + ); + + reg a, b; + + always @(posedge clk or posedge reset) begin + if( reset == 1 ) begin + a <= 1'b0; + b <= 1'b1; + end else begin + if( init_iv ) begin + a <= 1'b0; + b <= 1'b1; + end else if( advance ) begin + a <= sel ? din : b; + b <= sel ? a : din; + end else begin + a <= a; + b <= b; + end + end // else: !if( reset == 1 ) + end // always @ (posedge clk or posedge reset) + + assign dout = sel ? b : a; + +endmodule // shuffle_network diff --git a/hdmi_overlay.v b/hdmi_overlay.v new file mode 100755 index 0000000..5bf8fac --- /dev/null +++ b/hdmi_overlay.v @@ -0,0 +1,1991 @@ +////////////////////////////////////////////////////////////////////////////// +// 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. +// +////////////////////////////////////////////////////////////////////////////// +// Notes +// 1280 x 720p60 = 74.25 MHz pclk; 45 kHz horiz rate, 750 vertical +// 1920 x 1080p24 = 74.25 MHz pclk; 27k Hz horiz rate, 1125 vertical +////////////////////////////////////////////////////////////////////////////// + +// removes I2C control elements when this is commented +`define PRODUCTION // comment out for simulation, leave in for production + +// removes Rx device when this is commented, self-only mode is possible +// also, uncomment the CLOCK_DEDICATED_ROUTE line inside the .ucf file +// and also comment out the TIG paths as noted +`define PASSTHROUGH 1 + +// enable SS clocking to the LCD interface to reduce noise (valid in both pass-thru and self-timed mode) +`define SS_CLOCKING_TOP 1 + +// uncomment the below to enable pre version-8 timing detector behavior, i.e. detect timing +// of the LCD stream during self-timed mode, instead of the Rx stream +//`define TIMING_POSTX 1 + +`timescale 1 ns / 1 ps + +module hdmi_overlay ( + input wire rstbtn_n, + input wire clk26, //26 mhz oscillator + input wire [3:0] RX0_TMDS, + input wire [3:0] RX0_TMDSB, + + output wire [3:0] TX0_TMDS, + output wire [3:0] TX0_TMDSB, + + input wire [7:2] LCD_R, + input wire [7:2] LCD_G, + input wire [7:2] LCD_B, + output wire LCDO_DCLK, // changed to output... + input wire LCD_DE, + input wire LCD_HSYNC, + input wire LCD_VSYNC, + + output wire VSYNC_STB, + + output reg LED0, + output wire LED1, + inout wire SDA, + input wire SCL, + inout wire DDC_SDA, + output wire DDC_SDA_PU, + output wire DDC_SDA_PD, + input wire DDC_SCL, + input wire HPD_N, + output wire HPD_NOTIFY, + output wire HPD_OVERRIDE, + output wire SOURCE_NOTIFY, + + input wire CEC, // just a placeholder for CEC for now + input wire CHUMBY_BEND, // to prevent this from being tied-off + + input wire LOWVOLT_N, + output reg LOWVOLT_NOTIFY, // tell the CPU that we have a low voltage condition GPIO 93 + output reg HDCP_AKSV // tell the CPU that AKSV was writ, so generate Km...GPIO 92 +); + +// wire LCD_DCLK_intbuf; + + wire box_active; + wire box_active_raw; + wire chroma_match; + + wire [7:0] blend_b; + wire [7:0] blend_r; + wire [7:0] blend_g; + + wire [7:0] chroma_r; + wire [7:0] chroma_g; + wire [7:0] chroma_b; + + wire [23:0] cipher_stream; + + wire clk26buf; + + // extend sync pulses and detect edge + reg vsync_v; + reg hsync_v; + reg hsync_v2; + reg vsync_v2; + wire hsync_rising; + wire vsync_rising; + + // autodetect sync polarity + reg hdmi_vsync_pol; + reg hdmi_hsync_pol; + + // compute HPD relay to host (delay one vsync) + reg hpd_saw_vsync; + + // try to make it so that hdcp isn't used if it isn't ready + wire hdcp_comp_ready; + wire hdcp_is_ready; + + // timing derivation interface + wire [11:0] t_hactive; // in pixels + wire [11:0] t_vactive; // in lines + wire [11:0] t_htotal; // in pixels + wire [23:0] t_vtotal; // ** in PIXELS ** must divide by htotal in software + wire [7:0] t_h_fp; // in pixels + wire [7:0] t_h_bp; // in pixels + wire [23:0] t_v_fp; // ** in PIXELS ** + wire [23:0] t_v_bp; // ** in PIXELS ** + wire [7:0] t_hsync_width; // in pixels + wire [23:0] t_vsync_width; // ** in PIXELS ** + wire [11:0] t_lcd_de_latency; // in lines + wire [11:0] t_vsync_latency; // in lines + wire [23:0] t_refclkcnt; // number of refclocks in a field + + // break self-mode toggle to a wire so we force it in the self-mode configuration + reg self_mode; + + // note if HDCP was requested on a frame + reg hdcp_requested; + + // de type selection + wire de_sync; + wire use_basic_de; + + // device DNA -- state machine at bottom + reg [55:0] dna_data; + + // lock state of PLLs and channels + wire ss_locked; + wire tx0_plllckd; + wire rx0_plllckd; + wire rx0_psalgnerr; // channel phase alignment error + wire rx0_blue_vld; + wire rx0_green_vld; + wire rx0_red_vld; + wire rx0_blue_rdy; + wire rx0_green_rdy; + wire rx0_red_rdy; + wire m720p_locked; + + ////////// I2C host interface //////// + wire SDA_pd; + wire [7:0] reg_addr; + wire wr_stb; + wire [7:0] reg_data_in; + wire [7:0] reg_a2; + wire SDA_int; + wire [7:0] snoop_ctl; + wire [7:0] snoop_rbk_adr; + wire [7:0] snoop_rbk_dat; + + wire [7:0] hdcp_snoop_data; + wire [7:0] hdcp_snoop_addr; + wire [7:0] edid_snoop_data; + wire [7:0] edid_snoop_addr; + + wire [7:0] comp_ctl; + wire [15:0] window_w; + wire [14:0] window_h; + wire window_update; + wire [15:0] window_x; + wire [15:0] window_y; + reg [11:0] window_x_buf; + reg [11:0] window_y_buf; + reg [11:0] window_w_buf; + reg [11:0] window_h_buf; + + wire [7:0] ext1_ctl; + + wire [55:0] Km; + wire [63:0] An; + wire Aksv14_write; + reg Km_rdy0; + reg Km_rdy1; + reg Km_rdy2; + wire Km_ready; + wire [7:0] edid_daddr; + + wire rst_skewmach; + + wire [3:0] line_full_level; + wire [3:0] line_empty_level; + wire [3:0] write_init_level; + wire [3:0] read_init_level; + + wire [23:0] target_lead_pixels; + wire reset_lock_machine; + wire genlock_locked; + wire [15:0] lock_tolerance; + wire smartlock_on; + + wire modeline_write; + wire [7:0] modeline_adr; + wire [7:0] modeline_dat; + + wire rx_all_valid; + assign rx_all_valid = (rx0_blue_vld & rx0_green_vld & rx0_red_vld); + +`ifdef PRODUCTION + i2c_slave host_i2c( + .SCL(SCL), + .SDA(SDA_int), + .SDA_pd(SDA_pd), + + .clk(clk26buf), + .reset(~rstbtn_n), + + .i2c_device_addr(8'h3C), + .reg_addr(reg_addr), + .reg_data_in(reg_data_in), + .wr_stb(wr_stb), + .reg_0(snoop_ctl), + .reg_1(snoop_rbk_adr), + // reg_2 is nc internally + .reg_3(comp_ctl), +//`define REGWINDOWS +`ifdef REGWINDOWS + .reg_4(window_w[7:0]), + .reg_5(window_w[15:8]), + .reg_6(window_h[7:0]), + .reg_7({window_update,window_h[14:8]}), + .reg_8(window_x[7:0]), + .reg_9(window_x[15:8]), + .reg_a(window_y[7:0]), + .reg_b(window_y[15:8]), +`endif + .reg_c(ext1_ctl), + + // hard coded now +// .reg_d({line_full_level[3:0],line_empty_level[3:0]}), +// .reg_e({write_init_level[3:0], read_init_level[3:0]}), + + // hard-wired to 240, 0, 240 +// .reg_d(chroma_r), +// .reg_e(chroma_g), +// .reg_f(chroma_b), + + .reg_10({hdcp_requested,hdmi_vsync_pol,hdmi_hsync_pol,LOWVOLT_NOTIFY,1'b0,CEC, + genlock_locked, CHUMBY_BEND}), + + + .reg_11(lock_tolerance[7:0]), + .reg_12(lock_tolerance[15:8]), + + .reg_13({modeline_write,modeline_adr[6:0]}), + .reg_14(modeline_dat), + + .reg_15(target_lead_pixels[7:0]), + .reg_16(target_lead_pixels[15:8]), + .reg_17(target_lead_pixels[23:16]), + +// .reg_18(edid_daddr[7:0]), + .reg_18({rx_all_valid, + rx0_blue_rdy, rx0_green_rdy, rx0_red_rdy, + rx0_psalgnerr, + m720p_locked, tx0_plllckd, rx0_plllckd}), + + .reg_19(Km[7:0]), + .reg_1a(Km[15:8]), + .reg_1b(Km[23:16]), + .reg_1c(Km[31:24]), + .reg_1d(Km[39:32]), + .reg_1e(Km[47:40]), + .reg_1f(Km[55:48]), + + //// read-only registers after this point + .reg_20(t_hactive[7:0]), + .reg_21({4'b0,t_hactive[11:8]}), + .reg_22(t_vactive[7:0]), + .reg_23({4'b0,t_vactive[11:8]}), + .reg_24(t_htotal[7:0]), + .reg_25({4'b0,t_htotal[11:8]}), + .reg_26(t_vtotal[7:0]), + .reg_27(t_vtotal[15:8]), + .reg_28(t_vtotal[23:16]), + .reg_29(t_h_fp[7:0]), + .reg_2a(t_h_bp[7:0]), + .reg_2b(t_v_fp[7:0]), + .reg_2c(t_v_fp[15:8]), + .reg_2d(t_v_fp[23:16]), + .reg_2e(t_v_bp[7:0]), + .reg_2f(t_v_bp[15:8]), + .reg_30(t_v_bp[23:16]), + .reg_31(t_hsync_width[7:0]), + .reg_32(t_vsync_width[7:0]), + .reg_33(t_vsync_width[15:8]), + .reg_34(t_vsync_width[23:16]), + .reg_35(t_refclkcnt[7:0]), + .reg_36(t_refclkcnt[15:8]), + .reg_37(t_refclkcnt[23:16]), +// .reg_38(t_lcd_de_latency[7:0]), +// .reg_39({4'b0,t_lcd_de_latency[11:8]}), +// .reg_3a(t_vsync_latency[7:0]), +// .reg_3b({4'b0,t_vsync_latency[11:8]}), + + .reg_38(dna_data[7:0]), + .reg_39(dna_data[15:8]), + .reg_3a(dna_data[23:16]), + .reg_3b(dna_data[31:24]), + .reg_3c(dna_data[39:32]), + .reg_3d(dna_data[47:40]), + .reg_3e(dna_data[55:48]), + + .reg_3f(8'hD) // version number + ); + /////// version 4 changes + // - added input registers to LCD path to clean up timing + // - added a pipeline stage to the core video processing pipe + // - adjusted the position of chroma decision versus chroma to remove right pink stripe + // - fixed chroma to 240, 0, 240 to reduce computational complexity + // - fixed timing files to get better coverage of the FPGA + // - inverted clock to device DNA state machine to fix hold time race condition + // - self-timed mode is now native, no need to switch FPGA configs + // - added PLL and alignment/valid feedback registers to detect when source is present + // - touch-up clock tree to improve clarity of timing definition & rule propagation + // - full switch-over to PlanAhead tool for compilation + + /////// version 5 changes (log created 8/11/2011) + // - changed blue LED from flashing to breathing + + /////// version 6 changes (log created 8/12/2011) + // - added off state to LED which is automatically triggered when output not plugged in + + /////// version 7 changes (log created 8/12/2011) + // - added SOURCE_NOTIFY reporting trigger + // - removed HPD debounce circuit, so HPD reports even when no source is present + + /////// version 8 changes (log cerated 8/21/2011) + // - changed timing detector to always report the timing of the Rx stream, even in self-timed mode + + /////// version 9 changes (log created 8/22/2011) + // - removed setbox functionality. Box is always "full screen". + // Registers 4-B are now deprecated (they are NOPs if written in this implementation, may + // change to be active later). + + /////// version A changes (log created 8/22/2011) + // - HDCP cipher now resets properly when Ksv is double-initialized + // - EDID snooper for HDCP now limits read bounds to 5 to compensate + // for tivo series 3 weirdness. + + /////// version B changes (log created 8/27/2011) + // - timing closure only + + /////// version C changes (log created 8/27/2011) + // - fix chroma issue in overlay mode, was checking too many bits, causing jagged edges on photos + + /////// version D changes (log created 9/5/2011) + // + + assign SOURCE_NOTIFY = rx0_plllckd; + + // hard wire chroma because it never changes, optimize out some gates + assign chroma_r = 8'd240; + assign chroma_g = 8'd0; + assign chroma_b = 8'd240; + + assign modeline_adr[7] = snoop_ctl[6]; // hack to add another bank into the modeline RAM +// assign use_basic_de = ext1_ctl[0]; + + // these are hard-coded now, used to be registers + assign line_full_level[3:0] = 4'h2; + assign line_empty_level[3:0] = 4'h2; + assign write_init_level[3:0] = 4'h1; + assign read_init_level[3:0] = 4'h2; + +`else // !`ifdef PRODUCTION + assign comp_ctl = 8'h09; // setup for testing stand-alone mode + assign snoop_ctl = 8'h0; + assign snoop_ctl = 8'h1; +`endif // !`ifdef PRODUCTION + +// IBUF IBUF_sda (.I(SDA), .O(SDA_int)); + IOBUF #(.DRIVE(12), .SLEW("SLOW")) IOBUF_sda (.IO(SDA), .I(1'b0), .T(!SDA_pd), .O(SDA_int)); + + //// I2C internal control wiring //// + ///////////////// + /// register 0: control snoop state (SNOOP_CTL r/w) + // bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 + // | MODEBNK | | | HPD_FRC | SQUASH | RD_STB | RD_HDCP + // + // bit 0 - RD_HDCP. When 1, select readback of the HDCP set; when 0, select EDID set + // bit 1 - RD_STB. When high, update the contents of SNOOP_DAT with data at SNOOP_ADR + // bit 2 - enable EDID squashing + // bit 3 - when high, force HPD to show that nothing is plugged in; low, act as normal + // bit 6 - sets the bank to write with register 0x13 (yah yah this is a hack) + // + ///////////////// + /// register 1: snoop readback address (SNOOP_ADR r/w) + // bits 7:0 are the address to read back from the snoop unit + // + ///////////////// + /// register 2: snoop readback data (SNOOP_DAT ro) + // bits 7:0 are the data corresponding to the last loaded snoop address as + // selected by RD_HDCP bits in SNOOP_CTL when RD_STB was last toggled + // + // REVISION -- now dynamically relays the value specified by snoop_adr without having + // to toggle RD_STB. RD_STB has no effect currently. + ///////////////// + // register 3: Compositing control (COMP_CTL r/w) + // bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 + // KM_SEM | RST_GNLK| SMRTLCK | RST_PLL | SELF | COMP_ON | KM_RDY | HDCP_ON + // + // bit 0 - enable HDCP encryption of the composited stream. Enables HDCP encryption + // only if the HDCP cipher is used. If the input stream has no HDCP on it + // then this bit has no meaning. + // bit 1 - indicates that Km is ready and loaded (currently ignored) + // bit 2 - enable compositing of incoming data from LCD port to HDMI stream + // bit 3 - when set, ignore incoming data and generate sync based on LCD port signals + // bit 4 - when set, resets PLLs only on the paths designated by bit3 ("self") + // bit 5 - when set, enable "smart locking", i.e., genlock turns off once we're locked + // bit 6 - reset the genlock control machine + // bit 7 - Km semaphore -- used to indicate to the kernel whether an existing process + // is controlling the Km derivation process. This is to resolve the issue where + // the HPD will generate two events to cover Km generation in the case that + // the final protocol requires a "restart" to get the correct Km value to stick + // + ///////////////// + +`ifdef REGWINDOWS + ///////////// REGISTER 4 - B are now DEPRECATED. + // register 4: window width + // bits 7:0 are LSB of window width + // + ///////////////// + // register 5: window width + // bits 7:0 are MSB of window width + // + ///////////////// + // register 6: window height + // bits 7:0 are LSB of window height + // + ///////////////// + // register 7: window height + // bits 6:0 are MSB of window height + // bit 7 is the "update" bit which informs the system to take the new values on the next + // vsync period + // + ///////////////// + // register 8: window X position + // bits 7:0 are LSB of window X position + // + ///////////////// + // register 9: window X position + // bits 7:0 are MSB of window X position + // + ///////////////// + // register A: window Y position + // bits 7:0 are LSB of window Y position + // + ///////////////// + // register B: window Y position + // bits 7:0 are MSB of window Y position + // + /////////////////////////// END DEPRACATION BLOCK +`endif + + ///////////////// + // register C: extended control set (EXT1_CTL r/w) + // bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 + // | | | | | | CHROMA | + // + // bit 1: when set, turn on chroma keying; otherwise, always blend in + // + ///////////////// + // NOTE: these are now hard-wired to 240, 0, 240. These registers will likely be deprecated soon. + // register D: chroma R value, 8 bits + // register E: chroma G value, 8 bits + // register F: chroma B value, 8 bits + // + ///////////////// + // register 0x10 is read-only: + // bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 + // HDCPDET| VSYNCPOL| HSYNCPOL| LOWVOLT | | CEC | LOCKED | BEND + // bit 0: chumby_bend pin state (tied off to insure configuratios as an input) + // bit 1: indicates that the genlock machine has locked LCD to HDMI streams + // bit 2: CEC pin state (tied off to insure configuratios as an input) + // bit 4: when high indicates that a low voltage condition was detected; only active during condition + // there is also an interrupt to the CPU that fires + // bit 5: indicates the polarity of the HSYNC detected on the HDMI stream, 1 = active high + // bit 6: indicates the polarity of the VSYNC detected on the HDMI stream, 1 = active high + // bit 7: when high, indicates that an HDCP stream is being encrypted. Not active during + // horiz and vert sync periods. + // + ///////////////// + // register 0x11-12: + // lock tolerance, in pixels, in little-endian byte order + // This defines the tolerance of the "lock" threshold. This value matters mostly when + // "smart locking" is turned on, i.e., when we want to disable genlock once we're within + // our locking tolerance window. + // + ///////////////// + // register 0x13 is address of modeline RAM to write + // 7 is a write strobe (write when high) + // 6:0 is the actual modeline address + // + ///////////////// + // register 0x14 is the data to write into the modeline RAM + // 7:0 is the data + // + ///////////////// + // registers 0x15-0x17: + // lock target count, in pixels, in little-endian byte order. + // Lock target count is the amount of time that the LCD interface should lead the + // HDMI inteface for producing data. The amount of time should be large enough to + // absorb timing variations in the interrupt latency handling of the PXA168, but + // in all cases smaller than 8 lines of video (that's the size of the line buffers). + // + // The total time should be expressed in units of pixels, not lines. + // + ///////////////// + // register 0x18 is read-only: + // bit 7 | bit 6 | bit 5 | bit 4 | bit 3 | bit 2 | bit 1 | bit 0 + // RX_VLD | B_RDY | G_RDY | R_RDY | ALGNERR | SYNLCK | TXLCK | RXLCK + // bit 0: rx PLL is locked, indicates that a clock is present and cable is plugged in + // bit 1: tx PLL is locked. This should generally always be the case. + // bit 2: synthesizer DCM is locked. This should generally always be the case. + // bit 3: rx alignment error + // bit 4: red channel has received a valid pixel + // bit 5: green chanel has received a valid pixel + // bit 6: blue channel has received a valid pixel + // bit 7: all RX chanels are phase aligned and producing valid data + // + ///////////////// + // registers 0x19-0x1f: Km + // 56-bit value of Km, entered in little-endian order. + // + ///////////////// + // All registers after this point are read-only, and byte-order is little-endian + ///////////////// + // register 0x20-21: horizontal active in pixels + // register 0x22-23: vertical active in lines + // register 0x24-25: horizontal total width in pixels + // register 0x26-28: vertical total height in pixels (not lines) + // register 0x29: horizontal front porch in pixels + // register 0x2a: horizontal back porch in pixels + // register 0x2b-2d: vertical front porch in pixels (not lines) + // register 0x2e-30: vertical back porch in pixels (not lines) + // register 0x31: horizontal sync width in pixels + // register 0x32-0x34: vertical sync width in pixels (not lines) + // register 0x35-0x37: reference clock count cycles + // + // register 0x38-0x3e: device ID (7 bytes) + // + // register 0x3f: version number + + + //////////// DEPRACATED REGISTERS ////////////// (but not confident they are dead yet) + // register D: line empty/full levels + // This sets the level at which we declare the line buffers to be "empty" or "full" + // This is on a the granularity of a full video line, not at the pixel level. The + // pixel-level full/empty is hard-coded by the FIFO primitive. + // bit 7 is ignored + // bits [6:4] are the full level target: nominally 2 + // bit 3 is ignored + // bits [2:0] are the empty level target: nominally 2 + // + ///////////////// + // register E: write and read initial levels + // This sets the initial state of the write/read pointers, reset on every vsync. + // Note that the actual bit vector that keeps track of active lines can be much longer + // than 4 bits, but we only get to diddle with the bottom four bits in this implementation. + // bits [7:4] are for the write: nominally 1 + // bits [3:0] are for the read: nominally 2 + // + ///////////////// + // register F: reserved + // + +`ifdef PRODUCTION +`define EDID_SNOOP 1 // turn on EDID snooping + + assign reg_addr = 8'h02; + assign wr_stb = snoop_ctl[1]; +`ifdef EDID_SNOOP + assign reg_data_in[7:0] = snoop_ctl[0] ? hdcp_snoop_data[7:0] : edid_snoop_data[7:0]; + assign edid_snoop_addr = snoop_rbk_adr; +`else + assign reg_data_in[7:0] = hdcp_snoop_data[7:0]; +`endif + assign hdcp_snoop_addr = snoop_rbk_adr; + + /////// DDC snooping interface ////// + wire DDC_SDA_int; + wire DDC_SDA_pu_int; + wire DDC_SDA_pd_int; + i2c_snoop ddc_hdcp_snoop ( + .SCL(!DDC_SCL), // inverters on inputs... + .SDA(!DDC_SDA_int), + .clk(clk26buf), + .reset(~rstbtn_n), + + .i2c_snoop_addr(8'h74), // HDCP address *only* is snooped by this one + + .reg_addr(hdcp_snoop_addr), + .reg_dout(hdcp_snoop_data), + + .An(An), + .Aksv14_write(Aksv14_write) + ); +`ifdef EDID_SNOOP + /////// EDID snooping interface ////// + i2c_snoop_edid ddc_edid_snoop ( + .SCL(!DDC_SCL), + .SDA(!DDC_SDA_int), + .clk(clk26buf), + .reset(~rstbtn_n), + + .i2c_snoop_addr(8'h74), // all *but* this address is snooped by this one + + .reg_addr(edid_snoop_addr), + .reg_dout(edid_snoop_data) + ); + + i2c_squash_edid ddc_edid_squash ( + .SCL(!DDC_SCL), + .SDA(!DDC_SDA_int), + .clk(clk26buf), + .reset(~rstbtn_n), + + .SDA_pu(DDC_SDA_pu_int), + .SDA_pd(DDC_SDA_pd_int), + + .i2c_snoop_addr(8'ha0), // only reads from this addres can be squashed + + .modeline_write(modeline_write), + .modeline_dat(modeline_dat), + .modeline_adr(modeline_adr) + ); +// IOBUF #(.DRIVE(24), .SLEW("SLOW")) IOBUF_DDC_sda (.IO(DDC_SDA), .I(DDC_SDA_pu), +// .T(!(DDC_SDA_pd | DDC_SDA_pu) | !snoop_ctl[2]), +// .O(DDC_SDA_int)); + + // and with inverse of SDA_PD to ensure we never get overlap of pullup/pulldown and thus DISASTER + assign DDC_SDA_PU = (DDC_SDA_pu_int & snoop_ctl[2]) & !DDC_SDA_pd_int; + assign DDC_SDA_PD = DDC_SDA_pd_int & snoop_ctl[2]; + + IBUF IBUF_DDC_sda (.I(DDC_SDA), .O(DDC_SDA_int)); + +`endif +`endif + + //////////////////////////////////////////////////// + // utility clock buffer + //////////////////////////////////////////////////// +// BUFIO2 #(.DIVIDE_BYPASS("FALSE"), .DIVIDE(5)) +// sysclk_div (.DIVCLK(clk26m), .IOCLK(), .SERDESSTROBE(), .I(clk26)); + wire clk26_ibuf; + IBUFG clk26buf_ibuf(.I(clk26), .O(clk26ibuf)); + BUFG clk26buf_buf (.I(clk26ibuf), .O(clk26buf)); +// BUFG clk26buf_bufx(.I(clk26ibuf), .O(clk26bufpll)); + + wire tx0_pclk; // tx clock got promoted above input port because + // input port is compiled out in some cases + + ///////////////////////// + // + // Input Port 0 + // + ///////////////////////// + wire rx0_pclk, rx0_pclkx2, rx0_pclkx10, rx0_pllclk0; + wire rx0_reset; + wire rx0_serdesstrobe; + wire rx0_hsync; // hsync data + wire rx0_vsync; // vsync data + wire rx0_de; // data enable + wire rx0_basic_de; + wire [7:0] rx0_red; // pixel data out + wire [7:0] rx0_green; // pixel data out + wire [7:0] rx0_blue; // pixel data out + wire [29:0] rx0_sdata; + wire rx0_cv; + + wire rx0_encoding; + wire rx0_hdcp_ena; + wire [3:0] rx0_red_di; + wire [3:0] rx0_blue_di; + wire [3:0] rx0_green_di; + wire rx0_data_gb; + wire rx0_video_gb; + wire [3:0] rx0_ctl_code; + + wire rx0_line_end; + +`ifdef PASSTHROUGH + dvi_decoder dvi_rx0 ( + //These are input ports + .tmdsclk_p (RX0_TMDS[3]), + .tmdsclk_n (RX0_TMDSB[3]), + .blue_p (RX0_TMDS[0]), + .green_p (RX0_TMDS[1]), + .red_p (RX0_TMDS[2]), + .blue_n (RX0_TMDSB[0]), + .green_n (RX0_TMDSB[1]), + .red_n (RX0_TMDSB[2]), + .exrst (~rstbtn_n || HPD_N), + .pllreset ((comp_ctl[4] && !self_mode)), + + //These are output ports + .reset (rx0_reset), + .pclk (rx0_pclk), + .pclkx2 (rx0_pclkx2), + .pclkx10 (rx0_pclkx10), + .pllclk0 (rx0_pllclk0), // PLL x10 output + .pllclk1 (rx0_pllclk1), // PLL x1 output + .pllclk2 (rx0_pllclk2), // PLL x2 output + .pll_lckd (rx0_plllckd), + .tmdsclk (rx0_tmdsclk), + .serdesstrobe(rx0_serdesstrobe), + .hsync (rx0_hsync), + .vsync (rx0_vsync), + .de (rx0_de), + .basic_de (rx0_basic_de), + + .blue_vld (rx0_blue_vld), + .green_vld (rx0_green_vld), + .red_vld (rx0_red_vld), + .blue_rdy (rx0_blue_rdy), + .green_rdy (rx0_green_rdy), + .red_rdy (rx0_red_rdy), + + .psalgnerr (rx0_psalgnerr), + + .sdout (rx0_sdata), + .red (rx0_red), + .green (rx0_green), + .blue (rx0_blue), + + .encoding (rx0_encoding), + .hdcp_ena (rx0_hdcp_ena), + .red_di (rx0_red_di), + .green_di (rx0_green_di), + .blue_di (rx0_blue_di), + .data_gb (rx0_data_gb), + .video_gb (rx0_video_gb), + .ctl_code (rx0_ctl_code), + .cv (rx0_cv), + .line_end (rx0_line_end) + ); + + // + // This BUFG mirrors the rx0 clock + // This way we have a matched skew between the RX pclk clocks and the TX pclk + // +// BUFG tx0_bufg_pclk (.I(self_mode ? tx0_pclk_self : rx0_pllclk1), .O(tx0_pclk)); // clock comes from rx is passthru mode + wire tx0_pclk_ss; + wire tx0_pclk_ss_unbuf; + +// assign self_mode = comp_ctl[3]; + reg self_mode_pre; + always @(posedge tx0_pclk_self) begin + self_mode_pre <= comp_ctl[3]; + self_mode <= self_mode_pre; + end + + wire tx0_pclk_todcm; + BUFGMUX tx0_bufg_mux (.I0(rx0_pllclk1), .I1(tx0_pclk_self), .S(self_mode_pre), .O(tx0_pclk)); + BUFGMUX tx0_bufg_muxdcm (.I0(rx0_pllclk1), .I1(tx0_pclk_self), .S(self_mode_pre), .O(tx0_pclk_todcm)); + +// clkgendcm_720p60hz selfclockgen (.CLK_IN1(clk26buf), .CLK_OUT1(tx0_pclk_ss), .RESET((comp_ctl[4] && self_mode)), .LOCKED(m720p_locked) ); + clkgendcm_720p60hz selfclockgen (.CLK_IN1(clk26buf), .CLK_OUT1(tx0_pclk_self), .RESET((comp_ctl[4] && self_mode)), .LOCKED(m720p_locked) ); + +/* + // use spread spectrum clocking to reduce emissions... + DCM_CLKGEN #( + .DFS_OSCILLATOR_MODE("PHASE_FREQ_LOCK"), + .CLKIN_PERIOD ("13.48"), + .SPREAD_SPECTRUM ("CENTER_LOW_SPREAD"), +// .CLKFX_MD_MAX("1.0"), + .CLKFX_MULTIPLY (4), + .CLKFX_DIVIDE (4) ) + dcm_fcc_spreads_me_wide ( + .CLKIN (tx0_pclk_ss), + .FREEZEDCM (1'b0), + .PROGDATA (1'b0), + .PROGEN (1'b0), + .PROGCLK (1'b0), +// .PROGDONE (1'b0), +// .CLKFX180 (CLKFX180_DCM), +// .CLKFXDV (CLKFXDV_DCM), +// .LOCKED (clkgen_locked), + .RST (self_mode || comp_ctl[4]), + .CLKFX (tx0_pclk_ss_unbuf), + .LOCKED(ss_locked) + ); + BUFG tmdsclk_bfgss (.I(tx0_pclk_ss_unbuf), .O(tx0_pclk_self) ); + */ + +`else // !`ifdef PASSTHROUGH + assign self_mode = 1'b1; + + // generate internal 720p clock for self-timed mode operation + wire m720p_clk; + +`ifdef SS_CLOCKING_TOP + wire m720p_locked; // not currently used + + wire progdata_int; + wire progen_int; + wire progdone; + wire clkgen_locked; + wire tx0_pclk_unbuf; + + wire tx0_pclk_ss; + wire tx0_pclk_ss_unbuf; + + clk_720p_60hz clk720p (.CLK_IN1(clk26bufpll), .CLK_OUT1(tx0_pclk_ss), .RESET((comp_ctl[4] && self_mode)), + .LOCKED(m720p_locked) ); + + // use spread spectrum clocking to reduce emissions... + DCM_CLKGEN #( + .DFS_OSCILLATOR_MODE("PHASE_FREQ_LOCK"), + .CLKIN_PERIOD ("13.48"), + .SPREAD_SPECTRUM ("CENTER_HIGH_SPREAD"), +// .CLKFX_MD_MAX("1.0"), + .CLKFX_MULTIPLY (4), + .CLKFX_DIVIDE (4) ) + dcm_fcc_spreads_me_wide ( + .CLKIN (tx0_pclk_ss), + .FREEZEDCM (1'b0), + .PROGDATA (1'b0), + .PROGEN (1'b0), + .PROGCLK (1'b0), +// .PROGDONE (1'b0), +// .CLKFX180 (CLKFX180_DCM), +// .CLKFXDV (CLKFXDV_DCM), +// .LOCKED (clkgen_locked), + .RST (comp_ctl[4]), + .CLKFX (tx0_pclk_ss_unbuf), + .LOCKED(ss_locked) + ); + BUFG tmdsclk_bfgss (.I(tx0_pclk_ss_unbuf), .O(tx0_pclk) ); // + + +`else // !`ifdef SS_CLOCKING_TOP + + assign ss_locked = 1'b1; + + clk_720p_60hz clk720p (.CLK_IN1(clk26bufpll), .CLK_OUT1(tx0_pclk), .RESET((comp_ctl[4] && self_mode)), + .LOCKED(m720p_locked) ); + + // BUFG tx0_bufg_pclk (.I(self_mode ? LCD_DCLK_intbuf : rx0_pllclk1), .O(tx0_pclk)); + // BUFG tx0_bufg_pclk (.I(m720p_clk), .O(tx0_pclk)); // clock comes from freqsynth if internal +`endif // !`ifdef SS_CLOCKING_TOP + + ///// dummy tie-offs so we don't need a separate .UCF for the self-timed case + wire [3:0] dummy_tmds; + + IBUFDS #(.IOSTANDARD("TMDS_33"), .DIFF_TERM("FALSE") + ) ibuf_dummy0 (.I(RX0_TMDS[0]), .IB(RX0_TMDSB[0]), .O(dummy_tmds[0])); + IBUFDS #(.IOSTANDARD("TMDS_33"), .DIFF_TERM("FALSE") + ) ibuf_dummy1 (.I(RX0_TMDS[1]), .IB(RX0_TMDSB[1]), .O(dummy_tmds[1])); + IBUFDS #(.IOSTANDARD("TMDS_33"), .DIFF_TERM("FALSE") + ) ibuf_dummy2 (.I(RX0_TMDS[2]), .IB(RX0_TMDSB[2]), .O(dummy_tmds[2])); + IBUFDS #(.IOSTANDARD("TMDS_33"), .DIFF_TERM("FALSE") + ) ibuf_dummy3 (.I(RX0_TMDS[3]), .IB(RX0_TMDSB[3]), .O(dummy_tmds[3])); + +`endif // !`ifdef PASSTHROUGH + + // a simple pipeline register for LCD input retiming + reg lcdr_de; + reg lcdr_hsync; + reg lcdr_vsync; + reg [5:0] lcdr_b; + reg [5:0] lcdr_g; + reg [5:0] lcdr_r; + always @(posedge tx0_pclk) begin + lcdr_de <= LCD_DE; + lcdr_hsync <= LCD_HSYNC; + lcdr_vsync <= LCD_VSYNC; + lcdr_b <= LCD_B[7:2]; + lcdr_g <= LCD_G[7:2]; + lcdr_r <= LCD_R[7:2]; + end + + // this should be safe to have outside the ifdef zone + wire chroma_match_self; + assign chroma_match_self = (({lcdr_b[5:0],lcdr_b[0],lcdr_b[0]} == chroma_b[7:0]) && + ( {lcdr_r[5:0],lcdr_r[0],lcdr_r[0]} == chroma_r[7:0]) && + ( {lcdr_g[5:0],lcdr_g[0],lcdr_g[0]} == chroma_g[7:0])) && ext1_ctl[1]; + + ///////////////// + // + // Output Port 0 + // + ///////////////// + reg tx0_de; + reg tx0_basic_de; + wire tx0_pclkx2; + wire tx0_pclkx10; + wire tx0_serdesstrobe; + wire tx0_reset; + reg [7:0] tx0_blue; + reg [7:0] tx0_green; + reg [7:0] tx0_red; + reg tx0_hsync; + reg tx0_vsync; + wire tx0_pll_reset; + + reg tx0_vid_pa; + reg tx0_vid_gb; + reg tx0_dat_pa; + reg tx0_dat_gb; + reg tx0_dat_ena; + reg [3:0] tx0_ctl_code; + reg [9:0] tx0_dat_din; + reg [29:0] tx0_sdata; + reg tx0_cv; + + reg tx1_de; + reg tx1_basic_de; + reg [7:0] tx1_blue; + reg [7:0] tx1_green; + reg [7:0] tx1_red; + reg tx1_hsync; + reg tx1_vsync; + reg tx1_vid_pa; + reg tx1_vid_gb; + reg tx1_dat_pa; + reg tx1_dat_gb; + reg tx1_dat_ena; + reg [3:0] tx1_ctl_code; + reg [9:0] tx1_dat_din; + reg [29:0] tx1_sdata; + reg tx1_cv; + + reg tx2_de; + reg tx2_basic_de; + reg [7:0] tx2_blue; + reg [7:0] tx2_green; + reg [7:0] tx2_red; + reg tx2_hsync; + reg tx2_vsync; + reg tx2_vid_pa; + reg tx2_vid_gb; + reg tx2_dat_pa; + reg tx2_dat_gb; + reg tx2_dat_ena; + reg [3:0] tx2_ctl_code; + reg [9:0] tx2_dat_din; + reg [29:0] tx2_sdata; + reg tx2_cv; + + wire computed_gb; + wire computed_ctl_code; + + always @ (posedge tx0_pclk or posedge tx0_reset) begin + if( tx0_reset == 1'b1 ) begin + tx0_de <= 1'b0; + tx0_blue <= 8'b0; + tx0_green <= 8'b0; + tx0_red <= 8'b0; + tx0_hsync <= 1'b0; + tx0_vsync <= 1'b0; + tx0_vid_pa <= 1'b0; + tx0_vid_gb <= 1'b0; + tx0_dat_pa <= 1'b0; + tx0_dat_gb <= 1'b0; + tx0_dat_ena <= 1'b0; + tx0_ctl_code <= 4'b0; + tx0_dat_din <= 10'b0; + tx0_sdata <= 30'b0; + tx0_cv <= 1'b0; + + tx1_de <= 1'b0; + tx1_blue <= 8'b0; + tx1_green <= 8'b0; + tx1_red <= 8'b0; + tx1_hsync <= 1'b0; + tx1_vsync <= 1'b0; + tx1_vid_pa <= 1'b0; + tx1_vid_gb <= 1'b0; + tx1_dat_pa <= 1'b0; + tx1_dat_gb <= 1'b0; + tx1_dat_ena <= 1'b0; + tx1_ctl_code <= 4'b0; + tx1_dat_din <= 10'b0; + tx1_sdata <= 30'b0; + tx1_cv <= 1'b0; + + tx2_de <= 1'b0; + tx2_hsync <= 1'b0; + tx2_vsync <= 1'b0; + tx2_vid_pa <= 1'b0; + tx2_vid_gb <= 1'b0; + tx2_dat_pa <= 1'b0; + tx2_dat_gb <= 1'b0; + tx2_dat_ena <= 1'b0; + tx2_ctl_code <= 4'b0; + tx2_dat_din <= 10'b0; + tx2_sdata <= 30'b0; + tx2_cv <= 1'b0; + end else begin // if ( reset == 1'b1 ) + ///////// earlier pipe stage + tx0_de <= rx0_de; + tx0_basic_de <= rx0_basic_de; + tx0_blue <= rx0_blue; + tx0_green <= rx0_green; + tx0_red <= rx0_red; + + tx0_hsync <= rx0_hsync; + tx0_vsync <= rx0_vsync; + tx0_vid_gb <= rx0_video_gb; + tx0_vid_pa <= (rx0_ctl_code[3:0] == 4'b0001); + tx0_dat_gb <= rx0_data_gb; + tx0_dat_pa <= (rx0_ctl_code[3:0] == 4'b0101); + tx0_dat_ena <= rx0_encoding; + + tx0_ctl_code <= rx0_ctl_code; + tx0_dat_din[9:0] <= {rx0_blue_di[1:0],rx0_red_di[3:0],rx0_green_di[3:0]}; + tx0_sdata <= rx0_sdata; + tx0_cv <= rx0_cv; + + ///////// later pipe stage + if( self_mode ) begin + // self-timed + tx1_de <= lcdr_de; // de is always active high + tx1_blue <= chroma_match_self ? 8'b0 : {lcdr_b[5:0],lcdr_b[0],lcdr_b[0]}; + tx1_green <= chroma_match_self ? 8'b0 : {lcdr_g[5:0],lcdr_g[0],lcdr_g[0]}; + tx1_red <= chroma_match_self ? 8'b0 : {lcdr_r[5:0],lcdr_r[0],lcdr_r[0]}; + tx1_hsync <= lcdr_hsync; // sync signals from CPU are always active high + tx1_vsync <= lcdr_vsync; + +// tx1_vid_gb <= computed_gb; + tx1_vid_gb <= 0; + tx1_vid_pa <= 0; // this is a deprecated interface + tx1_dat_gb <= 0; + tx1_dat_pa <= 0; + tx1_dat_ena <= 0; + + tx1_dat_din[9:0] <= 0; + tx1_sdata <= 0; + tx1_cv <= 1'b0; // not used +// tx1_ctl_code <= computed_ctl_code; // this is the real pa (encoded as ctl code) + tx1_ctl_code <= 4'b0; + end else begin + // passing through + tx1_de <= tx0_de; + tx1_basic_de <= tx0_basic_de; + tx1_blue <= tx0_blue; + tx1_green <= tx0_green; + tx1_red <= tx0_red; + tx1_hsync <= tx0_hsync; + tx1_vsync <= tx0_vsync; + + tx1_vid_gb <= tx0_vid_gb; + tx1_vid_pa <= tx0_vid_pa; + tx1_dat_gb <= tx0_dat_gb; + tx1_dat_pa <= tx0_dat_pa; + tx1_dat_ena <= tx0_dat_ena; + + tx1_ctl_code <= tx0_ctl_code; + tx1_dat_din[9:0] <= tx0_dat_din[9:0]; + tx1_sdata <= tx0_sdata; + tx1_cv <= tx0_cv; + end // else: !if( self_mode ) + + tx2_de <= tx1_de; + tx2_hsync <= tx1_hsync; + tx2_vsync <= tx1_vsync; + tx2_vid_pa <= tx1_vid_pa; + tx2_vid_gb <= tx1_vid_gb; + tx2_dat_pa <= tx1_dat_pa; + tx2_dat_gb <= tx1_dat_gb; + tx2_dat_ena <= tx1_dat_ena; + tx2_ctl_code <= tx1_ctl_code; + tx2_dat_din <= tx1_dat_din; + tx2_sdata <= tx1_sdata; + tx2_cv <= tx1_cv; + + end // else: !if( reset == 1'b1 ) + end // always @ (posedge pclk or posedge reset) + +// assign de_sync = use_basic_de ? tx0_basic_de : tx0_de; + assign de_sync = tx0_de; + + // assuming this doesn't need to be pipelined... + assign tx0_pll_reset = (rx0_reset && !self_mode) || (comp_ctl[4] && self_mode); + +// reg tx0_reset_wire; +// always @(posedge tx0_pclk) begin +// tx0_reset_wire <= rx0_reset || (comp_ctl[4] && self_mode); +// end + +// BUFG tx0_reset_bufg( .I(tx0_reset_wire), .O(tx0_rstin)); + + assign tx0_rstin = (rx0_reset && !self_mode) || (comp_ctl[4] && self_mode); + + gbgen gbgen ( + .pclk(tx0_pclk), + .rstin(tx0_rstin), + .vsync(lcdr_vsync), + .hsync(lcdr_hsync), + .sync_pol(1'b1), // sync from CPU is always active high + .de(lcdr_de), + + .gb(computed_gb), + .code(computed_ctl_code) + ); + + ////////////////////////////////////////////////////////////////// + // Instantiate a dedicate PLL for output port + ////////////////////////////////////////////////////////////////// + wire tx0_clkfbout, tx0_clkfbin; + wire tx0_pllclk0, tx0_pllclk2; + + PLL_BASE # ( +// .CLKIN_PERIOD(10.526315), // 95 MHz +// .CLKIN_PERIOD(35.34), // 28.29 MHz 480p/60 + .CLKIN_PERIOD(13.481449525), // 74.176 MHz + .CLKFBOUT_MULT(10), //set VCO to 10x of CLKIN + .CLKOUT0_DIVIDE(1), + .CLKOUT1_DIVIDE(10), + .CLKOUT2_DIVIDE(5), +// .BANDWIDTH("LOW"), // normally not here + .COMPENSATION("SOURCE_SYNCHRONOUS") + ) PLL_OSERDES_0 ( + .CLKFBOUT(tx0_clkfbout), + .CLKOUT0(tx0_pllclk0), + .CLKOUT1(), + .CLKOUT2(tx0_pllclk2), + .CLKOUT3(), + .CLKOUT4(), + .CLKOUT5(), + .LOCKED(tx0_plllckd), + .CLKFBIN(tx0_clkfbin), + .CLKIN(tx0_pclk_todcm), + .RST(tx0_pll_reset || comp_ctl[4] || (!m720p_locked & self_mode)) + ); + + wire lcd_intbuf; + //////////////////// buffer our Rx clock back to the CPU so we are meso-synchronous + ODDR2 lcd_refclk_buf (.D0(1'b1), .D1(1'b0), .C0(tx0_pclk), .C1(!tx0_pclk), .Q(LCDO_DCLK), .CE(1), .R(0), .S(0) ); +// wire LCD_fbkclk; +// IBUF lcd_fbk_ibuf(.I(LCDO_DCLK), .O(LCD_fbkclk)); +// BUFG lcd_fbk_buf(.I(LCD_fbkclk), .O(lcd_intbuf)); + + // + // This BUFG is needed in order to deskew between PLL clkin and clkout + // So the tx0 pclkx2 and pclkx10 will have the same phase as the pclk input + // + BUFG tx0_clkfb_buf (.I(tx0_clkfbout), .O(tx0_clkfbin)); + + // + // regenerate pclkx2 for TX + // + BUFG tx0_pclkx2_buf (.I(tx0_pllclk2), .O(tx0_pclkx2)); + + // + // regenerate pclkx10 for TX + // + wire tx0_bufpll_lock; + BUFPLL #(.DIVIDE(5)) tx0_ioclk_buf (.PLLIN(tx0_pllclk0), .GCLK(tx0_pclkx2), .LOCKED(tx0_plllckd), + .IOCLK(tx0_pclkx10), .SERDESSTROBE(tx0_serdesstrobe), .LOCK(tx0_bufpll_lock)); + + // reset off of master PLL lock, not BUFPLL lock + assign tx0_reset = (~tx0_plllckd) || (comp_ctl[4] & self_mode); + + wire byp_error; + + reg [7:0] blend_b1; + reg [7:0] blend_g1; + reg [7:0] blend_r1; + + assign hdcp_comp_ready = comp_ctl[0] && hdcp_is_ready; + always @ (posedge tx0_pclk or posedge tx0_reset) begin + if( tx0_reset ) begin + tx2_blue <= 0; + tx2_green <= 0; + tx2_red <= 0; + + blend_b1 <= 0; + blend_r1 <= 0; + blend_g1 <= 0; + end else begin + blend_b1 <= blend_b; + blend_r1 <= blend_r; + blend_g1 <= blend_g; + + tx2_blue <= !(box_active & comp_ctl[2]) ? tx1_blue : + hdcp_comp_ready ? blend_b1[7:0] ^ cipher_stream[7:0] : blend_b1[7:0]; + + tx2_green <= !(box_active & comp_ctl[2]) ? tx1_green : + hdcp_comp_ready ? blend_g1[7:0] ^ cipher_stream[15:8] : blend_g1[7:0]; + + tx2_red <= !(box_active & comp_ctl[2]) ? tx1_red : + hdcp_comp_ready ? blend_r1[7:0] ^ cipher_stream[23:16] : blend_r1[7:0]; + end // else: !if( tx0_reset ) + end // always @ (posedge tx0_pclk or posedge tx0_reset) + + dvi_encoder_top dvi_tx0 ( + .pclk (tx0_pclk), + .pclkx2 (tx0_pclkx2), + .pclkx10 (tx0_pclkx10), + .serdesstrobe(tx0_serdesstrobe), + .rstin (tx0_reset), + .blue_din (tx2_blue), + .green_din (tx2_green), + .red_din (tx2_red), + .hsync (tx2_hsync), + .vsync (tx2_vsync), + .de (tx2_de), + .TMDS (TX0_TMDS), + .TMDSB (TX0_TMDSB), + .vid_pa (tx2_vid_pa), + .vid_gb (tx2_vid_gb), + .dat_pa (tx2_dat_pa), + .dat_gb (tx2_dat_gb), + .dat_ena (tx2_dat_ena), + .dat_din (tx2_dat_din), + .ctl_code (tx2_ctl_code), + .bypass_sdata(tx2_sdata), + .bypass_ena (1'b1), + .byp_error (byp_error), + .box_active ((box_active && comp_ctl[2]) || self_mode) + ); + + assign hsync_rising = hsync_v & !hsync_v2; + assign vsync_rising = vsync_v & !vsync_v2; + // extend hsync's validity when control periods are inactive + always @(posedge tx0_pclk or posedge tx0_reset) begin + if( tx0_reset == 1'b1 ) begin + vsync_v <= 0; + hsync_v <= 0; + vsync_v2 <= 0; + hsync_v2 <= 0; + end else begin + vsync_v2 <= vsync_v; + hsync_v2 <= hsync_v; + if(tx0_cv) begin + vsync_v <= tx0_vsync ^ !hdmi_vsync_pol; + hsync_v <= tx0_hsync ^ !hdmi_hsync_pol; + end else begin + vsync_v <= vsync_v; + hsync_v <= hsync_v; + end + end // else: !if( tx0_reset == 1'b1 ) + end // always @ (posedge tx0_pclk or posedge tx0_reset) + +`ifdef REGWINDOWS + always @(posedge tx0_pclk or posedge tx0_rstin) begin + if(tx0_rstin) begin + window_x_buf <= 12'b0; + window_y_buf <= 12'b0; + window_h_buf <= 12'b0; + window_w_buf <= 12'b0; + end else begin + if( vsync_rising && window_update ) begin + window_x_buf[11:0] <= window_x[11:0]; + window_y_buf[11:0] <= window_y[11:0]; + window_w_buf[11:0] <= window_w[11:0]; + window_h_buf[11:0] <= window_h[11:0]; + end else begin + window_x_buf[11:0] <= window_x_buf[11:0]; + window_y_buf[11:0] <= window_y_buf[11:0]; + window_w_buf[11:0] <= window_w_buf[11:0]; + window_h_buf[11:0] <= window_h_buf[11:0]; + end // else: !if( vsync_rising && window_update ) + end // else: !if(tx0_rstin) + end // always @ (posedge tx0_pclk or posedge tx0_rstin) + + // instantiate the timing module that generates the mask box for enabling the overlay + boxtiming boxtimer ( + .pclk(tx0_pclk), + .rstin(tx0_rstin), +// .vsync(tx0_vsync ^ !hdmi_vsync_pol), +// .hsync(tx0_hsync ^ !hdmi_hsync_pol), + .vsync(vsync_v), + .hsync(hsync_v), + .sync_pol(1'b1), // with auto-detect, polarity is always "righted" + .de(de_sync), + .cv(tx0_cv), + + .hpos(window_x_buf[11:0]), + .hsize(window_w_buf[11:0]), + .vpos(window_y_buf[11:0]), + .vsize(window_h_buf[11:0]), + + .box_active(box_active_raw) + ); +`else // !`ifdef REGWINDOWS + assign box_active_raw = de_sync & !(vsync_v || hsync_v); +`endif // !`ifdef REGWINDOWS + + // chroma should only check high 6 bits because the lower bits don't exist + assign chroma_match = (blend_b[7:2] == chroma_b[7:2]) && + (blend_r[7:2] == chroma_r[7:2]) && + (blend_g[7:2] == chroma_g[7:2]); + + assign box_active = box_active_raw && (!chroma_match || !ext1_ctl[1]); + + // hpd debounce and delay + always @(posedge tx0_pclk or posedge tx0_reset) begin + if( tx0_reset == 1'b1 ) begin + hpd_saw_vsync <= 1'b0; + end else begin + if( HPD_N ) begin + hpd_saw_vsync <= 1'b0; + end else begin + if( self_mode ) begin + // in self-timed mode, there's no external vsync to sync to, so just pass this through + hpd_saw_vsync <= 1'b1; + end else begin + // in genlock mode, synch to a vsync edge as a debounce mechanism + if( vsync_rising ) begin + hpd_saw_vsync <= 1'b1; + end else begin + hpd_saw_vsync <= hpd_saw_vsync; + end + end + end + end + end // always @ (posedge tx0_pclk or posedge tx0_reset) +// assign HPD_NOTIFY = hpd_saw_vsync; + assign HPD_NOTIFY = !HPD_N; // fuck debouncing, the CPU can take care of it. 800 MHz of CPU power and all it has to do is... + + assign HPD_OVERRIDE = snoop_ctl[3]; // force hpd to look like nothing is plugged in hen asserted + + /////////// timing extraction machine +`ifdef TIMING_POSTX + // run the timing detector after the Tx mux, so that during self-timed mode we get + // the timing of what we're putting to the screen as feedback... + timing_detector timing_det ( + .pclk(tx0_pclk), + .rstin(tx0_rstin), + .vsync(self_mode ? tx1_vsync : vsync_v), // vsync_v is polarity-corrrected + .hsync(self_mode ? tx1_vsync : hsync_v), // hsync_v is polarity-corrected + .de(self_mode ? tx1_de : de_sync), + .refclk(clk26buf), // actually 26 MHz due to PXA168 multipliers + + .lcd_de(lcdr_de), + .lcd_vsync(lcdr_vsync), + + .hactive(t_hactive), // pixels + .vactive(t_vactive), // pixels + .htotal(t_htotal), // pixels + .vtotal(t_vtotal), // PIXELS + .h_frontporch(t_h_fp), // pixels + .h_backporch(t_h_bp), // pixels + .v_frontporch(t_v_fp), // PIXELS + .v_backporch(t_v_bp), // PIXELS + .hsync_width(t_hsync_width), // pixels + .vsync_width(t_vsync_width), // PIXELS + .lcd_de_latency(t_lcd_de_latency), + .lcd_vsync_latency(t_vsync_latency), + + .refclkcnt(t_refclkcnt) + ); +`else // !`ifdef TIMING_POSTX + // run the timing detector after the Rx mux, so that during self-timed mode we + // get the timing of whatever's coming in on the input port. + timing_detector timing_det ( + .pclk(rx0_pllclk1), // off of the rx clock + .rstin(tx0_rstin), + .vsync(vsync_v), // vsync_v is polarity-corrrected + .hsync(hsync_v), // hsync_v is polarity-corrected + .de(de_sync), + .refclk(clk26buf), // actually 26 MHz due to PXA168 multipliers + + .lcd_de(lcdr_de), + .lcd_vsync(lcdr_vsync), + + .hactive(t_hactive), // pixels + .vactive(t_vactive), // pixels + .htotal(t_htotal), // pixels + .vtotal(t_vtotal), // PIXELS + .h_frontporch(t_h_fp), // pixels + .h_backporch(t_h_bp), // pixels + .v_frontporch(t_v_fp), // PIXELS + .v_backporch(t_v_bp), // PIXELS + .hsync_width(t_hsync_width), // pixels + .vsync_width(t_vsync_width), // PIXELS + .lcd_de_latency(t_lcd_de_latency), + .lcd_vsync_latency(t_vsync_latency), + + .refclkcnt(t_refclkcnt) + ); + +`endif // !`ifdef TIMING_POSTX + + ///////////////// + // + // LCD input & FIFO + // + ///////////////// + + // This module will contain the input from the LCD + // and a FIFO that can hold a few lines of video data + wire dummy; + wire genlock; + assign VSYNC_STB = genlock & !self_mode; // only genlock when self-mode is off + + lcd_input lcd_input_top ( + .rstin(self_mode ? comp_ctl[4] : rx0_reset), +// .lcd_dclk(LCD_DCLK_intbuf), + .lcd_dclk(tx0_pclk), + .lcd_de(lcdr_de), + .lcd_hsync(lcdr_hsync), + .lcd_vsync(lcdr_vsync), + .lcd_sync_pol(1'b1), + .lcd_b(lcdr_b), + .lcd_g(lcdr_g), + .lcd_r(lcdr_r), + + .hdmi_pclk(tx0_pclk), + .hdmi_de(de_sync), +//nuke .hdmi_vsync(tx0_vsync), +// .hdmi_hsync(tx0_hsync), + .hdmi_vsync(tx0_vsync ^ !hdmi_vsync_pol), + .hdmi_hsync(tx0_hsync ^ !hdmi_hsync_pol), + .hdmi_sync_pol(1'b1), + .hdmi_cv(tx0_cv), + + .hdmi_b(blend_b), + .hdmi_r(blend_r), + .hdmi_g(blend_g), + .genlock(genlock), + .locked(genlock_locked), + + .dummy(dummy), + + .line_full_level(line_full_level[2:0]), + .line_empty_level(line_empty_level[2:0]), + .write_init_level(write_init_level), + .read_init_level(read_init_level), + + .target_lead_pixels(target_lead_pixels), + .reset_lock_machine(reset_lock_machine), + + .lock_tolerance(lock_tolerance), + .smartlock_on(smartlock_on) + ); + assign reset_lock_machine = comp_ctl[6]; + assign smartlock_on = comp_ctl[5]; + + reg hdmi_de_d; + wire hdmi_de_rising; + reg vsync_v_raw, hsync_v_raw; + wire actual_reset; + assign actual_reset = self_mode ? comp_ctl[4] : rx0_reset; + // hdmi sync polarity detector + always @(posedge tx0_pclk or posedge actual_reset) begin + if(actual_reset) begin + hdmi_vsync_pol <= 0; + hdmi_hsync_pol <= 0; + vsync_v_raw <= 0; + hsync_v_raw <= 0; + end else begin + hdmi_de_d <= de_sync; + + if(tx0_cv) begin + vsync_v_raw <= tx0_vsync; + hsync_v_raw <= tx0_hsync; + end else begin + vsync_v_raw <= vsync_v_raw; + hsync_v_raw <= hsync_v_raw; + end + // the theory goes that de is always active high so use this to adjust sync polarities + if( hdmi_de_rising ) begin + hdmi_vsync_pol <= !vsync_v_raw; + hdmi_hsync_pol <= !hsync_v_raw; + end else begin + hdmi_vsync_pol <= hdmi_vsync_pol; + hdmi_hsync_pol <= hdmi_hsync_pol; + end + end + end // always @ (posedge hdmi_pclk) + assign hdmi_de_rising = !hdmi_de_d & de_sync; + +`define HDCP_MODULE 1 +`ifdef HDCP_MODULE + /////// + // HDCP + /////// + // HDCP initialization procedure + // + // 1. Sniff An, KSV going across the wire + // 2. Generate private key table for one of the KSV's + // 3. Perform the Km computation using derived private key table + // 4. Enter An, Km into the register for the HDCP cipher + // 5. Initiate the authentication (pulse hdcpBlockCipher_init and + // authentication high one cycle simultaneously) + // 6. Wait until stream_ready is high + // + // Now begins the main loop: + // There is an ambiguity in the spec. Either a rekey operation happens immediately + // (since this happens during vertical retrace), or not. Either way, this is roughly + // what happens. + // + // 1. If hdcp_ena activates (or of de and data island enable), advance cipher + // 2. If vertical sync happens, pulse hdcpBlockCipher_init one cycle and wait + // until stream_ready; return to 1 + // 3. If horizontal sync happens, pulse hdcpRekeyCipher once cycle, wait until + // stream_ready; return to 1 + // + // That's it. So the only question is if vsync "happens" immediately after an authentication. + // The test vectors would suggest this is the case but I can't find it in the state machine + // diagrams, so perhaps good to try both options...? + parameter HDCP_UNPLUG = 18'b1 << 0; + parameter HDCP_WAIT_AKSV = 18'b1 << 1; + parameter HDCP_AUTH_PULSE = 18'b1 << 2; + parameter HDCP_AUTH = 18'b1 << 3; + parameter HDCP_AUTH_WAIT = 18'b1 << 4; + parameter HDCP_AUTH_VSYNC_PULSE = 18'b1 << 5; + parameter HDCP_AUTH_VSYNC = 18'b1 << 6; + parameter HDCP_AUTH_VSYNC_WAIT = 18'b1 << 7; + parameter HDCP_WAIT_1001 = 18'b1 << 8; + parameter HDCP_WAIT_1001_END = 18'b1 << 9; + parameter HDCP_VSYNC = 18'b1 << 10; + parameter HDCP_VSYNC_PULSE = 18'b1 << 11; + parameter HDCP_VSYNC_WAIT = 18'b1 << 12; + parameter HDCP_READY = 18'b1 << 13; + parameter HDCP_REKEY = 18'b1 << 14; + parameter HDCP_REKEY_PULSE = 18'b1 << 15; + parameter HDCP_REKEY_WAIT = 18'b1 << 16; + parameter HDCP_WAIT_KMRDY = 18'b1 << 17; + + parameter HDCP_nSTATES = 18; + + reg [(HDCP_nSTATES-1):0] HDCP_cstate = {{(HDCP_nSTATES-1){1'b0}}, 1'b1}; + reg [(HDCP_nSTATES-1):0] HDCP_nstate; + + reg auth_mode; + reg hdcp_init; + reg hdcp_rekey; + wire hdcp_stream_ena; + + reg active_line; + reg hdcp_rekey_2; + reg hdcp_rekey_1; + + assign hdcp_is_ready = (HDCP_cstate == HDCP_READY); + + // compute active_line. This tells you if the last line had active data + // in it or not. Reset the computation on falling edge of hsync + always @ (posedge tx0_pclk or posedge tx0_reset) begin + if( tx0_reset == 1'b1 ) begin + active_line <= 1'b0; + hdcp_rekey_2 <= 1'b0; + hdcp_rekey_1 <= 1'b0; + end else begin + hdcp_rekey_2 <= hdcp_rekey_1; + hdcp_rekey_1 <= hdcp_rekey || + (rx0_line_end && (HDCP_cstate == HDCP_READY) && + tx0_de); + if( tx0_de ) begin + active_line <= 1'b1; + end else if( !hsync_v & hsync_v2 ) begin // hsync falling + active_line <= 1'b0; + end + end + end + + always @ (posedge tx0_pclk or posedge HPD_N or posedge tx0_reset or negedge rstbtn_n) begin + if (~rstbtn_n | HPD_N | tx0_reset ) + HDCP_cstate <= HDCP_UNPLUG; + else + if( Aksv14_write ) begin + HDCP_cstate <= HDCP_AUTH_PULSE; // hack for tivo series 3 + end else begin + HDCP_cstate <=#1 HDCP_nstate; + end + end + + always @ (*) begin + case (HDCP_cstate) //synthesis parallel_case full_case + HDCP_UNPLUG: begin + HDCP_nstate = HPD_N ? HDCP_UNPLUG : HDCP_WAIT_AKSV; + end + HDCP_WAIT_AKSV: begin + // wait until the 14th byte is written to the HDCP register set + // this is the MSB of AKsv, and this triggers an authentication event + HDCP_nstate = Aksv14_write ? HDCP_AUTH_PULSE : HDCP_WAIT_AKSV; +// HDCP_nstate = Aksv14_write ? HDCP_WAIT_KMRDY : HDCP_WAIT_AKSV; + // in this implementation, skipe the HDCP_WAIT_KMRDY state + end + + // this state is unreachable + HDCP_WAIT_KMRDY: begin + HDCP_nstate = Km_ready ? HDCP_AUTH_PULSE : HDCP_WAIT_KMRDY; + end + + //////// + // maybe put a state here to wait for Km to become ready + // but for now, we assume host has pre-loaded Km. Km is fixed for every Tx/Rx HDMI pair. + // So once you have computed it, it can be pre-loaded even before the transaction happens. + // One way around this is to snag AKsv, Bksv; and if they are a new pair, compute Km + // and load it; and then override HPD_N high for a second to force a re-key *only* if + // this is new pair. Thus, the first time you plug in a new device you *might* see it + // flicker once, but it would never happen again, but I think typically you would + // not notice because the screen would stay dark the entire time. + // + // --> above is the wait KMRDY state. The way this should work now is: + // 1. Aksv is written, byte 14 triggers an interrupt to the CPU. + // 2. CPU derives Km, writes Km, sets Km ready + // 3. state machine then moves on to initiate auth pulse + // + //////// + HDCP_AUTH_PULSE: begin + HDCP_nstate = HDCP_AUTH; + end + HDCP_AUTH: begin + HDCP_nstate = stream_ready? HDCP_AUTH : HDCP_AUTH_WAIT; + end + HDCP_AUTH_WAIT: begin + HDCP_nstate = stream_ready ? HDCP_AUTH_VSYNC_PULSE : HDCP_AUTH_WAIT; + end + + // this is a special vsync-update state just for after auth + // because I don't know if there is more than 1 vsync period between + // the conclusion of auth and the first 1001 assertion + // if there is, then we end up unsynchronized on the Mi state + HDCP_AUTH_VSYNC_PULSE: begin + HDCP_nstate = HDCP_AUTH_VSYNC; + end + HDCP_AUTH_VSYNC: begin + HDCP_nstate = stream_ready ? HDCP_AUTH_VSYNC : HDCP_AUTH_VSYNC_WAIT; + end + HDCP_AUTH_VSYNC_WAIT: begin + HDCP_nstate = stream_ready ? HDCP_WAIT_1001 : HDCP_AUTH_VSYNC_WAIT; + end + + // our primary wait state + HDCP_WAIT_1001: begin + HDCP_nstate = (vsync_v && (rx0_ctl_code[3:0] == 4'b1001)) ? + HDCP_WAIT_1001_END : HDCP_WAIT_1001; + end + HDCP_WAIT_1001_END: begin + HDCP_nstate = (vsync_v && (rx0_ctl_code[3:0] == 4'b1001)) ? + HDCP_WAIT_1001_END : HDCP_READY; + end + + + HDCP_VSYNC_PULSE: begin + HDCP_nstate = HDCP_VSYNC; + end + HDCP_VSYNC: begin + HDCP_nstate = stream_ready ? HDCP_VSYNC : HDCP_VSYNC_WAIT; + end + HDCP_VSYNC_WAIT: begin + HDCP_nstate = stream_ready ? HDCP_WAIT_1001 : HDCP_VSYNC_WAIT; + end + + // our primary cipher state + HDCP_READY: begin +// HDCP_nstate = (!rx0_de & tx0_de) ? HDCP_REKEY_PULSE : + // i've now got a signal banging rekey outside this state machine + // it's unclean, but necessary to get rekey to happen early enough + // to meet hdcp spec requirement for rekey time. + // Core assumption: the only way stream becomes un-ready during + // HDCP_READY is due to the external rekey event. vsync_rising + // will never result in this triggering because it itself must + // transition this state machine to a new state before stream_ready + // changes; and furthermore, stream_ready is guaranteed to be high + // upon return to this state. + HDCP_nstate = (stream_ready == 1'b0) ? HDCP_REKEY_WAIT : + vsync_rising ? HDCP_VSYNC_PULSE : + HDCP_READY; + end + + HDCP_REKEY_PULSE: begin + HDCP_nstate = HDCP_REKEY; + end + HDCP_REKEY: begin + HDCP_nstate = stream_ready ? HDCP_REKEY : HDCP_REKEY_WAIT; + end + HDCP_REKEY_WAIT: begin + HDCP_nstate = stream_ready ? HDCP_READY : HDCP_REKEY_WAIT; + end + endcase // case (HDCP_cstate) + end + +// assign Km_ready = !Km_rdy2 & Km_rdy1; // rising edge pulse + assign Km_ready = Km_rdy2; // for now make it level triggered ("cheezy mode") + + always @ (posedge tx0_pclk or posedge HPD_N or posedge tx0_reset or negedge rstbtn_n) begin + if( ~rstbtn_n | HPD_N | tx0_reset ) begin + auth_mode <=#1 1'b0; + hdcp_init <=#1 1'b0; + hdcp_rekey <=#1 1'b0; + hdcp_requested <=#1 1'b0; + + Km_rdy0 <= 1'b0; + Km_rdy1 <= 1'b0; + Km_rdy2 <= 1'b0; + end else begin + Km_rdy0 <= comp_ctl[7]; + Km_rdy1 <= Km_rdy0; + Km_rdy2 <= Km_rdy1; + + case (HDCP_cstate) //synthesis parallel_case full_case + HDCP_UNPLUG: begin + auth_mode <=#1 1'b0; + hdcp_init <=#1 1'b0; + hdcp_rekey <=#1 1'b0; + hdcp_requested <=#1 1'b0; + end + HDCP_WAIT_AKSV: begin + auth_mode <=#1 1'b0; + hdcp_init <=#1 1'b0; + hdcp_rekey <=#1 1'b0; + hdcp_requested <=#1 1'b0; + end + + HDCP_WAIT_KMRDY: begin + auth_mode <=#1 1'b0; + hdcp_init <=#1 1'b0; + hdcp_rekey <=#1 1'b0; + hdcp_requested <=#1 1'b0; + end + + HDCP_AUTH_PULSE: begin + auth_mode <=#1 1'b1; + hdcp_init <=#1 1'b1; // pulse just one cycle + hdcp_rekey <=#1 1'b0; + hdcp_requested <=#1 1'b0; + end + HDCP_AUTH: begin + auth_mode <=#1 1'b0; + hdcp_init <=#1 1'b0; + hdcp_rekey <=#1 1'b0; + hdcp_requested <=#1 1'b0; + end + HDCP_AUTH_WAIT: begin + auth_mode <=#1 1'b0; + hdcp_init <=#1 1'b0; + hdcp_rekey <=#1 1'b0; + hdcp_requested <=#1 1'b0; + end + + HDCP_AUTH_VSYNC_PULSE: begin + auth_mode <=#1 1'b0; + hdcp_init <=#1 1'b1; // pulse init, but not with auth_mode + hdcp_rekey <=#1 1'b0; + hdcp_requested <=#1 1'b0; + end + HDCP_AUTH_VSYNC: begin + auth_mode <=#1 1'b0; + hdcp_init <=#1 1'b0; + hdcp_rekey <=#1 1'b0; + hdcp_requested <=#1 1'b0; + end + HDCP_AUTH_VSYNC_WAIT: begin + auth_mode <=#1 1'b0; + hdcp_init <=#1 1'b0; + hdcp_rekey <=#1 1'b0; + hdcp_requested <=#1 1'b0; + end + + HDCP_WAIT_1001: begin + auth_mode <=#1 1'b0; + hdcp_init <=#1 1'b0; + hdcp_rekey <=#1 1'b0; + hdcp_requested <=#1 1'b0; + end + HDCP_WAIT_1001_END: begin + auth_mode <=#1 1'b0; + hdcp_init <=#1 1'b0; + hdcp_rekey <=#1 1'b0; + hdcp_requested <=#1 1'b0; + end + + HDCP_VSYNC_PULSE: begin + auth_mode <=#1 1'b0; + hdcp_init <=#1 1'b1; // pulse init, but not with auth_mode + hdcp_rekey <=#1 1'b0; + hdcp_requested <=#1 1'b1; + end + HDCP_VSYNC: begin + auth_mode <=#1 1'b0; + hdcp_init <=#1 1'b0; + hdcp_rekey <=#1 1'b0; + hdcp_requested <=#1 1'b1; + end + HDCP_VSYNC_WAIT: begin + auth_mode <=#1 1'b0; + hdcp_init <=#1 1'b0; + hdcp_rekey <=#1 1'b0; + hdcp_requested <=#1 1'b1; + end + + HDCP_READY: begin + auth_mode <=#1 1'b0; + hdcp_init <=#1 1'b0; + hdcp_rekey <=#1 1'b0; + hdcp_requested <=#1 1'b1; + end + + HDCP_REKEY_PULSE: begin + auth_mode <=#1 1'b0; + hdcp_init <=#1 1'b0; +// hdcp_rekey <=#1 1'b1; // pulse rekey + hdcp_rekey <=#1 1'b0; // we're going to do this asychronously to save some cycles + // yes, it means hdcp_rekey gets optimized out + // but structurally this helps me remember what the code was intended to do + hdcp_requested <=#1 1'b1; + end + HDCP_REKEY: begin + auth_mode <=#1 1'b0; + hdcp_init <=#1 1'b0; + hdcp_rekey <=#1 1'b0; + hdcp_requested <=#1 1'b1; + end + HDCP_REKEY_WAIT: begin + auth_mode <=#1 1'b0; + hdcp_init <=#1 1'b0; + hdcp_rekey <=#1 1'b0; + hdcp_requested <=#1 1'b1; + end + endcase // case (HDCP_cstate) + end // else: !if( ~rstbtn_n | HPD_N ) + end // always @ (posedge tx0_pclk) + + + wire stream_ready; + hdcp_cipher cipher ( + .clk(tx0_pclk), + .reset(tx0_rstin), + .Km(Km), + .An(An), + .hdcpBlockCipher_init(hdcp_init), + .authentication(auth_mode), + .hdcpRekeyCipher(hdcp_rekey_2), + .hdcpStreamCipher(rx0_hdcp_ena && (HDCP_cstate == HDCP_READY)), + .pr_data(cipher_stream), + .stream_ready(stream_ready) + ); +`endif // `ifdef HDCP_MODULE + + ////////////////////////////////////// + // Status LED + ////////////////////////////////////// + reg [22:0] counter; + + always @(posedge clk26buf or negedge rstbtn_n) begin + if( rstbtn_n == 1'b0 ) begin + counter <= 1'b0; + LED0 <= 1'b0; + end else begin + counter <= counter + 1; +// LED <= counter[22] & !rx0_de; +// LED <= counter[22] & byp_error | dummy; +// LED <= counter[22] & dummy; +// LED0 <= dummy & snoop_ctl[7]; +`ifdef PASSTHROUGH +// LED0 <= dummy; + LED0 <= vsync_v; +`else +// LED0 <= tx0_plllckd && m720p_locked & ss_locked; +`endif + +// LED1 <= counter[22]; +// LED1 <= de_sync; + end // else: !if( rstbtn_n == 1'b0 ) + + LOWVOLT_NOTIFY <= LOWVOLT_N; + HDCP_AKSV <= Aksv14_write; // retime it into this domain to not screw up timing closure + end + + //assign LED[3:0] = {rx0_red_rdy | counter[23], rx0_green_rdy | counter[23], rx0_blue_rdy | counter[23], + // rx0_de | counter[23]}; + + + //////////////////////////////// + // serial number + //////////////////////////////// + + reg clk2M_unbuf; + (* clock_signal = "yes" *) + (* PERIOD = "period 0.8125 MHz" *) + wire clk2M; + wire clk1M; + reg clk1M_unbuf; + always @(posedge clk26buf) begin + clk2M_unbuf <= counter[4]; // 0.8MHz clock: device DNA only runs at 2 MHz + clk1M_unbuf <= counter[6]; + end + + BUFG clk2M_buf(.I(clk2M_unbuf), .O(clk2M)); + BUFG clk1M_buf(.I(clk1M_unbuf), .O(clk1M)); + + reg dna_pulse; + reg dna_shift; + wire dna_bit; + DNA_PORT device_dna( .CLK(clk2M), .DIN(1'b0), .DOUT(dna_bit), .READ(dna_pulse), .SHIFT(dna_shift) ); + parameter DNA_INIT = 4'b1 << 0; + parameter DNA_PULSE = 4'b1 << 1; + parameter DNA_SHIFT = 4'b1 << 2; + parameter DNA_DONE = 4'b1 << 3; + + parameter DNA_nSTATES = 4; + + reg [(DNA_nSTATES-1):0] DNA_cstate = {{(DNA_nSTATES-1){1'b0}}, 1'b1}; + reg [(DNA_nSTATES-1):0] DNA_nstate; + reg [5:0] dna_shift_count; + + always @ (negedge clk2M or posedge ~rstbtn_n) begin + if (~rstbtn_n) + DNA_cstate <= DNA_INIT; + else + DNA_cstate <=#1 DNA_nstate; + end + + always @ (*) begin + case (DNA_cstate) //synthesis paralell_case full_case + DNA_INIT: begin + DNA_nstate = DNA_PULSE; + end + DNA_PULSE: begin + DNA_nstate = DNA_SHIFT; + end + DNA_SHIFT: begin + // depending on if MSB or LSB first, want to use 56 or 55 + // especially if serial #'s are linear-incrementing + DNA_nstate = (dna_shift_count[5:0] == 6'd55) ? DNA_DONE : DNA_SHIFT; + end + DNA_DONE: begin + DNA_nstate = DNA_DONE; + end + endcase // case (DNA_cstate) + end + + always @ (negedge clk2M or posedge ~rstbtn_n) begin + if( ~rstbtn_n ) begin + dna_shift_count <= 6'h0; + dna_data <= 56'h0; + dna_pulse <= 1'b0; + dna_shift <= 1'b0; + end else begin + case (DNA_cstate) //synthesis paralell_case full_case + DNA_INIT: begin + dna_shift_count <= 6'h0; + dna_data <= 56'h0; + dna_pulse <= 1'b0; + dna_shift <= 1'b0; + end + DNA_PULSE: begin + dna_shift_count <= 6'h0; + dna_data <= 56'h0; + dna_pulse <= 1'b1; + dna_shift <= 1'b0; + end + DNA_SHIFT: begin + dna_shift_count <= dna_shift_count + 6'b1; + dna_data[55:0] <= {dna_data[54:0],dna_bit}; + dna_pulse <= 1'b0; + dna_shift <= 1'b1; + end + DNA_DONE: begin + dna_shift_count <= dna_shift_count; + dna_data[55:0] <= dna_data[55:0]; + dna_pulse <= 1'b0; + dna_shift <= 1'b0; + end + endcase // case (DNA_cstate) + end // else: !if( ~rstbtn_n ) + end // always @ (posedge clk2M or posedge ~rstbtn_n) + + //////////////////////////////// + // heartbeat + //////////////////////////////// + pwm heartbeat(.clk812k(clk1M), .reset(~rstbtn_n), .pwmout(blue_led), + .bright(12'b0000_1111_1000), .dim(12'b0000_0001_0000) ); + + assign LED1 = blue_led | HPD_N; + +endmodule diff --git a/ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.asy b/ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.asy new file mode 100755 index 0000000..83e8d5d --- /dev/null +++ b/ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.asy @@ -0,0 +1,21 @@ +Version 4 +SymbolType BLOCK +TEXT 32 32 LEFT 4 clkgendcm_720p60hz +RECTANGLE Normal 32 32 576 1088 +LINE Normal 0 80 32 80 +PIN 0 80 LEFT 36 +PINATTR PinName clk_in1 +PINATTR Polarity IN +LINE Normal 0 432 32 432 +PIN 0 432 LEFT 36 +PINATTR PinName reset +PINATTR Polarity IN +LINE Normal 608 80 576 80 +PIN 608 80 RIGHT 36 +PINATTR PinName clk_out1 +PINATTR Polarity OUT +LINE Normal 608 944 576 944 +PIN 608 944 RIGHT 36 +PINATTR PinName locked +PINATTR Polarity OUT + diff --git a/ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.ejp b/ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.ejp new file mode 100755 index 0000000..b308e6d --- /dev/null +++ b/ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.ejp @@ -0,0 +1,245 @@ +Encore.Project.ProjectDir = C:/largework/fpga/hdmi/release1/release1.srcs/sources_1/ip/clk_wiz_v3_1_0/tmp/_cg +Encore.Project.ElaborationDir = C:/largework/fpga/hdmi/release1/release1.srcs/sources_1/ip/clk_wiz_v3_1_0/tmp/_cg +Encore.Project.TmpDir = C:/largework/fpga/hdmi/release1/release1.srcs/sources_1/ip/clk_wiz_v3_1_0/tmp/_cg +Encore.Project.Path = C:/largework/fpga/hdmi/release1/release1.srcs/sources_1/ip/clk_wiz_v3_1_0/tmp/_cg +Encore.Project.FlowVendor = Other +Encore.Project.VhdlSim = false +Encore.Project.VerilogSim = true +Encore.Project.XDevice = xc6slx9 +Encore.Project.XDeviceFamily = spartan6 +Encore.Project.XSpeedGrade = -2 +Encore.Project.XPackage = tqg144 + +c_use_clkout1_bar = 0 +c_use_clkout2_bar = 0 +c_use_clkout3_bar = 0 +c_use_clkout4_bar = 0 +component_name = clkgendcm_720p60hz +c_platform = nt64 +c_use_freq_synth = 1 +c_use_phase_alignment = 0 +c_use_min_o_jitter = 0 +c_use_max_i_jitter = 0 +c_use_dyn_phase_shift = 0 +c_use_inclk_switchover = 0 +c_use_dyn_reconfig = 0 +c_use_spread_spectrum = 0 +c_primtype_sel = DCM_CLKGEN +c_use_clk_valid = 0 +c_prim_in_freq = 26 +c_in_freq_units = Units_MHz +c_secondary_in_freq = 100.000 +c_feedback_source = FDBK_AUTO +c_prim_source = No_buffer +c_secondary_source = Single_ended_clock_capable_pin +c_clkfb_in_signaling = SINGLE +c_use_reset = 1 +c_use_locked = 1 +c_use_inclk_stopped = 0 +c_use_power_down = 0 +c_use_status = 0 +c_use_freeze = 0 +c_num_out_clks = 1 +c_clkout1_drives = BUFG +c_clkout2_drives = BUFG +c_clkout3_drives = BUFG +c_clkout4_drives = BUFG +c_clkout5_drives = BUFG +c_clkout6_drives = BUFG +c_clkout7_drives = BUFG +c_inclk_sum_row0 = Input Clock Input Freq (MHz) Input Jitter (UI) +c_inclk_sum_row1 = primary 26 0.010 +c_inclk_sum_row2 = no secondary input clock +c_outclk_sum_row0a = Output Output Phase Duty Cycle Pk-to-Pk Phase +c_outclk_sum_row0b = Clock Freq (MHz) (degrees) (%) Jitter (ps) Error (ps) +c_outclk_sum_row1 = CLK_OUT1 74.287 0.000 N/A 200.000 N/A +c_outclk_sum_row2 = no CLK_OUT2 output +c_outclk_sum_row3 = no CLK_OUT3 output +c_outclk_sum_row4 = no CLK_OUT4 output +c_outclk_sum_row5 = no CLK_OUT5 output +c_outclk_sum_row6 = no CLK_OUT6 output +c_outclk_sum_row7 = no CLK_OUT7 output +c_clkout1_requested_out_freq = 74.285 +c_clkout2_requested_out_freq = 100.000 +c_clkout3_requested_out_freq = 100.000 +c_clkout4_requested_out_freq = 100.000 +c_clkout5_requested_out_freq = 100.000 +c_clkout6_requested_out_freq = 100.000 +c_clkout7_requested_out_freq = 100.000 +c_clkout1_requested_phase = 0.000 +c_clkout2_requested_phase = 0.000 +c_clkout3_requested_phase = 0.000 +c_clkout4_requested_phase = 0.000 +c_clkout5_requested_phase = 0.000 +c_clkout6_requested_phase = 0.000 +c_clkout7_requested_phase = 0.000 +c_clkout1_requested_duty_cycle = 50.0 +c_clkout2_requested_duty_cycle = 50.0 +c_clkout3_requested_duty_cycle = 50.0 +c_clkout4_requested_duty_cycle = 50.0 +c_clkout5_requested_duty_cycle = 50.0 +c_clkout6_requested_duty_cycle = 50.0 +c_clkout7_requested_duty_cycle = 50.0 +c_clkout1_out_freq = 74.287 +c_clkout2_out_freq = N/A +c_clkout3_out_freq = N/A +c_clkout4_out_freq = N/A +c_clkout5_out_freq = N/A +c_clkout6_out_freq = N/A +c_clkout7_out_freq = N/A +c_clkout1_phase = 0.000 +c_clkout2_phase = N/A +c_clkout3_phase = N/A +c_clkout4_phase = N/A +c_clkout5_phase = N/A +c_clkout6_phase = N/A +c_clkout7_phase = N/A +c_clkout1_duty_cycle = N/A +c_clkout2_duty_cycle = N/A +c_clkout3_duty_cycle = N/A +c_clkout4_duty_cycle = N/A +c_clkout5_duty_cycle = N/A +c_clkout6_duty_cycle = N/A +c_clkout7_duty_cycle = N/A +c_mmcm_notes = None +c_mmcm_bandwidth = OPTIMIZED +c_mmcm_clkfbout_mult_f = 4.000 +c_mmcm_clkin1_period = 10.000 +c_mmcm_clkin2_period = 10.000 +c_mmcm_clkout4_cascade = FALSE +c_mmcm_clock_hold = FALSE +c_mmcm_compensation = ZHOLD +c_mmcm_divclk_divide = 1 +c_mmcm_ref_jitter1 = 0.010 +c_mmcm_ref_jitter2 = 0.010 +c_mmcm_startup_wait = FALSE +c_mmcm_clkout0_divide_f = 4.000 +c_mmcm_clkout1_divide = 1 +c_mmcm_clkout2_divide = 1 +c_mmcm_clkout3_divide = 1 +c_mmcm_clkout4_divide = 1 +c_mmcm_clkout5_divide = 1 +c_mmcm_clkout6_divide = 1 +c_mmcm_clkout0_duty_cycle = 0.500 +c_mmcm_clkout1_duty_cycle = 0.500 +c_mmcm_clkout2_duty_cycle = 0.500 +c_mmcm_clkout3_duty_cycle = 0.500 +c_mmcm_clkout4_duty_cycle = 0.500 +c_mmcm_clkout5_duty_cycle = 0.500 +c_mmcm_clkout6_duty_cycle = 0.500 +c_mmcm_clkfbout_phase = 0.000 +c_mmcm_clkout0_phase = 0.000 +c_mmcm_clkout1_phase = 0.000 +c_mmcm_clkout2_phase = 0.000 +c_mmcm_clkout3_phase = 0.000 +c_mmcm_clkout4_phase = 0.000 +c_mmcm_clkout5_phase = 0.000 +c_mmcm_clkout6_phase = 0.000 +c_mmcm_clkfbout_use_fine_ps = FALSE +c_mmcm_clkout0_use_fine_ps = FALSE +c_mmcm_clkout1_use_fine_ps = FALSE +c_mmcm_clkout2_use_fine_ps = FALSE +c_mmcm_clkout3_use_fine_ps = FALSE +c_mmcm_clkout4_use_fine_ps = FALSE +c_mmcm_clkout5_use_fine_ps = FALSE +c_mmcm_clkout6_use_fine_ps = FALSE +c_pll_notes = None +c_pll_bandwidth = OPTIMIZED +c_pll_clk_feedback = CLKFBOUT +c_pll_clkfbout_mult = 4 +c_pll_clkin_period = 10.000 +c_pll_compensation = INTERNAL +c_pll_divclk_divide = 1 +c_pll_ref_jitter = 0.010 +c_pll_clkout0_divide = 1 +c_pll_clkout1_divide = 1 +c_pll_clkout2_divide = 1 +c_pll_clkout3_divide = 1 +c_pll_clkout4_divide = 1 +c_pll_clkout5_divide = 1 +c_pll_clkout0_duty_cycle = 0.500 +c_pll_clkout1_duty_cycle = 0.500 +c_pll_clkout2_duty_cycle = 0.500 +c_pll_clkout3_duty_cycle = 0.500 +c_pll_clkout4_duty_cycle = 0.500 +c_pll_clkout5_duty_cycle = 0.500 +c_pll_clkfbout_phase = 0.000 +c_pll_clkout0_phase = 0.000 +c_pll_clkout1_phase = 0.000 +c_pll_clkout2_phase = 0.000 +c_pll_clkout3_phase = 0.000 +c_pll_clkout4_phase = 0.000 +c_pll_clkout5_phase = 0.000 +c_dcm_notes = None +c_dcm_clkdv_divide = 2.000 +c_dcm_clkfx_divide = 1 +c_dcm_clkfx_multiply = 4 +c_dcm_clkin_divide_by_2 = FALSE +c_dcm_clkin_period = 10.000 +c_dcm_clkout_phase_shift = NONE +c_dcm_clk_feedback = 1X +c_dcm_clk_feedback_port = CLKOUT1 +c_dcm_deskew_adjust = SYSTEM_SYNCHRONOUS +c_dcm_phase_shift = 0 +c_dcm_startup_wait = FALSE +c_dcm_clk_out1_port = CLK0 +c_dcm_clk_out2_port = NONE +c_dcm_clk_out3_port = NONE +c_dcm_clk_out4_port = NONE +c_dcm_clk_out5_port = NONE +c_dcm_clk_out6_port = NONE +c_dcm_clkgen_notes = 720p60hzclocksource +c_dcm_clkgen_clkfxdv_divide = 2 +c_dcm_clkgen_clkfx_divide = 7 +c_dcm_clkgen_clkfx_multiply = 20 +c_dcm_clkgen_dfs_bandwidth = OPTIMIZED +c_dcm_clkgen_prog_md_bandwidth = OPTIMIZED +c_dcm_clkgen_clkin_period = 38.461 +c_dcm_clkgen_clkfx_md_max = 0.000 +c_dcm_clkgen_spread_spectrum = NONE +c_dcm_clkgen_startup_wait = FALSE +c_dcm_clkgen_clk_out1_port = CLKFX +c_dcm_clkgen_clk_out2_port = NONE +c_dcm_clkgen_clk_out3_port = NONE +c_clock_mgr_type = MANUAL +c_override_mmcm = 0 +c_override_pll = 0 +c_override_dcm = 0 +c_override_dcm_clkgen = 1 +c_dcm_pll_cascade = NONE +c_primary_port = CLK_IN1 +c_secondary_port = CLK_IN2 +c_clk_out1_port = CLK_OUT1 +c_clk_out2_port = CLK_OUT2 +c_clk_out3_port = CLK_OUT3 +c_clk_out4_port = CLK_OUT4 +c_clk_out5_port = CLK_OUT5 +c_clk_out6_port = CLK_OUT6 +c_clk_out7_port = CLK_OUT7 +c_reset_port = RESET +c_locked_port = LOCKED +c_clkfb_in_port = CLKFB_IN +c_clkfb_in_p_port = CLKFB_IN_P +c_clkfb_in_n_port = CLKFB_IN_N +c_clkfb_out_port = CLKFB_OUT +c_clkfb_out_p_port = CLKFB_OUT_P +c_clkfb_out_n_port = CLKFB_OUT_N +c_power_down_port = POWER_DOWN +c_daddr_port = DADDR +c_dclk_port = DCLK +c_drdy_port = DRDY +c_dwe_port = DWE +c_din_port = DIN +c_dout_port = DOUT +c_den_port = DEN +c_psclk_port = PSCLK +c_psen_port = PSEN +c_psincdec_port = PSINCDEC +c_psdone_port = PSDONE +c_clk_valid_port = CLK_VALID +c_status_port = STATUS +c_clk_in_sel_port = CLK_IN_SEL +c_input_clk_stopped_port = INPUT_CLK_STOPPED +c_clkin1_jitter_ps = 384.61 +c_clkin2_jitter_ps = 100.0 +ComponentName = clkgendcm_720p60hz diff --git a/ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.gise b/ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.gise new file mode 100755 index 0000000..885900b --- /dev/null +++ b/ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.gise @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + 11.1 + + + + + + + + + + + diff --git a/ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.v b/ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.v new file mode 100755 index 0000000..ba2ad2f --- /dev/null +++ b/ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.v @@ -0,0 +1,133 @@ +// file: clkgendcm_720p60hz.v +// +// (c) Copyright 2008 - 2011 Xilinx, Inc. All rights reserved. +// +// This file contains confidential and proprietary information +// of Xilinx, Inc. and is protected under U.S. and +// international copyright and other intellectual property +// laws. +// +// DISCLAIMER +// This disclaimer is not a license and does not grant any +// rights to the materials distributed herewith. Except as +// otherwise provided in a valid license issued to you by +// Xilinx, and to the maximum extent permitted by applicable +// law: (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND +// WITH ALL FAULTS, AND XILINX HEREBY DISCLAIMS ALL WARRANTIES +// AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING +// BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON- +// INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE; and +// (2) Xilinx shall not be liable (whether in contract or tort, +// including negligence, or under any other theory of +// liability) for any loss or damage of any kind or nature +// related to, arising under or in connection with these +// materials, including for any direct, or any indirect, +// special, incidental, or consequential loss or damage +// (including loss of data, profits, goodwill, or any type of +// loss or damage suffered as a result of any action brought +// by a third party) even if such damage or loss was +// reasonably foreseeable or Xilinx had been advised of the +// possibility of the same. +// +// CRITICAL APPLICATIONS +// Xilinx products are not designed or intended to be fail- +// safe, or for use in any application requiring fail-safe +// performance, such as life-support or safety devices or +// systems, Class III medical devices, nuclear facilities, +// applications related to the deployment of airbags, or any +// other applications that could lead to death, personal +// injury, or severe property or environmental damage +// (individually and collectively, "Critical +// Applications"). Customer assumes the sole risk and +// liability of any use of Xilinx products in Critical +// Applications, subject only to applicable laws and +// regulations governing limitations on product liability. +// +// THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS +// PART OF THIS FILE AT ALL TIMES. +// +//---------------------------------------------------------------------------- +// User entered comments +//---------------------------------------------------------------------------- +// 720p60hzclocksource +// +//---------------------------------------------------------------------------- +// Output Output Phase Duty Cycle Pk-to-Pk Phase +// Clock Freq (MHz) (degrees) (%) Jitter (ps) Error (ps) +//---------------------------------------------------------------------------- +// CLK_OUT1 74.287 0.000 N/A 200.000 N/A +// +//---------------------------------------------------------------------------- +// Input Clock Input Freq (MHz) Input Jitter (UI) +//---------------------------------------------------------------------------- +// primary 26 0.010 + +`timescale 1ps/1ps + +(* CORE_GENERATION_INFO = "clkgendcm_720p60hz,clk_wiz_v3_1,{component_name=clkgendcm_720p60hz,use_phase_alignment=false,use_min_o_jitter=false,use_max_i_jitter=false,use_dyn_phase_shift=false,use_inclk_switchover=false,use_dyn_reconfig=false,feedback_source=FDBK_AUTO,primtype_sel=DCM_CLKGEN,num_out_clk=1,clkin1_period=38.461,clkin2_period=38.461,use_power_down=false,use_reset=true,use_locked=true,use_inclk_stopped=false,use_status=false,use_freeze=false,use_clk_valid=false,feedback_type=SINGLE,clock_mgr_type=MANUAL,manual_override=true}" *) +module clkgendcm_720p60hz + (// Clock in ports + input CLK_IN1, + // Clock out ports + output CLK_OUT1, + // Status and control signals + input RESET, + output LOCKED + ); + + // Input buffering + //------------------------------------ + assign clkin1 = CLK_IN1; + + + // Clocking primitive + //------------------------------------ + // Instantiation of the DCM primitive + // * Unused inputs are tied off + // * Unused outputs are labeled unused + wire psdone_unused; + wire locked_int; + wire [2:1] status_int; + wire clkfx; + wire clkfx180_unused; + wire clkfxdv_unused; + + DCM_CLKGEN + #(.CLKFXDV_DIVIDE (2), + .CLKFX_DIVIDE (7), + .CLKFX_MULTIPLY (20), + .SPREAD_SPECTRUM ("NONE"), + .STARTUP_WAIT ("FALSE"), + .CLKIN_PERIOD (38.461), + .CLKFX_MD_MAX (2.8571)) + dcm_clkgen_inst + // Input clock + (.CLKIN (clkin1), + // Output clocks + .CLKFX (clkfx), + .CLKFX180 (clkfx180_unused), + .CLKFXDV (clkfxdv_unused), + // Ports for dynamic reconfiguration + .PROGCLK (1'b0), + .PROGDATA (PROGDATA), + .PROGEN (PROGEN), + .PROGDONE (progdone_unused), + // Other control and status signals + .FREEZEDCM (1'b0), + .LOCKED (locked_int), + .STATUS (status_int), + .RST (RESET)); + + assign LOCKED = locked_int; + + // Output buffering + //----------------------------------- + + BUFG clkout1_buf + (.O (CLK_OUT1), + .I (clkfx)); + + + + +endmodule diff --git a/ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.veo b/ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.veo new file mode 100755 index 0000000..ad61554 --- /dev/null +++ b/ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.veo @@ -0,0 +1,78 @@ +// +// (c) Copyright 2008 - 2011 Xilinx, Inc. All rights reserved. +// +// This file contains confidential and proprietary information +// of Xilinx, Inc. and is protected under U.S. and +// international copyright and other intellectual property +// laws. +// +// DISCLAIMER +// This disclaimer is not a license and does not grant any +// rights to the materials distributed herewith. Except as +// otherwise provided in a valid license issued to you by +// Xilinx, and to the maximum extent permitted by applicable +// law: (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND +// WITH ALL FAULTS, AND XILINX HEREBY DISCLAIMS ALL WARRANTIES +// AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING +// BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON- +// INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE; and +// (2) Xilinx shall not be liable (whether in contract or tort, +// including negligence, or under any other theory of +// liability) for any loss or damage of any kind or nature +// related to, arising under or in connection with these +// materials, including for any direct, or any indirect, +// special, incidental, or consequential loss or damage +// (including loss of data, profits, goodwill, or any type of +// loss or damage suffered as a result of any action brought +// by a third party) even if such damage or loss was +// reasonably foreseeable or Xilinx had been advised of the +// possibility of the same. +// +// CRITICAL APPLICATIONS +// Xilinx products are not designed or intended to be fail- +// safe, or for use in any application requiring fail-safe +// performance, such as life-support or safety devices or +// systems, Class III medical devices, nuclear facilities, +// applications related to the deployment of airbags, or any +// other applications that could lead to death, personal +// injury, or severe property or environmental damage +// (individually and collectively, "Critical +// Applications"). Customer assumes the sole risk and +// liability of any use of Xilinx products in Critical +// Applications, subject only to applicable laws and +// regulations governing limitations on product liability. +// +// THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS +// PART OF THIS FILE AT ALL TIMES. +// +//---------------------------------------------------------------------------- +// User entered comments +//---------------------------------------------------------------------------- +// 720p60hzclocksource +// +//---------------------------------------------------------------------------- +// Output Output Phase Duty Cycle Pk-to-Pk Phase +// Clock Freq (MHz) (degrees) (%) Jitter (ps) Error (ps) +//---------------------------------------------------------------------------- +// CLK_OUT1 74.287 0.000 N/A 200.000 N/A +// +//---------------------------------------------------------------------------- +// Input Clock Input Freq (MHz) Input Jitter (UI) +//---------------------------------------------------------------------------- +// primary 26 0.010 + +// The following must be inserted into your Verilog file for this +// core to be instantiated. Change the instance name and port connections +// (in parentheses) to your own signal names. + +//----------- Begin Cut here for INSTANTIATION Template ---// INST_TAG + + clkgendcm_720p60hz instance_name + (// Clock in ports + .CLK_IN1(CLK_IN1), // IN + // Clock out ports + .CLK_OUT1(CLK_OUT1), // OUT + // Status and control signals + .RESET(RESET),// IN + .LOCKED(LOCKED)); // OUT +// INST_TAG_END ------ End INSTANTIATION Template --------- diff --git a/ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.v~ b/ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.v~ new file mode 100755 index 0000000..d6fa44b --- /dev/null +++ b/ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.v~ @@ -0,0 +1,133 @@ +// file: clkgendcm_720p60hz.v +// +// (c) Copyright 2008 - 2011 Xilinx, Inc. All rights reserved. +// +// This file contains confidential and proprietary information +// of Xilinx, Inc. and is protected under U.S. and +// international copyright and other intellectual property +// laws. +// +// DISCLAIMER +// This disclaimer is not a license and does not grant any +// rights to the materials distributed herewith. Except as +// otherwise provided in a valid license issued to you by +// Xilinx, and to the maximum extent permitted by applicable +// law: (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND +// WITH ALL FAULTS, AND XILINX HEREBY DISCLAIMS ALL WARRANTIES +// AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING +// BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON- +// INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE; and +// (2) Xilinx shall not be liable (whether in contract or tort, +// including negligence, or under any other theory of +// liability) for any loss or damage of any kind or nature +// related to, arising under or in connection with these +// materials, including for any direct, or any indirect, +// special, incidental, or consequential loss or damage +// (including loss of data, profits, goodwill, or any type of +// loss or damage suffered as a result of any action brought +// by a third party) even if such damage or loss was +// reasonably foreseeable or Xilinx had been advised of the +// possibility of the same. +// +// CRITICAL APPLICATIONS +// Xilinx products are not designed or intended to be fail- +// safe, or for use in any application requiring fail-safe +// performance, such as life-support or safety devices or +// systems, Class III medical devices, nuclear facilities, +// applications related to the deployment of airbags, or any +// other applications that could lead to death, personal +// injury, or severe property or environmental damage +// (individually and collectively, "Critical +// Applications"). Customer assumes the sole risk and +// liability of any use of Xilinx products in Critical +// Applications, subject only to applicable laws and +// regulations governing limitations on product liability. +// +// THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS +// PART OF THIS FILE AT ALL TIMES. +// +//---------------------------------------------------------------------------- +// User entered comments +//---------------------------------------------------------------------------- +// 720p60hzclocksource +// +//---------------------------------------------------------------------------- +// Output Output Phase Duty Cycle Pk-to-Pk Phase +// Clock Freq (MHz) (degrees) (%) Jitter (ps) Error (ps) +//---------------------------------------------------------------------------- +// CLK_OUT1 74.287 0.000 N/A 200.000 N/A +// +//---------------------------------------------------------------------------- +// Input Clock Input Freq (MHz) Input Jitter (UI) +//---------------------------------------------------------------------------- +// primary 26 0.010 + +`timescale 1ps/1ps + +(* CORE_GENERATION_INFO = "clkgendcm_720p60hz,clk_wiz_v3_1,{component_name=clkgendcm_720p60hz,use_phase_alignment=false,use_min_o_jitter=false,use_max_i_jitter=false,use_dyn_phase_shift=false,use_inclk_switchover=false,use_dyn_reconfig=false,feedback_source=FDBK_AUTO,primtype_sel=DCM_CLKGEN,num_out_clk=1,clkin1_period=38.461,clkin2_period=38.461,use_power_down=false,use_reset=true,use_locked=true,use_inclk_stopped=false,use_status=false,use_freeze=false,use_clk_valid=false,feedback_type=SINGLE,clock_mgr_type=MANUAL,manual_override=true}" *) +module clkgendcm_720p60hz + (// Clock in ports + input CLK_IN1, + // Clock out ports + output CLK_OUT1, + // Status and control signals + input RESET, + output LOCKED + ); + + // Input buffering + //------------------------------------ + assign clkin1 = CLK_IN1; + + + // Clocking primitive + //------------------------------------ + // Instantiation of the DCM primitive + // * Unused inputs are tied off + // * Unused outputs are labeled unused + wire psdone_unused; + wire locked_int; + wire [2:1] status_int; + wire clkfx; + wire clkfx180_unused; + wire clkfxdv_unused; + + DCM_CLKGEN + #(.CLKFXDV_DIVIDE (2), + .CLKFX_DIVIDE (7), + .CLKFX_MULTIPLY (20), + .SPREAD_SPECTRUM ("NONE"), + .STARTUP_WAIT ("FALSE"), + .CLKIN_PERIOD (38.461), + .CLKFX_MD_MAX (0.000)) + dcm_clkgen_inst + // Input clock + (.CLKIN (clkin1), + // Output clocks + .CLKFX (clkfx), + .CLKFX180 (clkfx180_unused), + .CLKFXDV (clkfxdv_unused), + // Ports for dynamic reconfiguration + .PROGCLK (1'b0), + .PROGDATA (PROGDATA), + .PROGEN (PROGEN), + .PROGDONE (progdone_unused), + // Other control and status signals + .FREEZEDCM (1'b0), + .LOCKED (locked_int), + .STATUS (status_int), + .RST (RESET)); + + assign LOCKED = locked_int; + + // Output buffering + //----------------------------------- + + BUFG clkout1_buf + (.O (CLK_OUT1), + .I (clkfx)); + + + + +endmodule diff --git a/ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.xco b/ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.xco new file mode 100755 index 0000000..5d38e7f --- /dev/null +++ b/ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.xco @@ -0,0 +1,256 @@ +############################################################## +# +# Xilinx Core Generator version 13.1 +# Date: Tue Aug 09 21:40:17 2011 +# +############################################################## +# +# This file contains the customisation parameters for a +# Xilinx CORE Generator IP GUI. It is strongly recommended +# that you do not manually alter this file as it may cause +# unexpected and unsupported behavior. +# +############################################################## +# +# BEGIN Project Options +SET addpads = false +SET asysymbol = true +SET busformat = BusFormatAngleBracketNotRipped +SET createndf = false +SET designentry = Verilog +SET device = xc6slx9 +SET devicefamily = spartan6 +SET flowvendor = Other +SET formalverification = false +SET foundationsym = false +SET implementationfiletype = Ngc +SET package = tqg144 +SET removerpms = false +SET simulationfiles = Behavioral +SET speedgrade = -2 +SET verilogsim = true +SET vhdlsim = false +# END Project Options +# BEGIN Select +SELECT Clocking_Wizard family Xilinx,_Inc. 3.1 +# END Select +# BEGIN Parameters +CSET calc_done=DONE +CSET clk_in_sel_port=CLK_IN_SEL +CSET clk_out1_port=CLK_OUT1 +CSET clk_out1_use_fine_ps_gui=false +CSET clk_out2_port=CLK_OUT2 +CSET clk_out2_use_fine_ps_gui=false +CSET clk_out3_port=CLK_OUT3 +CSET clk_out3_use_fine_ps_gui=false +CSET clk_out4_port=CLK_OUT4 +CSET clk_out4_use_fine_ps_gui=false +CSET clk_out5_port=CLK_OUT5 +CSET clk_out5_use_fine_ps_gui=false +CSET clk_out6_port=CLK_OUT6 +CSET clk_out6_use_fine_ps_gui=false +CSET clk_out7_port=CLK_OUT7 +CSET clk_out7_use_fine_ps_gui=false +CSET clk_valid_port=CLK_VALID +CSET clkfb_in_n_port=CLKFB_IN_N +CSET clkfb_in_p_port=CLKFB_IN_P +CSET clkfb_in_port=CLKFB_IN +CSET clkfb_in_signaling=SINGLE +CSET clkfb_out_n_port=CLKFB_OUT_N +CSET clkfb_out_p_port=CLKFB_OUT_P +CSET clkfb_out_port=CLKFB_OUT +CSET clkin1_jitter_ps=384.61 +CSET clkin1_ui_jitter=0.010 +CSET clkin2_jitter_ps=100.0 +CSET clkin2_ui_jitter=0.010 +CSET clkout1_drives=BUFG +CSET clkout1_requested_duty_cycle=50.0 +CSET clkout1_requested_out_freq=74.285 +CSET clkout1_requested_phase=0.000 +CSET clkout2_drives=BUFG +CSET clkout2_requested_duty_cycle=50.0 +CSET clkout2_requested_out_freq=100.000 +CSET clkout2_requested_phase=0.000 +CSET clkout2_used=false +CSET clkout3_drives=BUFG +CSET clkout3_requested_duty_cycle=50.0 +CSET clkout3_requested_out_freq=100.000 +CSET clkout3_requested_phase=0.000 +CSET clkout3_used=false +CSET clkout4_drives=BUFG +CSET clkout4_requested_duty_cycle=50.0 +CSET clkout4_requested_out_freq=100.000 +CSET clkout4_requested_phase=0.000 +CSET clkout4_used=false +CSET clkout5_drives=BUFG +CSET clkout5_requested_duty_cycle=50.0 +CSET clkout5_requested_out_freq=100.000 +CSET clkout5_requested_phase=0.000 +CSET clkout5_used=false +CSET clkout6_drives=BUFG +CSET clkout6_requested_duty_cycle=50.0 +CSET clkout6_requested_out_freq=100.000 +CSET clkout6_requested_phase=0.000 +CSET clkout6_used=false +CSET clkout7_drives=BUFG +CSET clkout7_requested_duty_cycle=50.0 +CSET clkout7_requested_out_freq=100.000 +CSET clkout7_requested_phase=0.000 +CSET clkout7_used=false +CSET clock_mgr_type=MANUAL +CSET component_name=clkgendcm_720p60hz +CSET daddr_port=DADDR +CSET dclk_port=DCLK +CSET dcm_clk_feedback=1X +CSET dcm_clk_out1_port=CLK0 +CSET dcm_clk_out2_port=CLK0 +CSET dcm_clk_out3_port=CLK0 +CSET dcm_clk_out4_port=CLK0 +CSET dcm_clk_out5_port=CLK0 +CSET dcm_clk_out6_port=CLK0 +CSET dcm_clkdv_divide=2.0 +CSET dcm_clkfx_divide=1 +CSET dcm_clkfx_multiply=4 +CSET dcm_clkgen_clk_out1_port=CLKFX +CSET dcm_clkgen_clk_out2_port=CLKFX +CSET dcm_clkgen_clk_out3_port=CLKFX +CSET dcm_clkgen_clkfx_divide=7 +CSET dcm_clkgen_clkfx_md_max=0 +CSET dcm_clkgen_clkfx_multiply=20 +CSET dcm_clkgen_clkfxdv_divide=2 +CSET dcm_clkgen_clkin_period=38.461 +CSET dcm_clkgen_notes=720p60hzclocksource +CSET dcm_clkgen_spread_spectrum=NONE +CSET dcm_clkgen_startup_wait=false +CSET dcm_clkin_divide_by_2=false +CSET dcm_clkin_period=10.000 +CSET dcm_clkout_phase_shift=NONE +CSET dcm_deskew_adjust=SYSTEM_SYNCHRONOUS +CSET dcm_notes=None +CSET dcm_phase_shift=0 +CSET dcm_pll_cascade=NONE +CSET dcm_startup_wait=false +CSET den_port=DEN +CSET din_port=DIN +CSET dout_port=DOUT +CSET drdy_port=DRDY +CSET dwe_port=DWE +CSET feedback_source=FDBK_AUTO +CSET in_freq_units=Units_MHz +CSET in_jitter_units=Units_UI +CSET input_clk_stopped_port=INPUT_CLK_STOPPED +CSET jitter_options=UI +CSET jitter_sel=No_Jitter +CSET locked_port=LOCKED +CSET mmcm_bandwidth=OPTIMIZED +CSET mmcm_clkfbout_mult_f=4.000 +CSET mmcm_clkfbout_phase=0.000 +CSET mmcm_clkfbout_use_fine_ps=false +CSET mmcm_clkin1_period=10.000 +CSET mmcm_clkin2_period=10.000 +CSET mmcm_clkout0_divide_f=4.000 +CSET mmcm_clkout0_duty_cycle=0.500 +CSET mmcm_clkout0_phase=0.000 +CSET mmcm_clkout0_use_fine_ps=false +CSET mmcm_clkout1_divide=1 +CSET mmcm_clkout1_duty_cycle=0.500 +CSET mmcm_clkout1_phase=0.000 +CSET mmcm_clkout1_use_fine_ps=false +CSET mmcm_clkout2_divide=1 +CSET mmcm_clkout2_duty_cycle=0.500 +CSET mmcm_clkout2_phase=0.000 +CSET mmcm_clkout2_use_fine_ps=false +CSET mmcm_clkout3_divide=1 +CSET mmcm_clkout3_duty_cycle=0.500 +CSET mmcm_clkout3_phase=0.000 +CSET mmcm_clkout3_use_fine_ps=false +CSET mmcm_clkout4_cascade=false +CSET mmcm_clkout4_divide=1 +CSET mmcm_clkout4_duty_cycle=0.500 +CSET mmcm_clkout4_phase=0.000 +CSET mmcm_clkout4_use_fine_ps=false +CSET mmcm_clkout5_divide=1 +CSET mmcm_clkout5_duty_cycle=0.500 +CSET mmcm_clkout5_phase=0.000 +CSET mmcm_clkout5_use_fine_ps=false +CSET mmcm_clkout6_divide=1 +CSET mmcm_clkout6_duty_cycle=0.500 +CSET mmcm_clkout6_phase=0.000 +CSET mmcm_clkout6_use_fine_ps=false +CSET mmcm_clock_hold=false +CSET mmcm_compensation=ZHOLD +CSET mmcm_divclk_divide=1 +CSET mmcm_notes=None +CSET mmcm_ref_jitter1=0.010 +CSET mmcm_ref_jitter2=0.010 +CSET mmcm_startup_wait=false +CSET num_out_clks=1 +CSET override_dcm=false +CSET override_dcm_clkgen=true +CSET override_mmcm=false +CSET override_pll=false +CSET platform=nt64 +CSET pll_bandwidth=OPTIMIZED +CSET pll_clk_feedback=CLKFBOUT +CSET pll_clkfbout_mult=4 +CSET pll_clkfbout_phase=0.000 +CSET pll_clkin_period=10.000 +CSET pll_clkout0_divide=1 +CSET pll_clkout0_duty_cycle=0.500 +CSET pll_clkout0_phase=0.000 +CSET pll_clkout1_divide=1 +CSET pll_clkout1_duty_cycle=0.500 +CSET pll_clkout1_phase=0.000 +CSET pll_clkout2_divide=1 +CSET pll_clkout2_duty_cycle=0.500 +CSET pll_clkout2_phase=0.000 +CSET pll_clkout3_divide=1 +CSET pll_clkout3_duty_cycle=0.500 +CSET pll_clkout3_phase=0.000 +CSET pll_clkout4_divide=1 +CSET pll_clkout4_duty_cycle=0.500 +CSET pll_clkout4_phase=0.000 +CSET pll_clkout5_divide=1 +CSET pll_clkout5_duty_cycle=0.500 +CSET pll_clkout5_phase=0.000 +CSET pll_compensation=INTERNAL +CSET pll_divclk_divide=1 +CSET pll_notes=None +CSET pll_ref_jitter=0.010 +CSET power_down_port=POWER_DOWN +CSET prim_in_freq=26 +CSET prim_in_jitter=0.010 +CSET prim_source=No_buffer +CSET primary_port=CLK_IN1 +CSET primtype_sel=DCM_CLKGEN +CSET psclk_port=PSCLK +CSET psdone_port=PSDONE +CSET psen_port=PSEN +CSET psincdec_port=PSINCDEC +CSET relative_inclk=REL_PRIMARY +CSET reset_port=RESET +CSET secondary_in_freq=100.000 +CSET secondary_in_jitter=0.010 +CSET secondary_port=CLK_IN2 +CSET secondary_source=Single_ended_clock_capable_pin +CSET status_port=STATUS +CSET summary_strings=empty +CSET use_clk_valid=false +CSET use_dyn_phase_shift=false +CSET use_dyn_reconfig=false +CSET use_freeze=false +CSET use_freq_synth=true +CSET use_inclk_stopped=false +CSET use_inclk_switchover=false +CSET use_locked=true +CSET use_max_i_jitter=false +CSET use_min_o_jitter=false +CSET use_min_power=false +CSET use_phase_alignment=false +CSET use_power_down=false +CSET use_reset=true +CSET use_spread_spectrum=false +CSET use_status=false +# END Parameters +GENERATE +# CRC: fcd26fcc diff --git a/ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.xise b/ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.xise new file mode 100755 index 0000000..38cc24b --- /dev/null +++ b/ip/clk_wiz_v3_1_0/clkgendcm_720p60hz.xise @@ -0,0 +1,405 @@ + + + +
+ + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/ip/clk_wiz_v3_1_0/clkgendcm_720p60hz_flist.txt b/ip/clk_wiz_v3_1_0/clkgendcm_720p60hz_flist.txt new file mode 100755 index 0000000..2a77c03 --- /dev/null +++ b/ip/clk_wiz_v3_1_0/clkgendcm_720p60hz_flist.txt @@ -0,0 +1,33 @@ +# Output products list for +clkgendcm_720p60hz\clk_wiz_readme.txt +clkgendcm_720p60hz\clkgendcm_720p60hz.ucf +clkgendcm_720p60hz\doc\clk_wiz_ds709.pdf +clkgendcm_720p60hz\doc\clk_wiz_gsg521.pdf +clkgendcm_720p60hz\example_design\clkgendcm_720p60hz_exdes.v +clkgendcm_720p60hz\implement\implement.bat +clkgendcm_720p60hz\implement\implement.sh +clkgendcm_720p60hz\implement\planAhead_ise.bat +clkgendcm_720p60hz\implement\planAhead_ise.sh +clkgendcm_720p60hz\implement\planAhead_ise.tcl +clkgendcm_720p60hz\implement\xst.prj +clkgendcm_720p60hz\implement\xst.scr +clkgendcm_720p60hz\simulation\clkgendcm_720p60hz_tb.v +clkgendcm_720p60hz\simulation\functional\simcmds.tcl +clkgendcm_720p60hz\simulation\functional\simulate_isim.bat +clkgendcm_720p60hz\simulation\functional\simulate_isim.sh +clkgendcm_720p60hz\simulation\functional\simulate_mti.do +clkgendcm_720p60hz\simulation\functional\simulate_ncsim.sh +clkgendcm_720p60hz\simulation\functional\simulate_vcs.sh +clkgendcm_720p60hz\simulation\functional\ucli_commands.key +clkgendcm_720p60hz\simulation\functional\vcs_session.tcl +clkgendcm_720p60hz\simulation\functional\wave.do +clkgendcm_720p60hz\simulation\functional\wave.sv +clkgendcm_720p60hz.asy +clkgendcm_720p60hz.ejp +clkgendcm_720p60hz.gise +clkgendcm_720p60hz.v +clkgendcm_720p60hz.veo +clkgendcm_720p60hz.xco +clkgendcm_720p60hz.xise +clkgendcm_720p60hz_flist.txt +clkgendcm_720p60hz_xmdf.tcl diff --git a/ip/clk_wiz_v3_1_0/clkgendcm_720p60hz_xmdf.tcl b/ip/clk_wiz_v3_1_0/clkgendcm_720p60hz_xmdf.tcl new file mode 100755 index 0000000..6001376 --- /dev/null +++ b/ip/clk_wiz_v3_1_0/clkgendcm_720p60hz_xmdf.tcl @@ -0,0 +1,144 @@ +# The package naming convention is _xmdf +package provide clkgendcm_720p60hz_xmdf 1.0 + +# This includes some utilities that support common XMDF operations +package require utilities_xmdf + +# Define a namespace for this package. The name of the name space +# is _xmdf +namespace eval ::clkgendcm_720p60hz_xmdf { +# Use this to define any statics +} + +# Function called by client to rebuild the params and port arrays +# Optional when the use context does not require the param or ports +# arrays to be available. +proc ::clkgendcm_720p60hz_xmdf::xmdfInit { instance } { +# Variable containg name of library into which module is compiled +# Recommendation: +# Required +utilities_xmdf::xmdfSetData $instance Module Attributes Name clkgendcm_720p60hz +} +# ::clkgendcm_720p60hz_xmdf::xmdfInit + +# Function called by client to fill in all the xmdf* data variables +# based on the current settings of the parameters +proc ::clkgendcm_720p60hz_xmdf::xmdfApplyParams { instance } { + +set fcount 0 +# Array containing libraries that are assumed to exist +# Examples include unisim and xilinxcorelib +# Optional +# In this example, we assume that the unisim library will +# be magically +# available to the simulation and synthesis tool +utilities_xmdf::xmdfSetData $instance FileSet $fcount type logical_library +utilities_xmdf::xmdfSetData $instance FileSet $fcount logical_library unisim +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path clkgendcm_720p60hz/clk_wiz_readme.txt +utilities_xmdf::xmdfSetData $instance FileSet $fcount type Ignore +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path clkgendcm_720p60hz/clkgendcm_720p60hz.ucf +utilities_xmdf::xmdfSetData $instance FileSet $fcount type ucf +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path clkgendcm_720p60hz/doc/clk_wiz_ds709.pdf +utilities_xmdf::xmdfSetData $instance FileSet $fcount type Ignore +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path clkgendcm_720p60hz/doc/clk_wiz_gsg521.pdf +utilities_xmdf::xmdfSetData $instance FileSet $fcount type Ignore +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path clkgendcm_720p60hz/example_design/clkgendcm_720p60hz_exdes.v +utilities_xmdf::xmdfSetData $instance FileSet $fcount type verilog +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path clkgendcm_720p60hz/implement/implement.bat +utilities_xmdf::xmdfSetData $instance FileSet $fcount type Ignore +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path clkgendcm_720p60hz/implement/implement.sh +utilities_xmdf::xmdfSetData $instance FileSet $fcount type Ignore +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path clkgendcm_720p60hz/implement/xst.prj +utilities_xmdf::xmdfSetData $instance FileSet $fcount type Ignore +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path clkgendcm_720p60hz/implement/xst.scr +utilities_xmdf::xmdfSetData $instance FileSet $fcount type Ignore +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path clkgendcm_720p60hz/simulation/clkgendcm_720p60hz_tb.v +utilities_xmdf::xmdfSetData $instance FileSet $fcount type Ignore +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path clkgendcm_720p60hz/simulation/functional/simcmds.tcl +utilities_xmdf::xmdfSetData $instance FileSet $fcount type Ignore +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path clkgendcm_720p60hz/simulation/functional/simulate_isim.sh +utilities_xmdf::xmdfSetData $instance FileSet $fcount type Ignore +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path clkgendcm_720p60hz/simulation/functional/simulate_mti.do +utilities_xmdf::xmdfSetData $instance FileSet $fcount type Ignore +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path clkgendcm_720p60hz/simulation/functional/simulate_ncsim.sh +utilities_xmdf::xmdfSetData $instance FileSet $fcount type Ignore +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path clkgendcm_720p60hz/simulation/functional/simulate_vcs.sh +utilities_xmdf::xmdfSetData $instance FileSet $fcount type Ignore +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path clkgendcm_720p60hz/simulation/functional/ucli_commands.key +utilities_xmdf::xmdfSetData $instance FileSet $fcount type Ignore +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path clkgendcm_720p60hz/simulation/functional/vcs_session.tcl +utilities_xmdf::xmdfSetData $instance FileSet $fcount type Ignore +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path clkgendcm_720p60hz/simulation/functional/wave.do +utilities_xmdf::xmdfSetData $instance FileSet $fcount type Ignore +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path clkgendcm_720p60hz/simulation/functional/wave.sv +utilities_xmdf::xmdfSetData $instance FileSet $fcount type Ignore +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path clkgendcm_720p60hz.asy +utilities_xmdf::xmdfSetData $instance FileSet $fcount type asy +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path clkgendcm_720p60hz.ejp +utilities_xmdf::xmdfSetData $instance FileSet $fcount type AnyView +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path clkgendcm_720p60hz.v +utilities_xmdf::xmdfSetData $instance FileSet $fcount type verilog +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path clkgendcm_720p60hz.veo +utilities_xmdf::xmdfSetData $instance FileSet $fcount type verilog_template +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path clkgendcm_720p60hz.xco +utilities_xmdf::xmdfSetData $instance FileSet $fcount type coregen_ip +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path clkgendcm_720p60hz_xmdf.tcl +utilities_xmdf::xmdfSetData $instance FileSet $fcount type AnyView +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount associated_module clkgendcm_720p60hz +incr fcount + +} + +# ::gen_comp_name_xmdf::xmdfApplyParams diff --git a/ip/clk_wiz_v3_1_0/coregen.cgc b/ip/clk_wiz_v3_1_0/coregen.cgc new file mode 100755 index 0000000..0e18e96 --- /dev/null +++ b/ip/clk_wiz_v3_1_0/coregen.cgc @@ -0,0 +1,860 @@ + + + xilinx.com + projects + coregen + 1.0 + + + clkgendcm_720p60hz + Generated by PlanAhead + + + clkgendcm_720p60hz + 1 + 100.000 + false + false + PSEN + BUFG + 7 + 38.461 + false + 1 + LOCKED + false + 0.500 + CLK_IN_SEL + CLKFX + 0.000 + false + empty + true + 0.010 + 50.0 + 20 + 0.000 + false + Single_ended_clock_capable_pin + false + 74.285 + CLKFX + INPUT_CLK_STOPPED + 0.500 + OPTIMIZED + 10.000 + false + false + 0.000 + CLKFX + 1 + false + FDBK_AUTO + 0 + 26 + BUFG + 4 + 1 + 1 + CLKFB_OUT_N + 0 + PSINCDEC + 0.500 + ZHOLD + DRDY + 0.000 + 0.000 + 0.000 + None + false + DIN + false + 0.500 + CLKFB_OUT_P + CLKFB_IN + 0.000 + 0.010 + 1 + DWE + 0.000 + false + false + BUFG + true + 0.010 + 0.010 + RESET + 0.000 + false + CLK_IN2 + 1 + PSDONE + SINGLE + CLK0 + 0.500 + DOUT + 100.000 + false + 0.000 + false + CLK0 + 0.010 + 50.0 + 2 + 0.500 + CLK0 + false + DADDR + POWER_DOWN + 0.000 + CLKFB_OUT + true + CLK_VALID + 1 + 1X + OPTIMIZED + 384.61 + CLK0 + BUFG + 1 + 4.000 + DONE + BUFG + false + CLK0 + 100.000 + 1 + None + 50.0 + false + 0.000 + false + CLK0 + false + CLK_OUT1 + NONE + PSCLK + DCLK + 0.500 + INTERNAL + 0.000 + false + CLKFB_IN_N + CLK_OUT2 + 0.000 + true + DEN + false + Units_MHz + false + MANUAL + UI + 0.500 + CLK_OUT3 + 0.000 + 0.000 + 100.000 + 50.0 + false + 1 + false + CLKFB_IN_P + SYSTEM_SYNCHRONOUS + 4 + CLK_OUT4 + 2.0 + BUFG + false + false + false + 1 + CLK_OUT5 + 10.000 + 1 + CLKFBOUT + 0.500 + CLK_OUT6 + false + 0.000 + false + 0.500 + None + 100.000 + 50.0 + false + CLK_OUT7 + 0.000 + REL_PRIMARY + NONE + false + 0.500 + false + 0.000 + 4.000 + 0.010 + 1 + false + false + 0.000 + false + BUFG + Units_UI + STATUS + 50.0 + NONE + 100.000 + 0.010 + 100.000 + false + nt64 + 720p60hzclocksource + 1 + 0.500 + false + DCM_CLKGEN + CLK_IN1 + 0.000 + 100.0 + 1 + 10.000 + No_Jitter + No_buffer + false + false + 10.000 + 0.000 + 0.500 + 50.0 + false + 10.000 + DCM_CLKGEN + 1 + PSDONE + CLK_IN1 + 4 + None + 0 + 74.287 + N/A + CLK_OUT1 + 0 + MANUAL + 0 + 0.000 + CLK_OUT2 + 50.0 + 1 + 0.000 + 0.000 + Units_MHz + 100.000 + CLK_OUT3 + FALSE + 1 + BUFG + 0.500 + N/A + 0.500 + 100.000 + 0.000 + Single_ended_clock_capable_pin + CLK_OUT4 + 2 + 1 + 0 + 0 + 0.010 + CLK_OUT5 + 50.0 + 1 + 0 + 4.000 + FALSE + N/A + CLK_OUT6 + 100.000 + 0.000 + 0.010 + CLK_OUT7 + DEN + 0.500 + 0.000 + PSEN + No_Jitter + CLKFB_OUT_N + 26 + CLK_IN_SEL + 1 + FALSE + BUFG + 0.500 + N/A + 50.0 + NONE + 0.500 + 10.000 + 0 + Input Clock Input Freq (MHz) Input Jitter (UI) + primary 26 0.010 + no secondary input clock + 1 + 0.000 + CLKFB_OUT_P + 100.000 + N/A + CLKFX + 0 + 0.000 + 0.000 + FDBK_AUTO + N/A + NONE + 0.000 + 0.010 + 0.010 + 0 + FALSE + NONE + 0.000 + 0.500 + 0 + 1 + BUFG + 100.000 + N/A + 4 + N/A + 0.500 + STATUS + 10.000 + 1 + FALSE + DRDY + ZHOLD + 1 + 0.010 + nt64 + 1X + None + 384.61 + 4.000 + 1 + N/A + FALSE + OPTIMIZED + No_buffer + 0.000 + NONE + 7 + 100.000 + 38.461 + N/A + 0.000 + 0.000 + 0.000 + 1 + 0.500 + FALSE + INPUT_CLK_STOPPED + 0.500 + BUFG + 0 + 0 + 0.000 + 1 + 0.000 + N/A + INTERNAL + 0 + FALSE + 0 + 50.0 + CLKOUT1 + CLK0 + 1 + LOCKED + DOUT + POWER_DOWN + RESET + 0 + NONE + 100.000 + N/A + 0 + N/A + FALSE + FALSE + CLK_OUT1 74.287 0.000 N/A 200.000 N/A + no CLK_OUT2 output + no CLK_OUT3 output + PSINCDEC + no CLK_OUT4 output + 0.000 + 0 + 2.000 + no CLK_OUT5 output + NONE + no CLK_OUT6 output + no CLK_OUT7 output + N/A + SINGLE + 1 + 0.000 + 1 + 10.000 + 0 + NONE + 0.000 + clkgendcm_720p60hz + 0 + 50.0 + CLKFBOUT + CLKFB_IN + 0.500 + NONE + BUFG + 0 + DADDR + 1 + 0.500 + NONE + 20 + 0 + N/A + CLKFB_IN_N + 74.285 + N/A + CLK_IN2 + FALSE + NONE + 1 + FALSE + DCLK + 0.000 + None + N/A + Output Output Phase Duty Cycle Pk-to-Pk Phase + CLKFB_IN_P + 50.0 + Clock Freq (MHz) (degrees) (%) Jitter (ps) Error (ps) + PSCLK + 0.000 + 0.000 + CLKFB_OUT + CLK_VALID + N/A + FALSE + 1 + DIN + 0 + SYSTEM_SYNCHRONOUS + 0.000 + 720p60hzclocksource + BUFG + 0.500 + 1 + BUFG + 0 + 0.500 + DWE + N/A + OPTIMIZED + 100.0 + 50.0 + FALSE + + + + + coregen + ./ + ./tmp/ + ./tmp/_cg + + + xc6slx9 + spartan6 + tqg144 + -2 + + + BusFormatAngleBracketNotRipped + Verilog + true + Other + false + false + false + Ngc + false + + + Behavioral + Verilog + false + + + + + customization_generator + + + model_parameter_resolution_generator + + + ip_xco_generator + + ./clkgendcm_720p60hz.xco + xco + Tue Aug 09 21:40:17 GMT 2011 + 0x920E017E + generationid_1508053363 + + + + ngc_netlist_generator + + ./clkgendcm_720p60hz/clk_wiz_readme.txt + ignore + txt + Thu Feb 03 22:20:48 GMT 2011 + 0x63183E2A + generationid_1508053363 + + + ./clkgendcm_720p60hz/clkgendcm_720p60hz.ucf + ucf + Tue Aug 09 21:40:20 GMT 2011 + 0x5EC3B764 + generationid_1508053363 + + + ./clkgendcm_720p60hz/doc/clk_wiz_ds709.pdf + ignore + pdf + Thu Feb 03 22:20:48 GMT 2011 + 0xC01920CC + generationid_1508053363 + + + ./clkgendcm_720p60hz/doc/clk_wiz_gsg521.pdf + ignore + pdf + Thu Feb 03 22:20:48 GMT 2011 + 0xBA196AB0 + generationid_1508053363 + + + ./clkgendcm_720p60hz/example_design/clkgendcm_720p60hz_exdes.v + verilog + Tue Aug 09 21:40:17 GMT 2011 + 0x53DD6918 + generationid_1508053363 + + + ./clkgendcm_720p60hz/implement/implement.bat + ignore + unknown + Tue Aug 09 21:40:20 GMT 2011 + 0x98AE950F + generationid_1508053363 + + + ./clkgendcm_720p60hz/implement/implement.sh + ignore + unknown + Tue Aug 09 21:40:19 GMT 2011 + 0xD7364919 + generationid_1508053363 + + + ./clkgendcm_720p60hz/implement/planAhead_ise.bat + ignore + unknown + Tue Aug 09 21:40:19 GMT 2011 + 0x2D87B27F + generationid_1508053363 + + + ./clkgendcm_720p60hz/implement/planAhead_ise.sh + ignore + unknown + Tue Aug 09 21:40:19 GMT 2011 + 0x26D9E0AC + generationid_1508053363 + + + ./clkgendcm_720p60hz/implement/planAhead_ise.tcl + ignore + tcl + Tue Aug 09 21:40:19 GMT 2011 + 0xEE5C7326 + generationid_1508053363 + + + ./clkgendcm_720p60hz/implement/xst.prj + ignore + unknown + Tue Aug 09 21:40:20 GMT 2011 + 0x3E535B2C + generationid_1508053363 + + + ./clkgendcm_720p60hz/implement/xst.scr + ignore + unknown + Tue Aug 09 21:40:20 GMT 2011 + 0x392E1A17 + generationid_1508053363 + + + ./clkgendcm_720p60hz/simulation/clkgendcm_720p60hz_tb.v + ignore + verilog + Tue Aug 09 21:40:17 GMT 2011 + 0xE300FED0 + generationid_1508053363 + + + ./clkgendcm_720p60hz/simulation/functional/simcmds.tcl + ignore + tcl + Tue Aug 09 21:40:19 GMT 2011 + 0x522C4A54 + generationid_1508053363 + + + ./clkgendcm_720p60hz/simulation/functional/simulate_isim.bat + ignore + unknown + Tue Aug 09 21:40:18 GMT 2011 + 0x16B072E8 + generationid_1508053363 + + + ./clkgendcm_720p60hz/simulation/functional/simulate_isim.sh + ignore + unknown + Tue Aug 09 21:40:19 GMT 2011 + 0x6A4E1E9B + generationid_1508053363 + + + ./clkgendcm_720p60hz/simulation/functional/simulate_mti.do + ignore + unknown + Tue Aug 09 21:40:18 GMT 2011 + 0x22B3C2B0 + generationid_1508053363 + + + ./clkgendcm_720p60hz/simulation/functional/simulate_ncsim.sh + ignore + unknown + Tue Aug 09 21:40:18 GMT 2011 + 0x8FF291AA + generationid_1508053363 + + + ./clkgendcm_720p60hz/simulation/functional/simulate_vcs.sh + ignore + unknown + Tue Aug 09 21:40:19 GMT 2011 + 0x6CDC52B7 + generationid_1508053363 + + + ./clkgendcm_720p60hz/simulation/functional/ucli_commands.key + ignore + unknown + Tue Aug 09 21:40:19 GMT 2011 + 0x15A5CE19 + generationid_1508053363 + + + ./clkgendcm_720p60hz/simulation/functional/vcs_session.tcl + ignore + tcl + Tue Aug 09 21:40:19 GMT 2011 + 0x1A8D760C + generationid_1508053363 + + + ./clkgendcm_720p60hz/simulation/functional/wave.do + ignore + unknown + Tue Aug 09 21:40:18 GMT 2011 + 0xE0FCC1E4 + generationid_1508053363 + + + ./clkgendcm_720p60hz/simulation/functional/wave.sv + ignore + unknown + Tue Aug 09 21:40:18 GMT 2011 + 0xF42F3C45 + generationid_1508053363 + + + ./clkgendcm_720p60hz.ejp + unknown + Tue Aug 09 21:40:17 GMT 2011 + 0xDEF1B21C + generationid_1508053363 + + + ./clkgendcm_720p60hz.v + verilog + Tue Aug 09 21:40:17 GMT 2011 + 0xBB0CFF9A + generationid_1508053363 + + + ./clkgendcm_720p60hz.veo + veo + Tue Aug 09 21:40:17 GMT 2011 + 0x59A939F4 + generationid_1508053363 + + + ./clkgendcm_720p60hz_xmdf.tcl + tcl + Tue Aug 09 21:40:18 GMT 2011 + 0x2BF520E1 + generationid_1508053363 + + + + instantiation_template_generator + + ./clkgendcm_720p60hz.veo + veo + Tue Aug 09 21:40:20 GMT 2011 + 0x59A939F4 + generationid_1508053363 + + + + asy_generator + + ./clkgendcm_720p60hz.asy + asy + Tue Aug 09 21:40:24 GMT 2011 + 0x4709598E + generationid_1508053363 + + + + xmdf_generator + + + ise_generator + + ./clkgendcm_720p60hz.gise + ignore + gise + Tue Aug 09 21:40:26 GMT 2011 + 0x62A4AF22 + generationid_1508053363 + + + ./clkgendcm_720p60hz.xise + ignore + xise + Tue Aug 09 21:40:26 GMT 2011 + 0x23EDC58D + generationid_1508053363 + + + + deliver_readme_generator + + + flist_generator + + ./clkgendcm_720p60hz_flist.txt + ignore + txtFlist + txt + Tue Aug 09 21:40:26 GMT 2011 + 0xC1C72AE1 + generationid_1508053363 + + + + + + + clk_wiz_v3_1_0 + Generated by PlanAhead + + + clk_wiz_v3_1_0 + + + + + coregen + ./ + ./tmp/ + ./tmp/_cg + + + xc6slx9 + spartan6 + tqg144 + -2 + + + BusFormatAngleBracketNotRipped + Verilog + true + Other + false + false + false + Ngc + false + + + Behavioral + Verilog + false + + + + + + + + + coregen + ./ + ./tmp/ + ./tmp/_cg + + + xc6slx9 + spartan6 + tqg144 + -2 + + + BusFormatAngleBracketNotRipped + Verilog + true + Other + false + false + false + Ngc + false + + + Behavioral + Verilog + false + + + + diff --git a/ip/clk_wiz_v3_1_0/coregen.cgp b/ip/clk_wiz_v3_1_0/coregen.cgp new file mode 100755 index 0000000..555710a --- /dev/null +++ b/ip/clk_wiz_v3_1_0/coregen.cgp @@ -0,0 +1,22 @@ +# Date: Tue Aug 09 21:35:33 2011 + +SET addpads = false +SET asysymbol = true +SET busformat = BusFormatAngleBracketNotRipped +SET createndf = false +SET designentry = Verilog +SET device = xc6slx9 +SET devicefamily = spartan6 +SET flowvendor = Other +SET formalverification = false +SET foundationsym = false +SET implementationfiletype = Ngc +SET package = tqg144 +SET removerpms = false +SET simulationfiles = Behavioral +SET speedgrade = -2 +SET verilogsim = true +SET vhdlsim = false +SET workingdirectory = ./tmp/ + +# CRC: e7d90245 diff --git a/ip/clk_wiz_v3_1_0/coregen.log b/ip/clk_wiz_v3_1_0/coregen.log new file mode 100755 index 0000000..f93187b --- /dev/null +++ b/ip/clk_wiz_v3_1_0/coregen.log @@ -0,0 +1,42 @@ +CoreGen has not been configured with any user repositories. +CoreGen has been configured with the following Xilinx repositories: + - 'C:\Xilinx\13.1\ISE_DS\ISE\coregen\' [] +INFO:sim:927 - Generating component instance 'clkgendcm_720p60hz' of + 'xilinx.com:ip:clk_wiz:3.1' from + 'C:\Xilinx\13.1\ISE_DS\ISE\coregen\ip\xilinx\primary\com\xilinx\ip\clk_wiz_v3 + _1\clk_wiz_v3_1.xcd'. +Resolving generic values... +Initializing IP model... +Loading device for application Rf_Device from file '6slx9.nph' in environment +C:\Xilinx\13.1\ISE_DS\ISE\. +Finished initializing IP model. +Finished resolving generic values. +Generating IP... +WARNING:sim:89 - A core named already exists in the output + directory. Output products for this core may be overwritten. +Skipping VHDL instantiation template for clkgendcm_720p60hz... +Creating ISE instantiation template for clkgendcm_720p60hz... +Collating core files for clkgendcm_720p60hz +Collating core files for clkgendcm_720p60hz +Configuring files for clkgendcm_720p60hz root... +Finished Generation. +Generating IP instantiation template... +Finished generating IP instantiation template. +Generating ASY schematic symbol... +Initializing IP model... +Finished initializing IP model. +Finished generating ASY schematic symbol. +Generating metadata file... +Finished generating metadata file. +Generating ISE project... +WARNING:sim - This core does not have a top level called "/clkgendcm_720p60hz" +WARNING:sim - Top level has been set to "/clkgendcm_720p60hz_exdes" +Finished generating ISE project.Generating README file... +Finished generating README file. +Generating FLIST file... +Finished FLIST file generation. +Preparing output directory... +Finished preparing output directory. +Moving files to output directory... +Finished moving files to output directory +Saved options for project 'coregen'. diff --git a/ip/clk_wiz_v3_1_0/pa_cg_bom.xml b/ip/clk_wiz_v3_1_0/pa_cg_bom.xml new file mode 100755 index 0000000..7a61bc9 --- /dev/null +++ b/ip/clk_wiz_v3_1_0/pa_cg_bom.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ip/clk_wiz_v3_1_0/pa_cg_config_core_invoke.tcl b/ip/clk_wiz_v3_1_0/pa_cg_config_core_invoke.tcl new file mode 100755 index 0000000..d5918a7 --- /dev/null +++ b/ip/clk_wiz_v3_1_0/pa_cg_config_core_invoke.tcl @@ -0,0 +1,23 @@ +# Tcl script generated by PlanAhead + +set tclUtilsPath "C:/Xilinx/13.1/ISE_DS/PlanAhead/scripts/pa_cg_utils.tcl" + +set cgProjectPath "C:/largework/fpga/hdmi/release1/release1.srcs/sources_1/ip/clk_wiz_v3_1_0/coregen.cgc" + +set ipName "clk_wiz_v3_1_0" + +set vlnv "xilinx.com:ip:clk_wiz:3.1" + +set cgPartSpec "6slx9-2tqg144" + +set bomFilePath "C:/largework/fpga/hdmi/release1/release1.srcs/sources_1/ip/clk_wiz_v3_1_0/pa_cg_bom.xml" + +set hdlType "Verilog" + +set chains "CUSTOMIZE_CHAIN INSTANTIATION_TEMPLATES_CHAIN" + +# configure the IP +set result [source "C:/Xilinx/13.1/ISE_DS/PlanAhead/scripts/pa_cg_config_core.tcl"] + +exit $result + diff --git a/ip/clk_wiz_v3_1_0/pa_cg_gen_core_invoke.tcl b/ip/clk_wiz_v3_1_0/pa_cg_gen_core_invoke.tcl new file mode 100755 index 0000000..f504ffb --- /dev/null +++ b/ip/clk_wiz_v3_1_0/pa_cg_gen_core_invoke.tcl @@ -0,0 +1,17 @@ +# Tcl script generated by PlanAhead + +set tclUtilsPath "C:/Xilinx/13.1/ISE_DS/PlanAhead/scripts/pa_cg_utils.tcl" + +set cgProjectPath "C:/largework/fpga/hdmi/release1/release1.srcs/sources_1/ip/clk_wiz_v3_1_0/coregen.cgc" + +set ipName "clkgendcm_720p60hz" + +set chains "GENERATE_CHAIN" + +set bomFilePath "C:/largework/fpga/hdmi/release1/release1.srcs/sources_1/ip/clk_wiz_v3_1_0/pa_cg_bom.xml" + +# generate the IP +set result [source "C:/Xilinx/13.1/ISE_DS/PlanAhead/scripts/pa_cg_gen_core.tcl"] + +exit $result + diff --git a/ip/fifo_generator_v7_2_0/.lso b/ip/fifo_generator_v7_2_0/.lso new file mode 100755 index 0000000..05eddb4 --- /dev/null +++ b/ip/fifo_generator_v7_2_0/.lso @@ -0,0 +1 @@ +fifo_generator_v7_2 diff --git a/ip/fifo_generator_v7_2_0/coregen.cgc b/ip/fifo_generator_v7_2_0/coregen.cgc new file mode 100755 index 0000000..b07d8e9 --- /dev/null +++ b/ip/fifo_generator_v7_2_0/coregen.cgc @@ -0,0 +1,528 @@ + + + xilinx.com + projects + coregen + 1.0 + + + fifo_2kx18 + + + fifo_2kx18 + Active_High + false + Empty + 2048 + false + No_Programmable_Full_Threshold + false + Data_FIFO + 18 + Full + false + false + false + false + false + false + false + Common_Clock_Block_RAM + Full + 1 + false + false + 1024 + 4 + 0 + false + 1 + 4 + Common_Clock_Block_RAM + FIFO + 1023 + 1 + 64 + false + false + 16 + 1 + false + No_Programmable_Empty_Threshold + 2048 + Active_High + 4 + false + false + 1022 + false + Full + FIFO + Common_Clock_Block_RAM + false + Full + Common_Clock + Active_High + false + false + false + false + Slave_Interface_Clock_Enable + 1024 + 11 + 1 + Common_Clock_Block_RAM + Active_High + false + false + 18 + false + Empty + 16 + false + 2045 + Data_FIFO + false + 11 + false + false + 1022 + 4 + FIFO + false + false + true + 1022 + Independent_Clocks_Block_RAM + Full + false + 1023 + Common_Clock_Block_RAM + false + Empty + 2 + false + false + 1024 + false + false + false + Data_FIFO + false + false + false + false + Empty + false + 1022 + Active_High + false + FIFO + 1 + Data_FIFO + 1 + 1022 + 2044 + false + false + 1023 + Native + Asynchronous_Reset + FIFO + 11 + false + 1023 + false + Empty + false + false + 64 + false + Data_FIFO + false + false + Active_High + Empty + false + FIFO + false + 4 + 3 + Data_FIFO + false + true + Full + 1 + false + 1022 + false + false + Standard_FIFO + AXI4_Stream + false + false + Common_Clock_Block_RAM + 1023 + false + true + false + 32 + 8 + 16 + 1023 + false + false + 0 + 64 + 4 + 5 + 1023 + 0 + 0 + 32 + 64 + 2kx18 + 0 + 0 + 0 + 11 + 1023 + 1 + 0 + 0 + 0 + 0 + 4 + 32 + 0 + 18 + 11 + 0 + 1 + 0 + 0 + 1024 + 0 + 0 + 4 + 0 + 0 + 5 + 1022 + 1 + 0 + 16 + 0 + 5 + 8 + 0 + 0 + 0 + 0 + 2 + 1023 + 2044 + 0 + 1 + 0 + 0 + 1 + 1 + 1 + 1 + 4 + 1 + 0 + 2048 + 0 + 1 + 1 + 0 + 10 + 1 + 32 + 0 + 1024 + 1022 + 1 + 1 + 0 + 0 + 5 + 4 + 16 + 0 + 1022 + 0 + 2 + 0 + 0 + 0 + 5 + 0 + 0 + 0 + 0 + 64 + 0 + 0 + 0 + 11 + 0 + 0 + 10 + 2 + 5 + 0 + 1022 + 0 + 11 + 1 + 0 + 1024 + 4 + 0 + 5 + 0 + 1022 + 1023 + 0 + 2045 + 0 + 0 + 5 + 0 + 0 + 1 + 4 + 2048 + 0 + 11 + 0 + 5 + 64 + 4 + 0 + 0 + 0 + 0 + 10 + 5 + 1022 + 1023 + 1 + 1 + 1 + 1 + 0 + 0 + 5 + 0 + 0 + 3 + 1023 + 18 + 0 + 0 + 0 + 0 + 0 + 5 + 0 + 1 + 0 + 0 + 0 + 16 + + + + + coregen + ./ + ./tmp/ + ./tmp/_cg + + + xc6slx9 + spartan6 + tqg144 + -2 + + + BusFormatAngleBracketNotRipped + Verilog + true + Foundation_ISE + false + false + false + Ngc + false + + + Behavioral + Verilog + false + + + + + apply_current_project_options_generator + + + customization_generator + + + model_parameter_resolution_generator + + + ip_xco_generator + + ./fifo_2kx18.xco + xco + Tue Aug 09 21:46:42 GMT 2011 + 0x200AB8B2 + generationid_3501100592 + + + + ngc_netlist_generator + + ./fifo_2kx18.ngc + ngc + Tue Aug 09 21:47:05 GMT 2011 + 0x80830D9F + generationid_3501100592 + + + ./fifo_2kx18.v + verilog + Tue Aug 09 21:46:58 GMT 2011 + 0x6726F87E + generationid_3501100592 + + + ./fifo_2kx18.veo + veo + Tue Aug 09 21:46:58 GMT 2011 + 0x368CEE5B + generationid_3501100592 + + + ./fifo_generator_readme.txt + txt + Tue Aug 09 21:46:58 GMT 2011 + 0x8C1691E3 + generationid_3501100592 + + + ./fifo_generator_ug175.pdf + pdf + Tue Aug 09 21:46:58 GMT 2011 + 0x7B853EF8 + generationid_3501100592 + + + + instantiation_template_generator + + ./fifo_2kx18.veo + veo + Tue Aug 09 21:47:05 GMT 2011 + 0x368CEE5B + generationid_3501100592 + + + + asy_generator + + ./fifo_2kx18.asy + asy + Tue Aug 09 21:47:08 GMT 2011 + 0x903A7B6F + generationid_3501100592 + + + + xmdf_generator + + ./fifo_2kx18_xmdf.tcl + tclXmdf + tcl + Tue Aug 09 21:47:08 GMT 2011 + 0xBF5DD5B7 + generationid_3501100592 + + + + ise_generator + + ./fifo_2kx18.gise + ignore + gise + Tue Aug 09 21:47:10 GMT 2011 + 0xAC308EE9 + generationid_3501100592 + + + ./fifo_2kx18.xise + ignore + xise + Tue Aug 09 21:47:10 GMT 2011 + 0xC62EA0FE + generationid_3501100592 + + + + deliver_readme_generator + + + flist_generator + + ./fifo_2kx18_flist.txt + ignore + txtFlist + txt + Tue Aug 09 21:47:10 GMT 2011 + 0x64B4445D + generationid_3501100592 + + + + + + + + + + coregen + ./ + ./tmp/ + ./tmp/_cg + + + xc6slx9 + spartan6 + tqg144 + -2 + + + BusFormatAngleBracketNotRipped + Verilog + true + Foundation_ISE + false + false + false + Ngc + false + + + Behavioral + Verilog + false + + + + diff --git a/ip/fifo_generator_v7_2_0/coregen.cgp b/ip/fifo_generator_v7_2_0/coregen.cgp new file mode 100755 index 0000000..0725064 --- /dev/null +++ b/ip/fifo_generator_v7_2_0/coregen.cgp @@ -0,0 +1,22 @@ +# Date: Tue Aug 09 21:31:41 2011 + +SET addpads = false +SET asysymbol = true +SET busformat = BusFormatAngleBracketNotRipped +SET createndf = false +SET designentry = Verilog +SET device = xc6slx9 +SET devicefamily = spartan6 +SET flowvendor = Foundation_ISE +SET formalverification = false +SET foundationsym = false +SET implementationfiletype = Ngc +SET package = tqg144 +SET removerpms = false +SET simulationfiles = Behavioral +SET speedgrade = -2 +SET verilogsim = true +SET vhdlsim = false +SET workingdirectory = ./tmp/ + +# CRC: d3c0f20c diff --git a/ip/fifo_generator_v7_2_0/coregen.log b/ip/fifo_generator_v7_2_0/coregen.log new file mode 100755 index 0000000..b090d84 --- /dev/null +++ b/ip/fifo_generator_v7_2_0/coregen.log @@ -0,0 +1,44 @@ +CoreGen has not been configured with any user repositories. +CoreGen has been configured with the following Xilinx repositories: + - 'C:\Xilinx\13.1\ISE_DS\ISE\coregen\' [] +INFO:sim:927 - Generating component instance 'fifo_2kx18' of + 'xilinx.com:ip:fifo_generator:7.2' from + 'C:\Xilinx\13.1\ISE_DS\ISE\coregen\ip\xilinx\primary\com\xilinx\ip\fifo_gener + ator_v7_2\fifo_generator_v7_2.xcd'. +Resolving generic values... +Initializing IP model... +Finished initializing IP model. +Finished resolving generic values. +Generating IP... +WARNING:sim:975 - You are using Fifo Generator 7.2 which has been replaced with + a new version. This version of the core will be removed in a future release. + Cores in this state are not supported. +WARNING:sim:89 - A core named already exists in the output + directory. Output products for this core may be overwritten. +XST: HDL Parsing +XST: HDL Elaboration +XST: HDL Synthesis +XST: Advanced HDL Synthesis +XST: Low Level Synthesis +Generating Implementation files. +Picked up JAVA_TOOL_OPTIONS: -Xmx2048m +Generating NGC file. +Finished Generation. +Generating IP instantiation template... +Finished generating IP instantiation template. +Generating ASY schematic symbol... +Initializing IP model... +Finished initializing IP model. +Finished generating ASY schematic symbol. +Generating metadata file... +Finished generating metadata file. +Generating ISE project... +Finished generating ISE project.Generating README file... +Finished generating README file. +Generating FLIST file... +Finished FLIST file generation. +Preparing output directory... +Finished preparing output directory. +Moving files to output directory... +Finished moving files to output directory +Saved options for project 'coregen'. diff --git a/ip/fifo_generator_v7_2_0/fifo_2kx18.asy b/ip/fifo_generator_v7_2_0/fifo_2kx18.asy new file mode 100755 index 0000000..b01eb08 --- /dev/null +++ b/ip/fifo_generator_v7_2_0/fifo_2kx18.asy @@ -0,0 +1,41 @@ +Version 4 +SymbolType BLOCK +TEXT 32 32 LEFT 4 fifo_2kx18 +RECTANGLE Normal 32 32 800 3712 +LINE Normal 0 112 32 112 +PIN 0 112 LEFT 36 +PINATTR PinName rst +PINATTR Polarity IN +LINE Normal 0 240 32 240 +PIN 0 240 LEFT 36 +PINATTR PinName wr_clk +PINATTR Polarity IN +LINE Wide 0 272 32 272 +PIN 0 272 LEFT 36 +PINATTR PinName din[17:0] +PINATTR Polarity IN +LINE Normal 0 304 32 304 +PIN 0 304 LEFT 36 +PINATTR PinName wr_en +PINATTR Polarity IN +LINE Normal 0 496 32 496 +PIN 0 496 LEFT 36 +PINATTR PinName full +PINATTR Polarity OUT +LINE Normal 832 240 800 240 +PIN 832 240 RIGHT 36 +PINATTR PinName rd_clk +PINATTR Polarity IN +LINE Wide 832 272 800 272 +PIN 832 272 RIGHT 36 +PINATTR PinName dout[17:0] +PINATTR Polarity OUT +LINE Normal 832 304 800 304 +PIN 832 304 RIGHT 36 +PINATTR PinName rd_en +PINATTR Polarity IN +LINE Normal 832 496 800 496 +PIN 832 496 RIGHT 36 +PINATTR PinName empty +PINATTR Polarity OUT + diff --git a/ip/fifo_generator_v7_2_0/fifo_2kx18.gise b/ip/fifo_generator_v7_2_0/fifo_2kx18.gise new file mode 100755 index 0000000..4bcc080 --- /dev/null +++ b/ip/fifo_generator_v7_2_0/fifo_2kx18.gise @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + 11.1 + + + + + + + + + + + + diff --git a/ip/fifo_generator_v7_2_0/fifo_2kx18.ngc b/ip/fifo_generator_v7_2_0/fifo_2kx18.ngc new file mode 100755 index 0000000..e1c560f --- /dev/null +++ b/ip/fifo_generator_v7_2_0/fifo_2kx18.ngc @@ -0,0 +1,3 @@ +XILINX-XDB 0.1 STUB 0.1 ASCII +XILINX-XDM V1.6e +$8g:44<,[o}e~g`n;"2*736(-;0<45?01234567<91;<=>?0123456789:;<=>?0123456789:;<=>?0123456789:;<=>?0123456789::7<=41/211>772@D[YY4nxh]q[uthoVof|ywPtipfwm:>294986??:HLSQQ5IORVP?gaV}T|ah_gwohZrozlyc044?>3685535854:HLSQQ585>2;:6D@_UU8svjaXn|fgSyf}erj\evubz}6:<7>127925?OIX\^1|ah_gwohZrozlycSckhaug?55<76;:0=<4@UURVP?gaVxThlzn_bmvjq:683:5>>5>1;MVPUSS2hrbSQ}d^fbpdYdg|d0<>50?00?47=G\^[YY4nxh]q[rtXlh~jSnaznu>24?69:91:=7AZTQWW>d~nW~UomyoPcnwmp9772949?6?>:NWWTPR=iqcT{R|k_ecweZeh}g~7==4?>31854586n2;:6B[[PTV9e}otWmkmRm`uov?55<76;80=<4@UURVP?gazUyhRjnt`]`kphs48:1<3<=;039KPRW]]0jtd}Pws]geqgXkfex1??:1<2e>762F__\XZ5d`vb[firf}6:<7>11e925?IR\Y__6jPd`vb[firf}6:<7>11e925?IR\Y__6z|Pd`vb[firf}6:<7>115920?OIX\^1MIJ]A=30>586<2;?6D@_UU8B@ATE4891<3?=;0:9MKVR\3nbb1?8:1<20>7?2F__\XZ5dnww841=87;37<4FNQWW>hYfp`Uiy~k}<183:4><93CE\XZ5m^c{mZtr{lx7<7>11892>LHW]]0xSlvf_`pvw`t;83:5=45>:HLSQQ7=G\^[YY4b_`zj[dtr{lx7<7>11`92>JSSX\^1aRowi^ctqvcu490;2<741;MVPUSS2dUjtdQxurgq85<76830=7AZTQWW>vYfp`Uiy~k}<183:4?<93E^X][[:r]b|lYu}zoy0=4?>0915==52@D[YY4b_`zj[gtb{y6:6=0>8:09MKVR\3gTmugPrsgpt97=87;j7?4FNQWW>vYfp`Uj~o{}su>2>5861281EC^ZT;q\e}oXi{fdof2>:1<2e>4=AGZ^X7}Payk\erdrzz~7=7>11891>LHW]]0xSlvf_`uokfm;93:5=o5=:NWWTPR=eVkseRo}btppp97=87;j7?4@UURVP?kXiqcTmb`cj>2>586j281CXZ_UU8n[d~nWh}iy}{<083:4g<:3E^X][[:l]b|lYfeehg1?50?3:?7<5IORVP?BNI59:6=0>2:11>LHW]]0OEO2<1;2=5g=4:3CE\XZ5s^c{mZguimny0>?50?3a?64=AGZ^X7}Payk\ergcl{68=7>112906?IR\Y__6IAN<2394;743:81CXZ_UU8GKG:493:5=n5<2;MVPUSS2dUjtdQnr`fgv956294:o6==:NWWTPR=eVkseRoxaefq867=8720?>>=32235<=42@D[YY4|_`zj[dtwzf~7>7>11890>LHW]]0xSlvf_`ppmc;:3:5=45<:HLSQQ0c87?IR\Y__6`Qnxh]bvvo}m581<3?n;28LQQVR\3gTmugPavrqkq:5294:m6=5OTVSQQ?79?72597<44692>1EC^ZT;FJTD:4294:=6:5IORVP?BNXK686=0>0:69MKVR\3]NM1=50?33?195;:HLSQQ2=AGZ^X7owi^`\twi`Wlg{xtQ{hsgpl95=8788794FNQWW>d~nWkU{~biPftno[qnumzb7?7>11797>LHW]]0fSlvf_ckg86<768<087GAPTV9iZgaVxbh1=50?3b?17:69MKVR\3yTmugPaskg86<7682087GAPTV9wZgaVky}b}33;2=5g=32@D[YY4|_`zj[dtumncdb1=50?3b?17:69MKVR\3yTmugPavkg86<7682087GAPTV9wZgaVk|}b}33;2=5g=32@D[YY4|_`zj[dqumncdb1=50?35?10>58612>1EC^ZT;q\e}otW}ani}2<:1<2=>2=AGZ^X7}Paykp[quszk686=0>9:69MKVR\3yTmug|_uwpaw:4294:>6:5OTVSQQ11397>JSSX\^1HB^M<283:4d<<3E^X][[:l]b|lYfzjkhdh2<:1<2<>2=G\^[YY4b_`zj[dtnl591<3?6;58LQQVR\3gTmugPasslw95=87;h794@UURVP?kXiqcTm|jghmm86<768h087AZTQWW>hYfp`Uj{nolhd>0>58602>1CXZ_UU8n[d~nWh}bh1=50?3:?10>586i2>1CXZ_UU8n[d~n{V~oi~z33;2=5d=32F__\XZ5m^c{mvYsclo{0>4?>0c80?IR\Y__6`Qnxhq\pvruj591<3?n;58LQQVR\3gTmug|_uwpaw:4294:;6:5OTVSQQf:79KPRW]]0jtdQnr^fbpdYdg|d094?>3081?IR\Y__6lvf_`p\vaYci}kTob{at=694;453<0DYY^ZT;c{mZguW~xThlzn_bmvjq:3294:j6;5OTVSQQ7>586m2?1CXZ_UU8b|lYeWmkmRm`uov?0?69:81>6B[[PTV9e}oXjVxoSio{a^alqkr;<3:5><5::NWWTPR=iqcTnRy}_ecweZeh}g~787>11:42e>032@D[YY4b_`zj[wbf|h6=?7>11`950?OIX\^1Rowi^ugeqg;>:0;257?699k1=87AZTQWW>vYfp`Uyhlzn<7194;4<>0>0:4798:4:=3?1>0;;7;766845=<011?32:i59smz:pta6<='9=69=;6D7?=439=138??>8::9MKVR\3yTmugPasnfj91=87;3754FNQWW>vYfp`Uj{aka<683:4><03CE\XZ5s^c{mZqt|{h7;7>1169;>LHW]]0xSlvfs^vj`91=87;2754@UURVP?kXiqcTmbjn=594;7>310DYY^ZT;o\e}oXi~fnb1950?3:?=<1?<2:479119:23?110<=548667;1?D033HK=M55NA@C5EDG682KJMLONA@4BEDGFIH;;7LONA@CBEDGFI?KJM55NDEPB858>3HNO^L2>0?;8EABUI5;:245NDEPB8449j2KOH_O31283:<=FLMXJ0<=18:CG@WG;9720MIJ]A=0=<>GCL[K7?364AEFQE92902KOH_O35?:8EABUI5<546OKDSC?3;>GCL[H7==06;@FGVG:69730MIJ]B=31:g=FLMXI0<=50?;8EABUJ5;8255NDEPA848?3HNO^O2=>99B@ATE4:437LJKRC>7:==FLMXI0807;@FGVG:1611JHI\M<6<;?DBCZK63255NDEPA8<843HFG56O\YOA\V@A43K_946LZS^KMBJ73JEFADZ[EE37?FIUMVMNBH\NTHMM[LHAG>1H^HO[EE38@f=CI]KT[DJ[H^C`?AGSIV]BHYFPB39GB7=CA?1OEL2?>69GMD:687=0HDO310<4?AOF4885;6JFA=30:2=CAH6:8394DHC?50803MCJ0<817:FJE9706>1OEL2>8?58@LG;904=7IGN<0<4?AOF4;:5;6JFA=02:2=CAH69>394DHC?66803MCJ0?:17:FJE9426>1OEL2=6?58@LG;:>4<7IGN<3:=3>BNI5822;5KI@>1:2=CAH68<374DHC?74<76>1OEL2<1?48@LG;;7<0HDO34?48@LG;=7<0HDO36?48@LG;?7<0HDO38?48@LG;17<0HDL30?58@LD;994<7IGM<03=3>BNJ5;92:5KIC>27;169GMG:6?7=0HDL319<4?AOE4835:6JFB=3=3>BNJ58;2:5KIC>15;1?08;EKA8759?2NBN1<;>69GMG:5=7=0HDL327<4?AOE4;=5;6JFB=0;:2=CAK695384DH@?6;14=7IGM<9<5?AOE404<7IG_A=2=3>BNXH6:2:5KIQC?6;?4?>69GMUG;;7=0HD^M<1<4?AOWJ5;5;6JFPC>1:<=CAYH7?7>17:FJTG:46?1OCL2?>69GKD:687=0HBO310<4?AIF4885;6J@A=30:2=CGH6:8394DNC?50803MEJ0<817:FLE9706>1OCL2>8?58@JG;904=7IAN<0<4?AIF4;:5;6J@A=02:2=CGH69>394DNC?66803MEJ0?:17:FLE9426>1OCL2=6?58@JG;:>4<7IAN<3:=3>BHI5822;5KO@>1:2=CGH68<374DNC?74<76>1OCL2<1?48@JG;;7<0HBO34?48@JG;=7<0HBO36?48@JG;?7<0HBO38?48@JG;17=0HBOPRDE5?AIE494<7IAM<02=3>BHJ5;:2:5KOC>26;108;EMA8429?2NDN1?:>69GKG:6>7=0HBL316<4?AIE4825;6J@B=3::3=CGK6:2:5KOC>14;1<08;EMA8749?2NDN1<<>69GKG:5<7=0HBL324<4?AIE4;<5;6J@B=04:2=CGK694394DN@?6<813MEI0?08;EMA866912NDN1=>:1<4?AIE4:;5:6J@B=1=2>BHJ5>5:6J@B=7=2>BHJ5<5:6J@B=5=2>BHJ525:6J@B=;=3>BHJVXNK:5KOQC?4;13:2=CGYH7=394DNRA878>3ME[N1=50?58@JVE4:497H:<;DLB7>CIJk1NBR\\TSCN[Dd@C;2LOO95IDBG0?CBW<2LO\H??;GDEBC@AN>LMJKHIF09D7>AIL81B>6G?2:K26>O5:2C8>6G;2:K6<>OIA]ZT<=64IOKWTZ6602CEEY^P03:8MKOSXV:846GAIUR\41>:8:KMMQVX8?20ECG[P^24=>OIA]Y_MYK8;HLJPZ67?2CEEYQ?169JJLRX8;=0ECG[_114?LHN\V:?;6GAIU]312=NF@^T<;94IOKW[5103@DBXR>77:KMMQY71>1BBDZP0@58MKOSW9H<7D@FT^2@3>OIA]U;H:5FNHV\4@1169JJLRX9;=0ECG[_014?LHN\V;?;6GAIU]212=NF@^T=;94IOKW[4103@DBXR?77:KMMQY61>1BBDZP1@58MKOSW8H<7D@FT^3@3>OIA]U:H:5FNHV\5@1;94IOKW[7103@DBXR<77:KMMQY51>1BBDZP2@58MKOSW;H<7D@FT^0@3>OIA]U9H:5FNHV\6@11BBDZP3@58MKOSW:H<7D@FT^1@3>OIA]U8H:5FNHV\7@1H9;HLJPZG13@DBXRL6;HLJPZ@NDL90ECX9;MMB@@B03EELENOC4:NVP72059M55633G;;=95A1107?K77;=1E==:;;O3311=I9933G;;5>5A1068J477<2D:=<:4N0310>H69:>0B1668J47?;2D:>95A1300?K74;2D:8>5A1418J4043G;829M5<53:L166=I::90B?:<;O067>H5>:1E>:=4N3:0?K4>;2D8<>5A3718J6>53G>97C;=;O41?K153G297C7i;O@\FPUWAGU[^B^\7:LFPRIUC=1ECCK>;N08KN73:QJIZEHDECXEB@PCIG@Od=TADUOI[GLE79PKPTDM11X\L\[AO44?VTQIEUJ;6]]V@N\F1=TQGI?7Y\ZE59W]UC3i2_;#jm`nwdjbj(un&myj#|i/fa{*fjlp&GscQ]D^RMPW]7UVXOS]@[RZ3^[]IUW>?TcRv`<1<20d=R8&mhccxiigm-vc)`zo$yj"ilx/aoo})JpfxT^IQ_NUPX5XYULVZEX_U=]^ZLVZ13WfUsc1>115c8Q5)`kfd}jdh`.sd,cw`)zo%lou lljz,I}iuW[NT\CZ][3_\VAYWF]XP?PQWOS]47ZiXpf6;2<:n;T2,cfii~ocmc#|i/fpe*w`(ojr%oaew/LzlvZTCWYD_^V=R_SF\TKRUS=WTTB\P73]l[}i;87;?m6[?/faljs`nnf$yj"i}f/pe+be&jf`t"Cwos]Q@ZVI\[Q?QR\K_QLWV^3ZWQEYS:?Po^zl858606b?P6(ojeezkgio/pe+bta&{l$knv!cmi{+H~hzVXOS]@[RZ5^[WBXXG^YW5SPXNP\2=YhWqe7<3?;a:W3+behflbjb }f.eqb+ta'nis"nbdx.O{kwYULVZEX_U7]^PG[UHSZR3VSUA]_75\kZ~h494:8o5Z0.e`kkpaaoe%~k!hrg,qb*adp'iggu!Bxnp\VAYWF]XP5PQ]D^RMPW]68TUSC_Q96^m\|j:768>j7X> gbmmrcoag'xm#j|i.sd,cf~)keas#@v`r^UQ[UHSZR:VSZ\PPOVQ_4[XPFXT==Q`_ym?4;7312_;#jm`nwdjbj(un&myj#|i/fa{*fjlp&GscQXR^RMPW]6UV]YS]@[RZ0^[]IUW0UdSua30?37=>S7'nidb{hffn,qb*aun'xm#jmw.bnh|*Kg{U\^R^ATSY1YZQUWYD_^V=R_YMQ[=YhWqe7<3?;9:W3+behflbjb }f.eqb+ta'nis"nbdx.O{kwYPZVZEX_U<]^UQ[UHSZR>VSUA]_6]l[}i;87;?56[?/faljs`nnf$yj"i}f/pe+be&jf`t"Cwos]TVZVI\[Q?QRY]_QLWV^3ZWQEYS;Q`_ym?4;7312_;#jm`nwdjbj(un&myj#|i/fa{*fjlp&GscQXR^RMPW]2UV]YS]@[RZ4^[]IUWS7'nidb{hffn,qb*aun'xm#jmw.bnh|*Kg{U\^R^ATSY5YZQUWYD_^V9R_YMQ[1YhWqe7<3?;9:W3+behflbjb }f.eqb+ta'nis"nbdx.O{kwYPZVZEX_U8]^UQ[UHSZR2VSUA]_2]l[}i;87;?56[?/faljs`nnf$yj"i}f/pe+be&jf`t"Cwos]TVZVI\[Q3QRY]_QLWV^?ZWQEYS?Q`_ym?4;73i2_;#jm`nwdjbj(un&myj#|i/fa{*fjlp&GscQXR^RMPW]>UV]YS]@[RZ33YZ^HZV;TcRv`<1<27`=R8&mhccxiigm-vc)`zo$yj"ilx/aoo})ULVZEX_U?]^PG[UHSZR;VSUA]_67\k6c<]9%lob`yfhdl*w`(o{l%~k!hcy,`hn~(ZMU[BY\T1\]Q@ZVI\[Q9QRV@R^57[j5b3\:$knaavgkek+ta'nxm"h gbz-gim'[NT\CZ][3_\VAYWF]XP?PQWOS]47Zi4m2_;#jm`nwdjbj(un&myj#|i/fa{*fjlp&XOS]@[RZ1^[WBXXG^YW9SPXNP\37Yh;l1^<"ilootemci)zo%l~k }f.e`|+ekcq%YHR^ATSY7YZTCWYD_^V;R_YMQ[27Xg:o0Y=!hcnlubl`h&{l$kh!rg-dg}(ddbr$^IQ_NUPX1XYULVZEX_U9]^ZLVZ17Wf9n7X> gbmmrcoag'xm#j|i.sd,cf~)keas#_JPPOVQ_3[XZMU[BY\T7\][KWY11Ve8i6[?/faljs`nnf$yj"i}f/pe+be&jf`t"\K_QLWV^1ZW[NT\CZ][9_\\JTX>1Ud?h5Z0.e`kkpaaoe%~k!hrg,qb*adp'iggu!]D^RMPW]?UVXOS]@[RZ;^[]IUW?=Tc>h4U1-dgjhqn`ld"h gsd-vc)`kq$h`fv RE]SJQT\1TUYHR^ATSY24XY_G[U=:RaQRY]_QLWV^0ZWQEYS8Q`3e9V4*adgg|meka!rg-dvc(un&mht#mcky-TVZVI\[Q=QRY]_QLWV^1ZWQEYS9Q`3e9V4*adgg|meka!rg-dvc(un&mht#mcky-TVZVI\[QZWQEYS>Q`3e9V4*adgg|meka!rg-dvc(un&mht#mcky-TVZVI\[Q3QRY]_QLWV^?ZWQEYS?Q`3d9V4*adgg|meka!rg-dvc(un&mht#mcky-TVZVI\[Q2QRY]_QLWV^77UVRD^R?Po3g8Q5)`kfd}jdh`.sd,cw`)zo%lou lljz,vaYwf}xTnd`30?0e?P6(ojeezkgio/pe+bta&{l$knv!cmi{+wbXxg~ySoga<02=6`=R8&mhccxiigm-vc)`zo$yj"ilx/aoo})ulVzexQmio>2:7c<]9%lob`yfhdl*w`(o{l%~k!hcy,`hn~(zmU{by|Pbhl?6;4b3\:$knaavgkek+ta'nxm"h gbz-gim'{nT|cz}_ckm8685m2_;#jm`nwdjbj(un&myj#|i/fa{*fjlp&xoS}`{r^`jj929:l1^<"ilootemci)zo%l~k }f.e`|+ekcq%yhR~ats]amk:26;o0Y=!hcnlubl`h&{l$kh!rg-dg}(ddbr$~iQnup\flh;>78n7X> gbmmrcoag'xm#j|i.sd,cf~)keas#jPpovq[goi4>49i6[?/faljs`nnf$yj"i}f/pe+be&jf`t"|k_qlwvZdnf525>h5Z0.e`kkpaaoe%~k!hrg,qb*adp'iggu!}d^rmpwYeag622?j4U1-dgjhqn`ld"h gsd-vc)`kq$h`fv re]sjqtXj`dT0=d:W3+behflbjb }f.eqb+ta'nis"nbdx.pg[uhszVmh090=d:W3+behflbjb }f.eqb+ta'nis"nbdx.pg[uhszVmh080=d:W3+behflbjb }f.eqb+ta'nis"nbdx.pg[uhszVmh0;0=d:W3+behflbjb }f.eqb+ta'nis"nbdx.pg[uhszVmh0:0=d:W3+behflbjb }f.eqb+ta'nis"nbdx.pg[uhszVmh050=d:W3+behflbjb }f.eqb+ta'nis"nbdx.pg[uhszVmh040=c:W3+behflbjb }f.eqb+ta'nis"nbdx.pg[uhszVmhS=n5Z0.e`kkpaaoe%~k!hrg,qb*adp'iggu!}d^rmpwY`kV99o6[?/faljs`nnf$yj"i}f/pe+be&jf`t"|k_qlwvZadW=8h7X> gbmmrcoag'xm#j|i.sd,cf~)keas#jPpovq[beX=;i0Y=!hcnlubl`h&{l$kh!rg-dg}(ddbr$~iQnup\cfY1:j1^<"ilootemci)zo%l~k }f.e`|+ekcq%yhR~ats]dgZ15k2_;#jm`nwdjbj(un&myj#|i/fa{*fjlp&xoS}`{r^e`[=4d3\:$knaavgkek+ta'nxm"h gbz-gim'{nT|cz}_fa\=65<]9%lob`yfhdl*w`(o{l%~k!hcy,`hn~(zmU{by|Pgb]bwwc`494886[?/faljs`nnf$yj"i}f/pe+be&jf`t"|k_qlwvZadWhyyij2>0?10?P6(ojeezkgio/pe+bta&{l$knv!cmi{+wbXxg~ySjmParpfc979;:1^<"ilootemci)zo%l~k }f.e`|+ekcq%yhR~ats]dgZgtzlm7>3=<;T2,cfii~ocmc#|i/fpe*w`(ojr%oaew/sf\tkruWniTm~|jg=1=76=R8&mhccxiigm-vc)`zo$yj"ilx/aoo})ulVzexQhc^cpv`a;<7987X> gbmmrcoag'xm#j|i.sd,cf~)keas#jPpovq[beXizxnk1;1329V4*adgg|meka!rg-dvc(un&mht#mcky-q`Zvi|{UloRo|rde?2;543\:$knaavgkek+ta'nxm"h gbz-gim'{nT|cz}_fa\evtbo5=5?>5Z0.e`kkpaaoe%~k!hrg,qb*adp'iggu!}d^rmpwY`kVkx~hi38?10?P6(ojeezkgio/pe+bta&{l$knv!cmi{+wbXxg~ySjmParpfc9?9;;1^<"ilootemci)zo%l~k }f.e`|+ekcq%yhR~ats]dgZgtzlmT<><4U1-dgjhqn`ld"h gsd-vc)`kq$h`fv re]sjqtXojUjkh_010?P6(ojeezkgio/pe+bta&{l$knv!cmi{+wbXxg~ySjmParpfcZ77;;1^<"ilootemci)zo%l~k }f.e`|+ekcq%yhR~ats]dgZgtzlmT>><4U1-dgjhqn`ld"h gsd-vc)`kq$h`fv re]sjqtXojUjkh_211?P6(ojeezkgio/pe+bta&{l$knv!cmi{+wbXxg~ySjmParpfcZ24:2_;#jm`nwdjbj(un&myj#|i/fa{*fjlp&xoS}`{r^e`[duumnU>??5Z0.e`kkpaaoe%~k!hrg,qb*adp'iggu!}d^rmpwY`kVkx~hiP6208Q5)`kfd}jdh`.sd,cw`)zo%lou lljz,vaYwf}xTknQnssgd[2553\:$knaavgkek+ta'nxm"h gbz-gim'{nT|cz}_fa\evtboV28>6[?/faljs`nnf$yj"i}f/pe+be&jf`t"|k_qlwvZadWhyyijQ6379V4*adgg|meka!rg-dvc(un&mht#mcky-q`Zvi|{UloRo|rde\`4:76:=0Y=!hcnlubl`h&{l$kh!rg-dg}(ddbr$~iQnup\cfYf{{olSi?311<02>S7'nidb{hffn,qb*aun'xm#jmw.bnh|*tcWyd~Ril_`qqabYc95;5?;5Z0.e`kkpaaoe%~k!hrg,qb*adp'iggu!}d^rmpwY`kVkx~hiPd0>1:60<]9%lob`yfhdl*w`(o{l%~k!hcy,`hn~(zmU{by|Pgb]bwwc`Wm;7?3=9;T2,cfii~ocmc#|i/fpe*w`(ojr%oaew/sf\tkruWniTm~|jg^f28184>2_;#jm`nwdjbj(un&myj#|i/fa{*fjlp&xoS}`{r^e`[duumnUo=1;1379V4*adgg|meka!rg-dvc(un&mht#mcky-q`Zvi|{UloRo|rde\`4:16:<0Y=!hcnlubl`h&{l$kh!rg-dg}(ddbr$~iQnup\cfYf{{olSi?37?15?P6(ojeezkgio/pe+bta&{l$knv!cmi{+wbXxg~ySjmParpfcZb64148:6[?/faljs`nnf$yj"i}f/pe+be&jf`t"|k_qlwvZadWhyyijQk1=;=02=R8&mhccxiigm-vc)`zo$yj"ilx/aoo})ulVzexQhc^cpv`aXl8Q:9^m7<>S7'nidb{hffn,qb*aun'xm#jmw.bnh|*tcWyd~Ril_`qqabYc9R;;QR|jdtaf[}iuW83Tc<:k;T2,cfii~ocmc#|i/fpe*w`(ojr%oaew/sf\tkruWniTm~|jg^f2_46ZW{ooynkPxnp\657Xpf6;2<:8;T2,cfii~ocmc#|i/fpe*w`(ojr%oaew/sf\tkruWniTm~|jg^f2_46ZW{ooynkPxnp\65Yh0\]qaasdmVrd~R<>1^zl8586<>1^<"ilootemci)zo%l~k }f.e`|+ekcq%yhR~ats]dgZgtzlmTh0\]qaasdmVrd~R<>_n6g?P6(ojeezkgio/pe+bta&{l$knv!cmi{+wbXxg~ySjmParpfcZb6S8:VSkkubg\|jtX:;;Ttb2?>064?P6(ojeezkgio/pe+bta&{l$knv!cmi{+wbXxg~ySjmParpfcZb6S8:VSkkubg\|jtX:;Ud8n5Z0.e`kkpaaoe%~k!hrg,qb*adp'iggu!}d^rmpwY`kVkx~hiPd0Y24XYummhiRv`r^005Z~h494><6[?/faljs`nnf$yj"i}f/pe+be&jf`t"|k_qlwvZadWhyyijQk1Z33YZtbl|inSua}_312[}i;87UX[=:8;T2,cfii~ocmc#|i/fpe*w`(ojr%oaew/sf\tkruWniTm~|jg^f2_46ZW{ooynkPxnp\66Yh0\]qaasdmVrd~R<;1^zl858282_;#jm`nwdjbj(un&myj#|i/fa{*fjlp&xoS}`{r^e`[duumnUo=V??]^pf`pebWqeyS?:>_ym?4;YT_9><7X> gbmmrcoag'xm#j|i.sd,cf~)keas#jPpovq[beXizxnkRj>[02^[wcc}joTtb|P25]l0f=R8&mhccxiigm-vc)`zo$yj"ilx/aoo})ulVzexQhc^cpv`aXl8Q:=Rv`<1<64>S7'nidb{hffn,qb*aun'xm#jmw.bnh|*tcWyd~Ril_`qqabYc9R;;QR|jdtaf[}iuW;?:Sua30?]PS5203\:$knaavgkek+ta'nxm"h gbz-gim'{nT|cz}_fa\evtboVn:W<>R_sggqfcXpfxT>8Q`4b9V4*adgg|meka!rg-dvc(un&mht#mcky-q`Zvi|{UloRo|rde\`4]68TUyii{le^zlvZ419Vrd0=0:0:W3+behflbjb }f.eqb+ta'nis"nbdx.pg[uhszVmhSl}}ef]g5^77UVxnhxmj_ymq[706Wqe7<3Q\W164?P6(ojeezkgio/pe+bta&{l$knv!cmi{+wbXxg~ySjmParpfcZb6S8:VSkkubg\|jtX:?Ud8n5Z0.e`kkpaaoe%~k!hrg,qb*adp'iggu!}d^rmpwY`kVkx~hiPd0Y24XYummhiRv`r^045Z~h494><6[?/faljs`nnf$yj"i}f/pe+be&jf`t"|k_qlwvZadWhyyijQk1Z33YZtbl|inSua}_352[}i;87UX[=:8;T2,cfii~ocmc#|i/fpe*w`(ojr%oaew/sf\tkruWniTm~|jg^f2_46ZW{ooynkPxnp\62Yh;<1^<"ilootemci)zo%l~k }f.e`|+ekcq%yhR~ats]dgZgtzlmTh0278Q5)`kfd}jdh`.sd,cw`)zo%lou lljz,vaYwf}xTknQnssgd[a7X::?0Y=!hcnlubl`h&{l$kh!rg-dg}(ddbr$~iQnup\cfYf{{olSi?P3278Q5)`kfd}jdh`.sd,cw`)zo%lou lljz,vaYwf}xTknQnssgd[a7X<:?0Y=!hcnlubl`h&{l$kh!rg-dg}(ddbr$~iQnup\cfYf{{olSi?P5278Q5)`kfd}jdh`.sd,cw`)zo%lou lljz,vaYwf}xTknQnssgd[a7X>:?0Y=!hcnlubl`h&{l$kh!rg-dg}(ddbr$~iQnup\cfYf{{olSi?P7278Q5)`kfd}jdh`.sd,cw`)zo%lou lljz,vaYwf}xTknQnssgd[a7X0:?0Y=!hcnlubl`h&{l$kh!rg-dg}(ddbr$~iQnup\cfYf{{olSi?P93g8Q5)`kfd}jdh`.sd,cw`)zo%lou lljz,swYwf}xTnd`30?0e?P6(ojeezkgio/pe+bta&{l$knv!cmi{+rtXxg~ySoga<02=6`=R8&mhccxiigm-vc)`zo$yj"ilx/aoo})pzVzexQmio>2:7c<]9%lob`yfhdl*w`(o{l%~k!hcy,`hn~({U{by|Pbhl?6;4b3\:$knaavgkek+ta'nxm"h gbz-gim'~xT|cz}_ckm8685m2_;#jm`nwdjbj(un&myj#|i/fa{*fjlp&}yS}`{r^`jj929:l1^<"ilootemci)zo%l~k }f.e`|+ekcq%|~R~ats]amk:26;o0Y=!hcnlubl`h&{l$kh!rg-dg}(ddbr${Qnup\flh;>78n7X> gbmmrcoag'xm#j|i.sd,cf~)keas#z|Ppovq[goi4>49i6[?/faljs`nnf$yj"i}f/pe+be&jf`t"y}_qlwvZdnf525>h5Z0.e`kkpaaoe%~k!hrg,qb*adp'iggu!xr^rmpwYeag622?j4U1-dgjhqn`ld"h gsd-vc)`kq$h`fv ws]sjqtXj`dT0=d:W3+behflbjb }f.eqb+ta'nis"nbdx.uq[uhszVmh090=d:W3+behflbjb }f.eqb+ta'nis"nbdx.uq[uhszVmh080=d:W3+behflbjb }f.eqb+ta'nis"nbdx.uq[uhszVmh0;0=d:W3+behflbjb }f.eqb+ta'nis"nbdx.uq[uhszVmh0:0=d:W3+behflbjb }f.eqb+ta'nis"nbdx.uq[uhszVmh050=d:W3+behflbjb }f.eqb+ta'nis"nbdx.uq[uhszVmh040=c:W3+behflbjb }f.eqb+ta'nis"nbdx.uq[uhszVmhS=n5Z0.e`kkpaaoe%~k!hrg,qb*adp'iggu!xr^rmpwY`kV99o6[?/faljs`nnf$yj"i}f/pe+be&jf`t"y}_qlwvZadW=8h7X> gbmmrcoag'xm#j|i.sd,cf~)keas#z|Ppovq[beX=;i0Y=!hcnlubl`h&{l$kh!rg-dg}(ddbr${Qnup\cfY1:j1^<"ilootemci)zo%l~k }f.e`|+ekcq%|~R~ats]dgZ15k2_;#jm`nwdjbj(un&myj#|i/fa{*fjlp&}yS}`{r^e`[=4d3\:$knaavgkek+ta'nxm"h gbz-gim'~xT|cz}_fa\=65<]9%lob`yfhdl*w`(o{l%~k!hcy,`hn~({U{by|Pgb]bwwc`494886[?/faljs`nnf$yj"i}f/pe+be&jf`t"y}_qlwvZadWhyyij2>0?10?P6(ojeezkgio/pe+bta&{l$knv!cmi{+rtXxg~ySjmParpfc979;:1^<"ilootemci)zo%l~k }f.e`|+ekcq%|~R~ats]dgZgtzlm7>3=<;T2,cfii~ocmc#|i/fpe*w`(ojr%oaew/vp\tkruWniTm~|jg=1=76=R8&mhccxiigm-vc)`zo$yj"ilx/aoo})pzVzexQhc^cpv`a;<7987X> gbmmrcoag'xm#j|i.sd,cf~)keas#z|Ppovq[beXizxnk1;1329V4*adgg|meka!rg-dvc(un&mht#mcky-tvZvi|{UloRo|rde?2;543\:$knaavgkek+ta'nxm"h gbz-gim'~xT|cz}_fa\evtbo5=5?>5Z0.e`kkpaaoe%~k!hrg,qb*adp'iggu!xr^rmpwY`kVkx~hi38?10?P6(ojeezkgio/pe+bta&{l$knv!cmi{+rtXxg~ySjmParpfc9?9;;1^<"ilootemci)zo%l~k }f.e`|+ekcq%|~R~ats]dgZgtzlmT<><4U1-dgjhqn`ld"h gsd-vc)`kq$h`fv ws]sjqtXojUjkh_010?P6(ojeezkgio/pe+bta&{l$knv!cmi{+rtXxg~ySjmParpfcZ77;;1^<"ilootemci)zo%l~k }f.e`|+ekcq%|~R~ats]dgZgtzlmT>><4U1-dgjhqn`ld"h gsd-vc)`kq$h`fv ws]sjqtXojUjkh_211?P6(ojeezkgio/pe+bta&{l$knv!cmi{+rtXxg~ySjmParpfcZ24:2_;#jm`nwdjbj(un&myj#|i/fa{*fjlp&}yS}`{r^e`[duumnU>??5Z0.e`kkpaaoe%~k!hrg,qb*adp'iggu!xr^rmpwY`kVkx~hiP6208Q5)`kfd}jdh`.sd,cw`)zo%lou lljz,swYwf}xTknQnssgd[2553\:$knaavgkek+ta'nxm"h gbz-gim'~xT|cz}_fa\evtboV28>6[?/faljs`nnf$yj"i}f/pe+be&jf`t"y}_qlwvZadWhyyijQ6379V4*adgg|meka!rg-dvc(un&mht#mcky-tvZvi|{UloRo|rde\`4:76:=0Y=!hcnlubl`h&{l$kh!rg-dg}(ddbr${Qnup\cfYf{{olSi?311<02>S7'nidb{hffn,qb*aun'xm#jmw.bnh|*quWyd~Ril_`qqabYc95;5?;5Z0.e`kkpaaoe%~k!hrg,qb*adp'iggu!xr^rmpwY`kVkx~hiPd0>1:60<]9%lob`yfhdl*w`(o{l%~k!hcy,`hn~({U{by|Pgb]bwwc`Wm;7?3=9;T2,cfii~ocmc#|i/fpe*w`(ojr%oaew/vp\tkruWniTm~|jg^f28184>2_;#jm`nwdjbj(un&myj#|i/fa{*fjlp&}yS}`{r^e`[duumnUo=1;1379V4*adgg|meka!rg-dvc(un&mht#mcky-tvZvi|{UloRo|rde\`4:16:<0Y=!hcnlubl`h&{l$kh!rg-dg}(ddbr${Qnup\cfYf{{olSi?37?15?P6(ojeezkgio/pe+bta&{l$knv!cmi{+rtXxg~ySjmParpfcZb64148:6[?/faljs`nnf$yj"i}f/pe+be&jf`t"y}_qlwvZadWhyyijQk1=;=0f=R8&mhccxiigm-vc)`zo$yj"ilx/aoo})pzVzexQhc^cpv`aXl8Q:S7'nidb{hffn,qb*aun'xm#jmw.bnh|*quWyd~Ril_`qqabYc9R;;QR|jdtaf[}iuW8::Sua30?]PS5203\:$knaavgkek+ta'nxm"h gbz-gim'~xT|cz}_fa\evtboVn:W<>R_sggqfcXpfxT==Q`4b9V4*adgg|meka!rg-dvc(un&mht#mcky-tvZvi|{UloRo|rde\`4]68TUyii{le^zlvZ769Vrd0=0:0:W3+behflbjb }f.eqb+ta'nis"nbdx.uq[uhszVmhSl}}ef]g5^77UVxnhxmj_ymq[476Wqe7<3Q\W164?P6(ojeezkgio/pe+bta&{l$knv!cmi{+rtXxg~ySjmParpfcZb6S8:VSkkubg\|jtX98Ud8n5Z0.e`kkpaaoe%~k!hrg,qb*adp'iggu!xr^rmpwY`kVkx~hiPd0Y24XYummhiRv`r^315Z~h494><6[?/faljs`nnf$yj"i}f/pe+be&jf`t"y}_qlwvZadWhyyijQk1Z33YZtbl|inSua}_002[}i;87UX[=:8;T2,cfii~ocmc#|i/fpe*w`(ojr%oaew/vp\tkruWniTm~|jg^f2_46ZW{ooynkPxnp\57Yh0\]qaasdmVrd~R?<1^zl858282_;#jm`nwdjbj(un&myj#|i/fa{*fjlp&}yS}`{r^e`[duumnUo=V??]^pf`pebWqeyS<=>_ym?4;YT_9><7X> gbmmrcoag'xm#j|i.sd,cf~)keas#z|Ppovq[beXizxnkRj>[02^[wcc}joTtb|P12]l03=R8&mhccxiigm-vc)`zo$yj"ilx/aoo})pzVzexQhc^cpv`aXl8Q:Tc994U1-dgjhqn`ld"h gsd-vc)`kq$h`fv ws]sjqtXojUjkh_e3X55[Xzln~ohQwos]7[j73>2_;#jm`nwdjbj(un&myj#|i/fa{*fjlp&}yS}`{r^e`[duumnUo=V??]^pf`pebWqeyS8Q`469V4*adgg|meka!rg-dvc(un&mht#mcky-tvZvi|{UloRo|rde\`4]68TUyii{le^zlvZ3Xg8>h7X> gbmmrcoag'xm#j|i.sd,cf~)keas#z|Ppovq[beXizxnkRj>[02^[wcc}joTtb|P60]{k9699=<0Y=!hcnlubl`h&{l$kh!rg-dg}(ddbr${Qnup\cfYf{{olSi?T11_\v`brklUscQ9_n6`?P6(ojeezkgio/pe+bta&{l$knv!cmi{+rtXxg~ySjmParpfcZb6S8:VSkkubg\|jtX?8Usc1>11548Q5)`kfd}jdh`.sd,cw`)zo%lou lljz,swYwf}xTknQnssgd[a7\99WT~hjzcd]{kwY0Wf>h7X> gbmmrcoag'xm#j|i.sd,cf~)keas#z|Ppovq[beXizxnkRj>[02^[wcc}joTtb|P80]{k9699=<0Y=!hcnlubl`h&{l$kh!rg-dg}(ddbr${Qnup\cfYf{{olSi?T11_\v`brklUscQ7_n6a?P6(ojeezkgio/pe+bta&{l$knv!cmi{+rtXxg~ySjmParpfcZb6S8:VSkkubg\|jtX18Usc1>14g9V4*adgg|meka!rg-dvc(un&mht#mcky-tvZvi|{UloRo|rde\`4]68TUyii{le^zlvZ?6Wqe7<3Q\W165?P6(ojeezkgio/pe+bta&{l$knv!cmi{+rtXxg~ySjmParpfcZb6S8:VSkkubg\|jtX1Ve896[?/faljs`nnf$yj"i}f/pe+be&jf`t"y}_qlwvZadWhyyijQk1^201>S7'nidb{hffn,qb*aun'xm#jmw.bnh|*quWyd~Ril_`qqabYc9V;8:6[?/faljs`nnf$yj"i}f/pe+be&jf`t"y}_qlwvZadWhyyijQk1^3370=R8&mhccxiigm-vc)`zo$yj"ilx/aoo})pzVzexQhc^cpv`aXl8U9?85Z0.e`kkpaaoe%~k!hrg,qb*adp'iggu!xr^rmpwY`kVkx~hiPd0]070=R8&mhccxiigm-vc)`zo$yj"ilx/aoo})pzVzexQhc^cpv`aXl8U??85Z0.e`kkpaaoe%~k!hrg,qb*adp'iggu!xr^rmpwY`kVkx~hiPd0]670=R8&mhccxiigm-vc)`zo$yj"ilx/aoo})pzVzexQhc^cpv`aXl8U=?85Z0.e`kkpaaoe%~k!hrg,qb*adp'iggu!xr^rmpwY`kVkx~hiPd0]470=R8&mhccxiigm-vc)`zo$yj"ilx/aoo})pzVzexQhc^cpv`aXl8U3?85Z0.e`kkpaaoe%~k!hrg,qb*adp'iggu!xr^rmpwY`kVkx~hiPd0]:7g=R8&mhccxiigm-vc)`zo$yj"ic0/pg+btf{'xxx~!l0.enq}(di{xrbhz30?1a?P6(ojeezkgio/pe+bta&{l$ka>!re-dvdu)zz~x#n> glw{*fguzpdnx1?13c9V4*adgg|meka!rg-dvc(un&mg<#|k/fpbw+tt|z%h<"ibuy,`ewt~fl~7>3=m;T2,cfii~ocmc#|i/fpe*w`(oe:%~i!hr`q-vvrt'j:$k`{w.bcqv|hb|595?o5Z0.e`kkpaaoe%~k!hrg,qb*ak8'xo#j|ns/pppv)d8&mfyu laspzj`r;<79h7X> gbmmrcoag'xm#j|i.sd,ci6)zm%l~l}!rrvp+f6(ods"jcT0\,di4(j9:i0Y=!hcnlubl`h&{l$kh!rg-dh5(ul&mym~ }suq,g5)`e|r%k`U>]/enw+kt;j1^<"ilootemci)zo%l~k }f.eo4+tc'nxj#||tr-`4*aj}q$laVS!glq-iv5d3\:$knaavgkek+ta'nxm"h gm2-va)`zhy%~~z|/b2,chs&ngP8P hmr,nw6e<]9%lob`yfhdl*w`(o{l%~k!hl1,q`*auiz$yy} c1-dip~)odQ>Q#ibs/op75=R8&mhccxiigm-vc)`zo$yj"ic0/pg+btf{'xxx~!l0.t2858492_;#jm`nwdjbj(un&myj#|i/fn3*wb(o{kx"}{s.a3+s7;87;8<6[?/faljs`nnf$yj"i}f/pe+bj7&{n$ko|.sqww*e7';7=3=>;T2,cfii~ocmc#|i/fpe*w`(oe:%~i!hr`q-vvrt'j:$z<2>>013?P6(ojeezkgio/pe+bta&{l$ka>!re-dvdu)zz~x#n> v0>1:67<]9%lob`yfhdl*w`(o{l%~k!hl1,q`*auiz$yy} c1-u59499::0Y=!hcnlubl`h&{l$kh!rg-dh5(ul&mym~ }suq,g5)q9595?<5Z0.e`kkpaaoe%~k!hrg,qb*ak8'xo#j|ns/pppv)d8&|:0>0>319V4*adgg|meka!rg-dvc(un&mg<#|k/fpbw+tt|z%h<"x><5<05>S7'nidb{hffn,qb*aun'xm#jb?.sf,cwgt&{y"m?/w3?0;7482_;#jm`nwdjbj(un&myj#|i/fn3*wb(o{kx"}{s.a3+s7;=79:7X> gbmmrcoag'xm#j|i.sd,ci6)zm%l~l}!rrvp+f6(~86>2<=m;T2,cfii~ocmc#|i/fpe*w`(oe:%~i!hr`q-vvrt'j;$k`{w.bcqv|hb|5:5?o5Z0.e`kkpaaoe%~k!hrg,qb*ak8'xo#j|ns/pppv)d9&mfyu laspzj`r;979i7X> gbmmrcoag'xm#j|i.sd,ci6)zm%l~l}!rrvp+f7(ods"no}rxlfp949;k1^<"ilootemci)zo%l~k }f.eo4+tc'nxj#||tr-`5*aj}q$hm|vndv?7;5e3\:$knaavgkek+ta'nxm"h gm2-va)`zhy%~~z|/b3,chs&jky~t`jt=6=7f=R8&mhccxiigm-vc)`zo$yj"ic0/pg+btf{'xxx~!l1.enq}(`eR:V"jc>.l30g>S7'nidb{hffn,qb*aun'xm#jb?.sf,cwgt&{y"m>/fov|+ajS8W%k`}!mr1`?P6(ojeezkgio/pe+bta&{l$ka>!re-dvdu)zz~x#n? glw{*bk\:T$la~ bs2a8Q5)`kfd}jdh`.sd,cw`)zo%l`= }d.eqev(u{}y$o238Q5)`kfd}jdh`.sd,cw`)zo%l`= }d.eqev(u{}y$oS7'nidb{hffn,qb*aun'xm#jb?.sf,cwgt&{y"m>/w3?7;563\:$knaavgkek+ta'nxm"h gm2-va)`zhy%~~z|/b3,r4:4689;7X> gbmmrcoag'xm#j|i.sd,ci6)zm%l~l}!rrvp+f7(~86?2>?4U1-dgjhqn`ld"h gsd-vc)`d9$yh"i}ar,qwqu(k8%}=1:11228Q5)`kfd}jdh`.sd,cw`)zo%l`= }d.eqev(u{}y$oS7'nidb{hffn,qb*aun'xm#jb?.sf,cwgt&{y"m`mq302>S7'nidb{hffn,qb*aun'xm#jb?.sf,cwgt&{y"|nm^gntqXnkUb?>5Z0.e`kkpaaoe%~k!hrg,qb*ak8'xo#j|ns/pppv)uidUna}zv_h0f?P6(ojeezkgio/pe+bta&{l$ka>!re-q`Zvi|{U{`x}><1<7g>S7'nidb{hffn,qb*aun'xm#jb?.sf,vuhsz&GfyuQhc1,`kphsS8:VSJ@K_4:\kZkrpV8TCXZ>0073?P6(ojeezkgio/pe+bta&{l$ka>!re-qtkru'Dg~tRil0/alqkr\99WTKCJP59]l[hsW;UDYY??1^QT41e<]9%lob`yfhdl*w`(o{l%~k!hl1,q`*twf}x$A`{w_fa3*firf}Q:RAZT03215=R8&mhccxiigm-vc)`zo$yj"ic0/pg+wvi|{%FaxvPgb2-gjsi|R;;QRIAD^7;[jYj}qU9SB[[103\WR63i2_;#jm`nwdjbj(un&myj#|i/fn3*wb(zyd~"Cbuy]dg5(dg|dW<>R_FLG[0>XgVg~tRRAZT237f>S7'nidb{hffn,qb*aun'xm#jb?.sf,vuhsz&GfyuQhc1,`kphsS8:VSJ@K_4:\kZkrpV8TCXZ;15`8Q5)`kfd}jdh`.sd,cw`)zo%l`= }d.psjqt(EdsSjm?.bmvjq]68TULBIQ:8^m\ip~X:VE^X8?;c:W3+behflbjb }f.eqb+ta'nf;"j rqlwv*Kj}qUlo= lotlw_46ZWNDOS86Po^ov|Z4XG\^>=<:m;T2,cfii~ocmc#|i/fpe*w`(oe:%~i!}povq+HkrpVmh<#m`uovX55[XOGNT95Q`_lw{[7YH]]?98o5Z0.e`kkpaaoe%~k!hrg,qb*ak8'xo#~ats-Nip~Xoj:%ob{atZ33YZAILV?3SbQbuy]1[JSS>8>i7X> gbmmrcoag'xm#j|i.sd,ci6)zm%y|cz}/Lov|Zad8'idyczT11_\CKBX=1UdS`{w_3]LQQ163=?;T2,cfii~ocmc#|i/fpe*w`(oe:%~i!}povq+be7&je~by2<>228Q5)`kfd}jdh`.sd,cw`)zo%l`= }d.psjqt(oj:%ob{at=6=75=R8&mhccxiigm-vc)`zo$yj"ic0/pg+wvi|{%lo= lotlw808482_;#jm`nwdjbj(un&myj#|i/fn3*wb(zyd~"il0/alqkr;>79;7X> gbmmrcoag'xm#j|i.sd,ci6)zm%y|cz}/fa3*firf}6<2>>4U1-dgjhqn`ld"h gsd-vc)`d9$yh"|nup,cf6)kfex161319V4*adgg|meka!rg-dvc(un&mg<#|k/srmpw)`k9$hcx`{<8<7<>S7'nidb{hffn,qb*aun'xm#jb?.sf,vuhsz&mh<#m`uovX55[XOGNT95Q`_lw{[7YH]]6:<3:8;T2,cfii~ocmc#|i/fpe*w`(oe:%~i!}povq+be7&je~byU>0\]DJAY20VeTaxvP2^MVP979<>1^<"ilootemci)zo%l~k }f.eo4+tc'{zex!hc1,`kphsS8:VSJ@K_4:\kZkrpV8TCXZ32?64?P6(ojeezkgio/pe+bta&{l$ka>!re-qtkru'ni;"naznuY24XY@FMU>4RaPmtz\6ZIR\5958:5Z0.e`kkpaaoe%~k!hrg,qb*ak8'xo#~ats-dg5(dg|dW<>R_FLG[0>XgVg~tR7><7X> gbmmrcoag'xm#j|i.sd,ci6)zm%y|cz}/fa3*firf}Q:RAZT=5=02=R8&mhccxiigm-vc)`zo$yj"ic0/pg+wvi|{%lo= lotlw_46ZWNDOS86Po^ov|Z4XG\^743:8;T2,cfii~ocmc#|i/fpe*w`(oe:%~i!}povq+be7&je~byU>0\]DJAY20VeTaxvP2^MVP9?9:o1^<"ilootemci)zo%l~k }f.eo4+tc'{zex!hc1,`kphsW89;7X> gbmmrcoag'xm#j|i.sd,ci6)zm%y|cz}/fa3*firf}U:S7'nidb{hffn,qb*aun'xm#jb?.sf,vuhsz&mh<#m`uov\17`<]9%lob`yfhdl*w`(o{l%~k!hl1,q`*twf}x$kn>!cnwmpZ05n2_;#jm`nwdjbj(un&myj#|i/fn3*wb(zyd~"il0/alqkrX?;l0Y=!hcnlubl`h&{l$kh!rg-dh5(ul&x{by| gb2-gjsi|V29j6[?/faljs`nnf$yj"i}f/pe+bj7&{n$~}`{r.e`4+eh}g~T5>=4U1-dgjhqn`ld"h gsd-vc)`d9$yh"|nup,cf6)kfexRj><1<00>S7'nidb{hffn,qb*aun'xm#jb?.sf,vuhsz&mh<#m`uov\`4:687987X> gbmmrcoag'xm#j|i.sd,ci6)zm%y|cz}/fa3*firf}Uo=1?1329V4*adgg|meka!rg-dvc(un&mg<#|k/srmpw)`k9$hcx`{_e3?6;543\:$knaavgkek+ta'nxm"h gm2-va)uxg~y#jm?.bmvjqYc9595?>5Z0.e`kkpaaoe%~k!hrg,qb*ak8'xo#~ats-dg5(dg|dSi?34?10?P6(ojeezkgio/pe+bta&{l$ka>!re-qtkru'ni;"naznu]g5939;:1^<"ilootemci)zo%l~k }f.eo4+tc'{zex!hc1,`kphsWm;7:3=<;T2,cfii~ocmc#|i/fpe*w`(oe:%~i!}povq+be7&je~byQk1=5=76=R8&mhccxiigm-vc)`zo$yj"ic0/pg+wvi|{%lo= lotlw[a7;07987X> gbmmrcoag'xm#j|i.sd,ci6)zm%y|cz}/fa3*firf}Uo=171339V4*adgg|meka!rg-dvc(un&mg<#|k/srmpw)`k9$hcx`{_e3\464<]9%lob`yfhdl*w`(o{l%~k!hl1,q`*twf}x$kn>!cnwmpZb6W8987X> gbmmrcoag'xm#j|i.sd,ci6)zm%y|cz}/fa3*firf}Uo=R??339V4*adgg|meka!rg-dvc(un&mg<#|k/srmpw)`k9$hcx`{_e3\664<]9%lob`yfhdl*w`(o{l%~k!hl1,q`*twf}x$kn>!cnwmpZb6W:997X> gbmmrcoag'xm#j|i.sd,ci6)zm%y|cz}/fa3*firf}Uo=R:<2:W3+behflbjb }f.eqb+ta'nf;"j rqlwv*ad8'idyczPd0]677=R8&mhccxiigm-vc)`zo$yj"ic0/pg+wvi|{%lo= lotlw[a7X>:80Y=!hcnlubl`h&{l$kh!rg-dh5(ul&x{by| gb2-gjsi|Vn:S:==;T2,cfii~ocmc#|i/fpe*w`(oe:%~i!}povq+be7&je~byQk1^:06>S7'nidb{hffn,qb*aun'xm#jb?.sf,vuhsz&mh<#m`uov\`4Y>12`8Q5)`kfd}jdh`.sd,cw`)zo%l`= xr.etev(p{}y$ol4U1-dgjhqn`ld"h gsd-vc)`d9$|~"ixar,twqu(k8%laxv!c`pq}kcs4;48n6[?/faljs`nnf$yj"i}f/pe+bj7&~x$kzo|.vqww*e6'ng~t#mnrs{maq:46:h0Y=!hcnlubl`h&{l$kh!rg-dh5(pz&m|m~ xsuq,g4)`e|r%ol|}yogw8184k2_;#jm`nwdjbj(un&myj#|i/fn3*rt(o~kx"z}{s.a2+bkrp'mfW=S!gl3-i45d3\:$knaavgkek+ta'nxm"h gm2-sw)`hy%{~z|/b3,chs&ngP=P hmr,nw6e<]9%lob`yfhdl*w`(o{l%~k!hl1,tv*apiz$|y} c0-dip~)odQ9Q#ibs/op7f=R8&mhccxiigm-vc)`zo$yj"ic0/uq+bqf{'}xx~!l1.enq}(`eR9V"jc|.lq0g>S7'nidb{hffn,qb*aun'xm#jb?.vp,crgt&~y"m>/fov|+ajS=W%k`}!mr1`?P6(ojeezkgio/pe+bta&{l$ka>!ws-dsdu)z~x#n? glw{*bk\=T$la~ bs228Q5)`kfd}jdh`.sd,cw`)zo%l`= xr.etev(p{}y$o!ws-dsdu)z~x#n? v0>1:4573\:$knaavgkek+ta'nxm"h gm2-sw)`hy%{~z|/b3,r4:46:;0Y=!hcnlubl`h&{l$kh!rg-dh5(pz&m|m~ xsuq,g4)q9595=>>4U1-dgjhqn`ld"h gsd-vc)`d9$|~"ixar,twqu(k8%}=1:1309V4*adgg|meka!rg-dvc(un&mg<#y}/fubw+qt|z%h="x><5<275=R8&mhccxiigm-vc)`zo$yj"ic0/uq+bqf{'}xx~!l1.t2808492_;#jm`nwdjbj(un&myj#|i/fn3*rt(o~kx"z}{s.a2+s7;=7;8n6[?/faljs`nnf$yj"i}f/pe+bj7&~x$kzo|.vqww*e5'ng~t#mnrs{maq:76:h0Y=!hcnlubl`h&{l$kh!rg-dh5(pz&m|m~ xsuq,g7)`e|r%ol|}yogw8484j2_;#jm`nwdjbj(un&myj#|i/fn3*rt(o~kx"z}{s.a1+bkrp'ij~waeu>1:6d<]9%lob`yfhdl*w`(o{l%~k!hl1,tv*apiz$|y} c3-dip~)khxyuck{<2<0f>S7'nidb{hffn,qb*aun'xm#jb?.vp,crgt&~y"m=/fov|+efz{seiy2;>2a8Q5)`kfd}jdh`.sd,cw`)zo%l`= xr.etev(p{}y$o?!hmtz-ch]7U'mf=#c>3b9V4*adgg|meka!rg-dvc(un&mg<#y}/fubw+qt|z%h>"ibuy,di^7Z&ngx"`}m4U1-dgjhqn`ld"h gsd-vc)`d9$|~"ixar,twqu(k;%laxv!glY7Y+aj{'gx?n5Z0.e`kkpaaoe%~k!hrg,qb*ak8'}y#jyns/uppv)d:&mfyu hmZ7^*bkt&dy8<6[?/faljs`nnf$yj"i}f/pe+bj7&~x$kzo|.vqww*e5';7<3=>;T2,cfii~ocmc#|i/fpe*w`(oe:%{!hw`q-svrt'j8$z<2?>013?P6(ojeezkgio/pe+bta&{l$ka>!ws-dsdu)z~x#n< v0>2:67<]9%lob`yfhdl*w`(o{l%~k!hl1,tv*apiz$|y} c3-u59799::0Y=!hcnlubl`h&{l$kh!rg-dh5(pz&m|m~ xsuq,g7)q9585?<5Z0.e`kkpaaoe%~k!hrg,qb*ak8'}y#jyns/uppv)d:&|:0?0>319V4*adgg|meka!rg-dvc(un&mg<#y}/fubw+qt|z%h>"x><2<05>S7'nidb{hffn,qb*aun'xm#jb?.vp,crgt&~y"m=/w3?7;7482_;#jm`nwdjbj(un&myj#|i/fn3*rt(o~kx"z}{s.a1+s7;<79:7X> gbmmrcoag'xm#j|i.sd,ci6){%l{l}!wrvp+f4(~86?2<=?;T2,cfii~ocmc#|i/fpe*w`(oe:%{!hw`q-svrt'j8$z<2:>238Q5)`kfd}jdh`.sd,cw`)zo%l`= xr.etev(p{}y$o?!y1=7=57b<]9%lob`yfhdl*w`(o{l%~k!hl1,tv*apiz$|y} cnos56`<]9%lob`yfhdl*w`(o{l%~k!hl1,tv*apiz$|y} cnos5ZAILV=9SbQBUY]21Zi5l2_;#jm`nwdjbj(un&myj#|i/fn3*rt(o~kx"z}{s.aliu44=2_;#jm`nwdjbj(un&myj#|i/fn3*rt(o~kx"z}{s.pbiZ`rdeUmnRg<2:W3+behflbjb }f.eqb+ta'nf;"z| gvcp*rus{&xjaRhzlm]j6d=R8&mhccxiigm-vc)`zo$yj"ic0/uq+wgjW~xTicQf13a8Q5)`kfd}jdh`.sd,cw`)zo%l`= xr.pbiZquWldTe319V4*adgg|meka!rg-dvc(un&mg<#y}/scn[rtXmgUb=Rmjpnv02>S7'nidb{hffn,qb*aun'xm#jb?.vp,vdkX{UnbRg>_bgskqYNF_U;9<5Z0.e`kkpaaoe%~k!hrg,qb*ak8'}y#z~ats-Nip~Xo`i;"jm?.bmvjq]68TULBIQ80^m\ip~X:VE^X<>>549V4*adgg|meka!rg-dvc(un&mg<#y}/vrmpw)Je|rTkdm?.fa3*firf}Q:RAZT022[VQ7=81^<"ilootemci)zo%l~k }f.eo4+qu'~zex!Bmtz\cle7∋"naznuY24XY@FMU<_RU30c=R8&mhccxiigm-vc)`zo$yj"ic0/uq+rvi|{%FaxvPgha3*be7&je~byU>0\]DJAY08VeTaxvP2^MVP7343\:$knaavgkek+ta'nxm"h gm2-sw)pxg~y#@czx^ejg5(`k9$hcx`{[02^[BHCW>:TcRczx^0\KPR5WZ]99=5Z0.e`kkpaaoe%~k!hrg,qb*ak8'}y#z~ats-Nip~Xo`i;"jm?.bmvjq]68TULBIQ80^m\ip~X:VE^X>?:0:W3+behflbjb }f.eqb+ta'nf;"z| wqlwv*Kj}qUlen>!gb2-gjsi|R;;QRIAD^53[jYj}qU9SB[[4073?P6(ojeezkgio/pe+bta&{l$ka>!ws-ttkru'Dg~tRifc1,dg5(dg|dW<>R_FLG[26XgVg~tR!gb2-gjsi|R;;QRIAD^53[jYj}qU9SB[[7073?P6(ojeezkgio/pe+bta&{l$ka>!ws-ttkru'Dg~tRifc1,dg5(dg|dW<>R_FLG[26XgVg~tR278Q5)`kfd}jdh`.sd,cw`)zo%l`= xr.usjqt(o`i;"jm?.bmvjq:66:?0Y=!hcnlubl`h&{l$kh!rg-dh5(pz&}{by| gha3*be7&je~by2=>278Q5)`kfd}jdh`.sd,cw`)zo%l`= xr.usjqt(o`i;"jm?.bmvjq:46:?0Y=!hcnlubl`h&{l$kh!rg-dh5(pz&}{by| gha3*be7&je~by2;>278Q5)`kfd}jdh`.sd,cw`)zo%l`= xr.usjqt(o`i;"jm?.bmvjq:26:?0Y=!hcnlubl`h&{l$kh!rg-dh5(pz&}{by| gha3*be7&je~by29>278Q5)`kfd}jdh`.sd,cw`)zo%l`= xr.usjqt(o`i;"jm?.bmvjq:06:?0Y=!hcnlubl`h&{l$kh!rg-dh5(pz&}{by| gha3*be7&je~by27>278Q5)`kfd}jdh`.sd,cw`)zo%l`= xr.usjqt(o`i;"jm?.bmvjq:>6=n0Y=!hcnlubl`h&{l$kh!rg-dh5(pz&}{by| gha3*be7&je~byU>0\]DJAY08VeTaxvP2^MVP9776=i0Y=!hcnlubl`h&{l$kh!rg-dh5(pz&}{by| gha3*be7&je~byU>0\]DJAY08VeTaxvP2^MVP979R_FLG[26XgVg~tR7:1e<]9%lob`yfhdl*w`(o{l%~k!hl1,tv*qwf}x$kdm?.fa3*firf}Q:RAZT=7=0f=R8&mhccxiigm-vc)`zo$yj"ic0/uq+rvi|{%len>!gb2-gjsi|R;;QRIAD^53[jYj}qU9SB[[<7<7g>S7'nidb{hffn,qb*aun'xm#jb?.vp,suhsz&mbo= hc1,`kphsS8:VSJ@K_62\kZkrpV8TCXZ37?6`?P6(ojeezkgio/pe+bta&{l$ka>!ws-ttkru'nch<#il0/alqkr\99WTKCJP71]l[hsW;UDYY27>5a8Q5)`kfd}jdh`.sd,cw`)zo%l`= xr.usjqt(o`i;"jm?.bmvjq]68TULBIQ80^m\ip~X:VE^X171359V4*adgg|meka!rg-dvc(un&mg<#y}/vrmpw)`aj:%kn>!cnwmpZ74=2_;#jm`nwdjbj(un&myj#|i/fn3*rt(yd~"ifc1,dg5(dg|dS<><4:W3+behflbjb }f.eqb+ta'nf;"z| wqlwv*ank9$lo= lotlw[7533\:$knaavgkek+ta'nxm"h gm2-sw)pxg~y#jgl0/e`4+eh}g~T?>:4U1-dgjhqn`ld"h gsd-vc)`d9$|~"ynup,cle7∋"naznu]771=R8&mhccxiigm-vc)`zo$yj"ic0/uq+rvi|{%len>!gb2-gjsi|V?886[?/faljs`nnf$yj"i}f/pe+bj7&~x${}`{r.ejg5(`k9$hcx`{_717?P6(ojeezkgio/pe+bta&{l$ka>!ws-ttkru'nch<#il0/alqkrX?:>0Y=!hcnlubl`h&{l$kh!rg-dh5(pz&}{by| gha3*be7&je~byQ7359V4*adgg|meka!rg-dvc(un&mg<#y}/vrmpw)`aj:%kn>!cnwmpZ?402_;#jm`nwdjbj(un&myj#|i/fn3*rt(yd~"ifc1,dg5(dg|dSi?30?1:?P6(ojeezkgio/pe+bta&{l$ka>!ws-ttkru'nch<#il0/alqkrXl86:<3=7;T2,cfii~ocmc#|i/fpe*w`(oe:%{!xpovq+bod8'mh<#m`uov\`4:66:20Y=!hcnlubl`h&{l$kh!rg-dh5(pz&}{by| gha3*be7&je~byQk1=0=7==R8&mhccxiigm-vc)`zo$yj"ic0/uq+rvi|{%len>!gb2-gjsi|Vn:0>0<8:W3+behflbjb }f.eqb+ta'nf;"z| wqlwv*ank9$lo= lotlw[a7;<7937X> gbmmrcoag'xm#j|i.sd,ci6){%||cz}/fk`4+ad8'idyczPd0>6:6><]9%lob`yfhdl*w`(o{l%~k!hl1,tv*qwf}x$kdm?.fa3*firf}Uo=181399V4*adgg|meka!rg-dvc(un&mg<#y}/vrmpw)`aj:%kn>!cnwmpZb64>4846[?/faljs`nnf$yj"i}f/pe+bj7&~x${}`{r.ejg5(`k9$hcx`{_e3?<;5?3\:$knaavgkek+ta'nxm"h gm2-sw)pxg~y#jgl0/e`4+eh}g~Th<26>258Q5)`kfd}jdh`.sd,cw`)zo%l`= xr.usjqt(o`i;"jm?.bmvjqYc9V:8o6[?/faljs`nnf$yj"i}f/pe+bj7&~x${}`{r.ejg5(`k9$hcx`{_e3\4Zbwg}9<7X> gbmmrcoag'xm#j|i.sd,ci6){%||cz}/fk`4+ad8'idyczPd0]27==R8&mhccxiigm-vc)`zo$yj"ic0/uq+rvi|{%len>!gb2-gjsi|Vn:S<><7:W3+behflbjb }f.eqb+ta'nf;"z| wqlwv*ank9$lo= lotlw[a7X::=0Y=!hcnlubl`h&{l$kh!rg-dh5(pz&}{by| gha3*be7&je~byQk1^103>S7'nidb{hffn,qb*aun'xm#jb?.vp,suhsz&mbo= hc1,`kphsWm;T8>94U1-dgjhqn`ld"h gsd-vc)`d9$|~"ynup,cle7∋"naznu]g5Z34?2_;#jm`nwdjbj(un&myj#|i/fn3*rt(yd~"ifc1,dg5(dg|dSi?P6258Q5)`kfd}jdh`.sd,cw`)zo%l`= xr.usjqt(o`i;"jm?.bmvjqYc9V=8;6[?/faljs`nnf$yj"i}f/pe+bj7&~x${}`{r.ejg5(`k9$hcx`{_e3\<61<]9%lob`yfhdl*w`(o{l%~k!hl1,tv*qwf}x$kdm?.fa3*firf}Uo=R7<9:W3+behflbjb }f.eqb+ta'nf;"z| wqlwv*ank9$lo= lotlw[a4;994846[?/faljs`nnf$yj"i}f/pe+bj7&~x${}`{r.ejg5(`k9$hcx`{_e0?6;5?3\:$knaavgkek+ta'nxm"h gm2-sw)pxg~y#jgl0/e`4+eh}g~Th?2<>2:8Q5)`kfd}jdh`.sd,cw`)zo%l`= xr.usjqt(o`i;"jm?.bmvjqYc:5>5?55Z0.e`kkpaaoe%~k!hrg,qb*ak8'}y#z~ats-dmf6)oj:%ob{at^f1808402_;#jm`nwdjbj(un&myj#|i/fn3*rt(yd~"ifc1,dg5(dg|dSi<36?1;?P6(ojeezkgio/pe+bta&{l$ka>!ws-ttkru'nch<#il0/alqkrXl;6<2>64U1-dgjhqn`ld"h gsd-vc)`d9$|~"ynup,cle7∋"naznu]g69>9;11^<"ilootemci)zo%l~k }f.eo4+qu'~zex!hib2-cf6)kfexRj=<8<0<>S7'nidb{hffn,qb*aun'xm#jb?.vp,suhsz&mbo= hc1,`kphsWm8T===8;T2,cfii~ocmc#|i/fpe*w`(oe:%{!xpovq+bod8'mh<#m`uov\`7Y5;>1^<"ilootemci)zo%l~k }f.eo4+qu'~zex!hib2-cf6)kfexRj=_214?P6(ojeezkgio/pe+bta&{l$ka>!ws-ttkru'nch<#il0/alqkrXl;U??:5Z0.e`kkpaaoe%~k!hrg,qb*ak8'}y#z~ats-dmf6)oj:%ob{at^f1[0503\:$knaavgkek+ta'nxm"h gm2-sw)pxg~y#jgl0/e`4+eh}g~Th?Q9369V4*adgg|meka!rg-dvc(un&mg<#y}/vrmpw)`aj:%kn>!cnwmpZb5W>9<7X> gbmmrcoag'xm#j|i.sd,ci6){%||cz}/fk`4+ad8'idyczPd3];72=R8&mhccxiigm-vc)`zo$yj"ic0/uq+rvi|{%len>!gb2-gjsi|Vn9S4==;T2,cfii~ocmc#|i/fpe*w`(oe:%{!xr^rmpwYwd|y:0=0Piot0=>S7'nidb{hffn,qb*aun'xm#jb?.vp,swYwf}xT|a{|1=2=[lhq9VCEZR><2:W3+behflbjb }f.eqb+ta'nf;"z| ws]sjqtXxex>1?1_hlu7<=R8&mhccxiigm-vc)`zo$yj"ic0/uq+rtXxg~yS}bzs3>2:Zoi~8UBB[Q?849V4*adgg|meka!rg-dvc(un&gna"imm/eaib(`jdmj"cijcb,aib)edbUfi`Qheogqeqiu'kgei lsup,vdkkgfzP

3\:$knaavgkek+ta'nxm"h rrvahn)io~xo~~z!gsqw*wbXzz~T~hi31?1:?P6(ojeezkgio/pe+bta&{l$~~zmlj-mcrtczz~%k}{.sf\vvrXzlm7>3=7;T2,cfii~ocmc#|i/fpe*w`(zz~i`f!agvpgvvr)o{y"jPrrv\v`aX9:20Y=!hcnlubl`h&{l$kh!rg-qwqdkc&dl{j}su,dvvr)zmUyyQ}ef]17<=R8&mhccxiigm-vc)`zo$yj"||tcnh+kapzmxxx#i}su,tvZtt|Vxnk1>1389V4*adgg|meka!rg-dvc(un&xxxobd/oetvatt|'myy xr^pppZtbo5;5?55Z0.e`kkpaaoe%~k!hrg,qb*tt|kf`#cixreppp+au{}$|~R||t^pfcZ6402_;#jm`nwdjbj(un&myj#|i/sqwfim(fn}yh}{.fppp+quW{ySkh_00b?P6(ojeezkgio/pe+bta&{l$~~zmlj-q`Ztt|Vkx~hi=d:W3+behflbjb }f.eqb+ta'{ynae re]qwqYf{{olSi?=d:W3+behflbjb }f.eqb+ta'{ynae re]qwqYf{{olSi<=9:W3+behflbjb }f.eqb+ta'{ynae re]qwqYdgdh9m6[?/faljs`nnf$yj"i}f/pe+wusjea$~iQ}su]`khd6:h1^<"ilootemci)zo%l~k }f.pppgjl'~xT~~zParpfc7b<]9%lob`yfhdl*w`(o{l%~k!}su`oo*quW{ySl}}ef]g57b<]9%lob`yfhdl*w`(o{l%~k!}su`oo*quW{ySl}}ef]g667<]9%lob`yfhdl*w`(o{l%~k!}su`oo*quW{ySl}}ef]qwqvh|;30Y=!hcnlubl`h&{l$kh!rg-qwqdkc&}yS}{_bmnf7g<]9%lob`yfhdl*w`(o{l%~k!}su`oo*quW{ySnabb0c8QVCUWHFBM^m4URGQ[SOTAKFN?6XLC89UM@QX\PZN:6YJA=2=2>QBI5;5:6YJA=0=<>QBI591<384WDC?7;0<_LH7<384WD@?5;0<_LH7>364WD@?7?69>2]NN1=1b:UQMQCXN@XXXn5XRHVF[HICMVKh7Z\FTD]NKACXJ8n0TDBFNY/[@G&7&8*XXXL/0/3#EVENA?1S_YBFB69[WQY@FM=0T^ZPVBAa?]YDG[OTECH@6:ZgfZOcn2RodR^}ilTfvvohf8:0TicPM`hlvScu{`ee==5Wdl]Nmkiu^lxxeb`:e:]\[]JIEVUTP_^W3+behflbjb }f.eqb+ta'nis"nbdx.O{kwYULVZEX_U<]^PG[UHSZR>VSUA]_60\kZ~h494:945P_^ZOJHYXW8;TSR[?/faljs`nnf$yj"i}f/pe+be&jf`t"Cwos]Q@ZVI\[Q=QR\K_QLWV^1ZWQEYS;7Po^zl8586=01TSRVCNL]\[44XWV_;#jm`nwdjbj(un&myj#|i/fa{*fjlp&GscQXR^RMPW]>UV]YS]@[RZ33YZ^HZV;TcRv`<1<21==XWVRGB@QP_01\[ZS7'nidb{hffn,qb*aun'xm#jmw.bnh|*Kg{U\^R^ATSY0YZQUWYD_^V:R_YMQ[2YhWqe7<3?:8:]\[]JIEVUT=9QP_T2,cfii~ocmc#|i/fpe*w`(ojr%oaew/LzlvZQUWYD_^V8R_VP\TKRUS>WTTB\P4^m\|j:768927RQPXMLN[ZY6=VUTY=!hcnlubl`h&{l$kh!rg-qwqdkc&}yS}{_bmnf42e3VUTTA@B_^]22ZYX]9%lob`yfhdl*w`(o{l%~k!hl1,tv*qwf}x$kdm?.fa3*firf}Uo=R>Pdqmw1<=XWVRGB@QP_0]\[P6(ojeezkgio/pe+bta&{l$ka>!re-qtkru'Dg~tRil0/alqkr\99WTKCJP59]l[hsW;UDYY;=5c9\[Z^KFDUTS?QP_T2,cfii~ocmc#|i/fpe*w`(ojr%oaew/sf\tkruWniTm~|jg^f2_46ZW{ooynkPxnp\657Xpf6;2<;n;^]\\IHJWVU8SRQZ0.e`kkpaaoe%~k!hrg,qb*adp'iggu!xr^rmpwY`kVkx~hiPd0Y24XYummhiRv`r^42[}i;87;>56QP_YNMIZYX0\]DJAY20VeTaxvP2^MVP672>2UTSUBAM^]\1ZYX]9%lob`yfhdl*w`(o{l%~k!hcy,`hn~(zmU{by|Pgb]bwwc`Wm;P==SPrdfvg`Yg{U:4Ra>549\[Z^KFDUTS;QP_T2,cfii~ocmc#|i/fpe*w`(ojr%oaew/vp\tkruWniTm~|jg^f2_46ZW{ooynkPxnp\0Zi6=11TSRVCNL]\[2YXW\:$knaavgkek+ta'nxm"h gbz-gim'Drd~RY]_QLWV^6ZW^XT\CZ][0_\\JTX99UdSua30?36<>YXWQFEARQP8^]\Q5)`kfd}jdh`.sd,cw`)zo%lou lljz,I}iuW[NT\CZ][0_\VAYWF]XP>PQWOS]40ZiXpf6;2<;6;^]\\IHJWVU2SRQZ0.e`kkpaaoe%~k!hrg,qb*adp'iggu!Bxnp\VAYWF]XP5PQ]D^RMPW]68TUSC_Q96^m\|j:768i0mac`su]fiur~j2kgab}{_gwoh42gaVkySio{a^alqkr/: ;?7lvf_`p\`drfWje~by&<)068e}oXi{UomyoPcnwmp-2.9?1jtdQnr^fbpdYdg|d094?>d9b|lYfzVnieyk}r068e}oXi{Ubbgklte`jp`tu9=1jtdQnr^kmn`es{kci|i;`zj[dtXgoyjaax1c9b|lYfzVzycjQjmqvz[qnumzb#<$?m;`zj[dtXx{elShctx]wlwct`!;"=o5nxh]bvZvugnUna}zv_ujqavn/: ;i7lvf_`p\twi`Wlg{xtQ{hsgpl-5.9m1jtdQnr^rqkbYbey~rSyf}erj?7?699h1jtdQnr^rqkbYa}efTxe|jsi*3-4ga:c{mZguWyxdkRhzlm]wlwct`!9"=n5nxh]bvZvugnUmyabPtipfwm:4294:;6owi^cq[wbXlh~jSnaznu*3-417:c{mZguW{nThlzn_bmvjq.5!8=0mugPas]q`Zbf|hUhcx`{(2+23>gaVkySjPd`vb[firf}"?%<74ayk\ewYulVnjxlQlotlw81<76l1jtdQnr^qamqcuz8:0mugPas]vjacunee|=:5nxh]bvZquWmkmRm`uov+4,703hrbSl|Pws]geqgXkfex%?&169b|lYfzV}ySio{a^alqkr/: ;<7lvf_`p\swYci}kTob{at)1*52=fp`Uj~Ry}_ecweZeh}g~#8$?6;`zj[dtX{UomyoPcnwmp92=87;?7lvf_`u\`drfWje~by&?)068e}oXi~UomyoPcnwmp-7.9=1jtdQnw^fbpdYdg|d$?'>4:c{mZgpWmkmRm`uov+7,733hrbSlyPd`vb[firf}"?%<84ayk\erYci}kTob{at=694;cgaVk|Sbxjrgnls4dgaVk|S}|`g^gntqX|axne&>)0`8e}oXi~U{~biPelrw}Zrozlyc$?'>b:c{mZgpWyxdkRkbpu{\pmtb{a"8%1169b|lYfVxoSio{a^alqkr/8 ;<7lvf_`u\vaYci}kTob{at)3*52=fp`Uj{R|k_ecweZeh}g~#>$?8;`zj[dqXzmUomyoPcnwmp-5.9>1jtdQnw^pg[agsiVidycz'4(3:?d~nWh}T~iQkauc\gjsi|5>1<3k4ayk\erYtj`~n~??;`zj[dqX}gnn~kb`w058e}oXi~U|~Rjnt`]`kphs 9#:;6owi^ct[rtXlh~jSnaznu*2-417:c{mZgpW~xThlzn_bmvjq.4!8=0mugPav]tvZbf|hUhcx`{(5+2=>gaVk|Sz|Pd`vb[firf}6?6=0>3:c{mZdXlh~jSnaznu*3-45gaVhT|ah_gwohZrozlyc$='>9:c{mZdXx{elSk{cl^vkv`uo 8#:56owi^`\twi`Wog`Rzgrdqk,7/612kseRlPpsmd[cskdV~c~h}g(2+2f>gaVhT|ah_gwohZrozlyc0>4?>048e}oXjVxoSio{a^alqkr/8 ;=7lvf_c]q`Zbf|hUhcx`{(0+22>gaVhT~iQkauc\gjsi|!8"=;5nxh]a[wbXlh~jSnaznu*0-40018e}oXzVnjxlQlotlw,5/6;2kseR|Pd`vb[firf}":%<:4ayk\vZbf|hUhcx`{(02*56=fp`UySio{a^alqkr/: ;87lvf_s]geqgXkfex%=&129b|lYuWmkmRm`uov+0,743hrbSQkauc\gjsi|!?"=>5nxh]q[agsiVidycz'6(30?d~nW{UomyoPcnwmp-1.9:1jtdQ}_ecweZeh}g~#4$?<;`zj[wYci}kTob{at);*53=fp`UySio{a^alqkr;990;2i5nxh]q[adn|lxy=>5nxh]q[lhmmj~ondzjrs30?d~nW{Ubbgkltr`jp`tum2kseR|Powgqbiip9h1jtdQ}_qplcZcjx}sTxe|jsi*3-4ga:c{mZtXx{elShctx]wlwct`!9"=l5nxh]q[uthoVof|ywPtipfwm.3!8k0mugPr^rqkbYbey~rSyf}erj+1,7f3hrbSQrne\ahvsqV~c~h}g(7+2e>gaVxT|ah_dosp|Ys`{oxd%9&1`9b|lYuWyxdkRkbpu{\pmtb{a"3%)0;8e}oXzVzycjQiumn\pmtb{a"9%<74ayk\vZvugnUmyabPtipfwm.4!830mugPr^rqkbYa}efTxe|jsi*7-4?6:c{mZtXzmUomyoPcnwmp-6.9?1jtdQ}_sf\`drfWje~by&>)058e}oXzVxoSio{a^alqkr/99#::6owi^p\vaYci}kTob{at)0*53=fp`UySjPd`vb[firf}"8%<84ayk\vZtcWmkmRm`uov+0,713hrbSQ}d^fbpdYdg|d$8'>6:c{mZtXzmUomyoPcnwmp-0.9?1jtdQ}_sf\`drfWje~by&8)048e}oXzVxoSio{a^alqkr/0 ;=7lvf_s]q`Zbf|hUhcx`{(8+2=>gaVxT~iQkauc\gjsi|5;;6=0k;`zj[wYtj`~n~h4ayk\vZsillxm`by>6:c{mZtX{UomyoPcnwmp-6.9?1jtdQ}_vp\`drfWje~by&>)058e}oXzV}ySio{a^alqkr/99#::6owi^p\swYci}kTob{at)0*53=fp`UySz|Pd`vb[firf}"8%<84ayk\vZquWmkmRm`uov+0,713hrbSQxr^fbpdYdg|d$8'>6:c{mZtX{UomyoPcnwmp-0.9?1jtdQ}_vp\`drfWje~by&8)048e}oXzV}ySio{a^alqkr/0 ;=7lvf_s]tvZbf|hUhcx`{(8+2=>gaVxT{Qkauc\gjsi|5;;6=0>3:c{mZqXlh~jSnaznu*3-450(30?d~nW~UomyoPcnwmp-4.9:1jtdQx_ecweZeh}g~#?$?<;`zj[rYci}kTob{at)6*56=fp`U|Sio{a^alqkr/= ;87lvf_v]geqgXkfex%8&129b|lYpWmkmRm`uov+3,743hrbSzQkauc\gjsi|!2"=>5nxh]t[agsiVidycz'9(35?d~nW~UomyoPcnwmp977294o7lvf_v]gflrbz{;87lvf_v]jjocd|mhbxh|}129b|lYpW`dainz|bhvfvwcgaV}T|ah_dosp|Ys`{oxd%?&1`9b|lYpWyxdkRkbpu{\pmtb{a"9%:>58612kseRyPpsmd[cskdV~c~h}g(1+2=>gaV}T|ah_gwohZrozlyc$<'>9:c{mZqXx{elSk{cl^vkv`uo ;#:56owi^u\twi`Wog`Rzgrdqk,6/612kseRyPpsmd[cskdV~c~h}g(5+2=>gaV}T|ah_gwohZrozlyc$8'>9:c{mZqXx{elSk{cl^vkv`uo ?#:56owi^u\twi`Wog`Rzgrdqk,2/612kseRyPpsmd[cskdV~c~h}g(9+2=>gaV}T|ah_gwohZrozlyc$4'>b:c{mZqXx{elSk{cl^vkv`uo400;2<84ayk\sZtcWmkmRm`uov+4,713hrbSzQ}d^fbpdYdg|d$<'>7:c{mZqXzmUomyoPcnwmp-77!8<0mugPw^pg[agsiVidycz'2(35?d~nW~UyhRjnt`]`kphs :#::6owi^u\vaYci}kTob{at)6*53=fp`U|SjPd`vb[firf}">%<84ayk\sZtcWmkmRm`uov+2,713hrbSzQ}d^fbpdYdg|d$:'>6:c{mZqXzmUomyoPcnwmp->.9?1jtdQx_sf\`drfWje~by&6)0;8e}oXVxoSio{a^alqkr;990;2i5nxh]t[vdn|lxyj6owi^u\qkbbzofd{<84ayk\sZquWmkmRm`uov+4,713hrbSzQxr^fbpdYdg|d$<'>7:c{mZqX{UomyoPcnwmp-77!8<0mugPw^uq[agsiVidycz'2(35?d~nW~U|~Rjnt`]`kphs :#::6owi^u\swYci}kTob{at)6*53=fp`U|Sz|Pd`vb[firf}">%<84ayk\sZquWmkmRm`uov+2,713hrbSzQxr^fbpdYdg|d$:'>6:c{mZqX{UomyoPcnwmp->.9?1jtdQx_vp\`drfWje~by&6)0;8e}oXV}ySio{a^alqkr;990;2<<4aykp[agsiVidycz'0(31?d~n{VnjxlQlotlw,4/6;2kse~Qkauc\gjsi|!;;%<<4aykp[agsiVidycz'2(31?d~n{VnjxlQlotlw,6/6:2kse~Qkauc\gjsi|!>"=?5nxhq\`drfWje~by&:)008e}otWmkmRm`uov+2,753hrbRjnt`]`kphs >#:>6owir]geqgXkfex%6&139b|luXlh~jSnaznu*:-436owir]jjocd|mhbxh|}139b|luXag`noy}miugqva=fp`yTc{k}fmmt5<=fp`yT|ah_dosp|Ys`{oxd%>&189b|luXx{elShctx]wlwct`!;"=45nxhq\twi`Wlg{xtQ{hsgpl-4.901jtd}Ppsmd[`kw|pUdk|h)1*5<=fp`yT|ah_dosp|Ys`{oxd%:&189b|luXx{elShctx]wlwct`!?"=45nxhq\twi`Wlg{xtQ{hsgpl-0.901jtd}Ppsmd[`kw|pUdk|h)5*5<=fp`yT|ah_dosp|Ys`{oxd%6&189b|luXx{elShctx]wlwct`!3"=o5nxhq\twi`Wlg{xtQ{hsgpl9?=87;37lvfs^rqkbYa}efTxe|jsi*3-4>5:c{mvYpzVnjxlQlotlw,4/6>2kse~Qxr^fbpdYdg|d$<>&149b|luX{UomyoPcnwmp-4.9<1jtd}Pws]geqgXkfex%=&149b|luX{UomyoPcnwmp-2.9<1jtd}Pws]geqgXkfex%;&149b|luX{UomyoPcnwmp-0.9<1jtd}Pws]geqgXkfex%9&149b|luX{UomyoPcnwmp->.9<1jtd}Pws]geqgXkfex%7&199b|luX{UomyoPcnwmp977294=7oolktrg?ggdc|zTal|des18gimc3mkmRm`uov+4,bbf|hUhcx`{(2+g?agsiVidycz'4(f8`drfWje~by&:)e9geqgXkfex%8&d:fbpdYdg|d$:'k;ecweZeh}g~#4$j4d`vb[firf}"2%<>4d`vb[firf}6:<7>17:famqcuz?1oec&?)79gmk.6!>1oec&>0(58`lh/98#<7iga(00*3>bnf!;8%:5kio*20,11oec2>0?58`lh;984<7iga<00=3>bnf5;82:5kio>20;1bnf585:6jfn=1=2>bnf5>5:6jfn=7=2>bnf5<5:6jfn=5=2>bnf525:6jfn=;=3>bh}}";%:5kotv+5,>&8:flqq.69 20hb{{(00*<>bh}}":?$64dnww,42.02ndyy&>5(:8`jss 8<"46j`uu*23,199gkpr;98437iazt=31:==cg|~7=>07;emvp973611ocxz314<;?air|5;=2l5kotv?52<7611ocxz316<4?air|5;5;6j`uu>1:2=cg|~7?394dnww81803me~x1;17:flqq:16>1ocxz37?58`jss414<7iazt=;=<>ccao8eki;4elrw}d=aaoeT>fv>83:8bl`hW;as=5"iigm\c`hbzh~d~Rx8_3.MKKC+FFDNi874fhdl[7m91&mekaPgdlfvdrhzV|21*dWmceSzgkti]tvfn::%iThb{{_sqw[sgk59&hSiazt^uj`qn:91&hSh`nbmg\vvrX{pdh1<"l_dpqkwYnfcohxdaa_u{sa86+kVoy~b|PiohfgqohfV~r|hQnxhq>4)eXm{xd~RgajdawmjhX|pznSolh<2/gZcuzfxTecdjcukljZr~xlUyhnf20-a\awthzVcefhm{inl\p|vbW~khd0>#c^gqvjtXag`noyg`n^vzt`Ypljb64)eX`hyTmug|_ufbpd;7$jUcm~Qnxhq\pact|4:'oRfns^c{mvYsam7; nQgar]b|luX|bon|0>#c^jbwZgazU`l}{=1.`[mgtWhrbRz}e`fz94*dWakxSlvfs^vppwd:8%iTdl}Paykp[qstm{7; nQgar]geqgXkfex0>#c^jbwZbf|hUhcx`{s^c{mv;7$jUcm~Qkauc\gjsi|zUymnf20-a\lduXlh~jSnaznuq\vaeo59&hSeo|_ecweZeh}g~xSzolh<2/gZnf{VnjxlQlotlwwZqcka7; nQgar]geqgXkfex~Qxrbj>4)eX`hyTeczPcmi>4)eX`hyTal}{es]`a86+kVbjRayesdokr;7$jUcm~Qrne\big`{Vkse~3?,b]kevYwzfmTjaohs^pbgm;7$jUcm~Qrne\big`{Vxooe3?,b]kevYwzfmTjaohs^ubgm;7$jUcm~Qrne\big`{V}ooe3?,b]kevYwzfmTjaohs^uqgm;7$jUcm~Q}d^fbpdYdg|d1="l_icp[wus58&hSeo|_rnbr`Ydm4:'oRfns^qqwq;7$jUcm~Qznegqbiip59&hSeo|_wcoma;7$jUcm~Qxr^c`o86+kVbjRy}_ecweZeh}g~66)eXadzgi`kat`vjkkYsqyoTmug|=0.`[lkwdlgnbyo{inl\p|vbW{khd0?#c^knticjmg~jxdaa_u{saZtcka7: nQfmqnfi`hsi}cdbRzvpd]tefn:9%iTe`~celgmpdrnggUu}kPweak94*dW`g{`hcjnucwmjhX|pznSz|lh<3/gZoi|lxmmnkPtxrf95*dWdofcwPtxrf94*dWdylccQyam?2(fYh~lxm`byPlnu>4)eXx{ogcljPl`vfjf:9%iT|kco`f\v`at59&hS}|fm^djbjYsqyo6>fv>8-a\twi`Wlg{xtQ{hsgplZgt{lxS{oc=3.`[uthoVof|ywPtipfwmYf{zoyxRxnl^c{mv;68;8'oR~}of]fiur~W}byi~fParqfvqYqieUymnf21101(fYwzfmTi`~{y^vkv`uoWhyxizPv`n\vaeo58:9>!mPpsmd[`kw|pUdk|h^cpw`tsWkgSzolh<3367*dWyxdkRkbpu{\pmtb{aUj~k}t^tbhZqcka7:5545$jU{~biPelrw}ZrozlycSckhaug\rdj:;%iT|ah_dosp|Ysqyo6>"l_qplcZ`rdeUdk|h^cpw`tsWkgSolh<3366*dWyxdkRhzlm]wlwct`Vkxh|{_wco[wbd`4;;>>"l_qplcZ`rdeUdk|h^cpw`tsWkgSzolh<3366*dWyxdkRhzlm]wlwct`Vkxh|{_wco[rbd`4;;>>"l_qplcZ`rdeUdk|h^cpw`tsWkgSz|lh<3366*dWyxdkRhzlm]wlwct`Vdnklzj_wco9763<%iT|ah_gwohZr~xl7; nQrne\bpjkW}s{iRowir?6(fYwzfmTjxbc_u{saZtfka7> nQrne\bpjkW}s{iR|kci?6(fYwzfmTjxbc_u{saZqfka7> nQrne\bpjkW}s{iRykci?6(fYwzfmTjxbc_u{saZquka7> nQ}abj\p|vb59&hSjPd`vb[firf}U|eizg=03/gZtcWmo{xe3=05:/gZtcWoxn}0?#c^pg[uhszV}bhyf210.`[wbd`V~r|h3?,b]vjacunee|Saax=1.`[pubWjefab`Powgqbiip59&hSx}j_bmnijhX}gnn~kb`w<2/gZstmVndyyQ}su?3(fYr{lUnon3?,b]vw`YbkjUjtd}20-a\qvcXmjiT~lmg=1.`[pubWlihSjlh<2/gZstmVohoRynci?3(fYr{lUnonQxdbj>4)eX}zoTinmPwsak95*dW|ynShcmeeff`Ztbo4:'oR{|e^dtbqYci}kTob{at<2/gZpfd`nT`by20-a\sdeoW}s{i0>#c^uggmYsqyo6'oRy}_qlwvZqnl}bT~img=02/gZquWyd~Ryfduj\sdeo5=&hSz|Ppovq[roc|aU|hnf211.`[rtXxg~ySzgkti]tvfn:<%iT{mg_u{sa86+u;>0jdh`_3i{5=YaaoeTkh`jr`vlvZp0W;UsyQ>2g9emciXoldn~lz`r^t4[7*'P`fbbu.LOSG#C`hbzh~d~-?<.068bpjkl2cefhm{dckwawtc3`dainz|bhvfvw13dUjoaePeod8iZgaVkymij}(1+e?hYfp`Uj~ljkr)3*55=jWhrbSl|ndep+55/682gTmugPascg`w.69 ;;7`Qnxh]bvdbcz!;9%<>4m^c{mZguimny$<=&119n[d~nWhxjhi|'15+24>kXiqcTmokds*21,773dUjtdQnr`fgv-71!8:0aRowi^cqeabu 8="==5b_`zj[dtflmx#=5'>0:o\e}oXi{koh&>9(d8iZgaVkymij}(3+24>kXiqcTmokds*14,773dUjtdQnr`fgv-46!8:0aRowi^cqeabu ;8"==5b_`zj[dtflmx#>>'>0:o\e}oXi{koh&=4(33?hYfp`Uj~ljkr)06-46#:<6cPayk\ewgcl{"94$??;l]b|lYfzhno~%<6)g9n[d~nWhxjhi|'3(33?hYfp`Uj~ljkr)13-46:1<24>kXiqcTmlzrrv+4,773dUjtdQnrcwqwq.6!880aRowi^cqfptt|5;1<3??;l]b|lYfzjkhdh&?)028iZgaVkyolmge)3*55=jWhrbSl|labjf,7/682gTmugPasabgmc/; ;97`Qnxh]bvfgd`l686=0k;l]b|lYfz`n#<$j4m^c{mZguam":%i5b_`zj[dtnl!8"h6cPayk\ewoc :#m7`Qnxh]bvlb;;3:5i6cPayk\ewjbf!:"i6cPayk\ewjbf!;"i6cPayk\ewjbf!8"i6cPayk\ewjbf!9"i6cPayk\ewjbf!>"i6cPayk\ewjbf!?"i6cPayk\ewjbf!<"i6cPayk\ewjbf!="==5b_`zj[dtkmg6<6=0i;l]b|lYfzeehg%>&f:o\e}oXi{fdof&>)038iZgaVky`bmd<083:c=jWhrbSl|rnv+4,`;l]b|lYfz{oleb`'0(32?hYfp`Uj~khinl+5,763dUjtdQnrsgdmjh/: ;:7`Qnxh]bvwc`afd#?$?<;l]b|lYfz{oleb`33;2=b>kXiqcTm}fzd*3-c=jWhrbSl||i{g+5,`1d:o\e}oXi{|j`dji;l]b|lYfhno~%>&f:o\e}oXi~koh&>)028iZgaVk|mij}(02*55=jWhrbSlyndep+54/682gTmugPavcg`w.6: ;;7`Qnxh]bsdbcz!;8%<>4m^c{mZgpimny$<:&119n[d~nWh}jhi|'14+24>kXiqcTmzokds*22,773dUjtdQnw`fgv-70!8:0aRowi^cteabu 82"==5b_`zj[dqflmx#=4'i;l]b|lYfhno~%<&119n[d~nWh}jhi|'21+24>kXiqcTmzokds*15,773dUjtdQnw`fgv-45!8:0aRowi^cteabu ;9"==5b_`zj[dqflmx#>9'>0:o\e}oXi~koh&=5(33?hYfp`Uj{ljkr)05-46'>0:o\e}oXi~koh&<0(33?hYfp`Uj{ljkr)12-c=jWhrbSlyndep+0,`6cPayk\ergcl{68=7>1119n[d~nWh}iy}{(1+24>kXiqcTmzlzrrv+5,753dUjtdQnwcwqwq:6294:<6cPayk\erefkao#<$??;l]b|lYfjkhdh&>)028iZgaVk|olmge)0*55=jWhrbSlylabjf,6/6:2gTmugPavabgmc;;3:5h6cPayk\eroc 9#o7`Qnxh]bslb/9 n0aRowi^ctma.5!m1fSlvf_`uj`-5.n2gTmugPavkg86<76l1fSlvf_`uoak.7!l1fSlvf_`uoak.6!l1fSlvf_`uoak.5!l1fSlvf_`uoak.4!l1fSlvf_`uoak.3!l1fSlvf_`uoak.2!l1fSlvf_`uoak.1!l1fSlvf_`uoak.0!8:0aRowi^cth`h;?3:5j6cPayk\erjhkb";%k5b_`zj[dqkgja#=$?>;l]b|lYfeehg1?50?d8iZgaVk||a{(1+e?hYfp`Uj{}|`t)3*b>kXiqcTmz~}ou*1-471:o\e}oXi~xnkdaa(0+25>kXiqcTmz|jghmm,7/692gTmugPavpfclii :#:?6cPayk\ertbo`ee0>4?>g9n[d~nWh}xewk'0(d8iZgaVk|dtj(0+e?hYfp`Uj{~gue)0*54=jWhrbSly|i{g?6?69n2gTmugPavwpaw.7!8;0aRowi^ctqvcu490;2i5b_`zj[dqqiecoo6cPayk\flb/8 i0aRowi^`j`-7.k2gTmugPbhf+6,ekXiqcTndj33;2=g>kXiqcTnkndxg8iZgaVhyi~~'0(g8iZgaVhyi~~'1(33?hYfp`Ui~h}<083:`=jWhrbSo{|es*3-46kXiqcT~io{a)07-c=jWhrbSjnt`*11,`;&f:o\e}oXzmkm%=9)g9n[d~nW{njxl&<7(d8iZgaVxomyo'39+e?hYfp`Uyhlzn(2;*a>kXiqcT~io{a)6*b>kXiqcT~io{a)63-c=jWhrbSjnt`*75,`kXiqcT~io{a)75-c=jWhrbSjnt`*63,`;#m7`Qnxh]q`drf ?9"i6cPayk\vagsi!="i6cPayk\vagsi!2"i6cPayk\vagsi!3"=<5b_`zj[wbf|h6=?7>1c:o\e}oXz`n#<$m4m^c{mZtnl!;"o6cPayk\vlb/: i0aRowi^pj`-5.m2gTmugPrhf?7?69j2gTmugPrmcppf=jWhrbS|jae{f?hYfp`Uy~h}(1+f?hYfp`Uy~h}(0+24>kXiqcT~k|p=394;c"j6cPayk\sagsi!;>%k5b_`zj[rbf|h"::$h4m^c{mZqci}k#=:'i;l]b|lYplh~j$<6&f:o\e}oXmkm%?6)d9n[d~nW~njxl&=)g9n[d~nW~njxl&=0(d8iZgaV}omyo'20+e?hYfp`U|hlzn(30*b>kXiqcT{io{a)00-c=jWhrbSzjnt`*10,`$h4m^c{mZqci}k#?>'i;l]b|lYplh~j$>:&f:o\e}oXmkm%=:)g9n[d~nW~njxl&<6(d8iZgaV}omyo'36+e?hYfp`U|hlzn(2:*b>kXiqcT{io{a)1:-`=jWhrbSzjnt`*7-c=jWhrbSzjnt`*74,`<%k5b_`zj[rbf|h"?4$h4m^c{mZqci}k#84'j;l]b|lYplh~j$8'i;l]b|lYplh~j$8>&f:o\e}oXmkm%;>)g9n[d~nW~njxl&:2(d8iZgaV}omyo'52+e?hYfp`U|hlzn(46*b>kXiqcT{io{a)76-c=jWhrbSzjnt`*62,`.n2gTmugPwecwe-3>!l1fSlvf_vfbpd.1!o1fSlvf_vfbpd.18 l0aRowi^ugeqg/>8#m7`Qnxh]t`drf ?8"j6cPayk\sagsi!<8%h5b_`zj[rbf|h"<%h5b_`zj[rbf|h"3%h5b_`zj[rbf|h"2%4?>b9n[d~nW~co$='l;l]b|lYpam":%n5b_`zj[roc ;#h7`Qnxh]tma.4!l1fSlvf_vkg86<76k1fSlvf_vnbwqekXiqcT{~z}b)2*a>kXiqcT{~z}b)3*a>kXiqcT{~z}b)0*a>kXiqcT{~z}b)1*a>kXiqcT{~z}b)6*a>kXiqcT{~z}b)7*a>kXiqcT{~z}b)4*a>kXiqcT{~z}b)5*55=jWhrbSz}{rc>4>58b3dUjtdQxurgq,5/682gTmugPwtqfv96=87i0aRowi^uueiocn2gTmug|_ufbpd.7!o1fSlvfs^vgeqg/9 ;;7`Qnxhq\pagsi!;;%<>4m^c{mvYslh~j$kXiqcxSyjnt`*27,773dUjtd}Ptecwe-73!8:0aRowir]w`drf 8?"==5b_`zjwZrci}k#=;'>0:o\e}otW}njxl&>7(33?hYfp`yTxio{a)3;-46<'>0:o\e}otW}njxl&=2(33?hYfp`yTxio{a)00-464m^c{mvYslh~j$>9&119n[d~n{V~omyo'39+24>kXiqcxSyjnt`*0=,`:%<>4m^c{mvYslh~j$9<&119n[d~n{V~omyo'42+24>kXiqcxSyjnt`*70,773dUjtd}Ptecwe-22!8:0aRowir]w`drf =<"==5b_`zjwZrci}k#8:'>0:o\e}otW}njxl&;8(33?hYfp`yTxio{a)6:-c=jWhrbRzkauc+1,773dUjtd}Ptecwe-37!8:0aRowir]w`drf <;"==5b_`zjwZrci}k#9?'>0:o\e}otW}njxl&:3(33?hYfp`yTxio{a)77-46;$??;l]b|luX|mkm%;7)028iZgazUhlzn(4;*b>kXiqcxSyjnt`*5-468#:<6cPaykp[qbf|h"=>$??;l]b|luX|mkm%8<)g9n[d~n{V~omyo'7(d8iZgazUhlzn(9+e?hYfp`yTxio{a);*57=jWhrbRzkauc?26<76o1fSlvfs^vgavr/8 l0aRowir]w``us 8#m7`Qnxhq\pact|!8"j6cPaykp[qbb{}"8%4?>e9n[d~n{V~bh%>&d:o\e}otW}co$<'k;l]b|luX|`n#>$j4m^c{mvYsam"8%i5b_`zjwZrnl!>"h6cPaykp[qoc <#o7`Qnxhq\plb/> n0aRowir]wma.0!o1fSlvfs^vj`91=87l0aRowir]wo`cw 9#m7`Qnxhq\pncbx!;"j6cPaykp[qmbmy"9%k5b_`zjwZrlmlz#?$?>;l]b|luX|bon|1=50?a8iZgazU`l}{d:o\e}otW}xnmiwi;l]b|luX|z~yn%>&f:o\e}otW}y~o&>)g9n[d~n{V~xxl'2(d8iZgazUy|m(2+25>kXiqcxSy}{rc>0>58a3dUjtd}Pttqfv-6.n2gTmug|_uwpaw.6!o1fSlvfs^vvw`t/: l0aRowir]wqvcu :#:=6cPaykp[qstm{686=0k;l]b|luX|kgei64nfaaqljc02e}ihcovc8twi`Wlg{xt?;;qplcZcjx}sTxe|jsi*3-420|ah_dosp|Ys`{oxd%;&159svjaXmdzuRzgrdqk,3/6<2zycjQjmqvz[qnumzb#;$?;;qplcZcjx}sTxe|jsi*;-42b:rqkbYbey~rSyf}erj\evubz}"8%vugnUna}zv_ujqavnXizyn~y&6)0g8twi`Wlg{xtQ{hsgplZgt{lx0<>50?3a?uthoVof|ywPtipfwmYimnki%>&1c9svjaXmdzuRzgrdqk[kc`i}o#=$?l;qplcZcjx}sTxe|jsi]mabgsm!;;%vugnUna}zv_ujqavnXflmjxh&8)0`8twi`Wlg{xtQ{hsgplZhboh~n$5'>b:rqkbYbey~rSyf}erj\j`af|l"2%3:rqkbYa}efTxe|jsi*2-425rne\bpjkW}byi~f'2(30?uthoVl~`aQ{hsgpl-5.9:1{~biPftno[qnumzb#8$?<;qplcZ`rdeUdk|h)7*56=wzfmTjxbc_ujqavn/> ;87}|`g^dvhiYs`{oxd%9&129svjaXn|fgSyf}erj+<,743yxdkRhzlm]wlwct`!3"=;5rne\bpjkW}byi~f31183:4gb:rqkbYa}efTxe|jsi]bwvcu|!;;%#:m6~}of]eqijX|axneQnsrgqp->.9h1{~biPftno[qnumzbTm~}jru*:-4bb:rqkbYa}efTxe|jsi]mabgsm!;;%#:m6~}of]eqijX|axneQaefcwa->.9h1{~biPftno[qnumzbTbhintd*:-4b4re]geqgXkfex%>&119q`Zbf|hUhcx`{(0+25>tcWmkmRm`uov+55/682xoSio{a^alqkr/: ;;7jPd`vb[firf}"8%<>4re]geqgXkfex%:&119q`Zbf|hUhcx`{(4+24>tcWmkmRm`uov+2,773{nThlzn_bmvjq.0!8:0~iQkauc\gjsi|!2"==5}d^fbpdYdg|d$4'>3:pg[agsiVidycz31183:0=ulVoe:6|k_sqw7>tt|?1xSlmck89p[dekcVoe56}Pasgpaqha3zUjtdQnr`fgv-6.n2yTmugPascg`w.6!8:0Rowi^cqeabu 8:"==5|_`zj[dtflmx#=<'>0:q\e}oXi{koh&>2(33?vYfp`Uj~ljkr)30-46<{VkseRo}aefq,42.991xSlvf_`pb`at/9<#:<6}Payk\ewgcl{"::$??;r]b|lYfzhno~%?8)028wZgaVkymij}(0:*55=tWhrbSl|ndep+54s^c{mZguimny$?9&119p[d~nWhxjhi|'29+24>uXiqcTmokds*1=,`<{VkseRo}aefq,6/682yTmugPascg`w.48 ;;7~Qnxh]bvdbcz!9:%k5|_`zj[dtflmx#8$h4s^c{mZguimny$8'i;r]b|lYfzhno~%8&f:q\e}oXi{koh&8)g9p[d~nWhxjhi|'8(d8wZgaVkymij}(8+26>uXiqcTmokds>05?69991xSlvf_`paqwus 9#:<6}Payk\ewdrzz~#=$?=;r]b|lYfzkyy2>:1<24>uXiqcTmmncig+4,773zUjtdQnrbc`l`.6!8:0Rowi^cqgdeom!8"==5|_`zj[dtdijbn$>'>2:q\e}oXi{ijoek33;2=`>uXiqcTmgk(1+g?vYfp`Uj~dj'1(f8wZgaVkyei&=)e9p[d~nWhxbh%=&f:q\e}oXi{co0>4?>d9p[d~nWhxgic&?)d9p[d~nWhxgic&>)d9p[d~nWhxgic&=)d9p[d~nWhxgic&<)d9p[d~nWhxgic&;)d9p[d~nWhxgic&:)d9p[d~nWhxgic&9)d9p[d~nWhxgic&8)028wZgaVky`h`37;2=b>uXiqcTmb`cj*3-c=tWhrbSl|cobi+5,763zUjtdQnrmm`o97=87l0Rowi^cqtwis 9#m7~Qnxh]bvuth|!;"j6}Payk\ewvug}"9%d9p[d~nWhxzc~&?)d9p[d~nWhxzc~&>)d9p[d~nWhxzc~&=)d9p[d~nWhxzc~&<)028wZgaVky}b}33;2=`>uXiqcTm|jae{25>uXiqcTm|jghmm,5/692yTmugPaspfclii 8#:=6}Payk\ewtbo`ee$?'>1:q\e}oXi{xnkdaa(2+27>uXiqcTm|jghmm86<76o1xSlvf_`ppmc/8 l0Rowi^cqwl|b 8#m7~Qnxh]bvvo}m!8"=<5|_`zj[dttaso7>7>1f:q\e}oXi{xi&?)038wZgaVkyy~k}<183:a=tWhrbSl|yamkgb>uXiqcTmzokds*3-c=tWhrbSlyndep+5,773zUjtdQnw`fgv-77!8:0Rowi^cteabu 8;"==5|_`zj[dqflmx#=?'>0:q\e}oXi~koh&>3(33?vYfp`Uj{ljkr)37-46<{VkseRoxaefq,43.991xSlvf_`ub`at/9?#:<6}Payk\ergcl{":;$??;r]b|lYfhno~%?7)028wZgaVk|mij}(0;*b>uXiqcTmzokds*1-46<{VkseRoxaefq,76.991xSlvf_`ub`at/:8#:<6}Payk\ergcl{"9>$??;r]b|lYfhno~%<<)028wZgaVk|mij}(36*55=tWhrbSlyndep+60/682yTmugPavcg`w.5> ;;7~Qnxh]bsdbcz!8<%<>4s^c{mZgpimny$?6&119p[d~nWh}jhi|'28+e?vYfp`Uj{ljkr)1*55=tWhrbSlyndep+75/682yTmugPavcg`w.49 l0Rowi^cteabu =#m7~Qnxh]bsdbcz!?"j6}Payk\ergcl{"=%k5|_`zj[dqflmx#;$h4s^c{mZgpimny$5'i;r]b|lYfhno~%7&139p[d~nWh}jhi|33083:46<{VkseRoxbtppp-6.991xSlvf_`uaqwus 8#:>6}Payk\erdrzz~7=7>1119p[d~nWh}hmnfj(1+24>uXiqcTmzmncig+5,773zUjtdQnwbc`l`.5!8:0Rowi^ctgdeom!9"=?5|_`zj[dqdijbn0>4?>e9p[d~nWh}bh%>&d:q\e}oXi~co$<'k;r]b|lYf`n#>$j4s^c{mZgpam"8%k5|_`zj[dqnl591<3k4s^c{mZgpdld#<$k4s^c{mZgpdld#=$k4s^c{mZgpdld#>$k4s^c{mZgpdld#?$k4s^c{mZgpdld#8$k4s^c{mZgpdld#9$k4s^c{mZgpdld#:$k4s^c{mZgpdld#;$??;r]b|lYfeoe0:4?>g9p[d~nWh}gcne'0(d8wZgaVk|`bmd(0+25>uXiqcTmzb`cj>2>58a3zUjtdQnwqplp-6.n2yTmugPavrqkq.6!o1xSlvf_`usvjr/: ;:7~Qnxh]bsuth|581<3k4s^c{mZgpyfy#<$k4s^c{mZgpyfy#=$k4s^c{mZgpyfy#>$k4s^c{mZgpyfy#?$??;r]b|lYfxex0>4?>e9p[d~nWh}yiljv109p[d~nWh}yijg`n)2*54=tWhrbSly}efklj-7.981xSlvf_`uqabohf!8"=<5|_`zj[dqumncdb%=&129p[d~nWh}yijg`n=194;`<{VkseRoxshxf,5/a3zUjtdQnwrkya-7.n2yTmugPavqj~`.5!8;0Rowi^ctwl|b4;0;2k5|_`zj[dqr{lx#<$?>;r]b|lYf|yn~1>50?f8wZgaVk|zlbfdb9p[d~nWkco$='l;r]b|lYeam":%n5|_`zj[goc ;#h7~Qnxh]ama.4!l1xSlvf_ckg86<76j1xSlvf_cpfeab3zUjtdQmrdqs,5/b3zUjtdQmrdqs,4/682yTmugPbsgpt97=87o0Rowi^`vw`t/8 ;;7~Qnxh]aqvcu490;2n5|_`zj[gpfd`nn7~Qnxh]q`drf 9#n7~Qnxh]q`drf 8#m7~Qnxh]q`drf 8:"j6}Payk\vagsi!;:%k5|_`zj[wbf|h":>$h4s^c{mZtci}k#=>'i;r]b|lYulh~j$<:&f:q\e}oXzmkm%?:)g9p[d~nW{njxl&>6(d8wZgaVxomyo'16+e?vYfp`Uyhlzn(0:*b>uXiqcT~io{a)3:-`=tWhrbSjnt`*1-c=tWhrbSjnt`*14,`<{VkseR|kauc+64/a3zUjtdQ}d`vb,74.n2yTmugPrecwe-44!o1xSlvf_sfbpd.5< l0Rowi^pgeqg/:<#m7~Qnxh]q`drf ;<"j6}Payk\vagsi!8<%k5|_`zj[wbf|h"94$h4s^c{mZtci}k#>4'j;r]b|lYulh~j$>'i;r]b|lYulh~j$>>&f:q\e}oXzmkm%=>)g9p[d~nW{njxl&<2(d8wZgaVxomyo'32+e?vYfp`Uyhlzn(26*b>uXiqcT~io{a)16-c=tWhrbSjnt`*02,`<{VkseR|kauc+72/a3zUjtdQ}d`vb,6>.n2yTmugPrecwe-5>!l1xSlvf_sfbpd.3!o1xSlvf_sfbpd.38 l0Rowi^pgeqg/<8#m7~Qnxh]q`drf =8"j6}Payk\vagsi!>8%k5|_`zj[wbf|h"?8$h4s^c{mZtci}k#88'i;r]b|lYulh~j$98&f:q\e}oXzmkm%:8)g9p[d~nW{njxl&;8(d8wZgaVxomyo'48+f?vYfp`Uyhlzn(4+e?vYfp`Uyhlzn(42*b>uXiqcT~io{a)72-c=tWhrbSjnt`*66,`<{VkseR|kauc+16/a3zUjtdQ}d`vb,02.n2yTmugPrecwe-32!o1xSlvf_sfbpd.2> l0Rowi^pgeqg/=>#m7~Qnxh]q`drf <2"j6}Payk\vagsi!?2%h5|_`zj[wbf|h"=%k5|_`zj[wbf|h"=<$h4s^c{mZtci}k#:<'i;r]b|lYulh~j$;<&f:q\e}oXzmkm%8<)d9p[d~nW{njxl&8)d9p[d~nW{njxl&7)d9p[d~nW{njxl&6)038wZgaVxomyo36283:f=tWhrbSgk(1+`?vYfp`Uyei&>)b9p[d~nW{co$?'l;r]b|lYuam"8%h5|_`zj[woc4:0;2o5|_`zj[wjf{}i0Rowi^pqadb~m2yTmugPrsgpt-6.m2yTmugPrsgpt-7.991xSlvf_spfwu:6294n7~Qnxh]qqvcu 9#:<6}Payk\vpubz5:1<3m4s^c{mZtqiecoi6}Payk\sagsi!:"i6}Payk\sagsi!;"j6}Payk\sagsi!;;%k5|_`zj[rbf|h":=$h4s^c{mZqci}k#=?'i;r]b|lYplh~j$<=&f:q\e}oXmkm%?;)g9p[d~nW~njxl&>5(d8wZgaV}omyo'17+e?vYfp`U|hlzn(05*b>uXiqcT{io{a)3;-c=tWhrbSzjnt`*2=,c<{VkseRykauc+6,`<{VkseRykauc+65/a3zUjtdQxd`vb,77.n2yTmugPwecwe-45!o1xSlvf_vfbpd.5; l0Rowi^ugeqg/:=#m7~Qnxh]t`drf ;?"j6}Payk\sagsi!8=%k5|_`zj[rbf|h"9;$h4s^c{mZqci}k#>5'i;r]b|lYplh~j$?7&e:q\e}oXmkm%=&f:q\e}oXmkm%=?)g9p[d~nW~njxl&<1(d8wZgaV}omyo'33+e?vYfp`U|hlzn(21*b>uXiqcT{io{a)17-c=tWhrbSzjnt`*01,`<{VkseRykauc+73/a3zUjtdQxd`vb,61.n2yTmugPwecwe-5?!o1xSlvf_vfbpd.41 o0Rowi^ugeqg/< l0Rowi^ugeqg/<9#m7~Qnxh]t`drf =;"j6}Payk\sagsi!>9%k5|_`zj[rbf|h"??$h4s^c{mZqci}k#89'i;r]b|lYplh~j$9;&f:q\e}oXmkm%:9)g9p[d~nW~njxl&;7(d8wZgaV}omyo'49+e?vYfp`U|hlzn(5;*a>uXiqcT{io{a)7*b>uXiqcT{io{a)73-c=tWhrbSzjnt`*65,`<{VkseRykauc+17/a3zUjtdQxd`vb,05.n2yTmugPwecwe-33!o1xSlvf_vfbpd.2= l0Rowi^ugeqg/=?#m7~Qnxh]t`drf <="j6}Payk\sagsi!?3%k5|_`zj[rbf|h">5$k4s^c{mZqci}k#:$h4s^c{mZqci}k#:='i;r]b|lYplh~j$;?&f:q\e}oXmkm%8=)g9p[d~nW~njxl&93(g8wZgaV}omyo'7(g8wZgaV}omyo'8(g8wZgaV}omyo'9(32?vYfp`U|hlzn<7194;e<{VkseRyfd)2*g>uXiqcT{dj'1(a8wZgaV}bh%<&c:q\e}oX`n#?$k4s^c{mZqnl591<3l4s^c{mZqkiz~h7~Qnxh]tv`gcql1xSlvf_vqwvg.7!l1xSlvf_vqwvg.6!l1xSlvf_vqwvg.5!l1xSlvf_vqwvg.4!l1xSlvf_vqwvg.3!l1xSlvf_vqwvg.2!l1xSlvf_vqwvg.1!l1xSlvf_vqwvg.0!8:0Rowi^uppwd;?3:5i6}Payk\spubz!:"==5|_`zj[rstm{6;6=0l;r]b|lYp~hfbhk5|_`zjwZrci}k#<$h4s^c{mvYslh~j$<'>0:q\e}otW}njxl&>0(33?vYfp`yTxio{a)32-46<{Vkse~Q{d`vb,44.991xSlvfs^vgeqg/9:#:<6}Paykp[qbf|h":8$??;r]b|luX|mkm%?:)028wZgazUhlzn(04*55=tWhrbRzkauc+52/682yTmug|_ufbpd.60 ;;7~Qnxhq\pagsi!;2%k5|_`zjwZrci}k#>$??;r]b|luX|mkm%4s^c{mvYslh~j$?;&119p[d~n{V~omyo'27+24>uXiqcxSyjnt`*13,773zUjtd}Ptecwe-4?!8:0Rowir]w`drf ;3"j6}Paykp[qbf|h"8%<>4s^c{mvYslh~j$>>&119p[d~n{V~omyo'30+24>uXiqcxSyjnt`*06,773zUjtd}Ptecwe-54!8:0Rowir]w`drf :>"==5|_`zjwZrci}k#?8'>0:q\e}otW}njxl&<6(33?vYfp`yTxio{a)14-46<{Vkse~Q{d`vb,6>.991xSlvfs^vgeqg/;0#m7~Qnxhq\pagsi!>"==5|_`zjwZrci}k#8='>0:q\e}otW}njxl&;1(33?vYfp`yTxio{a)61-46<{Vkse~Q{d`vb,15.991xSlvfs^vgeqg/<=#:<6}Paykp[qbf|h"?9$??;r]b|luX|mkm%:9)028wZgazUhlzn(55*55=tWhrbRzkauc+0=/682yTmug|_ufbpd.31 l0Rowir]w`drf <#:<6}Paykp[qbf|h"><$??;r]b|luX|mkm%;>)028wZgazUhlzn(40*55=tWhrbRzkauc+16/682yTmug|_ufbpd.2< ;;7~Qnxhq\pagsi!?>%<>4s^c{mvYslh~j$88&119p[d~n{V~omyo'56+24>uXiqcxSyjnt`*6<,773zUjtd}Ptecwe-3>!o1xSlvfs^vgeqg/> ;;7~Qnxhq\pagsi!<;%<>4s^c{mvYslh~j$;?&119p[d~n{V~omyo'63+24>uXiqcxSyjnt`*57,`<{Vkse~Q{d`vb,2/a3zUjtd}Ptecwe->.n2yTmug|_ufbpd.>!880Rowir]w`drf4?91<3h4s^c{mvYslly$='i;r]b|luX|moxx%?&f:q\e}otW}nny&=)g9p[d~n{V~oi~z'3(32?vYfp`yTxik|t=194;b<{Vkse~Q{ie*3-a=tWhrbRzfd)3*`>uXiqcxSygk(3+g?vYfp`yTxdj'3(f8wZgazUei&;)e9p[d~n{V~bh%;&d:q\e}otW}co$;'k;r]b|luX|`n#;$h4s^c{mvYsam6<6=0i;r]b|luX|bon|%>&f:q\e}otW}ani}&>)g9p[d~n{V~`ih~'2(d8wZgazUghk(2+25>uXiqcxSyejeq>0>58d3zUjtd}Ptmcppa=tWhrbRz}e`fzb>uXiqcxSy}{rc*3-c=tWhrbRz|ts`+5,`<{Vkse~Q{supa,7/a3zUjtd}Ptrvqf-5.981xSlvfs^vppwd;;3:5j6}Paykp[qstm{";%k5|_`zjwZrr{lx#=$h4s^c{mvYs}zoy$?'i;r]b|luX||yn~%=&109p[d~n{V~~h|33;2=`>uXiqcxSyxnlhf4?vdn|lxy86}}su35?qkw'Vil#kgio^0h|4>)fne27x`kesdokr3<~hfbh;5xr^c`o3=pzVigg<>4ws]geqgXkfex%>&119tvZbf|hUhcx`{(0+25>quWmkmRm`uov+55/682}ySio{a^alqkr/: ;;7z|Pd`vb[firf}"8%<>4ws]geqgXkfex%:&119tvZbf|hUhcx`{(4+24>quWmkmRm`uov+2,773~xThlzn_bmvjq.0!8:0{Qkauc\gjsi|!2"==5xr^fbpdYdg|d$4'>3:uq[agsiVidycz31183:0=pzVoe:6y}_sqwyEFwn1n0LMv6d78E>1<6sZio6;>n:48277bcl0088;l?{o641?7n:48277bcl0088;l?;R3g5?1d>3:1=>0<6;;noh44<47`3?sR5:<0;6<4>:070Vec2?:j684>33fg`<<4P3?=09wx?ka;38q4be291v(??;:9`8f36d2909n7=52cyK00cv<>:329y!45j3<;o6*;738555=n08l1<75f9d594?=h>8;1<75`61`94?=n1mk1<75f83394?=h=lo1<7*=1786bc=i:8?1<65`5df94?"59?0>jk5a20795>=h=li1<7*=1786bc=i:8?1>65`5d`94?"59?0>jk5a20797>=h=lk1<7*=1786bc=i:8?1865`5d;94?"59?0>jk5a20791>=h=l21<7*=1786bc=i:8?1:65`5d594?"59?0>jk5a20793>=h=o<1<7*=1786bc=i:8?1465`5g794?"59?0>jk5a2079=>=h=o>1<7*=1786bc=i:8?1m65`5g194?"59?0>jk5a2079f>=h=o81<7*=1786bc=i:8?1o65`5g394?"59?0>jk5a2079`>=h=o:1<7*=1786bc=i:8?1i65`5dd94?"59?0>jk5a2079b>=h=l<1<7*=1786bc=i:8?1==54o4g6>5<#:8<19kh4n336>47<3`?h57>5$335>0bf3g8:97>4;h7`5$335>0bf3g8:97<4;h7`2?6=,;;=68jn;o021?5<3`?h97>5$335>0bf3g8:97:4;h7`0?6=,;;=68jn;o021?3<3`?h?7>5$335>0bf3g8:9784;h7`6?6=,;;=68jn;o021?1<3`?o=7>5$335>0bf3g8:9764;h7g4?6=,;;=68jn;o021??<3`?hj7>5$335>0bf3g8:97o4;h7`a?6=,;;=68jn;o021?d<3`?hh7>5$335>0bf3g8:97m4;h7`g?6=,;;=68jn;o021?b<3`?hn7>5$335>0bf3g8:97k4;h7`e?6=,;;=68jn;o021?`<3`?h=7>5$335>0bf3g8:97??;:k6g5<72-8::7;ka:l150<6921b4<<50;9a00`=83;1<7>tH57f?!45j3>>j6a=1283>>{e9>i1<7?50;2xL13b3-89n7?8c:m23g<722wio?4?:5`f>44d28?8wE::e:X`7?cbs??1494:d;1;>6`=:h0>>76>:0f92<<0=3>36?=5778:1?222:>1;:4:1;f95=<6>312=0<0i68o592873?522?i19>48b;0:>03=>90<=79;:4a93c32=:>0987m57180g?7228;1?>49d;492g<1038:64<53c820?152;h1:>48e;12>14=900:o7;i:449<7<>?39;6?;5478:5?2>2881==4;3;01>d<1938;6><59184g?>=:?03:7;6:2592d<>>3?<68l559811=:489;3b>7c=?m02879n:3d9650;32>0c=;j02=7:::23977<2l3>?68h561811?0621815?4=6;74>7b=:;0>m7=m:82906<5n39;68754386f?3d2;>195471;0f>75=0:0=878<:81927<1=3??68=59;:913<2=3?968?57;49f?g=:80:n7?l:0f930<4?38j6;j5418:1?2>2:?1?;49c;6;>3c=>o09479?:969=3<513==65853981f?0>2:l1594;7;10>62=>10?:78n:7`962k1;n48b;36>41=9?036a3-i=6;>k;%a`>36b3-8;i7<>2:m54g<722e?h94?::k:`4<72-8::77k2:l150<732c2h=4?:%022??c:2d9=84>;:k:gc<72-8::77k2:l150<532c2oh4?:%022??c:2d9=84<;:m40a<72-8::79:1:l150<732e<8n4?:%022?1292d9=84>;:m40g<72-8::79:1:l150<532e<8l4?:%022?1292d9=84<;:m40=<72-8::79:1:l150<332e<8:4?:%022?1292d9=84:;:m403<72-8::79:1:l150<132e<884?:%022?1292d9=848;:m401<72-8::79:1:l1504?:%022?1292d9=846;:m407<72-8::79:1:l150o50;&153<0=81e><;51098k25>290/><857438j77228807b9<8;29 7712>?:7c<>5;30?>i0;>0;6)<>6;565>h59<0:865`72494?"59?0<9<5a207950=9>6=4+20493076<84;n500?6=,;;=6:;>;o021?7032e8:9l367=83.9=;48509m643=9010c:=?:18'640=?<;0b??::0c8?j15n3:1(??9:672?k46=3;i76a82d83>!46>3=>=6`=1482g>=h?;n1<7*=178414=i:8?1=i54o60`>5<#:8<1;8?4n336>4c<3f=9n7>5$335>2363g8:97?i;:m46d<72-8::79:1:l150<5821d;?750;&153<0=81e><;52098k240290/><857438j7722;807b9=6;29 7712>?:7c<>5;00?>i0:<0;6)<>6;565>h59<09865`73694?"59?0<9<5a207960=886=4+20493076?84;n516?6=,;;=6:;>;o021?4032e<><4?:%022?1292d9=84=8:9l376=83.9=;48509m643=:010c:?i:18'640=?<;0b??::3c8?j16m3:1(??9:672?k46=38i76a81b83>!46>3=>=6`=1481g>=h?8h1<7*=178414=i:8?1>i54o63b>5<#:8<1;8?4n336>7c<3f=:57>5$335>2363g8:97<;53098k271290/><857438j7722:807b9>5;29 7712>?:7c<>5;10?>i09=0;6)<>6;565>h59<08865`70194?"59?0<9<5a207970=?;6=4+20493076>84;n57b?6=,;;=6:;>;o021?5032e<8h4?:%022?1292d9=84<8:9l31?=83.9=;48509m643=;010c:=j:18'640=?<;0b??::2c8?j14;3:1(??9:672?k46=39i76a82983>!46>3=>=6`=1480g>=h?8n1<7*=178414=i:8?1?i54o631>5<#:8<1;8?4n336>6c<3f=:=7>5$335>2363g8:97=i;:k6f5<722e?h:4?::m4g=<722c2h>4?::k;=f<72-8::766d:l150<732c35o4?:%022?>>l2d9=84>;:k;=d<72-8::766d:l150<532c3544?:%022?>>l2d9=84<;:m:5d<72-8::77>b:l150<732e2=44?:%022??6j2d9=84>;:m575<72-8::78<7:l150<732e=>k4?:%022?04?2d9=84>;:m56a<72-8::78<7:l150<532e=>n4?:%022?04?2d9=84<;:m56g<72-8::78<7:l150<332e=>l4?:%022?04?2d9=84:;:m56<<72-8::78<7:l150<132e=>54?:%022?04?2d9=848;:m562<72-8::78<7:l150;4?:%022?04?2d9=846;:m560<72-8::78<7:l15094?:%022?04?2d9=84m;:m567<72-8::78<7:l150<4?:%022?04?2d9=84k;:m565<72-8::78<7:l1501e><;51098k37d290/><856258j77228807b8>b;29 7712?9<7c<>5;30?>i19h0;6)<>6;403>h59<0:865`60;94?"59?0=?:5a207950=6<84;n401?6=,;;=6;=8;o021?7032e=?94?:%022?04?2d9=84>8:9l265=83.9=;49369m643=9010c;==:18'640=>:=0b??::0c8?j0493:1(??9:714?k46=3;i76a92d83>!46>3<8;6`=1482g>=h>;91<7*=178572=i:8?1=i54o73;>5<#:8<1:>94n336>4c<3f<:;7>5$335>3503g8:97?i;:k;g1<722e>:?4?::m;g`<72-8::76lf:l150<732c2:84?::k7=f<72-8::7:6d:l150<732c?5o4?:%022?2>l2d9=84>;:k7=d<72-8::7:6d:l150<532c?544?:%022?2>l2d9=84<;:k557<722c=jk4?:%022?17;2d9=84?;:k5b`<72-8::79?3:l150<632c=ji4?:%022?17;2d9=84=;:k5bf<72-8::79?3:l150<432c=jl4?:%022?17;2d9=84;;:k5b<<72-8::79?3:l150<232c=j54?:%022?17;2d9=849;:k5b2<72-8::79?3:l150<032c=j;4?:%022?17;2d9=847;:k5b0<72-8::79?3:l150<>32c=j94?:%022?17;2d9=84n;:k5b6<72-8::79?3:l1500:9j2`e=83.9=;48029m643=9810e;km:18'640=?990b??::008?l0bi3:1(??9:620?k46=3;876g9e883>!46>3=;?6`=14820>=n>l21<7*=178446=i:8?1=854i7g4>5<#:8<1;==4n336>40<3`5$335>2643g8:97?8;:k5a1<72-8::79?3:l150<6021b:h=50;&153<08:1e><;51898m3c5290/><857118j77228k07d8j1;29 7712>:87c<>5;3a?>o1m90;6)<>6;537>h59<0:o65f6ed94?"59?0<<>5a20795a=6<;o021?7a32c=hn4?:%022?17;2d9=84=0:9j2ad=83.9=;48029m643=:810e;j6:18'640=?990b??::308?l0c03:1(??9:620?k46=38876g9d683>!46>3=;?6`=14810>=n>m<1<7*=178446=i:8?1>854i7f6>5<#:8<1;==4n336>70<3`5$335>2643g8:97<8;:k5`6<72-8::79?3:l150<5021b:i<50;&153<08:1e><;52898m3b6290/><857118j7722;k07d8k0;29 7712>:87c<>5;0a?>o1kl0;6)<>6;537>h59<09o65f6bf94?"59?0<<>5a20796a=6?k4;h4`f?6=,;;=6:><;o021?4a32c=ol4?:%022?17;2d9=84<0:9j2f?=83.9=;48029m643=;810e;m7:18'640=?990b??::208?l0d?3:1(??9:620?k46=39876g9c783>!46>3=;?6`=14800>=n>j?1<7*=178446=i:8?1?854i621>5<#:8<1;==4n336>60<3`=;=7>5$335>2643g8:97=8;:k445<72-8::79?3:l150<4021b:kl50;&153<08:1e><;53898m3`7290/><857118j7722:k07d8j5;29 7712>:87c<>5;1a?>o1lh0;6)<>6;537>h59<08o65f6bd94?"59?0<<>5a20797a=6>k4;h4`7?6=,;;=6:><;o021?5a32c2i:4?::m:`a<72-8::77j6:l150<732e2i84?:%022??b>2d9=84>;:m:a1<72-8::77j6:l150<532e2i>4?:%022??b>2d9=84<;:m:a7<72-8::77j6:l150<332e2i<4?:%022??b>2d9=84:;:m:a5<72-8::77j6:l150<132e2hk4?:%022??b>2d9=848;:m:``<72-8::77j6:l1502d9=846;:m:`g<72-8::77j6:l1504?:%022?03<2d9=84?;:m507<72-8::78;4:l150<632e=8<4?:%022?03<2d9=84=;:m505<72-8::78;4:l150<432e>?k4?::m;g=<72-8::76l9:l150<732e3o:4?:%022?>d12d9=84>;:m;g3<72-8::76l9:l150<532e3o84?:%022?>d12d9=84<;:m5e6<72-8::78n4:l150<732e=m?4?:%022?0f<2d9=84>;:m5e4<72-8::78n4:l150<532c<2d9=84?;:m:51<72-8::77>6:l150<632e2=>4?:%022??6>2d9=84=;:m:57<72-8::77>6:l150<432e>;k4?::k:`0<722e=5n4?:%022?0>l2d9=84?;:m5=g<72-8::786d:l150<632e=5l4?:%022?0>l2d9=84=;:m5=<<72-8::786d:l150<432e=554?:%022?0>l2d9=84;;:m5=2<72-8::786d:l150<232e=5;4?:%022?0>l2d9=849;:m5=0<72-8::786d:l150<032c3o=4?:%022?>d92d9=84?;:k;fc<72-8::76l1:l150<632c3nh4?:%022?>d92d9=84=;:m:`1<722e=8k4?:%022?0282d9=84?;:m50`<72-8::78:0:l150<632e?4h4?:%022?2?n2d9=84?;:m702d9=84>;:k7=0<72-8::7:68:l150<532c?594?:%022?2>02d9=84<;:m:`<<722c?h84?::m7g2<722c3>=4?::k7`3<722c32c;?4?:%022?30j2d9=84?;:m63d<72-8::7;8b:l150<632e>;44?:%022?30j2d9=84=;:m63=<72-8::7;8b:l150<432e>;:4?:%022?30j2d9=84;;:m633<72-8::7;8b:l150<232e>;84?:%022?30j2d9=849;:m631<72-8::7;8b:l150<032e>;>4?:%022?30j2d9=847;:m634<72-8::7;8b:l150<>32e>;=4?:%022?30j2d9=84n;:m7g3<722c=o?4?::m61=<72-8::7;91:l150<732e>:=4?:%022?3192d9=84>;:m61c<72-8::7;91:l150<532e>9h4?:%022?3192d9=84<;:m61a<72-8::7;91:l150<332e>9n4?:%022?3192d9=84:;:m61g<72-8::7;91:l150<132e>9l4?:%022?3192d9=848;:m61<<72-8::7;91:l1509:4?:%022?3192d9=846;:m613<72-8::7;91:l1504?::m666<722e?i44?::m4f<<722e>=54?:%022?3592d9=84?;:m665<72-8::7;=1:l150<632e>=k4?:%022?3592d9=84=;:m65`<72-8::7;=1:l150<432e>=i4?:%022?3592d9=84;;:m65f<72-8::7;=1:l150<232e>=o4?:%022?3592d9=849;:m65d<72-8::7;=1:l150<032e>=44?:%022?3592d9=847;:m652<72-8::7;=1:l150<>32e>=;4?:%022?3592d9=84n;:k551<722c>994?:%022?32=2d9=84?;:k616<72-8::7;:5:l150<632c>9?4?:%022?32=2d9=84=;:k614<72-8::7;:5:l150<432c>9=4?:%022?32=2d9=84;;:k60c<72-8::7;:5:l150<232c>8h4?:%022?32=2d9=849;:k60a<72-8::7;:5:l150<032c>8n4?:%022?32=2d9=847;:k60g<72-8::7;:5:l150<>32c2o:4?::k;67<722e>n84?:%022?3em2d9=84?;:m6fa<72-8::7;me:l150<632e>nn4?:%022?3em2d9=84=;:m6fg<72-8::7;me:l150<432e>nl4?:%022?3em2d9=84;;:m6f<<72-8::7;me:l150<232e>n54?:%022?3em2d9=849;:m6f2<72-8::7;me:l150<032e>n;4?:%022?3em2d9=847;:m6f1<72-8::7;me:l150<>32e>n>4?:%022?3em2d9=84n;:k51d<722c3n<4?:%022?>e:2d9=84?;:k;f5<72-8::76m2:l150<632c3mk4?:%022?>e:2d9=84=;:k44<<722e32e<5;4?:%022?1f=2d9=84n;:m4=0<72-8::79n5:l1500:9l3=c=83.9=;48a49m643=9810c:6k:18'640=?h?0b??::008?j1?k3:1(??9:6c6?k46=3;876a88c83>!46>3=j96`=14820>=h?1k1<7*=1784e0=i:8?1=854o6::>5<#:8<1;l;4n336>40<3f=347>5$335>2g23g8:97?8;:m4<3<72-8::79n5:l150<6021d;5;50;&153<0i<1e><;51898k2>3290/><857`78j77228k07b973;29 7712>k>7c<>5;3a?>i00;0;6)<>6;5b1>h59<0:o65`79394?"59?02;6=4+20493d36!46>3=j96`=14810>=h?>21<7*=1784e0=i:8?1>854o654>5<#:8<1;l;4n336>70<3f=<:7>5$335>2g23g8:97<8;:m430<72-8::79n5:l150<5021d;::50;&153<0i<1e><;52898k214290/><857`78j7722;k07b982;29 7712>k>7c<>5;0a?>i0?90;6)<>6;5b1>h59<09o65`77d94?"59?06?k4;n55`?6=,;;=6:o:;o021?4a32e<:n4?:%022?1f=2d9=84<0:9l33d=83.9=;48a49m643=;810c:8n:18'640=?h?0b??::208?j1113:1(??9:6c6?k46=39876a86983>!46>3=j96`=14800>=h??=1<7*=1784e0=i:8?1?854o6c7>5<#:8<1;l;4n336>60<3f=j?7>5$335>2g23g8:97=8;:m4e7<72-8::79n5:l150<4021d;4j50;&153<0i<1e><;53898k2?5290/><857`78j7722:k07b977;29 7712>k>7c<>5;1a?>i0?j0;6)<>6;5b1>h59<08o65`76394?"59?0<=6=4+20493d36>k4;n551?6=,;;=6:o:;o021?5a32e=:;4?::m5f3<722c>594?:%022?3>=2d9=84?;:k6=6<72-8::7;65:l150<632c>5?4?:%022?3>=2d9=84=;:k6=4<72-8::7;65:l150<432c>5=4?:%022?3>=2d9=84;;:k64h4?:%022?3>=2d9=849;:k64n4?:%022?3>=2d9=847;:k632c39<4?:%022?>2:2d9=84?;:k;15<72-8::76:2:l150<632c38k4?:%022?>2:2d9=84=;:k;0`<72-8::76:2:l150<432e<;:k:f`<72-8::77l1:l150<532c2ni4?:%022??d92d9=84<;:k:ff<72-8::77l1:l150<332c2no4?:%022??d92d9=84:;:k:fd<72-8::77l1:l150<132c2n44?:%022??d92d9=848;:m63f<722e=:?4?:%022?01;2d9=84?;:m524<72-8::7893:l150<632e=:=4?:%022?01;2d9=84=;:m5<<<72-8::787a:l150<732e=454?:%022?0?i2d9=84>;:m5<`<72-8::787f:l150<732e=4i4?:%022?0?n2d9=84>;:m5;:k7f2<72-8::7:ma:l150<532c?n;4?:%022?2ei2d9=84<;:m7g1<72-8::7:l5:l150<732e?o>4?:%022?2d=2d9=84>;:m7g7<72-8::7:l5:l150<532e?o<4?:%022?2d=2d9=84<;:m7g5<72-8::7:l5:l150<332e?m94?::m412<722e<:?4?:%022?11;2d9=84?;:k7`g<72-8::7:kc:l150<732c?hl4?:%022?2ck2d9=84>;:k7`<<72-8::7:kc:l150<532c?h54?:%022?2ck2d9=84<;:k553<722c?ji4?::k;ff<72-8::76md:l150<732c3no4?:%022?>el2d9=84>;:k;fd<72-8::76md:l150<532c3n44?:%022?>el2d9=84<;:m4e<<72-8::79na:l150<732e;:m4e2<72-8::79na:l150<532e4?:%022?1d<2d9=84?;:m4g7<72-8::79l4:l150<632e1l2d9=84?;:k;2g<72-8::769d:l150<632c3:l4?:%022?>1l2d9=84=;:k;2<<72-8::769d:l150<432c3>94?::m7a`<72-8::7:jf:l150<732e?ii4?:%022?2bn2d9=84>;:m7af<72-8::7:jf:l150<532e?io4?:%022?2bn2d9=84<;:m7ad<72-8::7:jf:l150<332c2mk4?:%022??e;2d9=84?;:k:e`<72-8::77m3:l150<632c2mi4?:%022??e;2d9=84=;:k:ef<72-8::77m3:l150<432c2ml4?:%022??e;2d9=84;;:k:e<<72-8::77m3:l150<232c2m54?:%022??e;2d9=849;:k:e2<72-8::77m3:l150<032c2m;4?:%022??e;2d9=847;:k:e0<72-8::77m3:l150<>32c2m94?:%022??e;2d9=84n;:k:e6<72-8::77m3:l1500:9j=i3:1(??9:8`0?k46=3;876g69883>!46>33i?6`=14820>=n1021<7*=178:f6=i:8?1=854i8;4>5<#:8<15o=4n336>40<3`32:7>5$335>j:1e><;51898m<859c18j77228k07d761;29 77120h87c<>5;3a?>o>190;6)<>6;;a7>h59<0:o65f99d94?"59?02n>5a20795a=6!46>33i?6`=14810>=n11<1<7*=178:f6=i:8?1>854i8:6>5<#:8<15o=4n336>70<3`3387>5$335>j:1e><;52898m<>6290/><859c18j7722;k07d770;29 77120h87c<>5;0a?>o>?l0;6)<>6;;a7>h59<09o65f96f94?"59?02n>5a20796a=6?k4;h;4f?6=,;;=64l<;o021?4a32c2;l4?:%022??e;2d9=84<0:9j=2?=83.9=;46b29m643=;810e497:18'640=1k90b??::208?l?0?3:1(??9:8`0?k46=39876g67783>!46>33i?6`=14800>=n1>?1<7*=178:f6=i:8?1?854i8`1>5<#:8<15o=4n336>60<3`3i=7>5$335>j:1e><;53898m<859c18j7722:k07d765;29 77120h87c<>5;1a?>o>0h0;6)<>6;;a7>h59<08o65f96d94?"59?02n>5a20797a=6>k4;h;47?6=,;;=64l<;o021?5a32e?m84?::m5=c<72-8::78n0:l150<732e=5h4?:%022?0f82d9=84>;:k651<72-8::7;>5:l150<732c>=>4?:%022?36=2d9=84>;:k657<72-8::7;>5:l150<532c>=<4?:%022?36=2d9=84<;:k655<72-8::7;>5:l150<332c>5:l150<132c>5:l150;:m7e5<72-8::7:n3:l150<532e?5k4?:%022?2f;2d9=84<;:m7=`<72-8::7:n3:l150<332c=nh4?:%022?0en2d9=84?;:k5fa<72-8::78mf:l150<632c?n>4?::m6e7<722c2on4?:%022??dl2d9=84?;:k:gg<72-8::77ld:l150<632c2ol4?:%022??dl2d9=84=;:k:g<<72-8::77ld:l150<432e?n?4?::m7<<<722e=:94?:%022?01=2d9=84?;:k;37<72-8::7683:l150<732c<954?::k4gg<72-8::79k4:l150<732c4?:%022?1c<2d9=84>;:k4`7<72-8::79k4:l150<532c2d9=84?;:m411<72-8::79:6:l150<632e<9>4?:%022?12>2d9=84=;:m417<72-8::79:6:l150<432c34h4?:%022?>>=2d9=84?;:k;>=2d9=84=;:k;>=2d9=84;;:k;<=<72-8::7665:l150<232c34:4?:%022?>>=2d9=849;:k;<3<72-8::7665:l150<032c3484?:%022?>>=2d9=847;:k;<1<72-8::7665:l150<>32c34>4?:%022?>>=2d9=84n;:k;<7<72-8::7665:l150>=2d9=84l;:k;3c<72-8::7665:l150>=2d9=84j;:k;3a<72-8::7665:l150>=2d9=84>0:9j<2d=83.9=;47949m643=9810e59n:18'640=00?0b??::008?l>013:1(??9:9;6?k46=3;876g77983>!46>32296`=14820>=n0>=1<7*=178;=0=i:8?1=854i9;7>5<#:8<144;4n336>40<3`22?7>5$335>=?23g8:97?8;:k;=7<72-8::7665:l150<6021b44?50;&153<;51898m=?7290/><858878j77228k07d67f;29 771213>7c<>5;3a?>o?0j0;6)<>6;::1>h59<0:o65f89394?"59?03585a20795a=6?l4?:%022?34j2d9=84>;:m67<<72-8::7;?54?:%022?34j2d9=84<;:m672<72-8::7;?;4?:%022?34j2d9=84:;:m670<72-8::7;?94?:%022?34j2d9=848;:m676<72-8::7;?<4?:%022?34j2d9=846;:m675<72-8::7;692d9=84?;:k;55<72-8::76>1:l150<632c3692d9=84=;:k;4`<72-8::76>1:l150<432c3692d9=84;;:k;4f<72-8::76>1:l150<232c3692d9=849;:k;4d<72-8::76>1:l150<032c3<44?:%022?>692d9=847;:k;42<72-8::76>1:l150<>32c3<;4?:%022?>692d9=84n;:m4aa<722e3h=4?::m57<<72-8::78;:m5=6<72-8::7864:l150<732e=5?4?:%022?0><2d9=84>;:m5=4<72-8::7864:l150<532e=5=4?:%022?0><2d9=84<;:k64<<72-8::7;?a:l150<732c><54?:%022?37i2d9=84>;:k642<72-8::7;?a:l150<532c><;4?:%022?37i2d9=84<;:k640<72-8::7;?a:l150<332c><94?:%022?37i2d9=84:;:k646<72-8::7;?a:l150<132c><=4?:%022?37i2d9=846;:m51`<72-8::78:f:l150<732e=9i4?:%022?02n2d9=84>;:m51f<72-8::78:f:l150<532e=9o4?:%022?02n2d9=84<;:m6fc<722e=?h4?:%022?04n2d9=84?;:m57a<72-8::78ii4?:%022?3an2d9=84>;:m6af<72-8::7;if:l150<532e>io4?:%022?3an2d9=84<;:m6ad<72-8::7;if:l150<332e>i44?:%022?3an2d9=84:;:m6a=<72-8::7;if:l150<132e>i:4?:%022?3an2d9=848;:m6b3<72-8::7;if:l150j84?:%022?3an2d9=846;:m6b1<72-8::7;if:l150j>4?:%022?3an2d9=84m;:m6b7<72-8::7;if:l150j<4?:%022?3an2d9=84k;:m6b5<72-8::7;if:l150ik4?:%022?3an2d9=84i;:m6a3<72-8::7;if:l150<6821d9h;50;&153<2no1e><;51098m<8597d8j7722810e48l:18'640=1?l0b??::398m<0e290/><8597d8j7722:10e48n:18'640=1?l0b??::598m<0>290/><8597d8j7722<10e487:18'640=1?l0b??::798m<00290/><8597d8j7722>10e5mn:188m=5b290/><858578j7722910e5=k:18'640=0=?0b??::098m=5e290/><858578j7722;10e5=n:18'640=0=?0b??::298m=5>290/><858578j7722=10e5=7:18'640=0=?0b??::498m=50290/><858578j7722?10e5=9:18'640=0=?0b??::698m=52290/><858578j7722110e5=;:18'640=0=?0b??::898m=54290/><858578j7722h10e5==:18'640=0=?0b??::c98m=57290/><858578j7722j10e5<858578j7722l10e5<858578j77228:07d6=b;29 77121>>7c<>5;32?>o?:h0;6)<>6;:71>h59<0:>65f83;94?"59?03885a207956=6<:4;h:13?6=,;;=65::;o021?7232c3894?:%022?>3=2d9=84>6:9j<15=83.9=;47449m643=9>10e5:=:18'640=0=?0b??::0:8?l>393:1(??9:966?k46=3;276g74183>!46>32?96`=1482e>=n0:l1<7*=178;00=i:8?1=o54i91`>5<#:8<149;4n336>4e<3`28=7>5$335>=223g8:97?k;:k;63<72-8::76;5:l150<6m21b4?;50;&153<;51g98k041290/><8553d8j7722910c8<8553d8j7722;10c8<8553d8j7722=10c8290/><8553d8j7722?10c8<7:18'640==;l0b??::698k040290/><8553d8j7722110c8<::18'640==;l0b??::898k043290/><8553d8j7722h10c:l8:18'640=?k20b??::198k2d1290/><857c:8j7722810c:l::18'640=?k20b??::398k2d3290/><857c:8j7722:10c4?l:18'640=18n0b??::198m<859b48j7722910e4m;:18'640=1j<0b??::098m<859b48j7722;10e4m=:18'640=1j<0b??::298k1c1290/><854d58j7722910c9k::18'640=<854d58j7722;10c9k<:18'640=<854d58j7722=10e578:18'640=0020b??::198m=?1290/><8588:8j7722810e4k7:188k:18'640=0?80b??::198m=07290/><858708j7722810e5;i:18'640=0?80b??::398k0g42900c9l::188k0??290/><855`38j7722910c8o?:18'640==h;0b??::098k0?a290/><855`38j7722;10c87j:18'640==h;0b??::298k0?c290/><855`38j7722=10c87l:18'640==h;0b??::498k0?e290/><855`38j7722?10c87n:18'640==h;0b??::698k0?>290/><855`38j7722110c878:18'640==h;0b??::898k0?1290/><855`38j7722h10e5<<:188k=72290/><8580g8j7722910c5?k:18'640=08o0b??::098k=7d290/><8580g8j7722;10c5?m:18'640=08o0b??::298k=7f290/><8580g8j7722=10c5?6:18'640=08o0b??::498k=7?290/><8580g8j7722?10c5?8:18'640=08o0b??::698k=71290/><8580g8j7722110c5?;:18'640=08o0b??::898k=74290/><8580g8j7722h10c5l7:188m=d1290/><858c58j7722910e5l::18'640=0k=0b??::098m=d3290/><858c58j7722;10e5l<:18'640=0k=0b??::298m1c7290/><854d38j7722910e9ji:18'640=<854d38j7722;10e9jk:18'640=<8594d8j7722910e4;n:18'640=1290/><8594d8j7722;10e4;7:18'640=1<8594d8j7722=10e4;::18'640=1<8594d8j7722?10e4;<:18'640=1<8594d8j7722110e4;>:18'640=1<8594d8j7722h10e4:i:18'640=1<8594d8j7722j10e4:k:18'640=1<8594d8j7722l10e4:n:18'640=1290/><8594d8j77228:07d7;8;29 77120?m7c<>5;32?>o><>0;6)<>6;;6b>h59<0:>65f95494?"59?029k5a207956=>6=4+2049=0`6<:4;h;70?6=,;;=64;i;o021?7232c28>4?:%022??2n2d9=84>6:9j=14=83.9=;465g9m643=9>10e4:?:18'640=1!46>33>j6`=1482e>=n1:n1<7*=178:1c=i:8?1=o54i81`>5<#:8<158h4n336>4e<3`38n7>5$335><3a3g8:97?k;:k:7d<72-8::77:f:l150<6m21b5>750;&153<>=o1e><;51g98m<5?290/><8594d8j7722;:07d7<7;29 77120?m7c<>5;02?>o>;<0;6)<>6;;6b>h59<09>65f92694?"59?029k5a207966=6?:4;h;06?6=,;;=64;i;o021?4232c2?<4?:%022??2n2d9=84=6:9j=66=83.9=;465g9m643=:>10e4!46>33>j6`=1481e>=n1;i1<7*=178:1c=i:8?1>o54i80b>5<#:8<158h4n336>7e<3`3957>5$335><3a3g8:97=o1e><;52g98m<41290/><8594d8j7722::07d7=5;29 77120?m7c<>5;12?>o>:=0;6)<>6;;6b>h59<08>65f93194?"59?029k5a207976=6>:4;h;15?6=,;;=64;i;o021?5232c29h4?:%022??2n2d9=84<6:9j=0b=83.9=;465g9m643=;>10e4;l:18'640=1!46>33>j6`=1480e>=n1=;1<7*=178:1c=i:8?1?o54i815>5<#:8<158h4n336>6e<3`39n7>5$335><3a3g8:97=k;:k:65<72-8::77:f:l150<4m21b5=o1e><;53g98k2e02900e9hj:188m0d52900c:m::188k3dd2900c:l=:18'640=?k90b??::198k2d6290/><857c18j7722810c:l?:18'640=?k90b??::398k2ga290/><857c18j7722:10c:oj:18'640=?k90b??::598k2gc290/><857c18j7722<10c:ol:18'640=?k90b??::798k2ge290/><857c18j7722>10c97?:188m2df2900e5;k:18'640=0<8584g8j7722810c9k7:188k2032900e5m=:18'640=0j90b??::198k1>?2900c;;<:18'640=><>0b??::198k335290/><856468j7722810c;;>:18'640=><>0b??::398m<152900e5on:18'640=0hh0b??::198m=g>290/><858``8j7722810e5o7:18'640=0hh0b??::398m=g0290/><858``8j7722:10e5o9:18'640=0hh0b??::598m=g2290/><858``8j7722<10e5o;:18'640=0hh0b??::798m=g4290/><858``8j7722>10e5<>:188k3d5290/><856c18j7722910c;l>:18'640=>k90b??::098k3d7290/><856c18j7722;10c9h=:18'640=<854g`8j7722810c9h6:18'640=<854g`8j7722:10c9h8:18'640=<854g`8j7722<10c9h::18'640=<854g`8j7722>10c9h<:18'640=<854g`8j7722010c9h?:18'640=<858178j7722910e5>;:18'640=09?0b??::098m=64290/><858178j7722;10e5>=:18'640=09?0b??::298m=66290/><858178j7722=10e5>?:18'640=09?0b??::498m2`a290/><858178j7722?10e:hj:18'640=09?0b??::698m2`c290/><858178j7722110e:hm:18'640=09?0b??::898m2`f290/><858178j7722h10e59?:18'640=0>;0b??::198m=0a290/><858638j7722810e58j:18'640=0>;0b??::398m3e7290/><856b38j7722910c88<:188m0>>290/><8559c8j7722910e867:18'640==1k0b??::098m0>0290/><8559c8j7722;10e869:18'640==1k0b??::298m0>2290/><8559c8j7722=10e86;:18'640==1k0b??::498m0>4290/><8559c8j7722?10e86=:18'640==1k0b??::698m0>6290/><8559c8j7722110e86?:18'640==1k0b??::898m<04290/><859768j7722910e48=:18'640=1?>0b??::098m<06290/><859768j7722;10e48?:18'640=1?>0b??::298m0e>290/><855ec8j7722910e8m7:18'640==mk0b??::098m0e0290/><855ec8j7722;10e8m9:18'640==mk0b??::298m0e2290/><855ec8j7722=10e8m;:18'640==mk0b??::498m0e4290/><855ec8j7722?10e8m=:18'640==mk0b??::698m0b6290/><855ec8j7722110e8j?:18'640==mk0b??::898m0ea290/><855ec8j7722h10e8mj:18'640==mk0b??::c98m0ec290/><855ec8j7722j10e8ml:18'640==mk0b??::e98m0ee290/><855ec8j7722l10e8mn:18'640==mk0b??::g98m0e6290/><855ec8j77228:07d;l0;29 77125;32?>o1ih0;66g7ae83>!46>32ji6`=1483?>o?ij0;6)<>6;:ba>h59<0:76g75`83>!46>32>n6`=1483?>o?=00;6)<>6;:6f>h59<0:76g75983>!46>32>n6`=1481?>o?=>0;6)<>6;:6f>h59<0876g75783>!46>32>n6`=1487?>o?=<0;6)<>6;:6f>h59<0>76g75583>!46>32>n6`=1485?>o?=:0;6)<>6;:6f>h59<0<76a;cb83>!46>3>hh6`=1483?>i3kk0;6)<>6;6``>h59<0:76a;c`83>!46>3>hh6`=1481?>i3k00;6)<>6;6``>h59<0876a;c983>!46>3>hh6`=1487?>o2<00;6)<>6;77e>h59<0;76g:4983>!46>3??m6`=1482?>o2<>0;6)<>6;77e>h59<0976g:4783>!46>3??m6`=1480?>o2<<0;6)<>6;77e>h59<0?76g:4583>!46>3??m6`=1486?>o2<:0;6)<>6;77e>h59<0=76g:4383>!46>3??m6`=1484?>o2<80;6)<>6;77e>h59<0376g:4183>!46>3??m6`=148:?>o>?90;6)<>6;;45>h59<0;76g61983>>i?>10;66a60e83>!46>33:=6`=1483?>i>8j0;6)<>6;;25>h59<0:76a60c83>!46>33:=6`=1481?>i>8h0;6)<>6;;25>h59<0876a60983>!46>33:=6`=1487?>i>8>0;6)<>6;;25>h59<0>76a60783>!46>33:=6`=1485?>i>8<0;6)<>6;;25>h59<0<76a60583>!46>33:=6`=148;?>i>8:0;6)<>6;;25>h59<0276a60383>!46>33:=6`=148b?>i>880;6)<>6;;25>h59<0i76a60183>!46>33:=6`=148`?>i?no0;6)<>6;;25>h59<0o76a7fe83>!46>33:=6`=148f?>i?nj0;6)<>6;;25>h59<0m76a7fc83>!46>33:=6`=14824>=h0ok1<7*=178:54=i:8?1=<54o9d:>5<#:8<1544<3f2m47>5$335><763g8:97?<;:m;b2<72-8::77>1:l150<6<21d4k850;&153<>981e><;51498k=`2290/><859038j77228<07b6i4;29 77120;:7c<>5;34?>i?n;0;6)<>6;;25>h59<0:465`8g394?"59?02=<5a20795<=6;o021?7e32e3ih4?:%022??692d9=84>c:9l<`b=83.9=;46109m643=9m10c5kl:18'640=18;0b??::0g8?j>bj3:1(??9:832?k46=3;m76a7e`83>!46>33:=6`=14814>=h0l31<7*=178:54=i:8?1><54o9g4>5<#:8<1574<3f2n:7>5$335><763g8:97<<;:m;a0<72-8::77>1:l150<5<21d4h:50;&153<>981e><;52498k=c4290/><859038j7722;<07b6j2;29 77120;:7c<>5;04?>i?m80;6)<>6;;25>h59<09465`8d294?"59?02=<5a20796<=6?o4;n:ga?6=,;;=64?>;o021?4e32e3hn4?:%022??692d9=84=c:9lc13:1(??9:832?k46=38m76a7d983>!46>33:=6`=14804>=h0m=1<7*=178:54=i:8?1?<54o9f5>5<#:8<1564<3f2o97>5$335><763g8:97=<;:m;`1<72-8::77>1:l150<4<21d4i=50;&153<>981e><;53498k<77290/><859038j7722:<07b7?f;29 77120;:7c<>5;14?>i>8l0;6)<>6;;25>h59<08465`91;94?"59?02=<5a20797<=6>o4;n:e7?6=,;;=64?>;o021?5e32e3i54?:%022??692d9=84c93:1(??9:832?k46=39m76g74b83>!46>32?h6`=1483?>o?6;:7`>h59<0:76g74`83>!46>32?h6`=1481?>o?<00;6)<>6;:7`>h59<0876a7cb83>!46>32hh6`=1483?>i?kk0;6)<>6;:``>h59<0:76a:3b83>>i1=10;6)<>6;46=>h59<0;76a95683>!46>3<>56`=1482?>i1=?0;6)<>6;46=>h59<0976a95483>!46>3<>56`=1480?>o3j=0;66a86183>!46>3===6`=1483?>i0=o0;6)<>6;555>h59<0:76a85d83>!46>3===6`=1481?>i0=m0;6)<>6;555>h59<0876a85b83>!46>3===6`=1487?>i0=k0;6)<>6;555>h59<0>76a85`83>!46>3===6`=1485?>i0=00;6)<>6;555>h59<0<76g:7d83>>o2?m0;66g:b083>>o1jh0;6)<>6;4af>h59<0;76g9b883>!46>3o1j10;6)<>6;4af>h59<0976g9b683>!46>3o?>?0;6)<>6;:53>h59<0;76g76483>!46>32=;6`=1482?>o?>=0;6)<>6;:53>h59<0976g76283>!46>32=;6`=1480?>o?9o0;66g80683>!46>3=;46`=1483?>o08?0;6)<>6;53<>h59<0:76g80483>!46>3=;46`=1481?>o08=0;6)<>6;53<>h59<0876a9b583>!46>3i>l>0;66g91283>>i1i10;6)<>6;4b=>h59<0;76a9a683>!46>3i1i?0;6)<>6;4b=>h59<0976a9a483>!46>3i1090;6)<>6;4;3>h59<0;76a97g83>!46>3<3;6`=1482?>i1?m0;6)<>6;4;3>h59<0976a97b83>!46>3<3;6`=1480?>i1?k0;6)<>6;4;3>h59<0?76a97`83>!46>3<3;6`=1486?>i1?00;6)<>6;4;3>h59<0=76a97983>!46>3<3;6`=1484?>i1?>0;6)<>6;4;3>h59<0376a97783>!46>3<3;6`=148:?>i1?<0;6)<>6;4;3>h59<0j76a97583>!46>3<3;6`=148a?>i1?;0;6)<>6;4;3>h59<0h76a97083>!46>3<3;6`=148g?>i1?90;6)<>6;4;3>h59<0n76a96g83>!46>3<3;6`=148e?>i1>l0;6)<>6;4;3>h59<0:<65`67f94?"59?0=4:5a207954=6<<4;n45f?6=,;;=6;68;o021?7432e=:l4?:%022?0??2d9=84>4:9l23?=83.9=;49869m643=9<10c;69:18'640=>1=0b??::048?j0?=3:1(??9:7:4?k46=3;<76a98583>!46>3<3;6`=1482<>=h>191<7*=1785<2=i:8?1=454o7:1>5<#:8<1:594n336>4g<3f<3=7>5$335>3>03g8:97?m;:m53`<72-8::7877:l150<6k21d::=50;&153<10>1e><;51e98k30?290/><856958j77228o07b897;29 7712?2<7c<>5;3e?>i1980;66g8f183>!46>3=m56`=1483?>o0n10;6)<>6;5e=>h59<0:76g8f683>!46>3=m56`=1481?>o0n?0;6)<>6;5e=>h59<0876g8f483>!46>3=m56`=1487?>o0n=0;6)<>6;5e=>h59<0>76g8f283>!46>3=m56`=1485?>o0n;0;6)<>6;5e=>h59<0<76g8f083>!46>3=m56`=148;?>o0mo0;6)<>6;5e=>h59<0276g8ed83>!46>3=m56`=148b?>o2;m0;66g7a083>!46>32j>6`=1483?>o?i90;6)<>6;:b6>h59<0:76g79g83>!46>32j>6`=1481?>o?1l0;6)<>6;:b6>h59<0876g6b683>!46>33i46`=1483?>o>j?0;6)<>6;;a<>h59<0:76g6b483>!46>33i46`=1481?>o>j=0;6)<>6;;a<>h59<0876g:3d83>>i0jl0;6)<>6;5ab>h59<0;76a8be83>!46>3=ij6`=1482?>i0jj0;6)<>6;5ab>h59<0976a8bc83>!46>3=ij6`=1480?>i>>?0;66a;fb83>>o31;0;66g8e283>!46>3=no6`=1483?>o0mk0;6)<>6;5fg>h59<0:76g8e`83>!46>3=no6`=1481?>o0m00;6)<>6;5fg>h59<0876g8e983>!46>3=no6`=1487?>o0m>0;6)<>6;5fg>h59<0>76g8e783>!46>3=no6`=1485?>o0m<0;6)<>6;5fg>h59<0<76g8e583>!46>3=no6`=148;?>o0m;0;6)<>6;5fg>h59<0276g8e083>!46>3=no6`=148b?>o3jl0;6)<>6;6ab>h59<0;76g;be83>!46>3>ij6`=1482?>o3jj0;6)<>6;6ab>h59<0976g;bc83>!46>3>ij6`=1480?>i2>?0;6)<>6;75b>h59<0;76a:6d83>!46>3?=j6`=1482?>i2>m0;6)<>6;75b>h59<0976a:6b83>!46>3?=j6`=1480?>i2>k0;6)<>6;75b>h59<0?76a:6`83>!46>3?=j6`=1486?>i2>00;6)<>6;75b>h59<0=76a:6983>!46>3?=j6`=1484?>i2>>0;6)<>6;75b>h59<0376a:6483>!46>3?=j6`=148:?>i2>=0;6)<>6;75b>h59<0j76a;a`83>!46>3>jn6`=1483?>i3i00;6)<>6;6bf>h59<0:76a;a983>!46>3>jn6`=1481?>i3i>0;6)<>6;6bf>h59<0876a;a783>!46>3>jn6`=1487?>o3180;66a:a783>!46>3?jj6`=1483?>i2il0;6)<>6;7bb>h59<0:76a:ae83>!46>3?jj6`=1481?>i2ij0;6)<>6;7bb>h59<0876a:ac83>!46>3?jj6`=1487?>i2ih0;6)<>6;7bb>h59<0>76a:a883>!46>3?jj6`=1485?>i2i10;6)<>6;7bb>h59<0<76a:a683>!46>3?jj6`=148;?>i2i<0;6)<>6;7bb>h59<0276a:a583>!46>3?jj6`=148b?>o08j0;6)<>6;53`>h59<0;76g80c83>!46>3=;h6`=1482?>i3l;0;6)<>6;6g7>h59<0;76a;d083>!46>3>o?6`=1482?>i3l90;6)<>6;6g7>h59<0976a;cg83>!46>3>o?6`=1480?>i3kl0;6)<>6;6g7>h59<0?76a94b83>!46>3i16;47`>h59<0:76a94`83>!46>3i1<00;6)<>6;47`>h59<0876a94983>!46>3i1<>0;6)<>6;47`>h59<0>76a94783>!46>3i1<<0;6)<>6;47`>h59<0<76a6c983>>i2:;0;66a;b183>!46>3>i=6`=1483?>i3io0;6)<>6;6a5>h59<0:76a;ad83>!46>3>i=6`=1481?>i3im0;6)<>6;6a5>h59<0876a;ab83>!46>3>i=6`=1487?>o?<>0;6)<>6;:7<>h59<0;76g74783>!46>32?46`=1482?>i>9>0;66g91483>>d3?90;6<4?:1y'67d=9>i0D98i;I66a>i6?k0;66sm46394?7=83:p(?>odm3:17d<>8;29?j4613:17pl<6983>1<729q/>?l516:8L10a3A>>i6*>8b87?le32900enk50;9j64>=831d><750;9~f6c6290?6=4?{%01f?7002B?:k5G44g8 4>d2=1bo94?::k`a?6=3`8:47>5;n02=?6=3th8i=4?:583>5}#:;h1=:64H54e?M22m2.:4n4;;ha7>5<5<3290;w)<=b;342>N3>o1C88k4$0:`>4=nk=0;66gla;29?leb2900c??6:188yg22<3:1?7>50z&16g<6?<1C8;h4H57f?Me63-;o47<=a:&25<4290;w)<=b;341>N3>o1C88k4Hb38 4b?2;8j7)?7c;38mf2=831boh4?::m15<<722wi89750;694?6|,;8i6<97;I65b>N3=l1Co<5+1e:967g<,82h695fc583>>odm3:17d<>8;29?j4613:17pl;5283>6<729q/>?l51678L10a3A>>i6Fl1:&2`=<5:h1/=5m51:k`0?6=3`in6=44o33:>5<>47>53;294~"5:k0:;85G47d8L13b3Ai:7)?k8;01e>"60j0:7dm;:188mfc=831d><750;9~f12?290?6=4?{%01f?7002B?:k5G44g8Lf7<,8n36?1<75fcd83>>o5910;66a=1883>>{e<=n1<7:50;2x 74e28=37E:9f:J71`=#91i186gl4;29?leb2900e??7:188k77>2900qo:;c;290?6=8r.9>o4>799K03`<@=?n7)?7c;68mf2=831boh4?::k15=<722e9=44?::a004=83?1<7>t$30a>41>3A>=j6F;5d9Kg4=#9m21>?o4ib694?=nk<0;66gle;29?l4603:17b<>9;29?xd3=80;694?:1y'67d=9>20D98i;I66a>"60j0?7dm;:188mfc=831b><650;9l64?=831vn9;?:187>5<7s-89n7?88:J72c=O<5;h025;|`702<72=0;6=u+23`952><@="6l109>l5+19a90>od<3:17dmj:188m77?2900c??6:188yg5e03:187>50z&16g<6l01C8;h4H57f?le02900ei<50;9j674=831d>1<7>t$30a>4b>3A>=j6F;5d9jg2<722co>7>5;h016?6=3f8:=7>5;|`0f0<72=0;6=u+23`95a?<@=5<5<5<3290;w)<=b;3g=>N3>o1C88k4ib594?=nl;0;66g=2383>>i5980;66sm3bd94?2=83:p(?>oc:3:17d<=2;29?j4693:17pl1<729q/>?l51e;8L10a3A>>i6gl7;29?lb52900e?<=:188k7762900qo=ld;290?6=8r.9>o4>d89K03`<@=?n7dm8:188ma4=831b>?<50;9l647=831vn>ml:187>5<7s-89n7?k9:J72c=O<?4?::m154<722wi?nl50;694?6|,;8i6N3=l1bo:4?::kg6?6=3`89>7>5;n025?6=3th8h=4?:583>5}#:;h1=i74H54e?M22m2ch;7>5;hf1>5<5<54;294~"5:k0:h45G47d8L13b3`i<6=44ie094?=n:;81<75`20394?=zj:9n6=4;:183!45j3;o56F;6g9K00c>o5:;0;66a=1083>>{e;:n1<7:50;2x 74e28n27E:9f:J71`=nk>0;66gk2;29?l45:3:17b<>1;29?xd4;j0;694?:1y'67d=9m30D98i;I66a>od?3:17dj=:188m7452900c??>:188yg54j3:187>50z&16g<6l01C8;h4H57f?le02900ei<50;9j674=831d>1<7>t$30a>4b>3A>=j6F;5d9jg2<722co>7>5;h016?6=3f8:=7>5;|`010<72=0;6=u+23`95a?<@=5<5<5<3290;w)<=b;3g=>N3>o1C88k4ib594?=nl;0;66g=2383>>i5980;66sm34094?2=83:p(?>oc:3:17d<=2;29?j4693:17pl<5683>1<729q/>?l51e;8L10a3A>>i6gl7;29?lb52900e?<=:188k7762900qo=n5;291?6=8r.9>o4>769K03`<@=?n7)?7c;38mf2=831bo84?::k`e?6=3`in6=44o33:>5<55;294~"5:k0:;:5G47d8L13b3-;3o7?4ib694?=nk<0;66gla;29?leb2900c??6:188yg5fk3:197>50z&16g<6?>1C8;h4H57f?!7?k3;0en:50;9jg0<722chm7>5;haf>5<1<75fc483>>odi3:17dmj:188k77>2900qo=na;291?6=8r.9>o4>769K03`<@=?n7)?7c;38mf2=831bo84?::k`e?6=3`in6=44o33:>5<55;294~"5:k0:;:5G47d8L13b3-;3o7?4ib694?=nk<0;66gla;29?leb2900c??6:188yg5f03:197>50z&16g<6?>1C8;h4H57f?!7?k3;0en:50;9jg0<722chm7>5;haf>5<1<75fc483>>odi3:17dmj:188k77>2900qo=n6;291?6=8r.9>o4>769K03`<@=?n7)?7c;38mf2=831bo84?::k`e?6=3`in6=44o33:>5<55;294~"5:k0:;:5G47d8L13b3-;3o7?4ib694?=nk<0;66gla;29?leb2900c??6:188yg5f;3:197>50z&16g<6?>1C8;h4H57f?!7?k3;0en:50;9jg0<722chm7>5;haf>5<1<75fc483>>odi3:17dmj:188k77>2900qo=67;291?6=8r.9>o4>769K03`<@=?n7)?7c;38mf2=831bo84?::k`e?6=3`in6=44o33:>5<55;294~"5:k0:;:5G47d8L13b3-;3o7?4ib694?=nk<0;66gla;29?leb2900c??6:188yg5>=3:197>50z&16g<6?>1C8;h4H57f?!7?k3;0en:50;9jg0<722chm7>5;haf>5<1<75fc483>>odi3:17dmj:188k77>2900qo=63;291?6=8r.9>o4>769K03`<@=?n7)?7c;38mf2=831bo84?::k`e?6=3`in6=44o33:>5<7>55;294~"5:k0:;:5G47d8L13b3-;3o7?4ib694?=nk<0;66gla;29?leb2900c??6:188yg5>93:197>50z&16g<6?>1C8;h4H57f?!7?k3;0en:50;9jg0<722chm7>5;haf>5<1<75fc483>>odi3:17dmj:188k77>2900qo=7e;291?6=8r.9>o4>769K03`<@=?n7)?7c;38mf2=831bo84?::k`e?6=3`in6=44o33:>5<8?7>55;294~"5:k0:;:5G47d8L13b3-;3o7?4ib694?=nk<0;66gla;29?leb2900c??6:188yg24j3:197>50z&16g<6?>1C8;h4H57f?!7?k3;0en:50;9jg0<722chm7>5;haf>5<1<75fc483>>odi3:17dmj:188k77>2900qo:<9;291?6=8r.9>o4>769K03`<@=?n7)?7c;38mf2=831bo84?::k`e?6=3`in6=44o33:>5<847>55;294~"5:k0:;:5G47d8L13b3-;3o7?4ib694?=nk<0;66gla;29?leb2900c??6:188yg24?3:197>50z&16g<6?>1C8;h4H57f?!7?k3;0en:50;9jg0<722chm7>5;haf>5<1<75fc483>>odi3:17dmj:188k77>2900qo:<5;291?6=8r.9>o4>769K03`<@=?n7)?7c;38mf2=831bo84?::k`e?6=3`in6=44o33:>5<887>55;294~"5:k0:;:5G47d8L13b3-;3o7?4ib694?=nk<0;66gla;29?leb2900c??6:188yg2593:197>50z&16g<6?>1C8;h4H57f?!7?k3;0en:50;9jg0<722chm7>5;haf>5<1<75fc483>>odi3:17dmj:188k77>2900qo:=8;291?6=8r.9>o4>769K03`<@=?n7)?7c;38mf2=831bo84?::k`e?6=3`in6=44o33:>5<9;7>55;294~"5:k0:;:5G47d8L13b3-;3o7?4ib694?=nk<0;66gla;29?leb2900c??6:188yg25>3:197>50z&16g<6?>1C8;h4H57f?!7?k3;0en:50;9jg0<722chm7>5;haf>5<1<75fc483>>odi3:17dmj:188k77>2900qo:=4;291?6=8r.9>o4>769K03`<@=?n7)?7c;38mf2=831bo84?::k`e?6=3`in6=44o33:>5<9?7>55;294~"5:k0:;:5G47d8L13b3-;3o7?4ib694?=nk<0;66gla;29?leb2900c??6:188yg25:3:197>50z&16g<6?>1C8;h4H57f?!7?k3;0en:50;9jg0<722chm7>5;haf>5<1<75fc483>>odi3:17dmj:188k77>2900qo:>e;291?6=8r.9>o4>789K03`<@=?n7)?7c;68mf2=831bo84?::k`a?6=3`8:47>5;n02=?6=3th?5}#:;h1=:94H54e?M22m2.:4n4>;ha7>5<>odm3:17b<>9;29?xd39;0;684?:1y'67d=9>=0D98i;I66a>"60j0:7dm;:188mf3=831bol4?::k`a?6=3f8:57>5;|`754<72<0;6=u+23`9521<@=0<729q/>?l51658L10a3A>>i6*>8b82?le32900en;50;9jgd<722chi7>5;n02=?6=3th?5}#:;h1=:94H54e?M22m2.:4n4>;ha7>5<>odm3:17b<>9;29?xd38l0;684?:1y'67d=9>=0D98i;I66a>"60j0:7dm;:188mf3=831bol4?::k`a?6=3f8:57>5;|`74a<72<0;6=u+23`9521<@=0<729q/>?l51658L10a3A>>i6*>8b82?le32900en;50;9jgd<722chi7>5;n02=?6=3th?5}#:;h1=:94H54e?M22m2.:4n4>;ha7>5<>odm3:17b<>9;29?xd3800;684?:1y'67d=9>30D98i;I66a>"60j0?7dm;:188mf3=831boh4?::k15=<722e9=44?::a6a4=83>1<7>t$30a>4113A>=j6F;5d9'5=e=92ch87>5;hab>5<>odi3:17dmj:188k77>2900qoo4>779K03`<@=?n7)?7c;38mf2=831bol4?::k`a?6=3f8:57>5;|`1`=<72=0;6=u+23`9520<@=>{e:m=1<7:50;2x 74e28==7E:9f:J71`=#91i1=6gl4;29?lef2900enk50;9l64?=831vn?j9:187>5<7s-89n7?86:J72c=O<5;haf>5<1<75fc`83>>odm3:17b<>9;29?xd5l=0;694?:1y'67d=9><0D98i;I66a>"60j0:7dm;:188mfg=831boh4?::m15<<722wi>i=50;694?6|,;8i6<99;I65b>N3=l1/=5m51:k`0?6=3`ij6=44ibg94?=h:831<75rb3f2>5<3290;w)<=b;342>N3>o1C88k4$0:`>4=nk=0;66gla;29?leb2900c??6:188yg4c83:187>50z&16g<6??1C8;h4H57f?!7?k3;0en:50;9jgd<722chi7>5;n02=?6=3th9:<4?:583>5}#:;h1=:84H54e?M22m2.:4n4>;ha7>5<>i5900;66sm27;94?2=83:p(?od<3:17dmn:188mfc=831d><750;9~f70?290?6=4?{%01f?70>2B?:k5G44g8 4>d281bo94?::k`e?6=3`in6=44o33:>5<54;294~"5:k0:;;5G47d8L13b3-;3o7?4ib694?=nkh0;66gle;29?j4613:17pl=6783>1<729q/>?l51648L10a3A>>i6*>8b82?le32900eno50;9jg`<722e9=44?::a633=83>1<7>t$30a>4113A>=j6F;5d9'5=e=92ch87>5;hab>5<>odi3:17dmj:188k77>2900qo<93;290?6=8r.9>o4>779K03`<@=?n7)?7c;38mf2=831bol4?::k`a?6=3f8:57>5;|`127<72=0;6=u+23`9520<@=>{e:?:1<7:50;2x 74e28==7E:9f:J71`=#91i1=6gl4;29?lef2900enk50;9l64?=831vn?;i:187>5<7s-89n7?86:J72c=O<5;haf>5<1<75fc`83>>odm3:17b<>9;29?xd4;<0;694?:1y'67d=9><0D98i;I66a>"60j0:7dm;:188mfg=831boh4?::m15<<722wi?>:50;694?6|,;8i6<99;I65b>N3=l1/=5m51:k`0?6=3`ij6=44ibg94?=h:831<75rb210>5<3290;w)<=b;342>N3>o1C88k4$0:`>4=nk=0;66gla;29?leb2900c??6:188yg54:3:187>50z&16g<6??1C8;h4H57f?!7?k3;0en:50;9jgd<722chi7>5;n02=?6=3th8?<4?:583>5}#:;h1=:84H54e?M22m2.:4n4>;ha7>5<>i5900;66sm32294?2=83:p(?od<3:17dmn:188mfc=831d><750;9~f64a290?6=4?{%01f?70>2B?:k5G44g8 4>d281bo94?::k`e?6=3`in6=44o33:>5<54;294~"5:k0:;;5G47d8L13b3-;3o7?4ib694?=nkh0;66gle;29?j4613:17pl<2b83>1<729q/>?l51648L10a3A>>i6*>8b82?le32900eno50;9jg`<722e9=44?::a77d=83>1<7>t$30a>4113A>=j6F;5d9'5=e=92ch87>5;hab>5<>odi3:17dmj:188k77>2900qoo4>779K03`<@=?n7)?7c;38mf2=831bol4?::k`a?6=3f8:57>5;|`1g6<72=0;6=u+23`9520<@=>{e:j81<7:50;2x 74e28==7E:9f:J71`=#91i1=6gl4;29?lef2900enk50;9l64?=831vn?m>:187>5<7s-89n7?86:J72c=O<5;haf>5<1<75fc`83>>odm3:17b<>9;29?xd5jo0;694?:1y'67d=9><0D98i;I66a>"60j0:7dm;:188mfg=831boh4?::m15<<722wi>ok50;694?6|,;8i6<99;I65b>N3=l1/=5m51:k`0?6=3`ij6=44ibg94?=h:831<75rb3`g>5<3290;w)<=b;342>N3>o1C88k4$0:`>4=nk=0;66gla;29?leb2900c??6:188yg4ej3:187>50z&16g<6??1C8;h4H57f?!7?k3;0en:50;9jgd<722chi7>5;n02=?6=3th9nl4?:583>5}#:;h1=:84H54e?M22m2.:4n4>;ha7>5<>i5900;66sm2gg94?2=83:p(?=:;k0(<6l:09jg1<722chm7>5;haf>5<8b82?le32900eno50;9jg`<722e9=44?::a753=83>1<7>t$30a>4113A>=j6F;5d9Kg4=#9m21>?o4$0:`>4=nk=0;66gla;29?leb2900c??6:188yg57<3:187>50z&16g<6??1C8;h4H57f?Me63-;o47<=a:&2>{e;991<7:50;2x 74e28==7E:9f:J71`=Ok81/=i6523c8 4>d281bo94?::k`e?6=3`in6=44o33:>5<7>54;294~"5:k0:;;5G47d8L13b3Ai:7)?k8;01e>"60j0:7dm;:188mfg=831boh4?::m15<<722wi?=?50;694?6|,;8i6<99;I65b>N3=l1Co<5+1e:967g<,82h6<5fc583>>odi3:17dmj:188k77>2900qo=?0;290?6=8r.9>o4>779K03`<@=?n7Em>;%3g;ha7>5<>i5900;66sm2gd94?2=83:p(?=:;k0(<6l:09jg1<722chm7>5;haf>5<8b82?le32900eno50;9jg`<722e9=44?::a6ce=83>1<7>t$30a>4113A>=j6F;5d9Kg4=#9m21>?o4$0:`>4=nk=0;66gla;29?leb2900c??6:188yg4?l3:187>50z&16g<6??1C8;h4H57f?Me63-;o47<=a:&2>{e:0?1<7:50;2x 74e28==7E:9f:J71`=Ok81/=i6523c8 4>d281bo94?::k`e?6=3`in6=44o33:>5<54;294~"5:k0:;;5G47d8L13b3Ai:7)?k8;01e>"60j0:7dm;:188mfg=831boh4?::m15<<722wi>4=50;694?6|,;8i6<99;I65b>N3=l1Co<5+1e:967g<,82h6<5fc583>>odi3:17dmj:188k77>2900qo<62;290?6=8r.9>o4>779K03`<@=?n7Em>;%3g;ha7>5<>i5900;66sm28394?2=83:p(?=:;k0(<6l:09jg1<722chm7>5;haf>5<8b82?le32900eno50;9jg`<722e9=44?::a6=`=83>1<7>t$30a>4113A>=j6F;5d9Kg4=#9m21>?o4$0:`>4=nk=0;66gla;29?leb2900c??6:188yg4?m3:187>50z&16g<6??1C8;h4H57f?Me63-;o47<=a:&2>{e:1i1<7:50;2x 74e28==7E:9f:J71`=Ok81/=i6523c8 4>d281bo94?::k`e?6=3`in6=44o33:>5<54;294~"5:k0:;;5G47d8L13b3Ai:7)?k8;01e>"60j0:7dm;:188mfg=831boh4?::m15<<722wi>:950;694?6|,;8i6<99;I65b>N3=l1/=5m51:k`0?6=3`ij6=44ibg94?=h:831<75rb35e>5<3290;w)<=b;342>N3>o1C88k4$0:`>4=nk=0;66gla;29?leb2900c??6:188yg40m3:187>50z&16g<6??1C8;h4H57f?!7?k3;0en:50;9jgd<722chi7>5;n02=?6=3th9;i4?:583>5}#:;h1=:84H54e?M22m2.:4n4>;ha7>5<>i5900;66sm26a94?2=83:p(?od<3:17dmn:188mfc=831d><750;9~f71e290?6=4?{%01f?70>2B?:k5G44g8 4>d281bo94?::k`e?6=3`in6=44o33:>5<54;294~"5:k0:;;5G47d8L13b3-;3o7?4ib694?=nkh0;66gle;29?j4613:17pl=7883>1<729q/>?l51648L10a3A>>i6*>8b82?le32900eno50;9jg`<722e9=44?::a62>=83>1<7>t$30a>4113A>=j6F;5d9'5=e=92ch87>5;hab>5<>odi3:17dmj:188k77>2900qo<85;290?6=8r.9>o4>779K03`<@=?n7)?7c;38mf2=831bol4?::k`a?6=3f8:57>5;|`1a=<72=0;6=u+23`9520<@=>{e:o:1<7:50;2x 74e28==7E:9f:J71`=#91i1=6gl4;29?lef2900enk50;9l64?=831vn?ki:187>5<7s-89n7?86:J72c=O<5;haf>5<1<75fc`83>>odm3:17b<>9;29?xd5mm0;694?:1y'67d=9><0D98i;I66a>"60j0:7dm;:188mfg=831boh4?::m15<<722wi>hm50;694?6|,;8i6<99;I65b>N3=l1/=5m51:k`0?6=3`ij6=44ibg94?=h:831<75rb3ga>5<3290;w)<=b;342>N3>o1C88k4$0:`>4=nk=0;66gla;29?leb2900c??6:188yg4bi3:187>50z&16g<6??1C8;h4H57f?!7?k3;0en:50;9jgd<722chi7>5;n02=?6=3th9i44?:583>5}#:;h1=:84H54e?M22m2.:4n4>;ha7>5<>i5900;66sm2d594?2=83:p(?od<3:17dmn:188mfc=831d><750;9~f7c1290?6=4?{%01f?70>2B?:k5G44g8 4>d281bo94?::k`e?6=3`in6=44o33:>5<??7>53;294~"5:k0:h>5G47d8L13b3-;3o75;n025?6=3th8i?4?:283>5}#:;h1=i=4H54e?M22m2.:4n4=c:k23`<722c:;k4?::m154<722wi?;850;794?6|,;8i6N3=l1/=5m51608m41b2900e<9i:188m4>72900e<6>:188k7762900qo=ne;292?6=8r.9>o4>d79K03`<@=?n7)?7c;62?l70m3:17d?8f;29?l7?83:17d?71;29?l7?:3:17b<>1;29?xd3=?0;6>4?:1y'67d=9m90D98i;I66a>"60j09o6g>7d83>>o6?o0;66a=1083>>{e<5<3`;5;h34b?6=3`;3<7>5;h3;5?6=3f8:=7>5;|`0g4<72<0;6=u+23`95a3<@=12c:;h4?::k23c<722c:4=4?::k2<4<722e9=<4?::a7g`=83?1<7>t$30a>4b23A>=j6F;5d9'5=e=101b=:k50;9j52`=831b=5>50;9j5=7=831d>6=4?{%01f?7c=2B?:k5G44g8 4>d2030e<9j:188m41a2900e<6?:188m4>62900c??>:188yg5ej3:197>50z&16g<6l<1C8;h4H57f?!7?k3327d?8e;29?l70n3:17d?70;29?l7?93:17b<>1;29?xd4k<0;6>4?:1y'67d=9m90D98i;I66a>"60j0246g>7d83>>o6?o0;66a=1083>>{e;mk1<7;50;2x 74e28n>7E:9f:J71`=#91i1545f16g94?=n9>l1<75f19294?=n91;1<75`20394?=zj:n36=4::183!45j3;o96F;6g9K00c<,82h65k4i05f>5<5<5<55;294~"5:k0:h85G47d8L13b3-;3o76j;h34a?6=3`;5;h3;4?6=3`;3=7>5;n025?6=3th8h94?:483>5}#:;h1=i;4H54e?M22m2.:4n47f:k23`<722c:;k4?::k2<5<722c:4<4?::m154<722wi?im50;194?6|,;8i6N3=l1/=5m5999j52c=831b=:h50;9l647=831vn>:n:186>5<7s-89n7?k5:J72c=O<72900e<6>:188k7762900qo=;8;291?6=8r.9>o4>d49K03`<@=?n7)?7c;:e?l70m3:17d?8f;29?l7?83:17d?71;29?j4693:17pl<4783>0<729q/>?l51e78L10a3A>>i6*>8b8;b>o6?l0;66g>7g83>>o6090;66g>8083>>i5980;66sm35694?3=83:p(?o1<75f16d94?=n91:1<75f19394?=h:8;1<75rb261>5<2290;w)<=b;3g1>N3>o1C88k4$0:`>=c5<5<5;h34b?6=3f8:=7>5;|`024<72<0;6=u+23`95a3<@=t$30a>4b23A>=j6F;5d9'5=e=0l1b=:k50;9j52`=831b=5>50;9j5=7=831d>6=4?{%01f?7c=2B?:k5G44g8 4>d21o0e<9j:188m41a2900e<6?:188m4>62900c??>:188yg52j3:197>50z&16g<6l<1C8;h4H57f?!7?k32m7d?8e;29?l70n3:17d?70;29?l7?93:17b<>1;29?xd4>:0;6>4?:1y'67d=9m90D98i;I66a>"60j0246g>7d83>>o6?o0;66a=1083>>{e;>21<7850;2x 74e28n=7E:9f:J71`=#91i15k5f16g94?=n9>l1<75f19294?=n91;1<75f19094?=h:8;1<75rb2d0>5<1290;w)<=b;3g2>N3>o1C88k4$0:`><`5<5<5<7>53;294~"5:k0:h>5G47d8L13b3-;3o76?;%023?2?;2c:;h4?::k23c<722e9=<4?::a6d2=83>1<7>t$30a>4b33A>=j6F;5d9'5=e=1h1/><954918m41b2900e<9i:188m4>72900c??>:188yg56n3:1?7>50z&16g<6l:1C8;h4H57f?!7?k32;7)<>7;6;0>o6?l0;66g>7g83>>i5980;66sm33394?2=83:p(?5<5<73-8:;7:8a:k23`<722c:;k4?::m154<722wi>>k50;194?6|,;8i6N3=l1/=5m5819'641=<>k0e<9j:188m41a2900c??>:188yg44l3:1?7>50z&16g<6l:1C8;h4H57f?!7?k32;7d?8e;29?l70n3:17b<>1;29?xd5;h0;6>4?:1y'67d=9m90D98i;I66a>"60j03<6g>7d83>>o6?o0;66a=1083>>{e::i1<7=50;2x 74e28n87E:9f:J71`=#91i14=5+205902e5<73-8:;7:8c:k23`<722c:;k4?::m154<722wi>>750;194?6|,;8i6N3=l1/=5m5819'641=<>h0e<9j:188m41a2900c??>:188yg4403:1?7>50z&16g<6l:1C8;h4H57f?!7?k32;7)<>7;64f>o6?l0;66g>7g83>>i5980;66sm22794?5=83:p(?5<5<53;294~"5:k0:h>5G47d8L13b3-;3o76?;%023?2??2c:;h4?::k23c<722e9=<4?::a665=8391<7>t$30a>4b43A>=j6F;5d9'5=e=091b=:k50;9j52`=831d>d21:0e<9j:188m41a2900c??>:188yg44:3:1?7>50z&16g<6l:1C8;h4H57f?!7?k32;7)<>7;64=>o6?l0;66g>7g83>>i5980;66sm22394?5=83:p(?5<5<53;294~"5:k0:h>5G47d8L13b3-;3o76?;%023?2002c:;h4?::k23c<722e9=<4?::a67c=8391<7>t$30a>4b43A>=j6F;5d9'5=e=091/><9546:8m41b2900e<9i:188k7762900qo<=d;297?6=8r.9>o4>d29K03`<@=?n7)?7c;:3?!46?3>3:6g>7d83>>o6?o0;66a=1083>>{e:;i1<7=50;2x 74e28n87E:9f:J71`=#91i14=5+20590=05<?3-8:;7:70:k23`<722c:;k4?::k2<5<722c:4<4?::m154<722wi>l950;494?6|,;8i6N3=l1/=5m59c9'641=<1:0e<9j:188m41a2900e<6?:188m4>62900e<6=:188k7762900qoo4>d69K03`<@=?n7)?7c;:b?l70m3:17d?8f;29?l7?83:17d?71;29?l7?:3:17d?73;29?j4693:17pl<2383>0<729q/>?l51e78L10a3A>>i6*>8b8;<>"59>0?4<5f16g94?=n9>l1<75f19294?=n91;1<75`20394?=zj:8?6=49:183!45j3;o:6F;6g9K00c<,82h64l4$334>1>63`;5;h34b?6=3`;3<7>5;h3;5?6=3`;3>7>5;n025?6=3th8>;4?:683>5}#:;h1=i94H54e?M22m2.:4n47a:k23`<722c:;k4?::k2<5<722c:4<4?::k2<7<722c:4>4?::m154<722wi>o650;594?6|,;8i6N3=l1/=5m58`9j52c=831b=:h50;9j5=6=831b=5?50;9j5=4=831b=5=50;9l647=831vn?l8:184>5<7s-89n7?k7:J72c=O<72900e<6>:188m4>52900e<6<:188k7762900qo=>c;293?6=8r.9>o4>d69K03`<@=?n7)?7c;:b?l70m3:17d?8f;29?l7?83:17d?71;29?l7?:3:17d?73;29?j4693:17pl<1c83>2<729q/>?l51e58L10a3A>>i6*>8b8;e>o6?l0;66g>7g83>>o6090;66g>8083>>o60;0;66g>8283>>i5980;66sm2`a94?5=83:p(?o1<75f16d94?=h:8;1<75rb3ca>5<0290;w)<=b;3g3>N3>o1C88k4$0:`>=g5<5<5<03`;5;h34b?6=3`;3<7>5;n025?6=3th9mh4?:683>5}#:;h1=i94H54e?M22m2.:4n46c:k23`<722c:;k4?::k2<5<722c:4<4?::k2<7<722c:4>4?::m154<722wi>o<50;794?6|,;8i6N3=l1/=5m5899j52c=831b=:h50;9j5=6=831b=5?50;9l647=831vn?l>:184>5<7s-89n7?k7:J72c=O<72900e<6>:188m4>52900e<6<:188k7762900qoo4>d79K03`<@=?n7)?7c;::?l70m3:17d?8f;29?l7?83:17d?71;29?l7?:3:17b<>1;29?xd5j=0;6:4?:1y'67d=9m=0D98i;I66a>"60j02o6g>7d83>>o6?o0;66g>8183>>o6080;66g>8383>>o60:0;66a=1083>>{e;891<7:50;2x 74e28n?7E:9f:J71`=#91i14:5f16g94?=n9>l1<75f19294?=h:8;1<75rb231>5<0290;w)<=b;3g3>N3>o1C88k4$0:`>5<5<5<73`;5;h34b?6=3f8:=7>5;|`06=<72>0;6=u+23`95a1<@=t$30a>4b23A>=j6F;5d9'5=e=011b=:k50;9j52`=831b=5>50;9j5=7=831d>d21k0e<9j:188m41a2900e<6?:188m4>62900e<6=:188m4>42900c??>:188yg5613:1:7>50z&16g<6l?1C8;h4H57f?!7?k3227d?8e;29?l70n3:17d?70;29?l7?93:17d?72;29?j4693:17pl<1983>2<729q/>?l51e58L10a3A>>i6*>8b8:g>o6?l0;66g>7g83>>o6090;66g>8083>>o60;0;66g>8283>>i5980;66sm44c94?2=83:p(?5;h3;4?6=3f8:=7>5;|`712<72=0;6=u+23`952><@="6l109>l5fc583>>odm3:17d<>8;29?j4613:17pl0<729q/>?l51e78L10a3A>>i6*>8b87g>o6?l0;66g>7g83>>o6090;66g>8083>>i5980;66sm34;94?3=83:p(?o1<75f16d94?=n91:1<75f19394?=h:8;1<75rb25:>5<0290;w)<=b;3g3>N3>o1C88k4$0:`>d65<5<5<5;h34b?6=3`;3<7>5;h3;5?6=3`;3>7>5;h3;7?6=3f8:=7>5;|`031<72=0;6=u+23`95a2<@=50;9l647=831vn>ki:187>5<7s-89n7?k4:J72c=O<72900c??>:188yg51m3:187>50z&16g<6l=1C8;h4H57f?!7?k32o7d?8e;29?l70n3:17d?70;29?j4693:17pl<6e83>2<729q/>?l51e58L10a3A>>i6*>8b8b5>o6?l0;66g>7g83>>o6090;66g>8083>>o60;0;66g>8283>>i5980;66sm37a94?5=83:p(?o1<75f16d94?=h:8;1<75rb24a>5<0290;w)<=b;3g3>N3>o1C88k4$0:`>d75<5<5<c3`;5;h34b?6=3`;3<7>5;n025?6=3th8i54?:683>5}#:;h1=i94H54e?M22m2.:4n4n1:k23`<722c:;k4?::k2<5<722c:4<4?::k2<7<722c:4>4?::m154<722wi?h950;194?6|,;8i6N3=l1/=5m58b9j52c=831b=:h50;9l647=831vn>k9:184>5<7s-89n7?k7:J72c=O<72900e<6>:188m4>52900e<6<:188k7762900qo=80;293?6=8r.9>o4>d69K03`<@=?n7)?7c;34e>o6?l0;66g>7g83>>o6090;66g>8083>>o60;0;66g>8283>>i5980;66sm37d94?1=83:p(?o1<75f16d94?=n91:1<75f19394?=n9181<75f19194?=h:8;1<75rb2ga>5<0290;w)<=b;3g3>N3>o1C88k4$0:`>41f3`;5;h34b?6=3`;3<7>5;h3;5?6=3`;3>7>5;h3;7?6=3f8:=7>5;|`0ad<72>0;6=u+23`95a1<@=t$30a>4b43A>=j6F;5d9'5=e=091/><954978m41b2900e<9i:188k7762900qo=87;293?6=8r.9>o4>d69K03`<@=?n7)?7c;c3?l70m3:17d?8f;29?l7?83:17d?71;29?l7?:3:17d?73;29?j4693:17pl<7483>0<729q/>?l51e78L10a3A>>i6*>8b8:a>"59>0?;k5f16g94?=n9>l1<75f19294?=n91;1<75`20394?=zj:==6=49:183!45j3;o:6F;6g9K00c<,82h64h4$334>11a3`;5;h34b?6=3`;3<7>5;h3;5?6=3`;3>7>5;n025?6=3th8;<4?:283>5}#:;h1=i=4H54e?M22m2.:4n468:&152<30;1b=:k50;9j52`=831d>d20n0(??8:5:1?l70m3:17d?8f;29?l7?83:17b<>1;29?xd4n;0;6:4?:1y'67d=9m=0D98i;I66a>"60j0j<6g>7d83>>o6?o0;66g>8183>>o6080;66g>8383>>o60:0;66a=1083>>{e;o:1<7;50;2x 74e28n>7E:9f:J71`=#91i15h5+20590215<5<3:11<7>t$30a>4b33A>=j6F;5d9'5=e=1m1b=:k50;9j52`=831b=5>50;9l647=831vn>kl:180>5<7s-89n7?k3:J72c=O<o4>d29K03`<@=?n7)?7c;;;?!46?3>396g>7d83>>o6?o0;66a=1083>>{e<8l1<7=50;2x 74e28n87E:9f:J71`=#91i14=5+205902c5<h50;094?6|,;8i6N3=l1b=:j50;9l647=831vn9=k:181>5<7s-89n7?k0:J72c=O<o4>d19K03`<@=?n7d?8d;29?j4693:17pl7<729q/>?l51e28L10a3A>>i6g>7e83>>i5980;66sm45294?dd290;w)<=b;02e>N3>o1C88k4Zb19g~7b28l18i496;6f>31=<=i9=:1<6`>6383?!7083;<86*>70816d=#91>1?6*>8480?!7?>390(<68:29'5=>=;2.:444<;%3;e?5<,82i6>5+19f97>"60l087)?7f;18 4?72:1/=4?53:&2=7<43-;2?7=4$0;7>6=#90?1?6*>9780?!7>?390(<77:29'55+18f97>"61l087)?6f;18 4g72:1/=l?53:&2e7<43-;j?7=4$0c7>6=#9h?1?6*>a780?!7f?390(5+1`f97>"6il087)?nf;18 4d72:1/=o?53:&2f7<43-;i?7=4$0`7>6=#9k?1?6*>b780?!7e?390(5+1cf97>"6jl087)?mf;18 4e72:1/=n?53:&2g7<43-;h?7=4$0a7>6=#9j?1?6*>c780?!7d?390(5+1bf96>"6kl097)?lf;024>"59o09>45+2329g0=#:;;1o85+2319g5=#:;>1>?84$304>7=#:;21>6*;6b872g=#5<>o59j0;66g=1e83>>o6>l0;66g>1883>!46>3;:46`=1483?>o69>0;6)<>6;32<>h59<0:76g>1783>!46>3;:46`=1481?>o69<0;6)<>6;32<>h59<0876g;6483>!46>3>=86`=1483?>o3>:0;6)<>6;650>h59<0:76g;6383>!46>3>=86`=1481?>o3>80;6)<>6;650>h59<0876a>3983>!46>3;8;6`=1483?>i6;?0;6)<>6;303>h59<0:76a>3583>!46>3;8;6`=1481?>i6;:0;6)<>6;303>h59<0876a>3383>!46>3;8;6`=1487?>i6;80;6)<>6;303>h59<0>76a>3183>!46>3;8;6`=1485?>i6:o0;6)<>6;303>h59<0<76a>2d83>!46>3;8;6`=148;?>i6:m0;6)<>6;303>h59<0276a>2b83>!46>3;8;6`=148b?>i6:k0;6)<>6;303>h59<0i76a>2883>!46>3;8;6`=148`?>i6:10;6)<>6;303>h59<0o76a>2683>!46>3;8;6`=148f?>i6:?0;6)<>6;303>h59<0m76a>2483>!46>3;8;6`=14824>=h9;>1<7*=178272=i:8?1=<54o000>5<#:8<1=>94n336>44<3f;9>7>5$335>4503g8:97?<;:m264<72-8::7?<7:l150<6<21d=?>50;&153<6;>1e><;51498k45a290/><851258j77228<07b?5;34?>i6;m0;6)<>6;303>h59<0:465`12a94?"59?0:?:5a20795<=6c:9l563=83.9=;4>369m643=9m10c<5$335>d06?54i`194?"59?0j:6`=1480?>oe83:1(??9:`48j7722=10elh50;&1532d9=84:;:kba?6=,;;=6l84n336>3=h59<0<76gnc;29 7712h<0b??::998mdd=83.9=;4n6:l150<>32cjm7>5$335>d06l54i`;94?"59?0j:6`=148a?>of03:1(??9:`48j7722j10el<50;&1532d9=84k;:ka2?6=,;;=6o;4n336>5=1<7*=178a1>h59<0:76gm3;29 7712k?0b??::398mg4=83.9=;4m5:l150<432cij7>5$335>g36954icg94?"59?0i96`=1486?>oel3:1(??9:c78j7722?10eom50;&153==h59<0276gm9;29 7712k?0b??::`98mg>=83.9=;4m5:l1505$335>g36n54ic394?"59?0i96`=148g?>o68m0;6)<>6;33g>h59<0;76g>0c83>!46>3;;o6`=1482?>o6800;6)<>6;33g>h59<0976g>0983>!46>3;;o6`=1480?>o68>0;6)<>6;33g>h59<0?76g>0783>!46>3;;o6`=1486?>o68<0;6)<>6;33g>h59<0=76g>0583>!46>3;;o6`=1484?>o68:0;6)<>6;33g>h59<0376g>0383>!46>3;;o6`=148:?>o6880;6)<>6;33g>h59<0j76g>0183>!46>3;;o6`=148a?>oam3:1(??9:02`?k46=3i07dhk:18'640=99i0b??::e98mce=83.9=;4>0b9m643=m21bjo4?:%022?77k2d9=84i;:kee?6=,;;=6<>l;o021?7732cm57>5$335>46d3g8:97?>;:kel;o021?7532cm;7>5$335>46d3g8:97?<;:ke2?6=,;;=6<>l;o021?7332cm97>5$335>46d3g8:97?:;:k251<72-8::7??c:l150<6>21b=<=50;&153<68j1e><;51698m475290/><8511a8j77228207d?>1;29 77128:h7c<>5;3:?>o6990;6)<>6;33g>h59<0:m65f11d94?"59?0:6l;o021?7c32cmj7>5$335>46d3g8:97?j;:ke0?6=,;;=6<>l;o021?7a32e:::4?:%022?71>2d9=84?;:m220<72-8::7?96:l150<632e::94?:%022?71>2d9=84=;:m226<72-8::7?96:l150<432c:=h4?:%022?76l2d9=84?;:k25f<72-8::7?>d:l150<632c:=o4?:%022?76l2d9=84=;:k25d<72-8::7?>d:l150<432e::n4?:%022?71j2d9=84?;:m22d<72-8::7?9b:l150<632e::44?:%022?71j2d9=84=;:m22=<72-8::7?9b:l150<432e:9l4?:%022?7212d9=84?;:m21=<72-8::7?:9:l150<632e:9;4?:%022?7212d9=84=;:m210<72-8::7?:9:l150<432e:994?:%022?7212d9=84;;:m216<72-8::7?:9:l150<232e:9?4?:%022?7212d9=849;:m214<72-8::7?:9:l150<032e:9=4?:%022?7212d9=847;:m20c<72-8::7?:9:l150<>32e:8h4?:%022?7212d9=84n;:m20a<72-8::7?:9:l1500:9l510=83.9=;4>589m643=9810c<:::18'640=9<30b??::008?j73<3:1(??9:07:?k46=3;876a>4283>!46>3;>56`=14820>=h9=81<7*=17821<=i:8?1=854o042>5<#:8<1=874n336>40<3f;=<7>5$335>43>3g8:97?8;:m21c<72-8::7?:9:l150<6021d=8k50;&153<6=01e><;51898k43c290/><8514;8j77228k07b?:c;29 77128?27c<>5;3a?>i6=k0;6)<>6;36=>h59<0:o65`14594?"59?0:945a20795a=h6=4+204950?65$335>`d6=54idc94?"59?0nn6`=1482?>ob03:1(??9:d`8j7722;10eh950;&1531=h59<0>76gj4;29 7712lh0b??::798m`5=83.9=;4jb:l150<032cn>7>5$335>`d6554id394?"59?0nn6`=148:?>ob83:1(??9:d`8j7722h10eih50;&153f=h59<0o76gkb;29 7712lh0b??::d98mag=83.9=;4jb:l1505$335>`d6<>4;hf;>5<#:8<1io5a207954=h59<0:>65fd783>!46>3oi7c<>5;30?>oc=3:1(??9:d`8j77228>07dj;:18'640=mk1e><;51498mc5=83.9=;4jb:l150<6>21bj?4?:%022?ce3g8:97?8;:ke5?6=,;;=6hl4n336>4><3`l;6=4+2049ag=i:8?1=454idd94?"59?0nn6`=1482e>=nml0;6)<>6;ga?k46=3;i76gjd;29 7712lh0b??::0a8?lc>290/><85ec9m643=9m10eik50;&153e:9j`6<72-8::7km;o021?7a32wi89?50;``>5<7s-89n7<>a:J72c=O<4`=k55180=?{iko027cj?:89m516=82d::?4?;%344?70<2.:;<4=2`9'5=2=;2.:484<;%3;2?5<,82<6>5+19:97>"600087)?7a;18 4>e2:1/=5j53:&2<`<43-;3j7=4$0;3>6=#90;1?6*>9380?!7>;390(<7;:29'5<3=;2.:5;4<;%3:3?5<,8336>5+18;97>"61h087)?6b;18 4?d2:1/=4j53:&2=`<43-;2j7=4$0c3>6=#9h;1?6*>a380?!7f;390(5+1`;97>"6ih087)?nb;18 4gd2:1/=lj53:&2e`<43-;jj7=4$0`3>6=#9k;1?6*>b380?!7e;390(5+1c;97>"6jh087)?mb;18 4dd2:1/=oj53:&2f`<43-;ij7=4$0a3>6=#9j;1?6*>c380?!7d;390(5+1b;97>"6kh087)?lb;18 4ed2:1/=nj52:&2g`<53-;hj7<>0:&15c<5:01/>?>5c49'677=k<1/>?=5c19'672=:;<0(?<8:39'67>=:2.?:n4;6c9'03b==831b=;h50;9j64c=831bo44?::k15f<722c9=i4?::k22`<722c:=44?:%022?7602d9=84?;:k252<72-8::7?>8:l150<632c:=;4?:%022?7602d9=84=;:k250<72-8::7?>8:l150<432c?:84?:%022?21<2d9=84?;:k726<72-8::7:94:l150<632c?:?4?:%022?21<2d9=84=;:k724<72-8::7:94:l150<432e:?54?:%022?74?2d9=84?;:m273<72-8::7?<7:l150<632e:?94?:%022?74?2d9=84=;:m276<72-8::7?<7:l150<432e:??4?:%022?74?2d9=84;;:m274<72-8::7?<7:l150<232e:?=4?:%022?74?2d9=849;:m26c<72-8::7?<7:l150<032e:>h4?:%022?74?2d9=847;:m26a<72-8::7?<7:l150<>32e:>n4?:%022?74?2d9=84n;:m26g<72-8::7?<7:l15044?:%022?74?2d9=84l;:m26=<72-8::7?<7:l150:4?:%022?74?2d9=84j;:m263<72-8::7?<7:l15084?:%022?74?2d9=84>0:9l572=83.9=;4>369m643=9810c<<<:18'640=9:=0b??::008?j75:3:1(??9:014?k46=3;876a>2083>!46>3;8;6`=14820>=h9;:1<7*=178272=i:8?1=854o01e>5<#:8<1=>94n336>40<3f;8i7>5$335>4503g8:97?8;:m27a<72-8::7?<7:l150<6021d=>m50;&153<6;>1e><;51898k45e290/><851258j77228k07b?5;3a?>i6;00;6)<>6;303>h59<0:o65`12794?"59?0:?:5a20795a=65$335>d06=54i`794?"59?0j:6`=1482?>of<3:1(??9:`48j7722;10el=50;&1532d9=84<;:ka4?6=,;;=6l84n336>1=h59<0>76gne;29 7712h<0b??::798mdb=83.9=;4n6:l150<032cjo7>5$335>d06554i``94?"59?0j:6`=148:?>ofi3:1(??9:`48j7722h10el750;&1532d9=84m;:kbf=h59<0o76gm6;29 7712k?0b??::198mg2=83.9=;4m5:l150<632ci?7>5$335>g36?54ic094?"59?0i96`=1480?>oen3:1(??9:c78j7722=10eok50;&1533=h59<0<76gmb;29 7712k?0b??::998mgg=83.9=;4m5:l150<>32ci57>5$335>g36l54ic:94?"59?0i96`=148a?>oe?3:1(??9:c78j7722j10eo?50;&153;:k24<<72-8::7??c:l150<532c:<54?:%022?77k2d9=84<;:k242<72-8::7??c:l150<332c:<;4?:%022?77k2d9=84:;:k240<72-8::7??c:l150<132c:<94?:%022?77k2d9=848;:k246<72-8::7??c:l150l;o021?e<3`lo6=4+204955e6i54iga94?"59?0:=nnk0;6)<>6;33g>h59<0m76gia;29 77128:h7c<>5;33?>oa13:1(??9:02`?k46=3;:76gi8;29 77128:h7c<>5;31?>oa?3:1(??9:02`?k46=3;876gi6;29 77128:h7c<>5;37?>oa=3:1(??9:02`?k46=3;>76g>1583>!46>3;;o6`=14822>=n9891<7*=17824f=i:8?1=:54i031>5<#:8<1==m4n336>4><3`;:=7>5$335>46d3g8:97?6;:k255<72-8::7??c:l150<6i21b==h50;&153<68j1e><;51c98m46b290/><8511a8j77228i07d??a;29 77128:h7c<>5;3g?>oan3:1(??9:02`?k46=3;n76gi4;29 77128:h7c<>5;3e?>i6>>0;6)<>6;352>h59<0;76a>6483>!46>3;=:6`=1482?>i6>=0;6)<>6;352>h59<0976a>6283>!46>3;=:6`=1480?>o69l0;6)<>6;32`>h59<0;76g>1b83>!46>3;:h6`=1482?>o69k0;6)<>6;32`>h59<0976g>1`83>!46>3;:h6`=1480?>i6>j0;6)<>6;35f>h59<0;76a>6`83>!46>3;=n6`=1482?>i6>00;6)<>6;35f>h59<0976a>6983>!46>3;=n6`=1480?>i6=h0;6)<>6;36=>h59<0;76a>5983>!46>3;>56`=1482?>i6=?0;6)<>6;36=>h59<0976a>5483>!46>3;>56`=1480?>i6==0;6)<>6;36=>h59<0?76a>5283>!46>3;>56`=1486?>i6=;0;6)<>6;36=>h59<0=76a>5083>!46>3;>56`=1484?>i6=90;6)<>6;36=>h59<0376a>4g83>!46>3;>56`=148:?>i66;36=>h59<0j76a>4e83>!46>3;>56`=148a?>i66;36=>h59<0h76a>4`83>!46>3;>56`=148g?>i6<00;6)<>6;36=>h59<0n76a>4983>!46>3;>56`=148e?>i6<>0;6)<>6;36=>h59<0:<65`15494?"59?0:945a207954=>6=4+204950?6<<4;n370?6=,;;=6<;6;o021?7432e:8>4?:%022?7212d9=84>4:9l514=83.9=;4>589m643=9<10c<8>:18'640=9<30b??::048?j7183:1(??9:07:?k46=3;<76a>5g83>!46>3;>56`=1482<>=h95<#:8<1=874n336>4g<3f;>o7>5$335>43>3g8:97?m;:m21g<72-8::7?:9:l150<6k21d=8950;&153<6=01e><;51e98k42d290/><8514;8j77228o07b?;1;29 77128?27c<>5;3e?>o3>h0;6)<>6;65=>h59<0;76g;6983>!46>3>=56`=1482?>o3>>0;6)<>6;65=>h59<0976g;6783>!46>3>=56`=1480?>obk3:1(??9:d`8j7722910eho50;&153;:kf7=h59<0876gj6;29 7712lh0b??::598m`3=83.9=;4jb:l150<232cn87>5$335>`d6;54id194?"59?0nn6`=1484?>ob:3:1(??9:d`8j7722110eh?50;&153d=h59<0i76gkd;29 7712lh0b??::b98mae=83.9=;4jb:l1505$335>`d6h54iec94?"59?0nn6`=148e?>oc13:1(??9:d`8j77228:07dj7:18'640=mk1e><;51098ma1=83.9=;4jb:l150<6:21bh;4?:%022?ce3g8:97?<;:kg1?6=,;;=6hl4n336>42<3`n?6=4+2049ag=i:8?1=854ig194?"59?0nn6`=14822>=nn;0;6)<>6;ga?k46=3;<76gi1;29 7712lh0b??::0:8?l`7290/><85ec9m643=9010ehh50;&153a:9ja`<72-8::7km;o021?7e32cnh7>5$335>`d65<#:8<1io5a20795a=h59<0:i65fd283>!46>3oi7c<>5;3e?>{t=mh1<7?=7kk;|q6`1<72;qU9n=4=562>`?52z\6g7=:<=;1hh5rs4g7>5<5sW?o=63;408g7>{t=l91<75eg9~w0ba2909wS;lc:?705?<7kk;|q6`a<72;qU9no4=563>`?52z\6g4=:<=:1hh5rs4f1>5<5sW?h<63;418g7>{t0;;1<79t^902?823>3ij70:;9;02<>;3<109=552440964><5=><6??7;<663?4602wx5io50;7;[?ci278i<4l4:?0a54l4:?77g44l4:?76=:4l4:?76384l4:?761>4l4:?767=4l4:?75`iwS6>2:?02<4l4:?70a4l4:?04l4:?0=7i4l4:?070h4l4:?06fo4l4:?1b`7d9>04`=9>o01>k<:05f?xu?9o0;6>uQ80d8912428=n70=96;34a>{t>9:1<7{t=on1<7i6s|5ga94?4|V{t=oh1<7o6s|5gc94?4|V{t=o31<7;6s|5g:94?4|V{t>931<770:;0;35<>{t>9=1<7j6s|61494?4|V{t>9?1<7h6s|61694?4|V{t>991<7n6s|61094?4|V{t>9;1<770:;0;375>{t=kl1<7k6{_7a1>X2jm1U9om4^4`a?[3ei2T>n45Q5c:8Z0d03W?i:6P:b59]1g57S6>d:\;5f=Y08h0R5?n;_:2=>X?911U4<94^935?[>6<2T3=>5Q9ef8ZX?k11U4n94^9a5?[>d=2T?445Q9e;8Z2e03W>346P6d99]=a1oo7S9l8:\:`1=Y=kl0199?:05a?823l3in70:;c;af?822:3in70::1;af?82283in70:;7;af?85e03n970=m7;f1?85e>3n970=m5;f1?85e<3n970=m9;f1?85dn3n970=le;f1?85dl3n970=lc;f1?85dj3n970=k0;f1?854n3n970=3n970=:5;f1?852<3n970=:3;f1?852:3n970=:7;f1?822i3;3<63;41815g=:<=:1>77d34>?<7<>d:?705<6901689>51058912728;=70:;0;c:?82383k370:;0;c1?82383h370:;0;`4?82383h:70:;0;33`>;3<90:;6<>7;<674?77?27?8=4>079>016=99?019:?:027?82383;;?63;418247=:<=:1==?4=563>46734>?<7hj;<674?`c34>?<7hl;<674?`e34>?<7hn;<674?`>34>?<7h7;<674?`034>?<7h9;<674?`234>?<7?>4:?705<69:1689>51008912728;:70:;0;324>;3<90:;6<>n;<674?`a34>?<7h;;<674?76m27?8=4>1b9>016=98h019:?:03b?82383>=m63;41872==:<=:18;94=563>10134>?<7kl;<674?cf34>?<7k7;<674?c034>?<7k9;<674?c234>?<7k;;<674?c434>?<7k=;<674?c634>?<7k?;<674?ba34>?<7jk;<674?bd34>?<7jm;<674?bf34>?<7j6;<674?b?34>?<7j8;<674?b134>?<7j:;<674?b334>?<7h<;<674?`534>?=7<>b:?704<59l1689?520a891262;;o70:;1;32=>;3<80:=:524539540<5=>:6l74=562>d><5=>:6l<4=562>g><5=>:6o94=562>g7<5=>:6<>k;<675?77j27?8<4>089>017=992019:>:024?82393;;:63;408240=:<=;1==:4=562>46434>?=7??2:?704<6881689?5112891262oo019:>:gf891262oi019:>:g`891262ok019:>:g;891262o2019:>:g5891262o<019:>:g78912628;?70:;1;327>;3<80:=?524539547<5=>:60d9>017=99k019:>:gd891262o>019:>:03f?82393;:o63;40825g=:<=;1=10f34>?=7:98:?704<3>>1689?5474891262li019:>:dc891262l2019:>:d5891262l<019:>:d7891262l>019:>:d1891262l8019:>:d3891262l:019:>:ed891262mn019:>:ea891262mh019:>:ec891262m3019:>:e:891262m=019:>:e4891262m?019:>:e6891262o9019:>:g08yv5b93:1>vP9109>7`7=:830q~=99;296~X18k16?;7520;8yv7ck3:19v3;708156=:;k>1o:523b`9g2=:;:h1o:523409g2=z{:<>6=4<{<15=?eb349=47mj;<152?4692wx89l50;3;851138:463<69815==:<=n1><74=2c6>fg<5:ko6no4=2c`>fg<5:ki6no4=2cb>fg<5:k26no4=2c;>fg<5:k<6no4=2c5>fg<5:k?6no4=2c0>fg<5:2m6no4=2;4>fg<5:3=6no4=2;6>fg<5:3?6no4=2;0>fg<5:396no4=2;2>fg<5:3;6no4=2:f>fg53z?02=<5901689=516d8960128=m7p}6}:;l;1oh523d29g`=:;ho1>7`6=:82019:6:bg8912?2;;27p}0}:;l:1><74=2g1>41a349ji7?70:?0a6<6?o16?h;516f8yv2313:1>v3;478`a>;3<009=45rs565>5<5s4>?:7<>9:?0e`<6?o1v9;<:180822<3in70::3;02=>;3=;0h96s|44694?4|5=??6??6;<662?70n2wx88650;1x913>2jo019;7:33:?822i3;3;41a34>>m7?8f:?712<5901v9:8:18182303in70:;7;02=>{t<77?34>?o7<>8:?713<5981v9:n:181`~;31ol522e19gd=::m;1ol522e29gd=:;;n1ol523279gd=:;:>1ol523219gd=:;:81ol523239gd=:;::1ol5233d9gd=:;;o1ol5233a9gd=:;;h1ol522gg9gd=:;9<1ol523179gd=:;9>1ol523119gd=:;981ol523139gd=:;9:1ol522gd9gd=::on1ol522ga9gd=::>=1ol5226d9gd=::>o1ol5226f9gd=::>i1ol5226`9gd=::>k1ol5226;9gd=::>21ol522649gd=::>?1ol5rs57a>5<4s4>>=7<>8:?715<5911688m52038yv23n3:1=ku2443964?<5=986no4=51a>fg<5=9j6no4=51:>fg<5=936no4=514>fg<5=9=6no4=516>fg<5=9?6no4=502>fg<5=826no4=50;>fg<5=8<6no4=505>fg<5=8>6no4=507>fg<5=886no4=501>fg<5=8;6no4=53f>77?34>;m7mn;<626?ef34>:=7mn;<624?ef34>;j7mn;<63a?ef34>;h7mn;<63g?ef34>;n7mn;<63=?4602wx89k50;0g822838:563=608`e>;5>00hm63=698`e>;5>>0hm63=678`e>;5><0hm63=658`e>;5>:0hm63=638`e>;5>90hm63=5g8`e>;5jj0hm63=c58`e>;5k:0hm63=c38`e>;5k80hm63=c18`e>;5jo0hm63=bd8`e>;5jm0hm63=bc8`e>;5jh0hm63=8e8`e>;51<0hm63=958`e>;51:0hm63=938`e>;5180hm63=918`e>;50o0hm63=8d8`e>;50j0hm63=8c8`e>;5m10hm63=f18`e>;5mo0hm63=ed8`e>;5mm0hm63=eb8`e>;5mk0hm63=e`8`e>;5m00hm63=e68`e>;5m?0hm6s|3c094?4|5:h36n94=2`4>7763ty8o?4?:3y>7g>=:;801>m<:332?xu4j:0;6?u23c:9647<5:h26n94}r1a5?6=:r78n:4l7:?0f3<5981v>m?:18185e?389>631:p7gc=838p1>l9:301?85en38:=6s|3`d94?4|5:h>6n94=2`7>7763ty8nn4?:3y>7g3=:;801>lk:332?xu4jh0;6?u23c69674<5:hi6??>;|q0g1<72;q6?o75230896e22;;:7p}7}:;k31>4>63ty8o44?:3y>7f`=k>16?nk52038yv5c13:1>v37a6=k>1v>m7:18185dm3i<70=ld;025>{t;m=1<7745349o47<>1:p7f1=838p1>mk:b5896ed2;;:7p}7}:;jn1>?<4=2f5>7763ty8o;4?:3y>7fe=k>16?nl52038yv5c;3:1>v31>7a4=:8;0q~=kb;296~;4l909>?523ea964752z?0`5<59816?lk51908yv5413:1>v3<3g8`3>;4;l09=<5rs26:>5<5s498j7<=2:?00d<5981v>=n:181854n38:=63<418`3>{t;:21<7f1<5:9o6??>;|q002<72;q6?>k52308962?2;;:7p}<3683>7}:;:n1o:5232a964752z?07a<5:;16?9852038yv54>3:1>v3<3b8`3>;4;k09=<5rs260>5<5s498o7<=2:?001<5981v>:>:181854j389>63<438154=z{:>i6=4={<174?45:2788n4=109~w6032909w0=;0;025>;4>?0:4=5rs273>5<5s49>:7m8;<161?4692wx?;>50;0x96312;8970=91;025>{t;<;1<7776349>;7m8;|q00c<72;q6?8;5c69>702=:8;0q~=:e;296~;4=<09>?5234d964752z?0114=109~w63d2909w0=:4;016>;4=m09=<5rs26g>5<5s49>?7m8;<166?4692wx?8o50;0x96342;8970=:b;025>{t;<21<7745349>57<>1:p734=838p1>;8:301?851;38:=6s|37594?4|5:?<6??>;<152?7?92wx89<50;3;85f=3i>70=nd;a6?85fk3i>70=nb;a6?85fi3i>70=n9;a6?85f03i>70=n7;a6?85f>3i>70=n4;a6?85f;3i>70=7f;a6?85>?3i>70=66;a6?85>=3i>70=64;a6?85>;3i>70=62;a6?85>93i>70=60;a6?85?m3i>70:;3;025>;3<90::k52453953`54z?0e0735=9>l01>8i:05f?xu4100;6;u23`7964?<5;=<6nk4=26`>41a348897?8e:?70527?8<4m6:p7=5=83?p1>ok:bg896?02;;270=91;3;4>;4>m0:;h52362952c7>56z?0ea<59016?9o51938975228=m70<<4;34b>;3<90i863;408a0>{t;181<78t=2c`>fc<5:3=6??6;<155?7?9278:h4>7d9>73d=9>o01>9?:05e?xu4i80;6;u23`a964?<5:>j6<6?;<000?70m279?>4>7g9>016=j:1689?5b29~w6>6290=38:563<5g82<5=:;>31=:k4=24f>41a349=o7?8e:?035<6091v>o?:18585fj38:563<4982<4=:::91=:k4=311>41a34>?<7l=;<675?d53ty84=4?:9y>7dg=kl16?4:520;8963a282:70=88;34a>;4?00:;k5237g95=6<5:;5;;0:;h52223952`<5=>;6oh4=562>g`59z?0e<4=189>70b=91:01>97:05e?85013;3=63<6e823c=:;?h1=:h4=253>4>5349<;7?8e:p7o6:33:?853>3;3=63=31823c=:::;1=:k4=563>gc<5=>:6ok4}r14a?6=ir78m54le:?0=7<59016?8j51938961?282:70=89;3;6>;4>m0:4=5237`95=6<5:7d9~w6?c290=w0=n8;02=>;4?=7lk;|q03a<72kq6?l95cd9>7<7=:8301>;m:0:2?85003;3<63<7882<5=:;?n1=5?4=24a>4>6349=j7?71:?032<60916?:;516g8961128=m7p}<9b83>3}:;h=1><74=267>4>63489j7?8e:?16`<6?o1689>5bb9>017=jj1v>9l:18:85f>3in70=60;02=>;4=k0:4=52366952c<5:819>720=91;01>9=:05f?xu41k0;6;u23`4964?<5:>?6<6?;<01a?70m279>i4>7g9>016=jk1689?5bc9~w61e290jw0=n4;af?85?m38:563<5882<5=:;>>1=:h4=24e>41a349<;7?71:?030<6?o16?:851928961628=m70=82;34b>{t;0k1<78t=2c7>77>349?>7?70:?16a<6?l16>?m516d891272kk019:>:cc8yv51i3:1>v3;3<<09=<5rs2;;>59:?007<60816>?m516g8963>282:70=84;3;4>;4?90:4>5236595=5<5:=>6<6>;<142?7?:278;<4>7d9>724=91:019:::05g?82383h270:;1;`:?xu40=0;6?u239d9g`=:;?l1>6l:18185>>3in70=9b;025>{t;1h1<7fc<5:=26??>;|q072>=:8;0q~=79;296~;41:0hi63<768154=z{:236=4={<1:6?eb349<:7<>1:p7=1=838p1>7>:bg896122;;:7p}<8783>7}:;0:1oh52360964752z?0<`;3;k0h963;3`8`1>;3;00h963;398`1>;3;>0h963;378`1>;3;<0h963;358`1>;38h0h963;138`1>;3980h963;118`1>;38o0h963;0d8`1>;38m0h963;0b8`1>;38k0h963;088`1>;4m;09=<5rs537>5<4s4>8?7mj;<615?461278o84>7g9~w14f290>w0:<3;02=>;5m10hi63=3g823`=:<=:1m:524539e2=z{=;o6=4<{<60f?eb34>957<>9:?0g6<6?o1v9==:186824j38:563=3g823c=:::o1=:h4=563>d3<5=>:6l;4}r62g?6=;r7??l4le:?76=<59016?n=51938yv2493:19v3;3`815<=:::o1=:k4=31g>41a34>?<7o;;<675?g33ty?=o4?:2y>06?=kl168?9520;896e628=m7p};3183>0}:<:31><74=31g>41b3488o7?8f:?705{t<;l1<7;t=51;>77>3488o7?8e:?17g<6?o1689>5b19>017=j91v9?6:180824?3in70:=5;02=>;4jo0:;k5rs50f>5<2s4>8;7<>9:?17d<6?o16>>l516g891272hl019:>:`d8yv2603:1?v3;378`a>;3:=09=4523cd95=79h7>55z?773<59016>>o516g8975>28=m70:;0;cf?82393kn7p};1683>6}:<:?1oh52431964?<5:ho6<9i;|q76f<72;520;8975>28=n70<<8;34b>;3<90jh63;408b`>{t<8<1<7=t=517>fc<5=896??6;<1a`?7?92wx8?l50;7x91532;;270<<8;34a>;5;>0:;k524529ef=:<=;1mn5rs2g1>5<6=r7?><4l5:?76<54l5:?762;4l5:?76094l5:?766?4l5:?7654=109>016=9?o019:?:546?82383>=?63;418727=:<=:18;?4=562>40b34>?=7:95:?704<3>:1689?5470891262=<:7p}1}:<;;1oh5241c964?<5:nh6<9i;<1fe?70m2wx?kk50;7x914>2jo019?=:33:?85ci3;r7?>54le:?754<59016?io5193896c>28=n70=j6;34a>;4mk0:;k5rs2d`>5<0s4>9;7mj;<624?461278h54>819>7c2=9>o01>k6:05e?85b?3;;j7<>9:?0`=<60816?k=516g896`328=m70=j9;3;4>;4m>0:;k523d`95=759z?7607a0=91:01>h<:05e?85a<3;3=634>5349m>7?8e:p7c?=83kp19<;:bg8916c2;;270=k6;3;5>;4n:0:4<523g695=4<5:o36<6?;<1f2?7?8278il4>839>7c4=9>l01>h>:05f?xu4n10;6ou24319g`=:<9i1><74=2f7>4>6349m?7?70:?0b1<60916?h65193896c1282:70=ja;3;5>;4n;0:4=523g2952c<5:l:6<9i;|q0b2<720q68?<5cd9>05d=:8301>j;:0:3?85bn3;4>7349m=7?71:?0aa<6?l1v>h9:18g82583in70:?9;02=>;4l;0:4<523dd95=6<5:oj6<9i;<002?70m278j?4>809>7c6=9>l01>h>:0:3?85bl3;41c3ty?=84?:3y>076=:8301>lm:05e?xu4m=0;6?u240g9g0=:;l?1>4=2ge>41a349nn7?73:?173<6?o16?k<5191896`7282:70=i1;3;6>;4mm0:4=523da952`<5=;m6<9i;<60`?70l2wx?kh50;0x916f2jo01>kn:332?xu3810;6?u24009g`=:;l21>9:18182683in70=i4;025>{t<9?1<7fc<5:l86??>;|q741<72;q68=k5cd9>7c4=:8;0q~:?3;296~;38m0hi631:p057=838p19>m:bg896cc2;;:7p};0183>7}:<931oh523da96475cz?1`7i4=189>74`=9>o01><>:05e?855:3;4>6349:>7?8f:?06=<6?l16?<;51928967?282:7p}=c783>6}::m81><74=26`>41b349=?7?8e:p74c=838p1?jn:bg8967a2;;:7p}=cg83>6}::mk1><74=26b>41a349==7?8f:p776=838p1?j6:bg896462;;:7p}=cd83>6}::m31><74=26b>41b349==7?8e:p775=838p1?j7:bg896452;;:7p}=ce83>6}::m21><74=26;>41a349>j7?8f:p773=838p1?j8:bg896432;;:7p}=cb83>6}::m=1><74=26;>41b349>j7?8e:p771=838p1?j9:bg896412;;:7p}=cc83>6}::m<1><74=265>41a349>h7?8f:p77g=838p1?j::bg8964?2;;:7p}=c`83>6}::m?1><74=265>41b349>h7?8e:p742=838p1?j;:bg896752;;:7p}=c883>6}::m>1><74=267>41a349>n7?8f:p741=838p1?j<:bg896722;;:7p}=c983>6}::m91><74=267>41b349>n7?8e:p74g=838p1?j>:bg8967?2;;:7p}=c683>6}::m;1><74=261>41a349>57?8f:p74b=838p1?j?:bg8967e2;;:7p}=c483>6}::m:1><74=261>41b349>57?8e:p6<1=83ip1?8>:bg897dd2;;270;5i=0:;h522`7952c<5;k<6<9j;<0b=?70m279n:4>809>6dd=9>o01?oj:05e?84e93;3<63=b582<4=z{;?>6=4<{<055?461278o84>7d9>7ae=9>o0q~00hi63=a38154=z{;?n6=4<{<05=?461278o>4>7d9>7ag=9>o0q~10hi63=a58154=z{;?o6=4<{<054>819>7ag=91:0q~>0hi63=a48154=z{;?h6=4<{<053?461278o<4>7d9>7a>=9>l0q~?0hi63=a68154=z{;?i6=4<{<052?461278o<4>819>7a>=9>o0q~<0hi63=a88154=z{;?j6=4<{<051?461278nk4>7d9>7a0=9>l0q~=0hi63=ac8154=z{;?26=4<{<050?461278nk4>819>7a0=9>o0q~:0hi63=ad8154=z{;?36=4<{<057?461278ni4>7d9>7a2=9>l0q~;0hi63=b08154=z{;?<6=4<{<056?461278ni4>819>7a2=9>o0q~90hi63=b58154=z{;?=6=4<{<054?461278no4>7d9>7a4=9>l0q~819>7a4=9>o0q~9:p747=83hp1>=::33:?856n3;4>53499:7?73:?05f<60:16?<<51908964?282:70=>6;3;5>;4900:;k5rs3db>5<5s49887mj;<131?4612wx?<>50;cx96532;;270==1;34a>;4:;0:4=5233695=7<5:8=6<6=;<12g?7?:278=>4>819>77>=91801>?9:0:3?85613;3<6s|2g;94?4|5:986nk4=227>77>3ty8765=:8301><=:05e?855<3;3<63<2782<4=:;8i1=5?4=230>41a349957?8f:?053<6?o16?<751938yv4a03:1>v3<338`a>;48:09=45rs22f>57<>9:?061<6?o16??851928967d282;70=>3;34a>;4:00:;h52304952c<5:;26<6=;|q1b2<72;q6?>?5cd9>754=:830q~=?d;293~;4;809=452334952`<5:;i6<6=;<126?7?8278>54>7g9>743=91;01>?7:0:1?xu5n?0;6?u23229g`=:;9;1><74}r13g?6=>r78?=4=189>74e=9>l01>?=:0:2?85503;3<63<1482<7=:;831=:k4}r0e1?6=:r78>k4le:?045<5901v>>m:186855n38:563<1c823`=:;881=:k4=236>41b349:47?8e:p6c2=838p1>1}:;;o1><74=23a>41a349:97?8f:?05=<6?o1v?h<:181855k3in70{t;931<7=t=20`>77>349:n7?70:?05=<6091v?h>:181855j3in70{t;9=1<777>349:o7?8e:p6=7=838p1?ll:bg897>c2;;27p}=8`83>7}::j>1oh52287964?5bz?1g1<59016>l<516d897g328=m70;5i>0:4?522`;95=5<5;h36<6<;<0bf?7?9279mh4>839>6g4=91;01?l::05e?xu5000;6?u22b19g`=::0>1><74}r0:b?6=ir79o>4=189>6d2=91:01?o::0:3?84f?3;3=63=a882<7=::k21=5<4=3ca>4>5348jj7?70:?1f7<60916>o;51928yv4?03:1>v3=c38`a>;51:09=45rs3;f>5<>s48h>7<>9:?1e0<6?o16>l95192897g>282:70;5ij0:;k522`d952`<5;h96<9i;<0a1?7?92wx>5950;0x97e62jo01?7=:33:?xu51m0;65u22b3964?<5;k<6<9i;<0b=?7?8279n54>819>6de=9>o01?oi:05f?84e:3;9:p64>7348i=7?71:?1f1<60;1v?6::18184en3in70<60;02=>{t:0h1<78t=3`e>77>348i47?8f:?1eg<60916>lk5193897d6282970{t:1>1<7fc<5;2m6??6;|q1=d<72ok520;897d028=n70;5j80:;h522c6952c52z?1fa290?w0;5j>0:;k522c3952`<5;h?6<9i;|q1<7<72;q6>ol5cd9>6=e=:830q~<68;297~;5jk09=4522c595=6<5;h?6<6?;|q1<5<72;q6>oo5cd9>6=d=:830q~<66;296~;5jh09=4522c:952c52z?1b`6nk4=3ge>77>3ty9i>4?:3y>752=kl16>hk520;8yv4b:3:1>v3<028`a>;5mm09=45rs3g2>5<5s49;>7mj;<0fg?4612wx>h>50;0x96662jo01?km:33:?xu5lo0;6?u23129g`=::lk1><74}r0ga?6=:r79jk4le:?1a<<5901v?jk:18184al3in70{t:mh1<7fc<5;o=6??6;|q12g<72;q6>5j5cd9>621=:830q~<84;296~;51<0hi63=7g815<=z{;=86=4={<0:0?eb3489:p624=838p1?7<:bg8971c2;;27p}=7083>7}::081oh5226a964?52z?1=477>3ty9:i4?:3y>6=c=kl16>:6520;8yv41k3:1>v3=8b8`a>;5??09=45rs34b>5<5s483n7mj;<041?4612wx>9750;0x971a2jo01?=::332?xu5<10;6?u226g9g`=:::>1>{t:=?1<7fc<5;9:6??>;|q101<72;q6>:o5cd9>666=:8;0q~<;3;296~;5?00hi63=2g8154=z{;>96=4={<041:p617=838p1?99:bg8974c2;;:7p}=4183>7}::>?1oh5223a9647?7>52z?1b57763ty99=4?:3y>6`b=kl16>>m52038yv43n3:1>v3=eb8`a>;5;k09=<5rs36f>5<5s48nn7mj;<00e?4692wx>9j50;0x97cf2jo01?=6:332?xu53in70<<6;025>{t;>91<7;t=25;>4>5349<57?73:?031<59816?;j51918960e28287p}0}:;o91=5<4=2d7>4>4349nj7<>1:?0a=<60:16?h851918yv4703:1>v3=b98154=::k=1=5=4}r03e?6=:r78=n4=109>74d=9190q~52z?1ec<59816>lk51918yv7a:3:1>v3=b38154=::k;1=5=4}r3e1?6=:r79n84=109>6g2=9190q~?i7;296~;49:09=<5230095=552z?06<<59816??651918yv7aj3:1>v3<178154=:;8?1=5=4}r3e`?6=:r78=44=109>74>=9190q~::a;296~;3=h09=<524459g`=z{8lm6=4={<15a?469278:i4>839~w7662909w0=9c;025>;4>k0:4?5rs320>5<5s49n57<>1:?0a=<60;1v?>::18185b?38:=63829~w7602909w0=jb;025>;4mh0:4>5rs51f>5<4s4>8j7<>1:?705{t1mk1<7{t0881<7{t=mh1<7{t=m31<7{t=m21<7{t=m=1<7{t=m<1<77)::d;3e2>{t=m?1<7{t=m>1<7{t=m91<7{t=l>1<7{t=l91<7{t=l81<7{t=l;1<7{t=l:1<7{t=ml1<7{t=mo1<7{t=mn1<7{t=mi1<7{t=m81<7{t1l=1<7{t08l1<7{t>9:1<7{t=oo1<7{t=on1<7{t=oi1<7{t=oh1<7{t=ok1<7{t=o31<7{t=o21<7{t>931<7{t>921<77)::d;3f3>{t>9=1<7{t>9<1<7{t>9?1<7{t>9>1<7{t>991<7{t>981<7{t>9;1<7{t=o=1<77)::d;3e4>{t>8;1<7{t>9h1<7{zfl:m6=4={I66a>{im8:1<7vF;5d9~j`742909wE::e:ma42=838pD9;j;|lf50<72;qC88k4}og22?6=:rB?9h5rnd34>5<5sA>>i6sae0:94?4|@=?n7p`j1883>7}O<a;296~N3=l1vbh?m:181M22m2wei52zJ71`=zfl;m6=4={I66a>{im;:1<7vF;5d9~j`442909wE::e:ma72=838pD9;j;|lf60<72;qC88k4}og12?6=:rB?9h5rnd04>5<5sA>>i6sae3:94?4|@=?n7p`j2883>7}O<i4?:3yK00c52zJ71`=zfl8m6=4={I66a>{im::1<7vF;5d9~j`542909wE::e:ma62=838pD9;j;|lf70<72;qC88k4}og02?6=:rB?9h5rnd14>5<5sA>>i6sae2:94?4|@=?n7p`j3883>7}O<51zJ71`=zfjoh6=4>{I66a>{ikln1<7?tH57f?xhdml0;65<6sA>>i6sacg794?7|@=?n7p`lf783>4}O<51zJ71`=zfjlh6=4>{I66a>{ikon1<7?tH57f?xhdnl0;65<6sA>>i6sad1794?7|@=?n7p`k0783>4}O<7:182M22m2weh=750;3xL13b3tdo51zJ71`=zfm:h6=4>{I66a>{il9n1<7?tH57f?xhc8l0;65<6sA>>i6sad0794?7|@=?n7p`k1783>4}O<7;295~N3=l1vbi?7:182M22m2weh<750;3xL13b3tdo=l4?:0yK00c51zJ71`=zfm;h6=4>{I66a>{il8n1<7?tH57f?xhc9l0;65<6sA>>i6sad3794?7|@=?n7p`k2783>4}O<l4?:0yK00c51zJ71`=zfm8h6=4>{I66a>{il;n1<7?tH57f?xhc:l0;65<6sA>>i6sad2794?7|@=?n7p`k3783>4}O<750;3xL13b3tdo?l4?:0yK00c51zJ71`=zfm9h6=4>{I66a>{il:n1<7?tH57f?xhc;l0;65<6sA>>i6sad5794?7|@=?n7p`k4783>4}O<51zJ71`=zfm>h6=4>{I66a>{il=n1<7?tH57f?xhc5<6sA>>i6sad4794?7|@=?n7p`k5783>4}O<n7>51zJ71`=zfm?h6=4>{I66a>{il5<6sA>>i6sad7794?7|@=?n7p`k6783>4}O<51zJ71`=zfm{I66a>{il?n1<7?tH57f?xhc>l0;65<6sA>>i6sad6794?7|@=?n7p`k7783>4}O<51zJ71`=zfm=h6=4>{I66a>{il>n1<7?tH57f?xhc?l0;67290:wE::e:m`=7=83;pD9;j;|lg<7<728qC88k4}of;7?6=9rB?9h5rne:7>5<6sA>>i6sad9794?7|@=?n7p`k8783>4}O<51zJ71`=zfm2h6=4>{I66a>{il1n1<7?tH57f?xhc0l0;65<6sA>>i6sad8794?7|@=?n7p`k9783>4}O<51zJ71`=zfm3h6=4>{I66a>{il0n1<7?tH57f?xhc1l0;6n3:1=vF;5d9~jag7290:wE::e:m`d7=83;pD9;j;|lge7<728qC88k4}ofb7?6=9rB?9h5rnec7>5<6sA>>i6sad`794?7|@=?n7p`ka783>4}O<51zJ71`=zfmkh6=4>{I66a>{ilhn1<7?tH57f?xhcil0;65<6sA>>i6sadc794?7|@=?n7p`kb783>4}O<51zJ71`=zfmhh6=4>{I66a>{ilkn1<7?tH57f?xhcjl0;65<6sA>>i6sadb794?7|@=?n7p`kc783>4}O<51zJ71`=zfmih6=4>{I66a>{iljn1<7?tH57f?xhckl0;65<6sA>>i6sade794?7|@=?n7p`kd783>4}O<51zJ71`=zfmnh6=4>{I66a>{ilmn1<7?tH57f?xhcll0;65<6sA>>i6sadd794?7|@=?n7p`ke783>4}O<51zJ71`=zfmoh6=4>{I66a>{illn1<7?tH57f?xhcml0;65<6sA>>i6sadg794?7|@=?n7p`kf783>4}O<51zJ71`=zfmlh6=4>{I66a>{ilon1<7?tH57f?xhcnl0;65<6sA>>i6sae1794?7|@=?n7p`j0783>4}O<7:182M22m2wei=750;3xL13b3tdn51zJ71`=zfl:h6=4>{I66a>{im9n1<7?tH57f?xhb8l0;6 + + +

+ + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ip/fifo_generator_v7_2_0/fifo_2kx18_flist.txt b/ip/fifo_generator_v7_2_0/fifo_2kx18_flist.txt new file mode 100755 index 0000000..fb00e08 --- /dev/null +++ b/ip/fifo_generator_v7_2_0/fifo_2kx18_flist.txt @@ -0,0 +1,12 @@ +# Output products list for +fifo_2kx18.asy +fifo_2kx18.gise +fifo_2kx18.ngc +fifo_2kx18.v +fifo_2kx18.veo +fifo_2kx18.xco +fifo_2kx18.xise +fifo_2kx18_flist.txt +fifo_2kx18_xmdf.tcl +fifo_generator_readme.txt +fifo_generator_ug175.pdf diff --git a/ip/fifo_generator_v7_2_0/fifo_2kx18_xmdf.tcl b/ip/fifo_generator_v7_2_0/fifo_2kx18_xmdf.tcl new file mode 100755 index 0000000..629d396 --- /dev/null +++ b/ip/fifo_generator_v7_2_0/fifo_2kx18_xmdf.tcl @@ -0,0 +1,76 @@ +# The package naming convention is _xmdf +package provide fifo_2kx18_xmdf 1.0 + +# This includes some utilities that support common XMDF operations +package require utilities_xmdf + +# Define a namespace for this package. The name of the name space +# is _xmdf +namespace eval ::fifo_2kx18_xmdf { +# Use this to define any statics +} + +# Function called by client to rebuild the params and port arrays +# Optional when the use context does not require the param or ports +# arrays to be available. +proc ::fifo_2kx18_xmdf::xmdfInit { instance } { +# Variable containg name of library into which module is compiled +# Recommendation: +# Required +utilities_xmdf::xmdfSetData $instance Module Attributes Name fifo_2kx18 +} +# ::fifo_2kx18_xmdf::xmdfInit + +# Function called by client to fill in all the xmdf* data variables +# based on the current settings of the parameters +proc ::fifo_2kx18_xmdf::xmdfApplyParams { instance } { + +set fcount 0 +# Array containing libraries that are assumed to exist +# Examples include unisim and xilinxcorelib +# Optional +# In this example, we assume that the unisim library will +# be magically +# available to the simulation and synthesis tool +utilities_xmdf::xmdfSetData $instance FileSet $fcount type logical_library +utilities_xmdf::xmdfSetData $instance FileSet $fcount logical_library unisim +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path fifo_2kx18.asy +utilities_xmdf::xmdfSetData $instance FileSet $fcount type asy +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path fifo_2kx18.ngc +utilities_xmdf::xmdfSetData $instance FileSet $fcount type ngc +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path fifo_2kx18.v +utilities_xmdf::xmdfSetData $instance FileSet $fcount type verilog +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path fifo_2kx18.veo +utilities_xmdf::xmdfSetData $instance FileSet $fcount type verilog_template +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path fifo_2kx18.xco +utilities_xmdf::xmdfSetData $instance FileSet $fcount type coregen_ip +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path fifo_2kx18_xmdf.tcl +utilities_xmdf::xmdfSetData $instance FileSet $fcount type AnyView +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path fifo_generator_readme.txt +utilities_xmdf::xmdfSetData $instance FileSet $fcount type text +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount relative_path fifo_generator_ug175.pdf +utilities_xmdf::xmdfSetData $instance FileSet $fcount type AnyView +incr fcount + +utilities_xmdf::xmdfSetData $instance FileSet $fcount associated_module fifo_2kx18 +incr fcount + +} + +# ::gen_comp_name_xmdf::xmdfApplyParams diff --git a/ip/fifo_generator_v7_2_0/fifo_generator_readme.txt b/ip/fifo_generator_v7_2_0/fifo_generator_readme.txt new file mode 100755 index 0000000..3cc2fc3 --- /dev/null +++ b/ip/fifo_generator_v7_2_0/fifo_generator_readme.txt @@ -0,0 +1,185 @@ + Core Name: Xilinx LogiCORE FIFO Generator + Version: 7.2 + Release Date: September 21, 2010 + + +================================================================================ + +This document contains the following sections: + +1. Introduction +2. New Features +3. Supported Devices +4. Resolved Issues +5. Known Issues +6. Technical Support +7. Core Release History +8. Legal Disclaimer + +================================================================================ + +1. INTRODUCTION + +For the most recent updates to the IP installation instructions for this core, +please go to: + + http://www.xilinx.com/products/ipcenter/FIFO_Generator.htm + + +For software requirements, please go to the "Software Requirements" link on that page. + + +This file contains release notes for the Xilinx LogiCORE IP FIFO Generator v7.2 +solution. For the latest core updates, see the product page at: + + http://www.xilinx.com/products/ipcenter/FIFO_Generator.htm + + +2. NEW FEATURES + + - AXI4 (AXI4-Stream, AXI4 and AXI4-Lite) Support (Spartan-6 and Virtex-6 devices only) + - ISE 12.3 software support + +3. SUPPORTED DEVICES + + The following device families are supported by the core for this release. + + - Virtex-6 XC CXT/LXT/SXT/HXT + - Virtex-6 XQ LXT/SXT + - Virtex-6 -1L XC LXT/SXT + + - Spartan-6 XC LX/LXT + - Spartan-6 XA + - Spartan-6 XQ LX/LXT + - Spartan-6 -1L XC LX + + - Virtex-5 XC LX/LXT/SXT/TXT/FXT + - Virtex-5 XQ LX/ LXT/SXT/FXT + + - Virtex-4 XC LX/SX/FX + - Virtex-4 XQ LX/SX/FX + - Virtex-4 XQR LX/SX/FX + + - Spartan-3 XC + - Spartan-3 XA + - Spartan-3A XC 3A / 3A DSP / 3AN DSP + - Spartan-3A XA 3A / 3A DSP + - Spartan-3E XC + - Spartan-3E XA + +4. RESOLVED ISSUES + + - In the FIFO Generator GUI, navigation buttons at the bottom are not accessible unless the screen resolution + is set to 1600x1200 or 1900x1200. + - CR 568630 + + - The FIFO Generator GUI does not generate the core if the depth is reduced after the data count option is selected. + - CR 570314 + +5. KNOWN ISSUES + + The following are known issues for v7.2 of this core at time of release: + + - In the FIFO Generator GUI, after importing an XCO file (Independent clock, distributed memory configuration) + into a Virtex-4 CORE Generator project, if the FIFO type is changed to "Independent Clocks, Built-in FIFO" in page 1, + page 2 does not correctly offer the Read Clock Frequency and Write Clock Frequency options as it should. + - CR 467240 + - AR 31379 + + - The FIFO Generator GUI does not generate the core if the Family is Spartan-6, and FIFO Implementation Type is + either Common or Independent Clock Block RAM, and the depth is 64K and the width is 36. + - CR 570041 + - AR 37201 + + The most recent information, including known issues, workarounds, and + resolutions for this version is provided in the IP Release Notes User Guide + located at + + www.xilinx.com/support/documentation/user_guides/xtp025.pdf + + +6. TECHNICAL SUPPORT + + To obtain technical support, create a WebCase at www.xilinx.com/support. + Questions are routed to a team with expertise using this product. + + Xilinx provides technical support for use of this product when used + according to the guidelines described in the core documentation, and + cannot guarantee timing, functionality, or support of this product for + designs that do not follow specified guidelines. + + +7. CORE RELEASE HISTORY + +Date By Version Description +================================================================================ +09/21/2010 Xilinx, Inc. 7.2 ISE 12.3 support; AXI4 Support +07/30/2010 Xilinx, Inc. 7.1 ISE 13.0.1 support +06/18/2010 Xilinx, Inc. 6.2 ISE 12.2 support +04/19/2010 Xilinx, Inc. 6.1 ISE 12.1 support +12/02/2009 Xilinx, Inc. 5.3 rev 1 ISE 11.4 support; Spartan-6 Low Power and Automotive Spartan-6 Device support +09/16/2009 Xilinx, Inc. 5.3 Update to add 11.3; Virtex-6 Low Power and Virtex-6 HXT Device support +06/24/2009 Xilinx, Inc. 5.2 Update to add 11.2 and Virtex-6 CXT device support +04/24/2009 Xilinx, Inc. 5.1 Update to add 11.1 and Virtex-6 and Spartan-6 device support +09/19/2008 Xilinx, Inc. 4.4 Update to add 10.1 SP3 and Virtex-5 TXT device support and miscellaneous bug fixes +03/24/2008 Xilinx, Inc. 4.3 Update to add 10.1 support and miscellaneous bug fixes +10/03/2007 Xilinx, Inc. 4.2 Support for FWFT for Block RAM and Distributed RAM Common Clock FIFOs +08/08/2007 Xilinx, Inc. 4.1 Update to add 9.2i support; Revised to v4.1; ECC support for block RAM FIFO +04/02/2007 Xilinx, Inc. 3.3 Update to add 9.1i support; Revised to v3.3; Spartan-3A and Spartan-3A DSP support; ECC support +09/21/2006 Xilinx, Inc. 3.2 Revised to v3.2; Spartan-3 and Virtex-4 automotive device support +07/13/2006 Xilinx, Inc. 3.1 Update to add 8.2i support; Revised to v3.1; Virtex-5 support +01/11/2006 Xilinx, Inc. 2.3 Update to add 8.1i support; Revised to v2.3 +08/31/2005 Xilinx, Inc. 2.2 Update to add 7.1i SP4 support; Revised to v2.2 +04/28/2005 Xilinx, Inc. 2.1 Update to add 7.1i SP1 support; Revised to v2.1 +11/04/2004 Xilinx, Inc. 2.0 Update to add 6.3i support; Revised to v2.0 +05/21/2004 Xilinx, Inc. 1.1 Revised to v1.1; Virtex-4 support +04/23/2004 Xilinx, Inc. 1.0 Update to add 6.2i support; First release +================================================================================ + +8. Legal Disclaimer + + (c) Copyright 2002 - 2010 Xilinx, Inc. All rights reserved. + + This file contains confidential and proprietary information + of Xilinx, Inc. and is protected under U.S. and + international copyright and other intellectual property + laws. + + DISCLAIMER + This disclaimer is not a license and does not grant any + rights to the materials distributed herewith. Except as + otherwise provided in a valid license issued to you by + Xilinx, and to the maximum extent permitted by applicable + law: (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND + WITH ALL FAULTS, AND XILINX HEREBY DISCLAIMS ALL WARRANTIES + AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, INCLUDING + BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON- + INFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE; and + (2) Xilinx shall not be liable (whether in contract or tort, + including negligence, or under any other theory of + liability) for any loss or damage of any kind or nature + related to, arising under or in connection with these + materials, including for any direct, or any indirect, + special, incidental, or consequential loss or damage + (including loss of data, profits, goodwill, or any type of + loss or damage suffered as a result of any action brought + by a third party) even if such damage or loss was + reasonably foreseeable or Xilinx had been advised of the + possibility of the same. + + CRITICAL APPLICATIONS + Xilinx products are not designed or intended to be fail- + safe, or for use in any application requiring fail-safe + performance, such as life-support or safety devices or + systems, Class III medical devices, nuclear facilities, + applications related to the deployment of airbags, or any + other applications that could lead to death, personal + injury, or severe property or environmental damage + (individually and collectively, "Critical + Applications"). Customer assumes the sole risk and + liability of any use of Xilinx products in Critical + Applications, subject only to applicable laws and + regulations governing limitations on product liability. + + THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS + PART OF THIS FILE AT ALL TIMES. diff --git a/ip/fifo_generator_v7_2_0/pa_cg_bom.xml b/ip/fifo_generator_v7_2_0/pa_cg_bom.xml new file mode 100755 index 0000000..ec972e8 --- /dev/null +++ b/ip/fifo_generator_v7_2_0/pa_cg_bom.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ip/fifo_generator_v7_2_0/pa_cg_gen_core_invoke.tcl b/ip/fifo_generator_v7_2_0/pa_cg_gen_core_invoke.tcl new file mode 100755 index 0000000..1863d00 --- /dev/null +++ b/ip/fifo_generator_v7_2_0/pa_cg_gen_core_invoke.tcl @@ -0,0 +1,17 @@ +# Tcl script generated by PlanAhead + +set tclUtilsPath "C:/Xilinx/13.1/ISE_DS/PlanAhead/scripts/pa_cg_utils.tcl" + +set cgProjectPath "C:/largework/fpga/hdmi/release1/release1.srcs/sources_1/ip/fifo_generator_v7_2_0/coregen.cgc" + +set ipName "fifo_2kx18" + +set chains "GENERATE_CHAIN" + +set bomFilePath "C:/largework/fpga/hdmi/release1/release1.srcs/sources_1/ip/fifo_generator_v7_2_0/pa_cg_bom.xml" + +# generate the IP +set result [source "C:/Xilinx/13.1/ISE_DS/PlanAhead/scripts/pa_cg_gen_core.tcl"] + +exit $result + diff --git a/ip/fifo_generator_v7_2_0/pa_cg_migrate_project_invoke.tcl b/ip/fifo_generator_v7_2_0/pa_cg_migrate_project_invoke.tcl new file mode 100755 index 0000000..c491aad --- /dev/null +++ b/ip/fifo_generator_v7_2_0/pa_cg_migrate_project_invoke.tcl @@ -0,0 +1,25 @@ +# Tcl script generated by PlanAhead + +set tclUtilsPath "C:/Xilinx/13.1/ISE_DS/PlanAhead/scripts/pa_cg_utils.tcl" + +set cgProjectPath "C:/largework/fpga/hdmi/release1/release1.srcs/sources_1/ip/fifo_generator_v7_2_0/coregen.cgc" + +set ipFile "C:/largework/fpga/hdmi/release1/release1.srcs/sources_1/ip/fifo_generator_v7_2_0/fifo_2kx18.xco" + +set ipName "fifo_2kx18" + +set chains "APPLY_CURRENT_PROJECT_OPTIONS_CHAIN BATCH_CUSTOMIZE_CHAIN INSTANTIATION_TEMPLATES_CHAIN" + +set vlnv "xilinx.com:ip:fifo_generator:7.2" + +set cgPartSpec "6slx9-2tqg144" + +set bomFilePath "C:/largework/fpga/hdmi/release1/release1.srcs/sources_1/ip/fifo_generator_v7_2_0/pa_cg_bom.xml" + +set hdlType "Verilog" + +# migrate the project +set result [source "C:/Xilinx/13.1/ISE_DS/PlanAhead/scripts/pa_cg_migrate_project.tcl"] + +exit $result + diff --git a/ip/fifo_generator_v7_2_0/pa_cg_reconfig_core_invoke.tcl b/ip/fifo_generator_v7_2_0/pa_cg_reconfig_core_invoke.tcl new file mode 100755 index 0000000..15041a4 --- /dev/null +++ b/ip/fifo_generator_v7_2_0/pa_cg_reconfig_core_invoke.tcl @@ -0,0 +1,21 @@ +# Tcl script generated by PlanAhead + +set tclUtilsPath "C:/Xilinx/13.1/ISE_DS/PlanAhead/scripts/pa_cg_utils.tcl" + +set cgProjectPath "C:/largework/fpga/hdmi/release1/release1.srcs/sources_1/ip/fifo_generator_v7_2_0/coregen.cgc" + +set ipName "fifo_2kx18" + +set chains "CUSTOMIZE_CURRENT_CHAIN INSTANTIATION_TEMPLATES_CHAIN" + +set bomFilePath "C:/largework/fpga/hdmi/release1/release1.srcs/sources_1/ip/fifo_generator_v7_2_0/pa_cg_bom.xml" + +set cgPartSpec "6slx9-2tqg144" + +set hdlType "Verilog" + +# generate the IP +set result [source "C:/Xilinx/13.1/ISE_DS/PlanAhead/scripts/pa_cg_reconfig_core.tcl"] + +exit $result + diff --git a/ip/license.txt b/ip/license.txt new file mode 100755 index 0000000..6777efc --- /dev/null +++ b/ip/license.txt @@ -0,0 +1,11 @@ +The files in this directory tree are auto-generated by the Xilinx IP Core +Generator System. + +The configuration of the IP embodied in these files is the expression of +Andrew "bunnie" Huang's thoughts and these thoughts are licensed in a +CC-BY-SA 2.0 fashion. The copyright notice is located here instead of +in the file, since the configuration files are stored in a machine- +manipulated format that is not suitable for insertion of copyright notices. + +The generation of the IP from these configurations is guided by the requirements +of Xilinx's IP Core Generator System. diff --git a/lcd_input_v4.v b/lcd_input_v4.v new file mode 100755 index 0000000..2ac7a55 --- /dev/null +++ b/lcd_input_v4.v @@ -0,0 +1,853 @@ +////////////////////////////////////////////////////////////////////////////// +// 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 takes in video data via an LCD interface +// and attempts to align it to an ongoing HDMI stream. +// +// This is a reboot of the LCD module, version 3 +// +// In this iteration, we attempt to make a very strong guarantee that: +// -- the incoming HDMI stream is the master source of all timing +// -- that the LCD data is always squared into the HDMI stream, and strictly +// resynchronized every V *and* H period +// -- that at least 8 lines of buffering are available +// -- in practice, I'm seeing about +/- 0.08% difference in clocks. That is +// about +/- one full line of data due to absolute clock skew alone. +// -- I see hiccoughs from the PXA168 on the order of 5-6 lines per frame +// +// Version 4 takes advantage of synchronous clocking and makes a +// feedback-loop controlled system that attempts to use genlock to "kick" +// the host CPU into a closer sync relationship, within 8 lines of leading +// the HDMI display. +// +// The feedback loop *only* works if the host controller has the identical +// timing to the incoming stream. Fortunately, a timing measurement module +// has also been created so that the CPU can read out the timing of the incoming +// stream and adjust the controller to match. +// +///////////// +`timescale 1 ns / 1 ps + +module lcd_input( + input wire rstin, + input wire lcd_dclk, + input wire lcd_de, // active high + input wire lcd_hsync, // always active high + input wire lcd_vsync, // always active high + input wire lcd_sync_pol, // force to 1 at top level module + input wire [5:0] lcd_b, + input wire [5:0] lcd_g, + input wire [5:0] lcd_r, + + input wire hdmi_pclk, + input wire hdmi_de, // active high + input wire hdmi_vsync, // use sync_pol + input wire hdmi_hsync, // use sync_pol + input wire hdmi_sync_pol, + input wire hdmi_cv, // indicates that hdmi sync signals are valid + + output reg [7:0] hdmi_b, + output reg [7:0] hdmi_r, + output reg [7:0] hdmi_g, + + output reg genlock, + output reg locked, + + output wire dummy, + + input wire [2:0] line_full_level, // nominally 2 + input wire [2:0] line_empty_level, // nominally 2 + input wire [3:0] write_init_level, // nominally 1 + input wire [3:0] read_init_level, // nominally 2 + + input wire [23:0] target_lead_pixels, + input wire reset_lock_machine, + + input wire [15:0] lock_tolerance, + input wire smartlock_on + ); + + //////// FIFO connections + wire [(NUMLINES-1) : 0] fifo_rst; + + wire [17:0] fifo_din; + wire [(NUMLINES-1) : 0] fifo_write; + wire [(NUMLINES-1) : 0] fifo_full; + wire [(NUMLINES-1) : 0] fifo_over; + wire lcd_en; + + wire [(NUMLINES-1) : 0] fifo_read; + wire [(NUMLINES-1) : 0] fifo_empty; + wire [(NUMLINES-1) : 0] fifo_under; + wire hdmi_en; + + wire [17:0] fifo_dout0; + wire [17:0] fifo_dout1; + wire [17:0] fifo_dout2; + wire [17:0] fifo_dout3; + wire [17:0] fifo_dout4; + wire [17:0] fifo_dout5; + wire [17:0] fifo_dout6; + wire [17:0] fifo_dout7; + + + /// other connections + ////// HDMI clock domain + reg hdmi_hsync_v; // active when high + reg hdmi_hsync_v2; + wire hdmi_hsync_rising; + + reg hdmi_vsync_v; + reg hdmi_vsync_v2; + wire hdmi_vsync_rising; + wire hdmi_vsync_falling; + + wire lcd_de_rising__hdmi; + wire lcd_de_falling__hdmi; + wire lcd_firstbyte__hdmi; + + reg lcd_de_s__hdmi; + reg lcd_de__hdmi; + reg lcd_de_d__hdmi; + + reg lcd_overflow_s__hdmi; + reg lcd_overflow__hdmi; + + reg lcd_vsync_1hdmi; + reg lcd_vsync_2hdmi; + reg lcd_vsync_hdmi; + wire lcd_vsync_rising__hdmi; + + wire hdmi_underflow; + wire hdmi_empty; + wire [17:0] hdmi_fifo_d; + + reg hdmi_de_d; + wire hdmi_de_rising; + wire hdmi_de_falling; + reg hdmi_first_de; + reg hdmi_first_de_state; + + reg hdmi_read_en; + + ////// LCD clock domain + wire lcd_de_vsync; + reg lcd_de_d; + reg lcd_de_rising; + reg lcd_de_falling; + wire lcd_overflow; + reg lcd_hsync_d; + reg lcd_hsync_rising; + + reg hdmi_vsync_s__lcd; // synchronize hdmi vsync to LCD clock domain + reg hdmi_vsync__lcd; + + reg hdmi_hsync_s__lcd; // synchronize hdmi hsync to LCD clock domain + reg hdmi_hsync__lcd; + + reg hdmi_hsync_rising__lcd; + reg hdmi_vsync_rising__lcd; + reg hdmi_vsync_falling__lcd; + reg hdmi_hsync_d__lcd; + reg hdmi_vsync_d__lcd; + + wire advance_write; + wire advance_read; + + /////////////// + // Synchronous FIFO feedback loop. + // + // Assume: vsync from HDMI drives all timing. + // + // A counter is used to delay the genlock output to the CPU, based on a certain number of + // pixel counts. The feedback loop attempts to tune the delay so that the actual measured + // LCD vsync timing hits a target number of pixels before the HDMI vsync signal. If this + // target is achieved, then fifo read/write timing is simply the LCD DE and HDMI DE signals. + // + /////////////// + parameter GENLOCK_LENGTH = 24'h400; // approx 6 us minimum pulse length, typ 12 us or so + // just needs to be long enough for the CPU to catch the interrupt + + reg [23:0] current_genlock_delay; // delay in pixels of genlock pulse from vsync + reg [23:0] genlock_count; + reg [23:0] measured_vsync_to_vsync; + reg hdmi_vsync_happened; + + reg hdmi_vsync_happened_d; + wire hdmi_vsync_happened_rising; + + wire [23:0] timing_difference; + always @(posedge hdmi_pclk or posedge rstin) begin + if( rstin || reset_lock_machine ) begin + current_genlock_delay <= 32'h40000; // get us a little closer to lock, but not all the way there + // this constant picked to not exceed final length for lowest resolution mode + + genlock_count <= 0; + + measured_vsync_to_vsync <= 0; + hdmi_vsync_happened <= 0; + hdmi_vsync_happened_d <= 0; + locked <= 0; + end else begin // if ( rstin || reset_lock_machine ) + // make a counter, starting at rising edge of vsync, to count time till genlock pulse + if(hdmi_vsync_rising) begin + genlock_count <= 0; + end else begin + genlock_count <= genlock_count + 24'b1; + end + + // generate a genlock pulse when the count is between the current delay target and the + // specified pulse length + if( (genlock_count > current_genlock_delay) && + (genlock_count < current_genlock_delay + GENLOCK_LENGTH) ) begin + // a flaw of this scheme is that we're always hitting genlock. This means we are + // building the jitter of the interrupt latency of the PXA168 into the loop every + // time we hit this. A potentially smarter way to do this is to eventually turn off + // the genlock interrupt and let the PXA168 LCD controller free-wheel once the + // delay is trimmed. This works because the controller and the HDMI stream are + // fully synchronous, so you will get no cumulative slip due to long term frequency + // offset between the two streams. However, a trivial circuit that just gates + // genlock based upon the "lock" bit comupted below will cause, on the next frame, + // vsync to come a full interrupt latency period earlier, which would "unlock" + // the circuit. In order to counter this, we can possibly just open up the lock + // state computation sufficiently wide so as to encompass the interrupt latency + // of the PXA168 -- but this is a little bit tricky in terms of loop dynamics + // so initially, we'll do a naive implementation and see if it's good enough. + if( smartlock_on && !locked ) begin + genlock <= 1'b1; + end else if( smartlock_on && locked ) begin + genlock <= 1'b0; + end else begin + genlock <= 1'b1; + end + end else begin + genlock <= 1'b0; + end + + // this counter measures the time between when the LCD vsync pulse actually rises + // and when the HDMI vsync pulse actually rises. + // It resets on the rising edge of the LCD vsync pulse, and stops counting when + // the hdmi vsync pulse rises + if( lcd_vsync_rising__hdmi ) begin + measured_vsync_to_vsync <= 0; + hdmi_vsync_happened <= 0; + end else begin + if( !hdmi_vsync_happened ) begin + measured_vsync_to_vsync <= measured_vsync_to_vsync + 24'b1; + end else begin + measured_vsync_to_vsync <= measured_vsync_to_vsync; + end + + if( hdmi_vsync_rising ) begin + hdmi_vsync_happened <= 1; + end else begin + hdmi_vsync_happened <= hdmi_vsync_happened; + end + end // else: !if( lcd_vsync_rising__hdmi ) + hdmi_vsync_happened_d <= hdmi_vsync_happened; + + // once the HDMI vsync pulse happens, and the difference counter has stopped, + // we can measure the value. If the difference is too big, take half the difference + // and add it to the current target. The difference is considered in absolute value + // so we need to pay attention to the sign in this logic, and also provide for + // a "zero state" that's fully locked. + if( hdmi_vsync_happened_rising ) begin + if( measured_vsync_to_vsync > target_lead_pixels ) begin + // in this case, LCD vsync is happening too early, so lengthen genlock delay + current_genlock_delay[23:0] <= current_genlock_delay[23:0] + {1'b0,timing_difference[23:1]}; + end else if (measured_vsync_to_vsync < target_lead_pixels ) begin + // + becomes - in 2's compliment represenation with 1's extension... + current_genlock_delay[23:0] <= current_genlock_delay[23:0] + {1'b1,timing_difference[23:1]}; + end else begin + current_genlock_delay <= current_genlock_delay; + end + end // if ( hdmi_vsync_happened_rising ) + + // In practice, because we are dividing by two to generate the offset, the + // lock can dither by a pixel around a zero point. + // So, compute "locked" based upon a bit of a slop to avoid dithering. + // The few-pixel offset is fully absorbed by the FIFO that bridges the + // LCD to the HDMI domain. + if( timing_difference[23] ) begin // negative + // technically, I've done a 1's compliment below, but who's counting? + // it's a tolerance band anyways that will be large relative to 1 typically, + // so save the adder. + if( timing_difference[23:0] > {8'b1,~lock_tolerance[15:0]} ) begin + locked <= 1; + end else begin + locked <= 0; + end + end else begin + if( timing_difference[23:0] < {8'b0,lock_tolerance[15:0]} ) begin + locked <= 1; + end else begin + locked <= 0; + end + end // else: !if( timing_difference[23] ) + + end // else: !if( rstin || reset_lock_machine ) + end // always @ (posedge hdmi_pclk) + assign hdmi_vsync_happened_rising = hdmi_vsync_happened & !hdmi_vsync_happened_d; + assign timing_difference[23:0] = measured_vsync_to_vsync[23:0] - target_lead_pixels[23:0]; + + reg hdmi_de_falling_d1; + reg hdmi_de_falling_d2; + //////////////////////////////// + ///////// line update machine + //////////////////////////////// + assign advance_write = lcd_de_falling && !lines_full; + + always @(posedge hdmi_pclk) begin + hdmi_de_falling_d1 <= hdmi_de_falling; + hdmi_de_falling_d2 <= hdmi_de_falling; + end + // advance a little bit after DE is done, to give the fifo time to asynch reset + assign advance_read = hdmi_de_falling_d2 & !lines_empty; // always assume data is ready for us...this may not be true + + assign hdmi_en = hdmi_de; + + assign lcd_en = lcd_de; + + ////////// + // Below is the line FIFO mechanism + // + // interfaces: + // lines_full / lines_empty + // hdmi_vsync_v -- resets the write and read trackers + // advance_read -- advance the FIFO one line on the read side + // advance_write -- advance the FIFO one line on the write side + // hdmi_en -- read from the fifo + // lcd_en -- write to the fifo + // + // write_init_level, read_init_level -- initial pointer settings for fifos + // line_full_level, line_empty_level -- set high/low water mark for fifos + + ///////////////////////////////// + //// line-level empty/full detectors + ///////////////////////////////// + reg lines_empty; + reg lines_full; + + // full case would be: + // --> roll direction + // write_tracker: 00100000 + // read_tracker: 00010000 + // in other words, if you were to advance the write tracker one more + // position, you'd start writing the actively read line. + always @(posedge lcd_dclk) begin + case (line_full_level) + 3'b000: begin // +2 + if( write_tracker == {read_tracker[(NUMLINES-4) : 0], + read_tracker[(NUMLINES-1) : (NUMLINES-3)]} ) begin + lines_full <= 1; + end else begin + lines_full <= 0; + end + end + 3'b001: begin // +1 + if( write_tracker == {read_tracker[(NUMLINES-3) : 0], + read_tracker[(NUMLINES-1) : (NUMLINES-2)]} ) begin + lines_full <= 1; + end else begin + lines_full <= 0; + end + end + 3'b010: begin // nominal + if( write_tracker == {read_tracker[(NUMLINES-2) : 0], read_tracker[NUMLINES-1]} ) begin + lines_full <= 1; + end else begin + lines_full <= 0; + end + end + 3'b011: begin // -1 + if( write_tracker == read_tracker ) begin + lines_full <= 1; + end else begin + lines_full <= 0; + end + end + 3'b100: begin // -2 + if( write_tracker == {read_tracker[0], read_tracker[NUMLINES-1 : 1]} ) begin + lines_full <= 1; + end else begin + lines_full <= 0; + end + end + default: begin + lines_full <= 0; // disable the tracker + end + endcase // case (line_full_level) + end + + // empty case would be: + // --> roll direction + // write_tracker: 00001000 + // read_tracker: 00010000 + // in other words, if you were to advance the read tracker one more + // position, you'd start reading the actively written line. + always @(posedge hdmi_pclk) begin + case (line_empty_level) + 3'b000: begin // +2 + if( write_tracker == {read_tracker[2:0], read_tracker[(NUMLINES-1) : 3]} ) begin + lines_empty <= 1; + end else begin + lines_empty <= 0; + end + end + 3'b001: begin // +1 + if( write_tracker == {read_tracker[1:0], read_tracker[(NUMLINES-1) : 2]} ) begin + lines_empty <= 1; + end else begin + lines_empty <= 0; + end + end + 3'b010: begin // nominal + if( write_tracker == {read_tracker[0], read_tracker[(NUMLINES-1) : 1]} ) begin + lines_empty <= 1; + end else begin + lines_empty <= 0; + end + end + 3'b011: begin // -1 + if( write_tracker == read_tracker ) begin + lines_empty <= 1; + end else begin + lines_empty <= 0; + end + end + 3'b100: begin // -2 + if( write_tracker == {read_tracker[(NUMLINES-2) : 0], read_tracker[NUMLINES-1]} ) begin + lines_empty <= 1; + end else begin + lines_empty <= 0; + end + end + default: begin + lines_empty <= 0; + end + endcase // case (line_empty_level) + end + + ///////////////////////////////// + ///// active line tracking bits + ///////////////////////////////// + parameter NUMLINES = 4'h8; + + reg [(NUMLINES-1) : 0] read_tracker; + reg [(NUMLINES-1) : 0] write_tracker; + + // rolls to the "right" + // init at "empty" state + // reset line tracking at every hdmi vsync period, regardless of clock domai + always @(posedge lcd_dclk or posedge rstin) begin + if (rstin | hdmi_vsync_v) begin +// write_tracker[0] <= 1; +// write_tracker[1] <= 0; + write_tracker[3:0] <= write_init_level; + write_tracker[(NUMLINES-1):4] <= 0; + end else if (advance_write) begin + write_tracker <= {write_tracker[0], write_tracker[(NUMLINES-1) : 1]}; + end + end + + always @(posedge hdmi_pclk or posedge rstin) begin + if (rstin | hdmi_vsync_v) begin +// read_tracker[0] <= 0; +// read_tracker[1] <= 1; + read_tracker[3:0] <= read_init_level; + read_tracker[(NUMLINES-1):4] <= 0; + end else if (advance_read) begin + read_tracker <= {read_tracker[0], read_tracker[(NUMLINES-1) : 1]}; + end + end + + ////// + //// fifo output mux + ////// + reg [17:0] fifo_muxout; + always @(read_tracker, fifo_dout0, fifo_dout1, fifo_dout2, fifo_dout3, + fifo_dout4, fifo_dout5, fifo_dout6, fifo_dout7) begin + case( read_tracker ) // synthesis parallel_case full_case + 8'b00000001: fifo_muxout = fifo_dout0; + 8'b00000010: fifo_muxout = fifo_dout1; + 8'b00000100: fifo_muxout = fifo_dout2; + 8'b00001000: fifo_muxout = fifo_dout3; + 8'b00010000: fifo_muxout = fifo_dout4; + 8'b00100000: fifo_muxout = fifo_dout5; + 8'b01000000: fifo_muxout = fifo_dout6; + 8'b10000000: fifo_muxout = fifo_dout7; + endcase // case ( read_tracker ) + end // always @ (read_tracker, fifo_dout0, fifo_dout1, fifo_dout2, fifo_dout3,... + assign fifo_read[(NUMLINES-1) : 0] = read_tracker[(NUMLINES-1) : 0]; + + assign fifo_write[(NUMLINES-1) : 0] = write_tracker[(NUMLINES-1) : 0]; + + //// final connections to hdmi path + // add pipeline register here + always @(posedge hdmi_pclk or posedge rstin) begin + if( rstin ) begin + hdmi_g <= 8'b0; + hdmi_b <= 8'b0; + hdmi_r <= 8'b0; + end else begin + {hdmi_b[7:2],hdmi_g[7:2],hdmi_r[7:2]} <= fifo_muxout[17:0]; + // duplicate LSB's so as to not offset colors by the LSB amount + hdmi_g[1:0] <= {hdmi_g[2],hdmi_g[2]}; + hdmi_r[1:0] <= {hdmi_r[2],hdmi_r[2]}; + hdmi_b[1:0] <= {hdmi_b[2],hdmi_b[2]}; + end // else: !if( rstin ) + end // always @ (posedge hdmi_pclk or posedge rstin) + + + ///////////////////////////////// + ////// line fifos + ////// need to manually instantiate every instance, I don't think you can do array + ////// instantiations... + ///////////////////////////////// + + ////// warning: hard-coded against NUMLINES.... + // basically, reset myself if and only if I am the active line, and the read just finished. + + assign fifo_din = {lcd_b[5:0], lcd_g[5:0], lcd_r[5:0]}; + + assign fifo_rst[0] = fifo_read[0] & hdmi_de_falling; // this is not parameterized + assign fifo_rst[1] = fifo_read[1] & hdmi_de_falling; + assign fifo_rst[2] = fifo_read[2] & hdmi_de_falling; + assign fifo_rst[3] = fifo_read[3] & hdmi_de_falling; + assign fifo_rst[4] = fifo_read[4] & hdmi_de_falling; + assign fifo_rst[5] = fifo_read[5] & hdmi_de_falling; + assign fifo_rst[6] = fifo_read[6] & hdmi_de_falling; + assign fifo_rst[7] = fifo_read[7] & hdmi_de_falling; + + fifo_2kx18 line_fifo0( + .rst(rstin | fifo_rst[0] | hdmi_vsync_v), + + .wr_clk(lcd_dclk), + .din(fifo_din[17:0]), +// .wr_en(fifo_write[0] & !fifo_full[0] & lcd_en), + .wr_en(fifo_write[0] & lcd_en), +// .full(fifo_full[0]), +// .overflow(fifo_over[0]), + + .rd_clk(hdmi_pclk), +// .rd_en(fifo_read[0] & !fifo_empty[0] & hdmi_en), + .rd_en(fifo_read[0] & hdmi_en), + .dout(fifo_dout0[17:0]) +// .empty(fifo_empty[0]), +// .underflow(fifo_under[0]) + ); + + fifo_2kx18 line_fifo1( + .rst(rstin | fifo_rst[1] | hdmi_vsync_v), + + .wr_clk(lcd_dclk), + .din(fifo_din[17:0]), +// .wr_en(fifo_write[1] & !fifo_full[1] & lcd_en), + .wr_en(fifo_write[1] & lcd_en), +// .full(fifo_full[1]), +// .overflow(fifo_over[1]), + + .rd_clk(hdmi_pclk), +// .rd_en(fifo_read[1] & !fifo_empty[1] & hdmi_en), + .rd_en(fifo_read[1] & hdmi_en), + .dout(fifo_dout1[17:0]) +// .empty(fifo_empty[1]), +// .underflow(fifo_under[1]) + ); + + fifo_2kx18 line_fifo2( + .rst(rstin | fifo_rst[2] | hdmi_vsync_v), + + .wr_clk(lcd_dclk), + .din(fifo_din[17:0]), +// .wr_en(fifo_write[2] & !fifo_full[2] & lcd_en), + .wr_en(fifo_write[2] & lcd_en), +// .full(fifo_full[2]), +// .overflow(fifo_over[2]), + + .rd_clk(hdmi_pclk), +// .rd_en(fifo_read[2] & !fifo_empty[2] & hdmi_en), + .rd_en(fifo_read[2] & hdmi_en), + .dout(fifo_dout2[17:0]) +// .empty(fifo_empty[2]), +// .underflow(fifo_under[2]) + ); + + fifo_2kx18 line_fifo3( + .rst(rstin | fifo_rst[3] | hdmi_vsync_v), + + .wr_clk(lcd_dclk), + .din(fifo_din[17:0]), +// .wr_en(fifo_write[3] & !fifo_full[3] & lcd_en), + .wr_en(fifo_write[3] & lcd_en), +// .full(fifo_full[3]), +// .overflow(fifo_over[3]), + + .rd_clk(hdmi_pclk), +// .rd_en(fifo_read[3] & !fifo_empty[3] & hdmi_en), + .rd_en(fifo_read[3] & hdmi_en), + .dout(fifo_dout3[17:0]) +// .empty(fifo_empty[3]), +// .underflow(fifo_under[3]) + ); + + fifo_2kx18 line_fifo4( + .rst(rstin | fifo_rst[4] | hdmi_vsync_v), + + .wr_clk(lcd_dclk), + .din(fifo_din[17:0]), +// .wr_en(fifo_write[4] & !fifo_full[4] & lcd_en), + .wr_en(fifo_write[4] & lcd_en), +// .full(fifo_full[4]), +// .overflow(fifo_over[4]), + + .rd_clk(hdmi_pclk), +// .rd_en(fifo_read[4] & !fifo_empty[4] & hdmi_en), + .rd_en(fifo_read[4] & hdmi_en), + .dout(fifo_dout4[17:0]) +// .empty(fifo_empty[4]), +// .underflow(fifo_under[4]) + ); + + fifo_2kx18 line_fifo5( + .rst(rstin | fifo_rst[5] | hdmi_vsync_v), + + .wr_clk(lcd_dclk), + .din(fifo_din[17:0]), +// .wr_en(fifo_write[5] & !fifo_full[5] & lcd_en), + .wr_en(fifo_write[5] & lcd_en), +// .full(fifo_full[5]), +// .overflow(fifo_over[5]), + + .rd_clk(hdmi_pclk), +// .rd_en(fifo_read[5] & !fifo_empty[5] & hdmi_en), + .rd_en(fifo_read[5] & hdmi_en), + .dout(fifo_dout5[17:0]) +// .empty(fifo_empty[5]), +// .underflow(fifo_under[5]) + ); + + fifo_2kx18 line_fifo6( + .rst(rstin | fifo_rst[6] | hdmi_vsync_v), + + .wr_clk(lcd_dclk), + .din(fifo_din[17:0]), +// .wr_en(fifo_write[6] & !fifo_full[6] & lcd_en), + .wr_en(fifo_write[6] & lcd_en), +// .full(fifo_full[6]), +// .overflow(fifo_over[6]), + + .rd_clk(hdmi_pclk), +// .rd_en(fifo_read[6] & !fifo_empty[6] & hdmi_en), + .rd_en(fifo_read[6] & hdmi_en), + .dout(fifo_dout6[17:0]) +// .empty(fifo_empty[6]), +// .underflow(fifo_under[6]) + ); + + fifo_2kx18 line_fifo7( + .rst(rstin | fifo_rst[7] | hdmi_vsync_v), + + .wr_clk(lcd_dclk), + .din(fifo_din[17:0]), +// .wr_en(fifo_write[7] & !fifo_full[7] & lcd_en), + .wr_en(fifo_write[7] & lcd_en), +// .full(fifo_full[7]), +// .overflow(fifo_over[7]), + + .rd_clk(hdmi_pclk), +// .rd_en(fifo_read[7] & !fifo_empty[7] & hdmi_en), + .rd_en(fifo_read[7] & hdmi_en), + .dout(fifo_dout7[17:0]) +// .empty(fifo_empty[7]), +// .underflow(fifo_under[7]) + ); + + assign dummy = 1'b0; +// assign dummy = !( (fifo_under[(NUMLINES-1) : 0] != 0) || lines_empty); + // trigger LED flash if we see any fifo go underflow, or if the overall line structure is empty + + ///////////////////////////////////// + ///// jellybean routines (synchronizers, rising edge finders, etc. etc.) + ///////////////////////////////////// + // clean up the sync signals, as they are invalid + // outside of control periods and have a programmable polarity + always @(posedge hdmi_pclk or posedge rstin) begin + if( rstin ) begin + hdmi_hsync_v <= 0; + hdmi_vsync_v <= 0; + + hdmi_hsync_v2 <= 0; + hdmi_vsync_v2 <= 0; + + hdmi_de_d <= 0; + + hdmi_first_de <= 0; + hdmi_first_de_state <= 0; + + lcd_vsync_1hdmi <= 0; + lcd_vsync_2hdmi <= 0; + lcd_vsync_hdmi <= 0; + end else begin + hdmi_de_d <= hdmi_de; + if( hdmi_cv ) begin + hdmi_hsync_v <= hdmi_hsync ^ !hdmi_sync_pol; + hdmi_vsync_v <= hdmi_vsync ^ !hdmi_sync_pol; + end else begin + hdmi_hsync_v <= hdmi_hsync_v; + hdmi_vsync_v <= hdmi_vsync_v; + end + + hdmi_hsync_v2 <= hdmi_hsync_v; // just a delayed version + hdmi_vsync_v2 <= hdmi_vsync_v; // just a delayed version + + if( hdmi_vsync_v ) begin + hdmi_first_de <= 0; + hdmi_first_de_state <= 0; + end else begin + if( hdmi_de_rising && (hdmi_first_de_state == 0) ) begin + hdmi_first_de <= 1; + hdmi_first_de_state <= 1; + end else begin + hdmi_first_de <= 0; + hdmi_first_de_state <= hdmi_first_de_state; + end + end // else: !if( hdmi_vsync_v ) + + lcd_vsync_2hdmi <= lcd_vsync; + lcd_vsync_1hdmi <= lcd_vsync_2hdmi; + lcd_vsync_hdmi <= lcd_vsync_1hdmi; + end // else: !if( rstin ) + end // always @ (posedge hdmi_pclk or posedge rstin) + assign hdmi_hsync_rising = hdmi_hsync_v && !hdmi_hsync_v2; + assign hdmi_vsync_rising = hdmi_vsync_v && !hdmi_vsync_v2; + assign hdmi_de_rising = hdmi_de && !hdmi_de_d; + assign hdmi_de_falling = !hdmi_de && hdmi_de_d; + assign lcd_vsync_rising__hdmi = !lcd_vsync_hdmi & lcd_vsync_1hdmi; + + reg lcd_vsync_rising; + reg lcd_vsync_falling; + reg lcd_vsync_d; + + assign lcd_de_vsync = lcd_de & !(lcd_vsync ^ !lcd_sync_pol); + // utility to find rising edges + always @(posedge lcd_dclk or posedge rstin) begin + if(rstin) begin + hdmi_hsync_rising__lcd <= 0; + hdmi_vsync_rising__lcd <= 0; + hdmi_vsync_falling__lcd <= 0; + hdmi_hsync_d__lcd <= 0; + hdmi_vsync_d__lcd <= 0; + + lcd_de_d <= 0; + lcd_de_rising <= 0; + + lcd_vsync_rising <= 0; + lcd_vsync_d <= 0; + lcd_de_rising <= 0; + + lcd_hsync_d <= 0; + end else begin // if (rstin) + lcd_hsync_d <= lcd_hsync ^ !lcd_sync_pol; + lcd_hsync_rising <= (lcd_hsync ^ !lcd_sync_pol) && !lcd_hsync_d; + + lcd_vsync_d <= lcd_vsync ^ !lcd_sync_pol; + lcd_vsync_rising <= (lcd_vsync ^ !lcd_sync_pol) && !lcd_vsync_d; + lcd_vsync_falling <= !(lcd_vsync ^ !lcd_sync_pol) && lcd_vsync_d; + + hdmi_hsync_d__lcd <= hdmi_hsync__lcd; + hdmi_vsync_d__lcd <= hdmi_vsync__lcd; + lcd_de_d <= lcd_de_vsync; + + if( hdmi_hsync__lcd && !hdmi_hsync_d__lcd ) + hdmi_hsync_rising__lcd <= 1; + else + hdmi_hsync_rising__lcd <= 0; + + if( hdmi_vsync__lcd && !hdmi_vsync_d__lcd ) + hdmi_vsync_rising__lcd <= 1; + else + hdmi_vsync_rising__lcd <= 0; + + if( !hdmi_vsync__lcd && hdmi_vsync_d__lcd ) + hdmi_vsync_falling__lcd <= 1; + else + hdmi_vsync_falling__lcd <= 0; + + if( lcd_de_vsync && !lcd_de_d ) + lcd_de_rising <= 1; + else + lcd_de_rising <= 0; + + if( !lcd_de_vsync && lcd_de_d ) + lcd_de_falling <= 1; + else + lcd_de_falling <= 0; + end // else: !if(rstin) + end // always @ (posedge lcd_dclk or posedge rstin) + + always @(posedge hdmi_pclk or posedge rstin) begin + if(rstin) begin + lcd_de_d__hdmi <= 0; + end else begin + lcd_de_d__hdmi <= lcd_de__hdmi; + end + end + assign lcd_de_rising__hdmi = lcd_de__hdmi && !lcd_de_d__hdmi; + assign lcd_de_falling__hdmi = !lcd_de__hdmi && lcd_de_d__hdmi; + + // cross-domain synchronization + always @(posedge lcd_dclk or posedge rstin) begin + if(rstin) begin + hdmi_vsync_s__lcd <= 0; + hdmi_vsync__lcd <= 0; + hdmi_hsync_s__lcd <= 0; + hdmi_hsync__lcd <= 0; + end else begin + hdmi_vsync_s__lcd <= hdmi_vsync_v; + hdmi_vsync__lcd <= hdmi_vsync_s__lcd; + hdmi_hsync_s__lcd <= hdmi_hsync_v; + hdmi_hsync__lcd <= hdmi_hsync_s__lcd; + end // else: !if(rstin) + end // always @ (posedge lcd_dclk or posedge rstin) + + always @(posedge hdmi_pclk or posedge rstin) begin + if(rstin) begin + lcd_de_s__hdmi <= 0; + lcd_de__hdmi <= 0; + + lcd_overflow_s__hdmi <= 0; + lcd_overflow__hdmi <= 0; + end else begin + lcd_de_s__hdmi <= lcd_de_vsync; + lcd_de__hdmi <= lcd_de_s__hdmi; + + lcd_overflow_s__hdmi <= lcd_overflow; + lcd_overflow__hdmi <= lcd_overflow_s__hdmi; + end + end // always @ (posedge hdmi_pclk or posedge rstin) + +endmodule // lcd_input diff --git a/release1.ucf b/release1.ucf new file mode 100755 index 0000000..0875665 --- /dev/null +++ b/release1.ucf @@ -0,0 +1,357 @@ +############################################################################## +## 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. +## +############################################################################## + +########################################### +# Setting VCCAUX for Spartan 6 TMDS +########################################### +CONFIG VCCAUX = 3.3; + +# +# Constraint for RX0 +# +#NET "dvi_rx0/rxclk" TNM_NET = "DVI_CLOCK0"; +#TIMESPEC TS_DVI_CLOCK0 = PERIOD "DVI_CLOCK0" 155 MHz HIGH 50%; +# +# Multi-cycle paths for TX0 +# +TIMEGRP bramgrp_0 = RAMS("dvi_tx0/pixel2x/dataint[*]"); +TIMEGRP fddbgrp_0 = FFS("dvi_tx0/pixel2x/db[*]"); +TIMEGRP bramra_0 = FFS("dvi_tx0/pixel2x/ra[*]"); + +TIMESPEC TS_ramdo_0 = FROM "bramgrp_0" TO "fddbgrp_0" TS_pixclocks ; +TIMESPEC TS_ramra_0 = FROM "bramra_0" TO "fddbgrp_0" TS_pixclocks ; + +############################################################################## +# SYSCLK Input +############################################################################## +NET "clk26" IOSTANDARD = LVCMOS33; +NET "clk26" LOC = P21; +#NET "clk26" TNM_NET = "clk26"; +#TIMESPEC TS_clk26_ = PERIOD "clk26" 26 MHz HIGH 50 % PRIORITY 255; + +############################################ +# TMDS pairs for Atlys top OUT: J2 - Bank 0 +############################################ +# Clock +NET "TX0_TMDS[3]" IOSTANDARD = TMDS_33; +NET "TX0_TMDSB[3]" IOSTANDARD = TMDS_33; +#INST "dvi_tx0/clkout/oserdes_s" LOC = OLOGIC_X6Y0; +INST "dvi_tx0/TMDS3" LOC = P51; +#INST "dvi_tx0/clkout/oserdes_m" LOC = OLOGIC_X6Y1; +NET "dvi_tx0/TMDS[3]" LOC = P51; +NET "dvi_tx0/TMDSB[3]" LOC = P50; +# Red +NET "TX0_TMDS[2]" IOSTANDARD = TMDS_33; +NET "TX0_TMDSB[2]" IOSTANDARD = TMDS_33; +INST "dvi_tx0/TMDS2" LOC = P62; +#INST "dvi_tx0/oserdes2/oserdes_s" LOC = OLOGIC_X10Y2; +#INST "dvi_tx0/oserdes2/oserdes_m" LOC = OLOGIC_X10Y3; +NET "dvi_tx0/TMDS[2]" LOC = P62; +NET "dvi_tx0/TMDSB[2]" LOC = P61; +# Green +NET "TX0_TMDS[1]" IOSTANDARD = TMDS_33; +NET "TX0_TMDSB[1]" IOSTANDARD = TMDS_33; +#INST "dvi_tx0/oserdes1/oserdes_s" LOC = OLOGIC_X9Y2; +INST "dvi_tx0/TMDS1" LOC = P58; +#INST "dvi_tx0/oserdes1/oserdes_m" LOC = OLOGIC_X9Y3; +NET "dvi_tx0/TMDS[1]" LOC = P58; +NET "dvi_tx0/TMDSB[1]" LOC = P57; +# Blue +NET "TX0_TMDS[0]" IOSTANDARD = TMDS_33; +NET "TX0_TMDSB[0]" IOSTANDARD = TMDS_33; +#INST "dvi_tx0/oserdes0/oserdes_s" LOC = OLOGIC_X7Y2; +INST "dvi_tx0/TMDS0" LOC = P56; +#INST "dvi_tx0/oserdes0/oserdes_m" LOC = OLOGIC_X7Y3; +NET "dvi_tx0/TMDS[0]" LOC = P56; +NET "dvi_tx0/TMDSB[0]" LOC = P55; + +############################################## +# TMDS pairs for Atlys IN (FPGA Bank 0): J1 +############################################## +# CLK +NET "RX0_TMDS[3]" IOSTANDARD = TMDS_33; +NET "dvi_rx0/tmdsclk_p" LOC = P124; +NET "RX0_TMDSB[3]" IOSTANDARD = TMDS_33; +NET "dvi_rx0/tmdsclk_n" LOC = P123; +# Red +NET "RX0_TMDS[2]" IOSTANDARD = TMDS_33; +NET "dvi_rx0/dec_r/des_0/datain_p" LOC = P134; +NET "RX0_TMDSB[2]" IOSTANDARD = TMDS_33; +NET "dvi_rx0/dec_r/des_0/datain_n" LOC = P133; +# Green +NET "RX0_TMDS[1]" IOSTANDARD = TMDS_33; +NET "dvi_rx0/dec_g/des_0/datain_p" LOC = P132; +NET "RX0_TMDSB[1]" IOSTANDARD = TMDS_33; +NET "dvi_rx0/dec_g/des_0/datain_n" LOC = P131; +# Blue +NET "RX0_TMDS[0]" IOSTANDARD = TMDS_33; +NET "dvi_rx0/dec_b/des_0/datain_p" LOC = P127; +NET "RX0_TMDSB[0]" IOSTANDARD = TMDS_33; +NET "dvi_rx0/dec_b/des_0/datain_n" LOC = P126; + +######## +# video in +######## +#R0 +NET "LCD_R[2]" IOSTANDARD = LVCMOS33; +NET "LCD_R[2]" LOC = P38; +#R1 +NET "LCD_R[3]" IOSTANDARD = LVCMOS33; +NET "LCD_R[3]" LOC = P27; +#R2 +NET "LCD_R[4]" IOSTANDARD = LVCMOS33; +NET "LCD_R[4]" LOC = P16; +#R3 +NET "LCD_R[5]" IOSTANDARD = LVCMOS33; +NET "LCD_R[5]" LOC = P23; +#R4 +NET "LCD_R[6]" IOSTANDARD = LVCMOS33; +NET "LCD_R[6]" LOC = P22; +#R5 +NET "LCD_R[7]" IOSTANDARD = LVCMOS33; +NET "LCD_R[7]" LOC = P15; +#G0 +NET "LCD_G[2]" IOSTANDARD = LVCMOS33; +NET "LCD_G[2]" LOC = P29; +#G1 +NET "LCD_G[3]" IOSTANDARD = LVCMOS33; +NET "LCD_G[3]" LOC = P34; +#G2 +NET "LCD_G[4]" IOSTANDARD = LVCMOS33; +NET "LCD_G[4]" LOC = P14; +#G3 +NET "LCD_G[5]" IOSTANDARD = LVCMOS33; +NET "LCD_G[5]" LOC = P12; +#G4 +NET "LCD_G[6]" IOSTANDARD = LVCMOS33; +NET "LCD_G[6]" LOC = P26; +#G5 +NET "LCD_G[7]" IOSTANDARD = LVCMOS33; +NET "LCD_G[7]" LOC = P30; +NET "LCDO_DCLK" IOSTANDARD = LVCMOS33; +NET "LCDO_DCLK" SLEW = SLOW; +NET "LCDO_DCLK" LOC = P17; +NET "LCDO_DCLK" DRIVE = 4; +NET "LCD_DE" IOSTANDARD = LVCMOS33; +NET "LCD_DE" LOC = P24; +NET "LCD_HSYNC" IOSTANDARD = LVCMOS33; +NET "LCD_HSYNC" LOC = P33; +NET "LCD_VSYNC" IOSTANDARD = LVCMOS33; +NET "LCD_VSYNC" LOC = P35; +#B0 +NET "LCD_B[2]" IOSTANDARD = LVCMOS33; +NET "LCD_B[2]" LOC = P11; +#B1 +NET "LCD_B[3]" IOSTANDARD = LVCMOS33; +NET "LCD_B[3]" LOC = P10; +#B2 +NET "LCD_B[4]" IOSTANDARD = LVCMOS33; +NET "LCD_B[4]" LOC = P32; +#B3 +NET "LCD_B[5]" IOSTANDARD = LVCMOS33; +NET "LCD_B[5]" LOC = P40; +#B4 +NET "LCD_B[6]" IOSTANDARD = LVCMOS33; +NET "LCD_B[6]" LOC = P9; +#B5 +NET "LCD_B[7]" IOSTANDARD = LVCMOS33; +NET "LCD_B[7]" LOC = P8; + +NET "VSYNC_STB" IOSTANDARD = LVCMOS33; +NET "VSYNC_STB" LOC = P41; +NET "HPD_NOTIFY" IOSTANDARD = LVCMOS33; +NET "HPD_NOTIFY" LOC = P7; +NET "HDCP_AKSV" IOSTANDARD = LVCMOS33; +NET "HDCP_AKSV" LOC = P5; +NET "LOWVOLT_NOTIFY" IOSTANDARD = LVCMOS33; +NET "LOWVOLT_NOTIFY" LOC = P1; +NET "SOURCE_NOTIFY" IOSTANDARD = LVCMOS33; +NET "SOURCE_NOTIFY" LOC = P140; + +NET "SDA" LOC = P105; +NET "SDA" IOSTANDARD = LVCMOS33; +NET "SCL" IOSTANDARD = LVCMOS33; +NET "SCL" LOC = P104; + +NET "DDC_SCL" IOSTANDARD = LVCMOS33; +NET "DDC_SCL" LOC = P99; +NET "DDC_SDA" IOSTANDARD = LVCMOS33; +NET "DDC_SDA" LOC = P100; +NET "HPD_N" IOSTANDARD = LVCMOS33; +NET "HPD_N" LOC = P98; +NET "HPD_OVERRIDE" IOSTANDARD = LVCMOS33; +NET "HPD_OVERRIDE" LOC = P111; + +NET "DDC_SDA_PU" IOSTANDARD = LVCMOS33; +NET "DDC_SDA_PU" LOC = P66; +NET "DDC_SDA_PD" IOSTANDARD = LVCMOS33; +NET "DDC_SDA_PD" LOC = P67; + +######################################## +# Reset button and LEDs and Mechanical Switches (SW) +######################################## + +# nonsense pin for now +# add a pull-up, and check it happens in synthesis +NET "rstbtn_n" IOSTANDARD = LVCMOS33; +NET "rstbtn_n" PULLUP; +NET "rstbtn_n" LOC = P115; +NET "LED0" IOSTANDARD = LVCMOS33; +NET "LED0" LOC = P138; +NET "LED1" IOSTANDARD = LVCMOS33; +NET "LED1" LOC = P137; + +NET "CEC" LOC = P47; +NET "CEC" IOSTANDARD = LVCMOS33; + +NET "CHUMBY_BEND" LOC = P2; +NET "CHUMBY_BEND" IOSTANDARD = LVCMOS33; + +NET "LOWVOLT_N" IOSTANDARD = LVCMOS33; +NET "LOWVOLT_N" LOC = P44; +NET "LOWVOLT_N" PULLUP; + +################################## +## timing constraints +################################## + +###### 26 mhz clock period constraint +NET "clk26" TNM_NET = "clk26_pin"; +TIMESPEC TS_26mpin = PERIOD "clk26_pin" 26 MHz HIGH 50%; +NET "clk26buf" TNM_NET = "clk26_tnm"; +TIMESPEC TS_26m = PERIOD "clk26_tnm" 26 MHz HIGH 50%; + +###### clock period spec +NET "RX0_TMDS[3]" TNM_NET = "RXCLK_PIN"; +TIMESPEC TS_RXCLK_PIN = PERIOD "RXCLK_PIN" 74.25 MHz HIGH 50% PRIORITY -1; + +NET "RX0_TMDSB[3]" TNM_NET = "RXCLKB_PIN"; +TIMESPEC TS_RXCLKB_PIN = PERIOD "RXCLKB_PIN" 74.25 MHz HIGH 50% PRIORITY -1; + +# manually propagated constraint +NET "dvi_rx0/pclk" TNM = "pixclocks"; +TIMESPEC TS_pixclocks = PERIOD "pixclocks" 74.25 MHz; + +NET "dvi_rx0/pclkx2" TNM_NET = "pclkx2"; +TIMESPEC TS_pclkx2 = PERIOD "pclkx2" 148.5 MHz; + +NET "dvi_rx0/pclkx10" TNM_NET = "pclkx10"; +TIMESPEC TS_pclkx10 = PERIOD "pclkx10" 742.5 MHz; + +NET "dvi_rx0/rxclk" TNM_NET = "rxclk_to_dcm"; +TIMESPEC TS_rxclk_to_dcm = PERIOD "rxclk_to_dcm" 74.25 MHz; + +NET "tx0_pclk" TNM_NET = "tx0_pclk_net"; +TIMESPEC TS_tx0_pclk = PERIOD "tx0_pclk_net" 74.25 MHz; + +NET "selfclockgen/clkfx" TNM_NET = "self_clk"; +TIMESPEC TS_selfclk = PERIOD "self_clk" 74.285 MHz; + +NET "clk2M" TNM_NET = "clk2M_net"; +TIMESPEC TS_clk2M = PERIOD "clk2M_net" 815 kHz; + +######## input pad timing constraints +NET "LCD_*" TNM = PADS(LCD_*) "lcd_pads"; +OFFSET = IN 5 ns BEFORE "tx0_pclk" TIMEGRP "lcd_pads"; + +######## TIG constraints +NET "clk26buf" TNM_NET = FFS "I2C_REGS"; +NET "dvi_rx0/pclk" TNM_NET = FFS "rx_REGS"; +NET "tx0_pclk" TNM_NET = FFS "tx_REGS"; +NET "rx0_pllclk1" TNM_NET = FFS "td_REGS"; # used by timing detector in some versions +NET "tx0_pclk" TNM_NET = RAMS "tx_RAMS"; +NET "tx0_pclk" TNM_NET = BRAMS_PORTA "tx_RAMSA"; +NET "tx0_pclk" TNM_NET = BRAMS_PORTB "tx_RAMSB"; +NET "selfclockgen/clkfx" TNM_NET = FFS "self_REGS"; +TIMEGRP "tx0_syncgrp" = "tx_REGS" "tx_RAMS" "tx_RAMSA" "tx_RAMSB"; + +# I2C programming TIGs +NET "host_i2c/I2C_reg_update" TNM = I2C_reg_exception; +NET "ddc_hdcp_snoop/I2C_reg_update" TNM = I2C_reg_exception; +NET "ddc_hdcp_snoop/Aksv14_write_rstpot" TNM = I2C_REG_exception; +TIMESPEC TS_false_txw = FROM "I2C_reg_exception" TO "tx_REGS" TIG ; +TIMESPEC TS_false_txr = FROM "tx_REGS" TO "I2C_reg_exception" TIG ; +TIMESPEC TS_false_txd = FROM "td_REGS" TO "I2C_REGS" TIG ; + +TIMESPEC TS_false_rxw = FROM "I2C_reg_exception" TO "rx_REGS" TIG ; # there are none of these btw +TIMESPEC TS_false_rxr = FROM "rx_REGS" TO "I2C_reg_exception" TIG ; + +TIMESPEC TS_false_self = FROM "I2C_reg_exception" TO "self_REGS" TIG ; + +# reset TIGs +NET "tx0_reset" TIG; + +# self-clock TIGs +NET "selfclockgen/clkfx" TNM_NET = "self_REGS"; +TIMESPEC TS_false_self_tx = FROM "self_REGS" TO "tx_REGS" TIG ; +TIMESPEC TS_false_self_i2c = FROM "self_REGS" TO "I2C_REGS" TIG ; + +######## fix-up constraints +TIMESPEC TS_TX_SYNC = FROM "tx0_syncgrp" TO "tx0_syncgrp" 74.25MHz; +#TIMESPEC TS_DP_FFS = FROM "tx_REGS" TO "tx_REGS" 74.25MHz; +#TIMESPEC TS_RAMS_FFS = FROM "tx_RAMS" TO "tx_REGS" 74.25MHz; +#TIMESPEC TS_FFS_RAMS = FROM "tx_REGS" TO "tx_RAMS" 74.25MHz; + +TIMESPEC TS_RX0_FFS = FROM "rx_REGS" TO "rx_REGS" 74.25MHz; +TIMESPEC TS_RXTX_FFS = FROM "rx_REGS" TO "tx_REGS" 74.25MHz; + + +### comment out during self-timed mode +NET "dvi_rx0/bufpll_lock" TIG; + +### remove comment during self-timed mode +#NET "clk26" CLOCK_DEDICATED_ROUTE = FALSE; +#NET "comp_ctl[6]" TIG; + + +#################### LEGACY ##################### + +#Created by Constraints Editor (xc6slx9-tqg144-2) - 2011/03/16 +#NET "RX0_TMDS[3]" TNM_NET = "RX0_TMDS<3>"; +# limited by ADVPLL spec +#TIMESPEC TS_RX0_TMDS_3_ = PERIOD "RX0_TMDS<3>" 95 MHz HIGH 50 % PRIORITY 1; +#TIMESPEC TS_RX0_TMDS_3_ = PERIOD "RX0_TMDS<3>" 35.34 ns HIGH 50%; # 480p/60 +#TIMESPEC TS_RX0_TMDS_3_ = PERIOD "RX0_TMDS<3>" 13.48 ns HIGH 50%; # 720p/60 + +#NET "RX0_TMDSB[3]" TNM_NET = "RX0_TMDSB<3>"; +#TIMESPEC TS_RX0_TMDSB_3_ = PERIOD "RX0_TMDSB<3>" 95 MHz HIGH 50 % PRIORITY 1; +#TIMESPEC TS_RX0_TMDS_3_ = PERIOD "RX0_TMDSB<3>" 35.34 ns HIGH 50%; # 480p/60 +#TIMESPEC TS_RX0_TMDS_3_ = PERIOD "RX0_TMDSB<3>" 13.48 ns HIGH 50%; # 720p/60 + +#NET "tx0_pclk" TNM_NET = "tx0_pclk"; +#TIMESPEC TS_tx0_pclk = PERIOD "tx0_pclk" 74.25 MHz HIGH 50 % PRIORITY 2; + +#TIMESPEC TS_LCD_DCLK = PERIOD "LCD_DCLK" 74.25 MHz PRIORITY 200; +#TIMESPEC TS_rx_clk = PERIOD "rx_clk" 74.25 MHz PRIORITY 1; +#NET "LCD_DCLK" TNM_NET = "LCD_DCLK"; +#NET "dvi_rx0/rxclk" TNM_NET = "rx_clk"; + +#TIMESPEC TS_rxclk_main = PERIOD "rxclk_main" 74.25 MHz PRIORITY 1; + +#NET "RX0_TMDS[3]" TNM_NET = "rxclk_main"; +#TIMESPEC TS_rxclk_main_b = PERIOD "rxclk_main_b" 74.25 MHz PRIORITY 1; +#NET "RX0_TMDSB[3]" TNM_NET = "rxclk_main_b"; +#NET "clk26buf_BUFG" TNM = "clk26_tnm"; diff --git a/rx/chnlbond.v b/rx/chnlbond.v new file mode 100755 index 0000000..c497d86 --- /dev/null +++ b/rx/chnlbond.v @@ -0,0 +1,170 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Xilinx, Inc. 2007 www.xilinx.com +// +// XAPP xxx +// +////////////////////////////////////////////////////////////////////////////// +// +// File name : chnlbond.v +// +// Description : Channel Bonding Logic +// TMDS channel de-skew +// +// Author : Bob Feng +// +// Disclaimer: LIMITED WARRANTY AND DISCLAMER. These designs are +// provided to you "as is". Xilinx and its licensors makeand you +// receive no warranties or conditions, express, implied, +// statutory or otherwise, and Xilinx specificallydisclaims any +// implied warranties of merchantability, non-infringement,or +// fitness for a particular purpose. Xilinx does notwarrant that +// the functions contained in these designs will meet your +// requirements, or that the operation of these designswill be +// uninterrupted or error free, or that defects in theDesigns +// will be corrected. Furthermore, Xilinx does not warrantor +// make any representations regarding use or the results ofthe +// use of the designs in terms of correctness, accuracy, +// reliability, or otherwise. +// +// LIMITATION OF LIABILITY. In no event will Xilinx or its +// licensors be liable for any loss of data, lost profits,cost +// or procurement of substitute goods or services, or forany +// special, incidental, consequential, or indirect damages +// arising from the use or operation of the designs or +// accompanying documentation, however caused and on anytheory +// of liability. This limitation will apply even if Xilinx +// has been advised of the possibility of such damage. This +// limitation shall apply not-withstanding the failure ofthe +// essential purpose of any limited remedies herein. +// +// Copyright © 2004 Xilinx, Inc. +// All rights reserved +// +////////////////////////////////////////////////////////////////////////////// +`timescale 1 ns / 1ps + +module chnlbond ( + input wire clk, + input wire [9:0] rawdata, + input wire iamvld, + input wire other_ch0_vld, + input wire other_ch1_vld, + input wire other_ch0_rdy, + input wire other_ch1_rdy, + output reg iamrdy, + output reg [9:0] sdata +); + + parameter CTRLTOKEN0 = 10'b1101010100; + parameter CTRLTOKEN1 = 10'b0010101011; + parameter CTRLTOKEN2 = 10'b0101010100; + parameter CTRLTOKEN3 = 10'b1010101011; + + wire rawdata_vld; + assign rawdata_vld = other_ch0_vld & other_ch1_vld & iamvld; + + reg [3:0] wa, ra; + reg we; + + //////////////////////////////////////////////////////// + // FIFO Write Control Logic + //////////////////////////////////////////////////////// + always @ (posedge clk) begin + we <=#1 rawdata_vld; + end + + always @ (posedge clk) begin + if(rawdata_vld) + wa <=#1 wa + 1'b1; + else + wa <=#1 4'h0; + end + + wire [9:0] dpfo_dout; + DRAM16XN #(.data_width(10)) + cbfifo_i ( + .DATA_IN(rawdata), + .ADDRESS(wa), + .ADDRESS_DP(ra), + .WRITE_EN(we), + .CLK(clk), + .O_DATA_OUT(), + .O_DATA_OUT_DP(dpfo_dout)); + + always @ (posedge clk) begin + sdata <=#1 dpfo_dout; + end + + //////////////////////////////////////////////////////// + // FIFO read Control Logic + //////////////////////////////////////////////////////// + + //////////////////////////////// + // Use blank period beginning + // as a speical marker to + // align all channel together + //////////////////////////////// + reg rcvd_ctkn, rcvd_ctkn_q; //received control token + reg blnkbgn; //blank period begins + always @ (posedge clk) begin + rcvd_ctkn <=#1 ((sdata == CTRLTOKEN0) || (sdata == CTRLTOKEN1) || (sdata == CTRLTOKEN2) || (sdata == CTRLTOKEN3)); + rcvd_ctkn_q <=#1 rcvd_ctkn; + blnkbgn <=#1 !rcvd_ctkn_q & rcvd_ctkn; + end + + ///////////////////////////// + //skip the current line + ///////////////////////////// + wire next_blnkbgn; + reg skip_line; + always @ (posedge clk) begin + if(!rawdata_vld) + skip_line <=#1 1'b0; + else if(blnkbgn) + skip_line <=#1 1'b1; + end + + assign next_blnkbgn = skip_line & blnkbgn; + + ////////////////////////////// + //Declare my own readiness + ////////////////////////////// + always @ (posedge clk) begin + if(!rawdata_vld) + iamrdy <=#1 1'b0; + else if(next_blnkbgn) + iamrdy <=#1 1'b1; + end + + reg rawdata_vld_q; + reg rawdata_vld_rising; + always @ (posedge clk) begin + rawdata_vld_q <=#1 rawdata_vld; + rawdata_vld_rising <=#1 rawdata_vld & !rawdata_vld_q; + end + + ////////////////////////////////////////////////////////////////////////////////////////// + // 1. FIFO flow through first when all channels are found valid(phase aligned) + // 2. When the speical marker on my channel is found, the fifo read is hold + // 3. Until the same markers are found across all three channels, the fifo read resumes + ////////////////////////////////////////////////////////////////////////////////////////// + reg ra_en = 1'b0; + always @ (posedge clk) begin + if(rawdata_vld_rising || (other_ch0_rdy & other_ch1_rdy & iamrdy)) + ra_en <=#1 1'b1; + else if(next_blnkbgn && !(other_ch0_rdy & other_ch1_rdy & iamrdy)) + ra_en <=#1 1'b0; + end + + ///////////////////////////////////////// + //FIFO Read Address Counter + ///////////////////////////////////////// + always @ (posedge clk) begin + if(!rawdata_vld) + ra <=#1 4'h0; + else if(ra_en) + ra <=#1 ra + 1'b1; + end + +endmodule diff --git a/rx/decode.v b/rx/decode.v new file mode 100755 index 0000000..6e5a7e0 --- /dev/null +++ b/rx/decode.v @@ -0,0 +1,271 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Xilinx, Inc. 2010 www.xilinx.com +// +// XAPP xxx +// +////////////////////////////////////////////////////////////////////////////// +// +// File name : decoder.v +// +// Description : Spartan-6 dvi decoder +// +// +// Author : Bob Feng +// +// Disclaimer: LIMITED WARRANTY AND DISCLAMER. These designs are +// provided to you "as is". Xilinx and its licensors makeand you +// receive no warranties or conditions, express, implied, +// statutory or otherwise, and Xilinx specificallydisclaims any +// implied warranties of merchantability, non-infringement,or +// fitness for a particular purpose. Xilinx does notwarrant that +// the functions contained in these designs will meet your +// requirements, or that the operation of these designswill be +// uninterrupted or error free, or that defects in theDesigns +// will be corrected. Furthermore, Xilinx does not warrantor +// make any representations regarding use or the results ofthe +// use of the designs in terms of correctness, accuracy, +// reliability, or otherwise. +// +// LIMITATION OF LIABILITY. In no event will Xilinx or its +// licensors be liable for any loss of data, lost profits,cost +// or procurement of substitute goods or services, or forany +// special, incidental, consequential, or indirect damages +// arising from the use or operation of the designs or +// accompanying documentation, however caused and on anytheory +// of liability. This limitation will apply even if Xilinx +// has been advised of the possibility of such damage. This +// limitation shall apply not-withstanding the failure ofthe +// essential purpose of any limited remedies herein. +// +// Copyright © 2004 Xilinx, Inc. +// All rights reserved +// +////////////////////////////////////////////////////////////////////////////// +`timescale 1 ns / 1ps + +module decode ( + input wire reset, // + input wire pclk, // pixel clock + input wire pclkx2, // double pixel rate for gear box + input wire pclkx10, // IOCLK + input wire serdesstrobe, // serdesstrobe for iserdes2 + input wire din_p, // data from dvi cable + input wire din_n, // data from dvi cable + input wire other_ch0_vld, // other channel0 has valid data now + input wire other_ch1_vld, // other channel1 has valid data now + input wire other_ch0_rdy, // other channel0 has detected a valid starting pixel + input wire other_ch1_rdy, // other channel1 has detected a valid starting pixel + + output wire iamvld, // I have valid data now + output wire iamrdy, // I have detected a valid new pixel + output wire psalgnerr, // Phase alignment error + output reg c0, + output reg c1, + output reg de, + output reg [9:0] sdout, + output reg [7:0] dout, + output reg dgb, + output reg vgb); + + //////////////////////////////// + // + // 5-bit to 10-bit gear box + // + //////////////////////////////// + wire flipgear; + reg flipgearx2; + + always @ (posedge pclkx2) begin + flipgearx2 <=#1 flipgear; + end + + reg toggle = 1'b0; + + always @ (posedge pclkx2 or posedge reset) + if (reset == 1'b1) begin + toggle <= 1'b0 ; + end else begin + toggle <=#1 ~toggle; + end + + wire rx_toggle; + + assign rx_toggle = toggle ^ flipgearx2; //reverse hi-lo position + + wire [4:0] raw5bit; + reg [4:0] raw5bit_q; + reg [9:0] rawword; + + always @ (posedge pclkx2) begin + raw5bit_q <=#1 raw5bit; + + if(rx_toggle) //gear from 5 bit to 10 bit + rawword <=#1 {raw5bit, raw5bit_q}; + end + + //////////////////////////////// + // + // bitslip signal sync to pclkx2 + // + //////////////////////////////// + reg bitslipx2 = 1'b0; + reg bitslip_q = 1'b0; + wire bitslip; + + always @ (posedge pclkx2) begin + bitslip_q <=#1 bitslip; + bitslipx2 <=#1 bitslip & !bitslip_q; + end + + ///////////////////////////////////////////// + // + // 1:5 de-serializer working at x2 pclk rate + // + ///////////////////////////////////////////// + serdes_1_to_5_diff_data # ( + .DIFF_TERM("FALSE"), + .BITSLIP_ENABLE("TRUE") + ) des_0 ( + .use_phase_detector(1'b1), + .datain_p(din_p), + .datain_n(din_n), + .rxioclk(pclkx10), + .rxserdesstrobe(serdesstrobe), + .reset(reset), + .gclk(pclkx2), + .bitslip(bitslipx2), + .data_out(raw5bit) + ); + + ///////////////////////////////////////////////////// + // Doing word boundary detection here + ///////////////////////////////////////////////////// + wire [9:0] rawdata = rawword; + + /////////////////////////////////////// + // Phase Alignment Instance + /////////////////////////////////////// + phsaligner phsalgn_0 ( + .rst(reset), + .clk(pclk), + .sdata(rawdata), + .bitslip(bitslip), + .flipgear(flipgear), + .psaligned(iamvld) + ); + + assign psalgnerr = 1'b0; + + /////////////////////////////////////// + // Per Channel De-skew Instance + /////////////////////////////////////// + wire [9:0] sdata; + chnlbond cbnd ( + .clk(pclk), + .rawdata(rawdata), + .iamvld(iamvld), + .other_ch0_vld(other_ch0_vld), + .other_ch1_vld(other_ch1_vld), + .other_ch0_rdy(other_ch0_rdy), + .other_ch1_rdy(other_ch1_rdy), + .iamrdy(iamrdy), + .sdata(sdata) + ); + + ///////////////////////////////////////////////////////////////// + // Below performs the 10B-8B decoding function defined in DVI 1.0 + // Specification: Section 3.3.3, Figure 3-6, page 31. + ///////////////////////////////////////////////////////////////// + // Distinct Control Tokens + parameter CTRLTOKEN0 = 10'b1101010100; + parameter CTRLTOKEN1 = 10'b0010101011; + parameter CTRLTOKEN2 = 10'b0101010100; + parameter CTRLTOKEN3 = 10'b1010101011; + + parameter DATA_GB = 10'b0100110011; + parameter VID_B_GB = 10'b1011001100; + parameter VID_G_GB = 10'b0100110011; + parameter VID_R_GB = 10'b1011001100; + + wire [7:0] data; + assign data = (sdata[9]) ? ~sdata[7:0] : sdata[7:0]; + + always @ (posedge pclk) begin + if(iamrdy && other_ch0_rdy && other_ch1_rdy) begin + case (sdata) + CTRLTOKEN0: begin + c0 <=#1 1'b0; + c1 <=#1 1'b0; + de <=#1 1'b0; + dgb <= #1 1'b0; + vgb <= #1 1'b0; + end + + CTRLTOKEN1: begin + c0 <=#1 1'b1; + c1 <=#1 1'b0; + de <=#1 1'b0; + dgb <= #1 1'b0; + vgb <= #1 1'b0; + end + + CTRLTOKEN2: begin + c0 <=#1 1'b0; + c1 <=#1 1'b1; + de <=#1 1'b0; + dgb <= #1 1'b0; + vgb <= #1 1'b0; + end + + CTRLTOKEN3: begin + c0 <=#1 1'b1; + c1 <=#1 1'b1; + de <=#1 1'b0; + dgb <= #1 1'b0; + vgb <= #1 1'b0; + end + + DATA_GB: begin + c0 <=#1 1'b0; + c1 <=#1 1'b0; + de <=#1 1'b0; + dgb <= #1 1'b1; + vgb <= #1 1'b0; + end + + VID_B_GB: begin + c0 <=#1 1'b0; + c1 <=#1 1'b0; + de <=#1 1'b0; + dgb <= #1 1'b0; + vgb <= #1 1'b1; + end + + default: begin + dout[0] <=#1 data[0]; + dout[1] <=#1 (sdata[8]) ? (data[1] ^ data[0]) : (data[1] ~^ data[0]); + dout[2] <=#1 (sdata[8]) ? (data[2] ^ data[1]) : (data[2] ~^ data[1]); + dout[3] <=#1 (sdata[8]) ? (data[3] ^ data[2]) : (data[3] ~^ data[2]); + dout[4] <=#1 (sdata[8]) ? (data[4] ^ data[3]) : (data[4] ~^ data[3]); + dout[5] <=#1 (sdata[8]) ? (data[5] ^ data[4]) : (data[5] ~^ data[4]); + dout[6] <=#1 (sdata[8]) ? (data[6] ^ data[5]) : (data[6] ~^ data[5]); + dout[7] <=#1 (sdata[8]) ? (data[7] ^ data[6]) : (data[7] ~^ data[6]); + + de <=#1 1'b1; + + dgb <= #1 1'b0; + vgb <= #1 1'b0; + end + endcase + + sdout <=#1 sdata; + end else begin + c0 <= 1'b0; + c1 <= 1'b0; + de <= 1'b0; + dout <= 8'h0; + sdout <= 10'h0; + end + end +endmodule diff --git a/rx/decode_terc4.v b/rx/decode_terc4.v new file mode 100755 index 0000000..2c0f5a8 --- /dev/null +++ b/rx/decode_terc4.v @@ -0,0 +1,66 @@ +////////////////////////////////////////////////////////////////////////////// +// 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 ps / 1ps + +module decode_terc4 ( + input clkin, // pixel clock input + input rstin, // async. reset input (active high) + input [9:0] din, // data inputs: expect registered + output reg [3:0] dout // data outputs +); + reg [9:0] din_r; + + always @(posedge clkin or posedge rstin) begin + if( rstin ) begin + dout[3:0] <= 4'h0; + din_r <= 10'b0; + end else begin + din_r <= din; + case (din_r[9:0]) + 10'b1010011100: dout[3:0] <= 4'b0000; + 10'b1001100011: dout[3:0] <= 4'b0001; + 10'b1011100100: dout[3:0] <= 4'b0010; + 10'b1011100010: dout[3:0] <= 4'b0011; + 10'b0101110001: dout[3:0] <= 4'b0100; + 10'b0100011110: dout[3:0] <= 4'b0101; + 10'b0110001110: dout[3:0] <= 4'b0110; + 10'b0100111100: dout[3:0] <= 4'b0111; + 10'b1011001100: dout[3:0] <= 4'b1000; + 10'b0100111001: dout[3:0] <= 4'b1001; + 10'b0110011100: dout[3:0] <= 4'b1010; + 10'b1011000110: dout[3:0] <= 4'b1011; + 10'b1010001110: dout[3:0] <= 4'b1100; + 10'b1001110001: dout[3:0] <= 4'b1101; + 10'b0101100011: dout[3:0] <= 4'b1110; + 10'b1011000011: dout[3:0] <= 4'b1111; + // no default to allow for maximum coding flexibility... + endcase // case (din_q) + end // else: !if( rstin ) + end // always @ (posedge clkin or posedge rstin) + +endmodule // decode_terc4 + diff --git a/rx/decodeb.v b/rx/decodeb.v new file mode 100755 index 0000000..813a111 --- /dev/null +++ b/rx/decodeb.v @@ -0,0 +1,319 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Xilinx, Inc. 2010 www.xilinx.com +// +// XAPP xxx +// +////////////////////////////////////////////////////////////////////////////// +// +// File name : decoder.v +// +// Description : Spartan-6 dvi decoder +// +// +// Author : Bob Feng +// +// Disclaimer: LIMITED WARRANTY AND DISCLAMER. These designs are +// provided to you "as is". Xilinx and its licensors makeand you +// receive no warranties or conditions, express, implied, +// statutory or otherwise, and Xilinx specificallydisclaims any +// implied warranties of merchantability, non-infringement,or +// fitness for a particular purpose. Xilinx does notwarrant that +// the functions contained in these designs will meet your +// requirements, or that the operation of these designswill be +// uninterrupted or error free, or that defects in theDesigns +// will be corrected. Furthermore, Xilinx does not warrantor +// make any representations regarding use or the results ofthe +// use of the designs in terms of correctness, accuracy, +// reliability, or otherwise. +// +// LIMITATION OF LIABILITY. In no event will Xilinx or its +// licensors be liable for any loss of data, lost profits,cost +// or procurement of substitute goods or services, or forany +// special, incidental, consequential, or indirect damages +// arising from the use or operation of the designs or +// accompanying documentation, however caused and on anytheory +// of liability. This limitation will apply even if Xilinx +// has been advised of the possibility of such damage. This +// limitation shall apply not-withstanding the failure ofthe +// essential purpose of any limited remedies herein. +// +// Copyright © 2004 Xilinx, Inc. +// All rights reserved +// +////////////////////////////////////////////////////////////////////////////// +// Modifications copyright (c) 2011, Andrew "bunnie" Huang +// All rights reserved as permitted by law. +// +// 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 decodeb ( + input wire reset, // + input wire pclk, // pixel clock + input wire pclkx2, // double pixel rate for gear box + input wire pclkx10, // IOCLK + input wire serdesstrobe, // serdesstrobe for iserdes2 + input wire din_p, // data from dvi cable + input wire din_n, // data from dvi cable + input wire other_ch0_vld, // other channel0 has valid data now + input wire other_ch1_vld, // other channel1 has valid data now + input wire other_ch0_rdy, // other channel0 has detected a valid starting pixel + input wire other_ch1_rdy, // other channel1 has detected a valid starting pixel + + output wire iamvld, // I have valid data now + output wire iamrdy, // I have detected a valid new pixel + output wire psalgnerr, // Phase alignment error + output reg c0, + output reg c1, + output reg de, + output reg [9:0] sdout, + output reg [7:0] dout, + output reg dgb, + output reg vgb, + output reg ctl_vld, + output wire line_end); + + //////////////////////////////// + // + // 5-bit to 10-bit gear box + // + //////////////////////////////// + wire flipgear; + reg flipgearx2; + + always @ (posedge pclkx2) begin + flipgearx2 <=#1 flipgear; + end + + reg toggle = 1'b0; + + always @ (posedge pclkx2 or posedge reset) + if (reset == 1'b1) begin + toggle <= 1'b0 ; + end else begin + toggle <=#1 ~toggle; + end + + wire rx_toggle; + + assign rx_toggle = toggle ^ flipgearx2; //reverse hi-lo position + + wire [4:0] raw5bit; + reg [4:0] raw5bit_q; + reg [9:0] rawword; + + always @ (posedge pclkx2) begin + raw5bit_q <=#1 raw5bit; + + if(rx_toggle) //gear from 5 bit to 10 bit + rawword <=#1 {raw5bit, raw5bit_q}; + end + + //////////////////////////////// + // + // bitslip signal sync to pclkx2 + // + //////////////////////////////// + reg bitslipx2 = 1'b0; + reg bitslip_q = 1'b0; + wire bitslip; + + always @ (posedge pclkx2) begin + bitslip_q <=#1 bitslip; + bitslipx2 <=#1 bitslip & !bitslip_q; + end + + ///////////////////////////////////////////// + // + // 1:5 de-serializer working at x2 pclk rate + // + ///////////////////////////////////////////// + serdes_1_to_5_diff_data # ( + .DIFF_TERM("FALSE"), + .BITSLIP_ENABLE("TRUE") + ) des_0 ( + .use_phase_detector(1'b1), + .datain_p(din_p), + .datain_n(din_n), + .rxioclk(pclkx10), + .rxserdesstrobe(serdesstrobe), + .reset(reset), + .gclk(pclkx2), + .bitslip(bitslipx2), + .data_out(raw5bit) + ); + + ///////////////////////////////////////////////////// + // Doing word boundary detection here + ///////////////////////////////////////////////////// + wire [9:0] rawdata = rawword; + + /////////////////////////////////////// + // Phase Alignment Instance + /////////////////////////////////////// + phsaligner phsalgn_0 ( + .rst(reset), + .clk(pclk), + .sdata(rawdata), + .bitslip(bitslip), + .flipgear(flipgear), + .psaligned(iamvld) + ); + + assign psalgnerr = 1'b0; + + /////////////////////////////////////// + // Per Channel De-skew Instance + /////////////////////////////////////// + wire [9:0] sdata; + chnlbond cbnd ( + .clk(pclk), + .rawdata(rawdata), + .iamvld(iamvld), + .other_ch0_vld(other_ch0_vld), + .other_ch1_vld(other_ch1_vld), + .other_ch0_rdy(other_ch0_rdy), + .other_ch1_rdy(other_ch1_rdy), + .iamrdy(iamrdy), + .sdata(sdata) + ); + + //// + // hack to accelerate detection of line end so that HDCP rekey + // can meet stringent timing spec requirement + //// + assign line_end = de && + ((sdata == CTRLTOKEN0) || + (sdata == CTRLTOKEN1) || + (sdata == CTRLTOKEN2) || + (sdata == CTRLTOKEN3)); + + ///////////////////////////////////////////////////////////////// + // Below performs the 10B-8B decoding function defined in DVI 1.0 + // Specification: Section 3.3.3, Figure 3-6, page 31. + ///////////////////////////////////////////////////////////////// + // Distinct Control Tokens + parameter CTRLTOKEN0 = 10'b1101010100; + parameter CTRLTOKEN1 = 10'b0010101011; + parameter CTRLTOKEN2 = 10'b0101010100; + parameter CTRLTOKEN3 = 10'b1010101011; + + parameter DATA_GB = 10'b0100110011; + parameter VID_B_GB = 10'b1011001100; + parameter VID_G_GB = 10'b0100110011; + parameter VID_R_GB = 10'b1011001100; + + wire [7:0] data; + assign data = (sdata[9]) ? ~sdata[7:0] : sdata[7:0]; + + always @ (posedge pclk) begin + if(iamrdy && other_ch0_rdy && other_ch1_rdy) begin + case (sdata) + CTRLTOKEN0: begin + c0 <=#1 1'b0; + c1 <=#1 1'b0; + de <=#1 1'b0; + dgb <= #1 1'b0; + vgb <= #1 1'b0; + ctl_vld <= #1 1'b1; + end + + CTRLTOKEN1: begin + c0 <=#1 1'b1; + c1 <=#1 1'b0; + de <=#1 1'b0; + dgb <= #1 1'b0; + vgb <= #1 1'b0; + ctl_vld <= #1 1'b1; + end + + CTRLTOKEN2: begin + c0 <=#1 1'b0; + c1 <=#1 1'b1; + de <=#1 1'b0; + dgb <= #1 1'b0; + vgb <= #1 1'b0; + ctl_vld <= #1 1'b1; + end + + CTRLTOKEN3: begin + c0 <=#1 1'b1; + c1 <=#1 1'b1; + de <=#1 1'b0; + dgb <= #1 1'b0; + vgb <= #1 1'b0; + ctl_vld <= #1 1'b1; + end + + DATA_GB: begin + c0 <=#1 1'b0; + c1 <=#1 1'b0; + de <=#1 1'b0; + dgb <= #1 1'b1; + vgb <= #1 1'b0; + ctl_vld <= #1 1'b0; + end + + VID_B_GB: begin + c0 <=#1 1'b0; + c1 <=#1 1'b0; + de <=#1 1'b0; + dgb <= #1 1'b0; + vgb <= #1 1'b1; + ctl_vld <= #1 1'b0; + end + + default: begin + dout[0] <=#1 data[0]; + dout[1] <=#1 (sdata[8]) ? (data[1] ^ data[0]) : (data[1] ~^ data[0]); + dout[2] <=#1 (sdata[8]) ? (data[2] ^ data[1]) : (data[2] ~^ data[1]); + dout[3] <=#1 (sdata[8]) ? (data[3] ^ data[2]) : (data[3] ~^ data[2]); + dout[4] <=#1 (sdata[8]) ? (data[4] ^ data[3]) : (data[4] ~^ data[3]); + dout[5] <=#1 (sdata[8]) ? (data[5] ^ data[4]) : (data[5] ~^ data[4]); + dout[6] <=#1 (sdata[8]) ? (data[6] ^ data[5]) : (data[6] ~^ data[5]); + dout[7] <=#1 (sdata[8]) ? (data[7] ^ data[6]) : (data[7] ~^ data[6]); + + de <=#1 1'b1; + + dgb <= #1 1'b0; + vgb <= #1 1'b0; + ctl_vld <= #1 1'b0; + end + endcase + + sdout <=#1 sdata; + end else begin + c0 <= 1'b0; + c1 <= 1'b0; + de <= 1'b0; + dout <= 8'h0; + sdout <= 10'h0; + + dgb <= 1'b0; + vgb <= 1'b0; + ctl_vld <= 1'b0; + end + end +endmodule diff --git a/rx/decodeg.v b/rx/decodeg.v new file mode 100755 index 0000000..6d86925 --- /dev/null +++ b/rx/decodeg.v @@ -0,0 +1,319 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Xilinx, Inc. 2010 www.xilinx.com +// +// XAPP xxx +// +////////////////////////////////////////////////////////////////////////////// +// +// File name : decoder.v +// +// Description : Spartan-6 dvi decoder +// +// +// Author : Bob Feng +// +// Disclaimer: LIMITED WARRANTY AND DISCLAMER. These designs are +// provided to you "as is". Xilinx and its licensors makeand you +// receive no warranties or conditions, express, implied, +// statutory or otherwise, and Xilinx specificallydisclaims any +// implied warranties of merchantability, non-infringement,or +// fitness for a particular purpose. Xilinx does notwarrant that +// the functions contained in these designs will meet your +// requirements, or that the operation of these designswill be +// uninterrupted or error free, or that defects in theDesigns +// will be corrected. Furthermore, Xilinx does not warrantor +// make any representations regarding use or the results ofthe +// use of the designs in terms of correctness, accuracy, +// reliability, or otherwise. +// +// LIMITATION OF LIABILITY. In no event will Xilinx or its +// licensors be liable for any loss of data, lost profits,cost +// or procurement of substitute goods or services, or forany +// special, incidental, consequential, or indirect damages +// arising from the use or operation of the designs or +// accompanying documentation, however caused and on anytheory +// of liability. This limitation will apply even if Xilinx +// has been advised of the possibility of such damage. This +// limitation shall apply not-withstanding the failure ofthe +// essential purpose of any limited remedies herein. +// +// Copyright © 2004 Xilinx, Inc. +// All rights reserved +// +////////////////////////////////////////////////////////////////////////////// +// Modifications copyright (c) 2011, Andrew "bunnie" Huang +// All rights reserved as permitted by law. +// +// 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 decodeg ( + input wire reset, // + input wire pclk, // pixel clock + input wire pclkx2, // double pixel rate for gear box + input wire pclkx10, // IOCLK + input wire serdesstrobe, // serdesstrobe for iserdes2 + input wire din_p, // data from dvi cable + input wire din_n, // data from dvi cable + input wire other_ch0_vld, // other channel0 has valid data now + input wire other_ch1_vld, // other channel1 has valid data now + input wire other_ch0_rdy, // other channel0 has detected a valid starting pixel + input wire other_ch1_rdy, // other channel1 has detected a valid starting pixel + + output wire iamvld, // I have valid data now + output wire iamrdy, // I have detected a valid new pixel + output wire psalgnerr, // Phase alignment error + output reg c0, + output reg c1, + output reg de, + output reg [9:0] sdout, + output reg [7:0] dout, + output reg dgb, + output reg vgb, + output reg ctl_vld, + output wire line_end); + + //////////////////////////////// + // + // 5-bit to 10-bit gear box + // + //////////////////////////////// + wire flipgear; + reg flipgearx2; + + always @ (posedge pclkx2) begin + flipgearx2 <=#1 flipgear; + end + + reg toggle = 1'b0; + + always @ (posedge pclkx2 or posedge reset) + if (reset == 1'b1) begin + toggle <= 1'b0 ; + end else begin + toggle <=#1 ~toggle; + end + + wire rx_toggle; + + assign rx_toggle = toggle ^ flipgearx2; //reverse hi-lo position + + wire [4:0] raw5bit; + reg [4:0] raw5bit_q; + reg [9:0] rawword; + + always @ (posedge pclkx2) begin + raw5bit_q <=#1 raw5bit; + + if(rx_toggle) //gear from 5 bit to 10 bit + rawword <=#1 {raw5bit, raw5bit_q}; + end + + //////////////////////////////// + // + // bitslip signal sync to pclkx2 + // + //////////////////////////////// + reg bitslipx2 = 1'b0; + reg bitslip_q = 1'b0; + wire bitslip; + + always @ (posedge pclkx2) begin + bitslip_q <=#1 bitslip; + bitslipx2 <=#1 bitslip & !bitslip_q; + end + + ///////////////////////////////////////////// + // + // 1:5 de-serializer working at x2 pclk rate + // + ///////////////////////////////////////////// + serdes_1_to_5_diff_data # ( + .DIFF_TERM("FALSE"), + .BITSLIP_ENABLE("TRUE") + ) des_0 ( + .use_phase_detector(1'b1), + .datain_p(din_p), + .datain_n(din_n), + .rxioclk(pclkx10), + .rxserdesstrobe(serdesstrobe), + .reset(reset), + .gclk(pclkx2), + .bitslip(bitslipx2), + .data_out(raw5bit) + ); + + ///////////////////////////////////////////////////// + // Doing word boundary detection here + ///////////////////////////////////////////////////// + wire [9:0] rawdata = rawword; + + /////////////////////////////////////// + // Phase Alignment Instance + /////////////////////////////////////// + phsaligner phsalgn_0 ( + .rst(reset), + .clk(pclk), + .sdata(rawdata), + .bitslip(bitslip), + .flipgear(flipgear), + .psaligned(iamvld) + ); + + assign psalgnerr = 1'b0; + + /////////////////////////////////////// + // Per Channel De-skew Instance + /////////////////////////////////////// + wire [9:0] sdata; + chnlbond cbnd ( + .clk(pclk), + .rawdata(rawdata), + .iamvld(iamvld), + .other_ch0_vld(other_ch0_vld), + .other_ch1_vld(other_ch1_vld), + .other_ch0_rdy(other_ch0_rdy), + .other_ch1_rdy(other_ch1_rdy), + .iamrdy(iamrdy), + .sdata(sdata) + ); + + //// + // hack to accelerate detection of line end so that HDCP rekey + // can meet stringent timing spec requirement + //// + assign line_end = de && + ((sdata == CTRLTOKEN0) || + (sdata == CTRLTOKEN1) || + (sdata == CTRLTOKEN2) || + (sdata == CTRLTOKEN3)); + + ///////////////////////////////////////////////////////////////// + // Below performs the 10B-8B decoding function defined in DVI 1.0 + // Specification: Section 3.3.3, Figure 3-6, page 31. + ///////////////////////////////////////////////////////////////// + // Distinct Control Tokens + parameter CTRLTOKEN0 = 10'b1101010100; + parameter CTRLTOKEN1 = 10'b0010101011; + parameter CTRLTOKEN2 = 10'b0101010100; + parameter CTRLTOKEN3 = 10'b1010101011; + + parameter DATA_GB = 10'b0100110011; + parameter VID_B_GB = 10'b1011001100; + parameter VID_G_GB = 10'b0100110011; + parameter VID_R_GB = 10'b1011001100; + + wire [7:0] data; + assign data = (sdata[9]) ? ~sdata[7:0] : sdata[7:0]; + + always @ (posedge pclk) begin + if(iamrdy && other_ch0_rdy && other_ch1_rdy) begin + case (sdata) + CTRLTOKEN0: begin + c0 <=#1 1'b0; + c1 <=#1 1'b0; + de <=#1 1'b0; + dgb <= #1 1'b0; + vgb <= #1 1'b0; + ctl_vld <= #1 1'b1; + end + + CTRLTOKEN1: begin + c0 <=#1 1'b1; + c1 <=#1 1'b0; + de <=#1 1'b0; + dgb <= #1 1'b0; + vgb <= #1 1'b0; + ctl_vld <= #1 1'b1; + end + + CTRLTOKEN2: begin + c0 <=#1 1'b0; + c1 <=#1 1'b1; + de <=#1 1'b0; + dgb <= #1 1'b0; + vgb <= #1 1'b0; + ctl_vld <= #1 1'b1; + end + + CTRLTOKEN3: begin + c0 <=#1 1'b1; + c1 <=#1 1'b1; + de <=#1 1'b0; + dgb <= #1 1'b0; + vgb <= #1 1'b0; + ctl_vld <= #1 1'b1; + end + + DATA_GB: begin + c0 <=#1 1'b0; + c1 <=#1 1'b0; + de <=#1 1'b0; + dgb <= #1 1'b1; // green is ambiguous between data and video guardbands + vgb <= #1 1'b1; + ctl_vld <= #1 1'b0; + end + + VID_G_GB: begin + c0 <=#1 1'b0; + c1 <=#1 1'b0; + de <=#1 1'b0; + dgb <= #1 1'b1; // green is ambiguous between data and video guardbands + vgb <= #1 1'b1; + ctl_vld <= #1 1'b0; + end + + default: begin + dout[0] <=#1 data[0]; + dout[1] <=#1 (sdata[8]) ? (data[1] ^ data[0]) : (data[1] ~^ data[0]); + dout[2] <=#1 (sdata[8]) ? (data[2] ^ data[1]) : (data[2] ~^ data[1]); + dout[3] <=#1 (sdata[8]) ? (data[3] ^ data[2]) : (data[3] ~^ data[2]); + dout[4] <=#1 (sdata[8]) ? (data[4] ^ data[3]) : (data[4] ~^ data[3]); + dout[5] <=#1 (sdata[8]) ? (data[5] ^ data[4]) : (data[5] ~^ data[4]); + dout[6] <=#1 (sdata[8]) ? (data[6] ^ data[5]) : (data[6] ~^ data[5]); + dout[7] <=#1 (sdata[8]) ? (data[7] ^ data[6]) : (data[7] ~^ data[6]); + + de <=#1 1'b1; + + dgb <= #1 1'b0; + vgb <= #1 1'b0; + ctl_vld <= #1 1'b0; + end + endcase + + sdout <=#1 sdata; + end else begin + c0 <= 1'b0; + c1 <= 1'b0; + de <= 1'b0; + dout <= 8'h0; + sdout <= 10'h0; + + dgb <= 1'b0; + vgb <= 1'b0; + ctl_vld <= 1'b0; + end + end +endmodule diff --git a/rx/decoder.v b/rx/decoder.v new file mode 100755 index 0000000..13210da --- /dev/null +++ b/rx/decoder.v @@ -0,0 +1,318 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Xilinx, Inc. 2010 www.xilinx.com +// +// XAPP xxx +// +////////////////////////////////////////////////////////////////////////////// +// +// File name : decoder.v +// +// Description : Spartan-6 dvi decoder +// +// +// Author : Bob Feng +// +// Disclaimer: LIMITED WARRANTY AND DISCLAMER. These designs are +// provided to you "as is". Xilinx and its licensors makeand you +// receive no warranties or conditions, express, implied, +// statutory or otherwise, and Xilinx specificallydisclaims any +// implied warranties of merchantability, non-infringement,or +// fitness for a particular purpose. Xilinx does notwarrant that +// the functions contained in these designs will meet your +// requirements, or that the operation of these designswill be +// uninterrupted or error free, or that defects in theDesigns +// will be corrected. Furthermore, Xilinx does not warrantor +// make any representations regarding use or the results ofthe +// use of the designs in terms of correctness, accuracy, +// reliability, or otherwise. +// +// LIMITATION OF LIABILITY. In no event will Xilinx or its +// licensors be liable for any loss of data, lost profits,cost +// or procurement of substitute goods or services, or forany +// special, incidental, consequential, or indirect damages +// arising from the use or operation of the designs or +// accompanying documentation, however caused and on anytheory +// of liability. This limitation will apply even if Xilinx +// has been advised of the possibility of such damage. This +// limitation shall apply not-withstanding the failure ofthe +// essential purpose of any limited remedies herein. +// +// Copyright © 2004 Xilinx, Inc. +// All rights reserved +// +////////////////////////////////////////////////////////////////////////////// +// Modifications copyright (c) 2011, Andrew "bunnie" Huang +// All rights reserved as permitted by law. +// +// 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 decoder ( + input wire reset, // + input wire pclk, // pixel clock + input wire pclkx2, // double pixel rate for gear box + input wire pclkx10, // IOCLK + input wire serdesstrobe, // serdesstrobe for iserdes2 + input wire din_p, // data from dvi cable + input wire din_n, // data from dvi cable + input wire other_ch0_vld, // other channel0 has valid data now + input wire other_ch1_vld, // other channel1 has valid data now + input wire other_ch0_rdy, // other channel0 has detected a valid starting pixel + input wire other_ch1_rdy, // other channel1 has detected a valid starting pixel + + output wire iamvld, // I have valid data now + output wire iamrdy, // I have detected a valid new pixel + output wire psalgnerr, // Phase alignment error + output reg c0, + output reg c1, + output reg de, + output reg [9:0] sdout, + output reg [7:0] dout, + output reg dgb, + output reg vgb, + output reg ctl_vld, + output wire line_end); + + //////////////////////////////// + // + // 5-bit to 10-bit gear box + // + //////////////////////////////// + wire flipgear; + reg flipgearx2; + + always @ (posedge pclkx2) begin + flipgearx2 <=#1 flipgear; + end + + reg toggle = 1'b0; + + always @ (posedge pclkx2 or posedge reset) + if (reset == 1'b1) begin + toggle <= 1'b0 ; + end else begin + toggle <=#1 ~toggle; + end + + wire rx_toggle; + + assign rx_toggle = toggle ^ flipgearx2; //reverse hi-lo position + + wire [4:0] raw5bit; + reg [4:0] raw5bit_q; + reg [9:0] rawword; + + always @ (posedge pclkx2) begin + raw5bit_q <=#1 raw5bit; + + if(rx_toggle) //gear from 5 bit to 10 bit + rawword <=#1 {raw5bit, raw5bit_q}; + end + + //////////////////////////////// + // + // bitslip signal sync to pclkx2 + // + //////////////////////////////// + reg bitslipx2 = 1'b0; + reg bitslip_q = 1'b0; + wire bitslip; + + always @ (posedge pclkx2) begin + bitslip_q <=#1 bitslip; + bitslipx2 <=#1 bitslip & !bitslip_q; + end + + ///////////////////////////////////////////// + // + // 1:5 de-serializer working at x2 pclk rate + // + ///////////////////////////////////////////// + serdes_1_to_5_diff_data # ( + .DIFF_TERM("FALSE"), + .BITSLIP_ENABLE("TRUE") + ) des_0 ( + .use_phase_detector(1'b1), + .datain_p(din_p), + .datain_n(din_n), + .rxioclk(pclkx10), + .rxserdesstrobe(serdesstrobe), + .reset(reset), + .gclk(pclkx2), + .bitslip(bitslipx2), + .data_out(raw5bit) + ); + + ///////////////////////////////////////////////////// + // Doing word boundary detection here + ///////////////////////////////////////////////////// + wire [9:0] rawdata = rawword; + + /////////////////////////////////////// + // Phase Alignment Instance + /////////////////////////////////////// + phsaligner phsalgn_0 ( + .rst(reset), + .clk(pclk), + .sdata(rawdata), + .bitslip(bitslip), + .flipgear(flipgear), + .psaligned(iamvld) + ); + + assign psalgnerr = 1'b0; + + /////////////////////////////////////// + // Per Channel De-skew Instance + /////////////////////////////////////// + wire [9:0] sdata; + chnlbond cbnd ( + .clk(pclk), + .rawdata(rawdata), + .iamvld(iamvld), + .other_ch0_vld(other_ch0_vld), + .other_ch1_vld(other_ch1_vld), + .other_ch0_rdy(other_ch0_rdy), + .other_ch1_rdy(other_ch1_rdy), + .iamrdy(iamrdy), + .sdata(sdata) + ); + + //// + // hack to accelerate detection of line end so that HDCP rekey + // can meet stringent timing spec requirement + //// + assign line_end = de && + ((sdata == CTRLTOKEN0) || + (sdata == CTRLTOKEN1) || + (sdata == CTRLTOKEN2) || + (sdata == CTRLTOKEN3)); + + ///////////////////////////////////////////////////////////////// + // Below performs the 10B-8B decoding function defined in DVI 1.0 + // Specification: Section 3.3.3, Figure 3-6, page 31. + ///////////////////////////////////////////////////////////////// + // Distinct Control Tokens + parameter CTRLTOKEN0 = 10'b1101010100; + parameter CTRLTOKEN1 = 10'b0010101011; + parameter CTRLTOKEN2 = 10'b0101010100; + parameter CTRLTOKEN3 = 10'b1010101011; + + parameter DATA_GB = 10'b0100110011; + parameter VID_B_GB = 10'b1011001100; + parameter VID_G_GB = 10'b0100110011; + parameter VID_R_GB = 10'b1011001100; + + wire [7:0] data; + assign data = (sdata[9]) ? ~sdata[7:0] : sdata[7:0]; + + always @ (posedge pclk) begin + if(iamrdy && other_ch0_rdy && other_ch1_rdy) begin + case (sdata) + CTRLTOKEN0: begin + c0 <=#1 1'b0; + c1 <=#1 1'b0; + de <=#1 1'b0; + dgb <= #1 1'b0; + vgb <= #1 1'b0; + ctl_vld <= #1 1'b1; + end + + CTRLTOKEN1: begin + c0 <=#1 1'b1; + c1 <=#1 1'b0; + de <=#1 1'b0; + dgb <= #1 1'b0; + vgb <= #1 1'b0; + ctl_vld <= #1 1'b1; + end + + CTRLTOKEN2: begin + c0 <=#1 1'b0; + c1 <=#1 1'b1; + de <=#1 1'b0; + dgb <= #1 1'b0; + vgb <= #1 1'b0; + ctl_vld <= #1 1'b1; + end + + CTRLTOKEN3: begin + c0 <=#1 1'b1; + c1 <=#1 1'b1; + de <=#1 1'b0; + dgb <= #1 1'b0; + vgb <= #1 1'b0; + ctl_vld <= #1 1'b1; + end + + DATA_GB: begin + c0 <=#1 1'b0; + c1 <=#1 1'b0; + de <=#1 1'b0; + dgb <= #1 1'b1; + vgb <= #1 1'b0; + ctl_vld <= #1 1'b0; + end + + VID_R_GB: begin + c0 <=#1 1'b0; + c1 <=#1 1'b0; + de <=#1 1'b0; + dgb <= #1 1'b0; + vgb <= #1 1'b1; + ctl_vld <= #1 1'b0; + end + + default: begin + dout[0] <=#1 data[0]; + dout[1] <=#1 (sdata[8]) ? (data[1] ^ data[0]) : (data[1] ~^ data[0]); + dout[2] <=#1 (sdata[8]) ? (data[2] ^ data[1]) : (data[2] ~^ data[1]); + dout[3] <=#1 (sdata[8]) ? (data[3] ^ data[2]) : (data[3] ~^ data[2]); + dout[4] <=#1 (sdata[8]) ? (data[4] ^ data[3]) : (data[4] ~^ data[3]); + dout[5] <=#1 (sdata[8]) ? (data[5] ^ data[4]) : (data[5] ~^ data[4]); + dout[6] <=#1 (sdata[8]) ? (data[6] ^ data[5]) : (data[6] ~^ data[5]); + dout[7] <=#1 (sdata[8]) ? (data[7] ^ data[6]) : (data[7] ~^ data[6]); + + de <=#1 1'b1; + + dgb <= #1 1'b0; + vgb <= #1 1'b0; + ctl_vld <= #1 1'b0; + end + endcase + + sdout <=#1 sdata; + end else begin + c0 <= 1'b0; + c1 <= 1'b0; + de <= 1'b0; + dout <= 8'h0; + sdout <= 10'h0; + + dgb <= 1'b0; + vgb <= 1'b0; + ctl_vld <= 1'b0; + end + end +endmodule diff --git a/rx/dvi_decoder.v b/rx/dvi_decoder.v new file mode 100755 index 0000000..1670c58 --- /dev/null +++ b/rx/dvi_decoder.v @@ -0,0 +1,634 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Xilinx, Inc. 2010 www.xilinx.com +// +// XAPPxxx +// +////////////////////////////////////////////////////////////////////////////// +// +// File name : dvi_decoder.v +// +// Description : Spartan-6 DVI decoder top module +// +// +// Author : Bob Feng +// +// Disclaimer: LIMITED WARRANTY AND DISCLAMER. These designs are +// provided to you "as is". Xilinx and its licensors makeand you +// receive no warranties or conditions, express, implied, +// statutory or otherwise, and Xilinx specificallydisclaims any +// implied warranties of merchantability, non-infringement,or +// fitness for a particular purpose. Xilinx does notwarrant that +// the functions contained in these designs will meet your +// requirements, or that the operation of these designswill be +// uninterrupted or error free, or that defects in theDesigns +// will be corrected. Furthermore, Xilinx does not warrantor +// make any representations regarding use or the results ofthe +// use of the designs in terms of correctness, accuracy, +// reliability, or otherwise. +// +// LIMITATION OF LIABILITY. In no event will Xilinx or its +// licensors be liable for any loss of data, lost profits,cost +// or procurement of substitute goods or services, or forany +// special, incidental, consequential, or indirect damages +// arising from the use or operation of the designs or +// accompanying documentation, however caused and on anytheory +// of liability. This limitation will apply even if Xilinx +// has been advised of the possibility of such damage. This +// limitation shall apply not-withstanding the failure ofthe +// essential purpose of any limited remedies herein. +// +// Copyright © 2004 Xilinx, Inc. +// All rights reserved +// +////////////////////////////////////////////////////////////////////////////// +// Modifications copyright (c) 2011, Andrew "bunnie" Huang +// All rights reserved as permitted by law. +// +// 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 + +// comment out the below line to turn off SS clocking +// turned off currently because this form causes hdmi locking to fail, suspect lack of +// phase lock to be the issue +//`define SS_CLOCKING 1 + +module dvi_decoder ( + input wire tmdsclk_p, // tmds clock + input wire tmdsclk_n, // tmds clock + input wire blue_p, // Blue data in + input wire green_p, // Green data in + input wire red_p, // Red data in + input wire blue_n, // Blue data in + input wire green_n, // Green data in + input wire red_n, // Red data in + input wire exrst, // external reset input, e.g. reset button + input wire pllreset, // just reset the PLL only (after LoL) + + output wire reset, // rx reset + output wire pclk, // regenerated pixel clock + output wire pclkx2, // double rate pixel clock + output wire pclkx10, // 10x pixel as IOCLK + output wire pllclk0, // send pllclk0 out so it can be fed into a different BUFPLL + output wire pllclk1, // PLL x1 output + output wire pllclk2, // PLL x2 output + + output wire pll_lckd, // send pll_lckd out so it can be fed into a different BUFPLL + output wire serdesstrobe, // BUFPLL serdesstrobe output + output wire tmdsclk, // TMDS cable clock + + output reg hsync, // hsync data + output reg vsync, // vsync data + output reg de, // data enable + output wire basic_de, // a DE that strobes even during data guardbands + + output wire blue_vld, + output wire green_vld, + output wire red_vld, + output wire blue_rdy, + output wire green_rdy, + output wire red_rdy, + + output wire psalgnerr, + + output wire [29:0] sdout, // note this comes 2 pixclk earlier than pix colors + output reg [7:0] red, // pixel data out + output reg [7:0] green, // pixel data out + output reg [7:0] blue, // pixel data out + + output reg encoding, // high when data island is valid + output wire hdcp_ena, // OR of data and video encyrption internal signals + output wire [3:0] red_di, // red data island + output wire [3:0] green_di, // green data island + output wire [3:0] blue_di, // blue data island + output reg data_gb, // guardbands + output reg video_gb, + output reg [3:0] ctl_code, // control code + output reg cv, + output wire line_end // fast-track signal that line ends for HDCP rekey + ) ; + + wire g_dgb, b_dgb, r_dgb; + wire g_vgb, b_vgb, r_vgb; + wire g_cv, b_cv, r_cv; + + wire [3:0] r_t4; + wire [3:0] b_t4; + wire [3:0] g_t4; + + wire [9:0] sdout_blue; + wire [9:0] sdout_green; + wire [9:0] sdout_red; + + wire [7:0] red_q1; + wire [7:0] blu_q1; + wire [7:0] grn_q1; + + reg [7:0] red_q2; + reg [7:0] blu_q2; + reg [7:0] grn_q2; + + reg de_q1, de_reg; + reg hsync_q1, vsync_q1; + wire de_q2; + wire hsync_q2, vsync_q2; + reg cv_q; + + reg data_gb_q, video_gb_q; + reg [3:0] ctl_code_q; + wire [3:0] ctl_code_wire; + +/* + assign sdout = {sdout_red[9], sdout_green[9], sdout_blue[9], sdout_red[8], sdout_green[8], sdout_blue[8], + sdout_red[7], sdout_green[7], sdout_blue[7], sdout_red[6], sdout_green[6], sdout_blue[6], + sdout_red[5], sdout_green[5], sdout_blue[5], sdout_red[4], sdout_green[4], sdout_blue[4], + sdout_red[3], sdout_green[3], sdout_blue[3], sdout_red[2], sdout_green[2], sdout_blue[2], + sdout_red[1], sdout_green[1], sdout_blue[1], sdout_red[0], sdout_green[0], sdout_blue[0]} ; +*/ + parameter INIT = 8'b1 << 0; + parameter GOING_T4 = 8'b1 << 1; + parameter TERC4 = 8'b1 << 2; + parameter LEAVE_T4 = 8'b1 << 3; + parameter GOING_VID = 8'b1 << 4; + parameter VIDEO = 8'b1 << 5; + parameter PREAM_T4 = 8'b1 << 6; + parameter PREAM_VID = 8'b1 << 7; + parameter nSTATES = 8; + + reg [(nSTATES-1):0] cstate = {{(nSTATES-1){1'b0}},1'b1}; + reg [(nSTATES-1):0] nstate; + + parameter ENC_TMDS = 1'b0; + parameter ENC_TERC4 = 1'b1; + parameter HDCP_OFF = 1'b0; + parameter HDCP_ON = 1'b1; + +// reg encoding; + reg encrypting_data; + reg encrypting_video; + + assign hdcp_ena = encrypting_data | encrypting_video; + + always @ (posedge pclk or posedge reset) begin + if (reset) + cstate <= INIT; + else + cstate <=#1 nstate; + end + + always @ (*) begin + case (cstate) //synthesis parallel_case full_case + //// NOTE NOTE NOTE + //// green channel uses same code for video and data gb + //// so we can't consider its information in this state machine + INIT: begin + if( b_vgb & r_vgb & g_vgb ) begin +// if( b_vgb | r_vgb | g_vgb ) begin + nstate = GOING_VID; + end else if (ctl_code_wire == 4'b0101) begin + // we've found a preamble for data + nstate = PREAM_T4; + end else if (ctl_code_wire == 4'b0001) begin + nstate = PREAM_VID; + end else begin + nstate = INIT; + end + end + PREAM_T4: begin + if( b_vgb & r_vgb & g_vgb ) begin +// if( b_vgb | r_vgb | g_vgb ) begin + nstate = GOING_VID; + end else if (r_dgb & g_dgb) begin + // data guardband only happens on b/r channels + nstate = GOING_T4; + end else if (ctl_code_wire == 4'b0101) begin + nstate = PREAM_T4; + end else begin + nstate = INIT; + end + end + GOING_T4: begin + // wait till both dgb signals drop + nstate = (r_dgb & g_dgb) ? GOING_T4 : TERC4; + end + TERC4: begin + if( b_cv | r_cv | g_cv ) begin + nstate = INIT; + end else if( b_vgb & r_vgb & g_vgb ) begin +// end else if( b_vgb | r_vgb | g_vgb ) begin + // if we see a video guardband and we think we're in terc4 encoding + // it means we missed the end of data guardband; simply re-initialize + // the machine so we always recover from bit error drops + nstate = GOING_VID; + end else if( r_dgb & g_dgb ) begin + // otherwise, gracefully leave + nstate = LEAVE_T4; + end else begin + nstate = TERC4; + end + end // case: TERC4 + LEAVE_T4: begin + // wait till both dgb signals drop + nstate = (r_dgb & g_dgb) ? LEAVE_T4 : INIT; + end + PREAM_VID: begin + if( ctl_code_wire == 4'b0001 ) begin + nstate = PREAM_VID; + end else if( b_vgb & r_vgb & g_vgb ) begin +// end else if( b_vgb | r_vgb | g_vgb ) begin + nstate = GOING_VID; + end else begin + nstate = INIT; + end + end + GOING_VID: begin + nstate = ( b_vgb & r_vgb & g_vgb ) ? GOING_VID : VIDEO; +// nstate = ( b_vgb | r_vgb | g_vgb ) ? GOING_VID : VIDEO; + end + VIDEO: begin + if( b_cv | r_cv | g_cv ) begin +// if( b_cv & r_cv & g_cv ) begin + nstate = INIT; + end else begin + nstate = VIDEO; + end + end + endcase // case (cstate) + end + + always @ (posedge pclk or posedge reset) begin + if( reset ) begin + encoding <=#1 ENC_TMDS; + encrypting_data <=#1 HDCP_OFF; + end else begin + case (cstate) // synthesis parallel_case full_case + INIT: begin + encoding <= #1 ENC_TMDS; + encrypting_data <= #1 HDCP_OFF; + encrypting_video <= #1 HDCP_OFF; + de <= #1 1'b0; + end + PREAM_T4: begin + encoding <= #1 ENC_TMDS; + encrypting_data <= #1 HDCP_OFF; + encrypting_video <= #1 HDCP_OFF; + de <= #1 1'b0; + end + GOING_T4: begin + encoding <= #1 ENC_TERC4; + encrypting_data <= #1 HDCP_OFF; + encrypting_video <= #1 HDCP_OFF; + de <= #1 1'b0; + end + TERC4: begin + encoding <= #1 ENC_TERC4; + encrypting_data <= #1 HDCP_ON; + encrypting_video <= #1 HDCP_OFF; + de <= #1 1'b0; + end + LEAVE_T4: begin + encoding <= #1 ENC_TERC4; + encrypting_video <= #1 HDCP_OFF; + encrypting_data <= #1 HDCP_OFF; + de <= #1 1'b0; + end + PREAM_VID: begin + encoding <= #1 ENC_TMDS; + encrypting_data <= #1 HDCP_OFF; + encrypting_video <= #1 HDCP_OFF; + de <= #1 1'b0; + end + GOING_VID: begin + encoding <= #1 ENC_TMDS; + encrypting_data <= #1 HDCP_OFF; + encrypting_video <= #1 HDCP_OFF; + de <= #1 1'b0; + end + VIDEO: begin + encoding <= #1 ENC_TMDS; + encrypting_data <= #1 HDCP_OFF; + encrypting_video <= #1 HDCP_ON; + de <= #1 1'b1; + end + endcase // case (cstate) + end // else: !if( reset ) + end // always @ (posedge pclk or posedge reset) + + + assign sdout = {sdout_red[9:5], sdout_green[9:5], sdout_blue[9:5], + sdout_red[4:0], sdout_green[4:0], sdout_blue[4:0]}; + + wire de_b, de_g, de_r; + + assign de_q2 = de_b; + // to do: modify this against guard bands -- if dgb activates, set a flag to set terc4 coding + // until dgb triggers again; include a force-clear if a vgb is encountered + + //wire blue_vld, green_vld, red_vld; + //wire blue_rdy, green_rdy, red_rdy; + + wire blue_psalgnerr, green_psalgnerr, red_psalgnerr; + + // + // Send TMDS clock to a differential buffer and then a BUFIO2 + // This is a required path in Spartan-6 feed a PLL CLKIN + // + wire rxclkint; + IBUFDS #(.IOSTANDARD("TMDS_33"), .DIFF_TERM("FALSE") + ) ibuf_rxclk (.I(tmdsclk_p), .IB(tmdsclk_n), .O(rxclkint)); + + wire rxclk; +`ifdef SS_CLOCKING + wire rxclk_pre_ss; // + + BUFIO2 #(.DIVIDE_BYPASS("TRUE"), .DIVIDE(1)) + bufio_tmdsclk (.DIVCLK(rxclk_pre_ss), .IOCLK(), .SERDESSTROBE(), .I(rxclkint)); + + BUFG tmdsclk_bufg (.I(rxclk), .O(tmdsclk)); + + // use spread spectrum clocking to reduce emissions... + DCM_CLKGEN #( + .DFS_OSCILLATOR_MODE("PHASE_FREQ_LOCK"), + .CLKIN_PERIOD ("13.48"), + .SPREAD_SPECTRUM ("CENTER_LOW_SPREAD"), + .CLKFX_MD_MAX("1.0"), + .CLKFX_MULTIPLY (4), + .CLKFX_DIVIDE (4) ) + dcm_fcc_spreads_me_wide ( + .CLKIN (rxclk_pre_ss), + .FREEZEDCM (1'b0), +// .PROGDATA (progdata_int), +// .PROGEN (progen_int), +// .PROGCLK (clk26bufpll), +// .PROGDONE (progdone), +// .CLKFX180 (CLKFX180_DCM), +// .CLKFXDV (CLKFXDV_DCM), + // .LOCKED (clkgen_locked), + .RST (rx0_reset), + .CLKFX (rxclk_ss) + ); + BUFG tmdsclk_bfgss (.I(rxclk_ss), .O(rxclk) ); // +`else // !`ifdef SS_CLOCKING + + BUFIO2 #(.DIVIDE_BYPASS("TRUE"), .DIVIDE(1)) + bufio_tmdsclk (.DIVCLK(rxclk), .IOCLK(), .SERDESSTROBE(), .I(rxclkint)); + + BUFG tmdsclk_bufg (.I(rxclk), .O(tmdsclk)); + +`endif // !`ifdef SS_CLOCKING + + + // + // PLL is used to generate three clocks: + // 1. pclk: same rate as TMDS clock + // 2. pclkx2: double rate of pclk used for 5:10 soft gear box and ISERDES DIVCLK + // 3. pclkx10: 10x rate of pclk used as IO clock + // + wire clkfbin; + wire clkfbout; + wire clkfbin_fb; + + PLL_BASE # ( +// .CLKIN_PERIOD(10.526315), // 95 MHz + .CLKIN_PERIOD(13.481449525), // 74.176 MHz + .CLKFBOUT_MULT(10), //set VCO to 10x of CLKIN + .CLKOUT0_DIVIDE(1), + .CLKOUT1_DIVIDE(10), + .CLKOUT2_DIVIDE(5), +// .COMPENSATION("INTERNAL") +// .BANDWIDTH("LOW"), // normally not here + .COMPENSATION("SOURCE_SYNCHRONOUS") + ) PLL_ISERDES ( + .CLKFBOUT(clkfbout), + .CLKOUT0(pllclk0), + .CLKOUT1(pllclk1), + .CLKOUT2(pllclk2), + .CLKOUT3(), + .CLKOUT4(), + .CLKOUT5(), + .LOCKED(pll_lckd), + .CLKFBIN(clkfbin_fb), + .CLKIN(rxclk), + .RST(exrst || pllreset) + ); + + // feedback to source-synchronize the clock + BUFG pclkfbk (.I(clkfbout), .O(clkfbin) ); + BUFIO2FB pclkfbk_fb (.I(clkfbin), .O(clkfbin_fb) ); + + // + // Pixel Rate clock buffer + // + BUFG pclkbufg (.I(pllclk1), .O(pclk)); + + ////////////////////////////////////////////////////////////////// + // 2x pclk is going to be used to drive IOSERDES2 DIVCLK + ////////////////////////////////////////////////////////////////// + BUFG pclkx2bufg (.I(pllclk2), .O(pclkx2)); + + ////////////////////////////////////////////////////////////////// + // 10x pclk is used to drive IOCLK network so a bit rate reference + // can be used by IOSERDES2 + ////////////////////////////////////////////////////////////////// + + wire bufpll_lock; + BUFPLL #(.DIVIDE(5)) ioclk_buf (.PLLIN(pllclk0), .GCLK(pclkx2), .LOCKED(pll_lckd), + .IOCLK(pclkx10), .SERDESSTROBE(serdesstrobe), .LOCK(bufpll_lock)); + + assign reset = ~bufpll_lock; + + wire line_end_r; + wire line_end_g; + wire line_end_b; + + // note instance-specific decode since each channel has its own specific + // guardband character. + decodeb dec_b ( + .reset (reset), + .pclk (pclk), + .pclkx2 (pclkx2), + .pclkx10 (pclkx10), + .serdesstrobe (serdesstrobe), + .din_p (blue_p), + .din_n (blue_n), + .other_ch0_rdy(green_rdy), + .other_ch1_rdy(red_rdy), + .other_ch0_vld(green_vld), + .other_ch1_vld(red_vld), + + .iamvld (blue_vld), + .iamrdy (blue_rdy), + .psalgnerr (blue_psalgnerr), + .c0 (hsync_q2), + .c1 (vsync_q2), + .de (de_b), + .sdout (sdout_blue), + .dout (blu_q1), + .dgb (b_dgb), + .vgb (b_vgb), + .ctl_vld (b_cv), + .line_end (line_end_b)) ; + + decodeg dec_g ( + .reset (reset), + .pclk (pclk), + .pclkx2 (pclkx2), + .pclkx10 (pclkx10), + .serdesstrobe (serdesstrobe), + .din_p (green_p), + .din_n (green_n), + .other_ch0_rdy(blue_rdy), + .other_ch1_rdy(red_rdy), + .other_ch0_vld(blue_vld), + .other_ch1_vld(red_vld), + + .iamvld (green_vld), + .iamrdy (green_rdy), + .psalgnerr (green_psalgnerr), + .c0 (ctl_code_wire[0]), + .c1 (ctl_code_wire[1]), + .de (de_g), + .sdout (sdout_green), + .dout (grn_q1), + .dgb (g_dgb), + .vgb (g_vgb), + .ctl_vld (g_cv), + .line_end (line_end_g)) ; + + decoder dec_r ( + .reset (reset), + .pclk (pclk), + .pclkx2 (pclkx2), + .pclkx10 (pclkx10), + .serdesstrobe (serdesstrobe), + .din_p (red_p), + .din_n (red_n), + .other_ch0_rdy(blue_rdy), + .other_ch1_rdy(green_rdy), + .other_ch0_vld(blue_vld), + .other_ch1_vld(green_vld), + + .iamvld (red_vld), + .iamrdy (red_rdy), + .psalgnerr (red_psalgnerr), + .c0 (ctl_code_wire[2]), + .c1 (ctl_code_wire[3]), + .de (de_r), + .sdout (sdout_red), + .dout (red_q1), + .dgb (r_dgb), + .vgb (r_vgb), + .ctl_vld (r_cv), + .line_end (line_end_r)) ; + + assign basic_de = de_b | de_r | de_g | b_vgb | r_vgb | g_vgb; + + assign line_end = line_end_g | line_end_r | line_end_b; + + assign psalgnerr = red_psalgnerr | blue_psalgnerr | green_psalgnerr; + + // pipe alignment registers + always @(posedge pclk or posedge reset) begin + if( reset ) begin + red <= 8'b0; + red_q2 <= 8'b0; + blue <= 8'b0; + blu_q2 <= 8'b0; + green <= 8'b0; + grn_q2 <= 8'b0; + + hsync <= 1'b0; + hsync_q1 <= 1'b0; + + vsync <= 1'b0; + vsync_q1 <= 1'b0; + + de_reg <= 1'b0; + de_q1 <= 1'b0; + + data_gb_q <= 1'b0; + data_gb <= 1'b0; + video_gb_q <= 1'b0; + video_gb <= 1'b0; + ctl_code_q <= 4'b0; + ctl_code <= 4'b0; + + cv_q <= 1'b0; + cv <= 1'b0; + end else begin + red_q2 <= red_q1; + red <= red_q2; + + blu_q2 <= blu_q1; + blue <= blu_q2; + + grn_q2 <= grn_q1; + green <= grn_q2; + + // reversed the naming convention for the following pipe stages + hsync <= hsync_q1; + hsync_q1 <= hsync_q2; + + vsync <= vsync_q1; + vsync_q1 <= vsync_q2; + + de_reg <= de_q1; + de_q1 <= de_q2; + + data_gb_q <= r_dgb & g_vgb; // data guardbands only on red and green channels + data_gb <= data_gb_q; + + video_gb_q <= r_vgb & b_vgb & g_vgb; + video_gb <= video_gb_q; + + ctl_code_q <= ctl_code_wire; + ctl_code <= ctl_code_q; + + cv_q <= b_cv & r_cv & g_cv; + cv <= cv_q; + end // else: !if( reset ) + end // always @ (posedge pclk or posedge reset) +// assign de = de_reg & (encoding == ENC_TMDS); + + decode_terc4 dec_t4_g ( + .rstin( reset ), + .clkin(pclk), + .din(sdout_green), + .dout(g_t4)); + + decode_terc4 dec_t4_r ( + .rstin( reset ), + .clkin(pclk), + .din(sdout_red), + .dout(r_t4)); + + decode_terc4 dec_t4_b ( + .rstin( reset ), + .clkin(pclk), + .din(sdout_blue), + .dout(b_t4)); + assign red_di = r_t4; + assign green_di = g_t4; + assign blue_di = b_t4; + +endmodule diff --git a/rx/phsaligner.v b/rx/phsaligner.v new file mode 100755 index 0000000..683ac50 --- /dev/null +++ b/rx/phsaligner.v @@ -0,0 +1,301 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Xilinx, Inc. 2010 www.xilinx.com +// +// XAPP xxx - TMDS serial stream phase aligner +// +////////////////////////////////////////////////////////////////////////////// +// +// File name : phasealigner.v +// +// Description : This module determines whether the Spartan-6 IOSERDES +// has validate the incoming TMDS data stream +// +// +// Note: +// +// Author : Bob Feng +// +// Disclaimer: LIMITED WARRANTY AND DISCLAMER. These designs are +// provided to you "as is". Xilinx and its licensors make and you +// receive no warranties or conditions, express, implied, +// statutory or otherwise, and Xilinx specifically disclaims any +// implied warranties of merchantability, non-infringement,or +// fitness for a particular purpose. Xilinx does not warrant that +// the functions contained in these designs will meet your +// requirements, or that the operation of these designs will be +// uninterrupted or error free, or that defects in the Designs +// will be corrected. Furthermore, Xilinx does not warrantor +// make any representations regarding use or the results of the +// use of the designs in terms of correctness, accuracy, +// reliability, or otherwise. +// +// LIMITATION OF LIABILITY. In no event will Xilinx or its +// licensors be liable for any loss of data, lost profits,cost +// or procurement of substitute goods or services, or for any +// special, incidental, consequential, or indirect damages +// arising from the use or operation of the designs or +// accompanying documentation, however caused and on any theory +// of liability. This limitation will apply even if Xilinx +// has been advised of the possibility of such damage. This +// limitation shall apply not-withstanding the failure of the +// essential purpose of any limited remedies herein. +// +// Copyright © 2006 Xilinx, Inc. +// All rights reserved +// +////////////////////////////////////////////////////////////////////////////// +// Modifications copyright (c) 2011, Andrew "bunnie" Huang +// All rights reserved as permitted by law. +// +// 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 +`define SIMULATION 1 + +// here's what this module does. +// 1. searches for an incoming control token +// 2. when it finds one, makes sure it sticks around at least CTKNCNTWD. +// 3. if it can't find one within SRCHTIMERWD, try a new bitslip and/or gearbox setting. +// keep on trying bitslips and gear settings over and over again ewvery SRCHTIMERWD +// until we've found a CTKNCNTWD. +// 4. once the token has stuck around for the pre-determined number of cycles, repeat +// the search to confirm it. (this function is degenerate in this implementation) +// 5. once setting has been confirmed, assume it never changes. + +module phsaligner # ( + parameter OPENEYE_CNT_WD = 3, // valid open eye counter width +// parameter CTKNCNTWD = 7, // Control Token Counter Width (dvi spec is 128) + parameter CTKNCNTWD = 3, // Control Token Counter Width (hdmi spec is 8) + parameter SRCHTIMERWD = 12 // Idle Timer Width +) +( + input wire rst, + input wire clk, + input wire [9:0] sdata, // 10 bit serial stream sync. to clk + output reg flipgear, + output reg bitslip, + output reg psaligned // FSM output +); + + parameter CTRLTOKEN0 = 10'b1101010100; + parameter CTRLTOKEN1 = 10'b0010101011; + parameter CTRLTOKEN2 = 10'b0101010100; + parameter CTRLTOKEN3 = 10'b1010101011; + + /////////////////////////////////////////////////////// + // Control Token Detection + /////////////////////////////////////////////////////// + reg rcvd_ctkn, rcvd_ctkn_q; + reg blnkbgn; //blank period begins + + always @ (posedge clk) begin + rcvd_ctkn <=#1 ((sdata == CTRLTOKEN0) || (sdata == CTRLTOKEN1) || + (sdata == CTRLTOKEN2) || (sdata == CTRLTOKEN3)); + + rcvd_ctkn_q <=#1 rcvd_ctkn; + blnkbgn <=#1 !rcvd_ctkn_q & rcvd_ctkn; + end + + ///////////////////////////////////////////////////// + // Control Token Search Timer + // + // DVI 1.0 Spec. says periodic blanking should start + // no less than every 50ms or 20HZ + // 2^24 of 74.25MHZ cycles is about 200ms + ///////////////////////////////////////////////////// + reg [(SRCHTIMERWD-1):0] ctkn_srh_timer; + reg ctkn_srh_rst; //FSM output + + always @ (posedge clk) begin + if (ctkn_srh_rst) + ctkn_srh_timer <=#1 {SRCHTIMERWD{1'b0}}; + else + ctkn_srh_timer <=#1 ctkn_srh_timer + 1'b1; + end + + reg ctkn_srh_tout; + always @ (posedge clk) begin + ctkn_srh_tout <=#1 (ctkn_srh_timer == {SRCHTIMERWD{1'b1}}); + end + + ///////////////////////////////////////////////////// + // Contorl Token Event Counter + // + // DVI 1.0 Spec. says the minimal blanking period + // is at least 128 pixels long in order to achieve + // synchronization + // + // HDMI reduces this to as little as 8 + ///////////////////////////////////////////////////// + reg [(CTKNCNTWD-1):0] ctkn_counter; + reg ctkn_cnt_rst; //FSM output + + always @ (posedge clk) begin + if(ctkn_cnt_rst) + ctkn_counter <=#1 {CTKNCNTWD{1'b0}}; + else + ctkn_counter <=#1 ctkn_counter + 1'b1; + end + + reg ctkn_cnt_tout; + always @ (posedge clk) begin + ctkn_cnt_tout <=#1 (ctkn_counter == {CTKNCNTWD{1'b1}}); + end + + ////////////////////////////////////////////////////////// + // Below starts the phase alignment state machine + ////////////////////////////////////////////////////////// + parameter INIT = 6'b1 << 0; + parameter SEARCH = 6'b1 << 1; // Searching for control tokens + parameter BITSLIP = 6'b1 << 2; + parameter RCVDCTKN = 6'b1 << 3; // Received at one Control Token and check for more + parameter BLNKPRD = 6'b1 << 4; + parameter PSALGND = 6'b1 << 5; // Phase alignment achieved + parameter nSTATES = 6; + + reg [(nSTATES-1):0] cstate = {{(nSTATES-1){1'b0}}, 1'b1}; //current and next states + reg [(nSTATES-1):0] nstate; + +`ifdef SIMULATION + // synthesis translate_off + reg [8*20:1] state_ascii = "INIT "; + always @(cstate) begin + if (cstate == INIT ) state_ascii <= "INIT "; + else if (cstate == SEARCH ) state_ascii <= "SEARCH "; + else if (cstate == BITSLIP ) state_ascii <= "BITSLIP "; + else if (cstate == RCVDCTKN ) state_ascii <= "RCVDCTKN "; + else if (cstate == BLNKPRD ) state_ascii <= "BLNKPRD "; + else state_ascii <= "PSALGND "; + end + // synthesis translate_on +`endif + + always @ (posedge clk or posedge rst) begin + if (rst) + cstate <= INIT; + else + cstate <=#1 nstate; + end + + ////////////////////////////////////////////////////////// + // Counter counts number of blank period detected + // in order to qualify the bitslip position + ////////////////////////////////////////////////////////// + parameter BLNKPRD_CNT_WD = 1; + + reg [(BLNKPRD_CNT_WD-1):0] blnkprd_cnt = {BLNKPRD_CNT_WD{1'b0}}; + + always @ (*) begin + case (cstate) //synthesis parallel_case full_case + INIT: begin + nstate = (ctkn_srh_tout) ? SEARCH : INIT; + end + + SEARCH: begin + if(blnkbgn) + nstate = RCVDCTKN; + else + nstate = (ctkn_srh_tout) ? BITSLIP : SEARCH; + end + + BITSLIP: begin + nstate = SEARCH; + end + + RCVDCTKN: begin + if(rcvd_ctkn) + nstate = (ctkn_cnt_tout) ? BLNKPRD : RCVDCTKN; + else + nstate = SEARCH; + end + + BLNKPRD: begin + nstate = (blnkprd_cnt == {BLNKPRD_CNT_WD{1'b1}}) ? PSALGND : SEARCH; + end + + PSALGND: begin + nstate = PSALGND; // Phase aligned so hang around here + end + endcase + end + + reg [2:0] bitslip_cnt; + + always @ (posedge clk or posedge rst) begin + if(rst) begin + psaligned <=#1 1'b0; //phase alignment success flag + bitslip <=#1 1'b0; + ctkn_srh_rst <=#1 1'b1; //control token search timer reset + ctkn_cnt_rst <=#1 1'b1; //control token counter reset + + bitslip <=#1 1'b0; + bitslip_cnt <=#1 3'h0; + flipgear <=#1 1'b0; + blnkprd_cnt <=#1 {BLNKPRD_CNT_WD{1'b0}}; + end else begin + case (cstate) // synthesis parallel_case full_case + INIT: begin + ctkn_srh_rst <=#1 1'b0; + ctkn_cnt_rst <=#1 1'b1; + bitslip <=#1 1'b0; + psaligned <=#1 1'b0; + + bitslip <=#1 1'b0; + bitslip_cnt <=#1 3'h0; + flipgear <=#1 1'b0; + blnkprd_cnt <=#1 {BLNKPRD_CNT_WD{1'b0}}; + end + + SEARCH: begin + ctkn_srh_rst <=#1 1'b0; + ctkn_cnt_rst <=#1 1'b1; + bitslip <=#1 1'b0; + psaligned <=#1 1'b0; + end + + BITSLIP: begin + ctkn_srh_rst <=#1 1'b1; + + bitslip <=#1 1'b1; + bitslip_cnt <=#1 bitslip_cnt + 1'b1; + flipgear <=#1 bitslip_cnt[2]; //bitslip has toggled for 4 times + end + + RCVDCTKN: begin + ctkn_srh_rst <=#1 1'b0; + ctkn_cnt_rst <=#1 1'b0; + end + + BLNKPRD: begin + blnkprd_cnt <=#1 blnkprd_cnt + 1'b1; + end + + PSALGND: begin + psaligned <=#1 1'b1; + end + endcase + end + end + +endmodule diff --git a/rx/serdes_1_to_5_diff_data.v b/rx/serdes_1_to_5_diff_data.v new file mode 100755 index 0000000..2465afa --- /dev/null +++ b/rx/serdes_1_to_5_diff_data.v @@ -0,0 +1,405 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Xilinx, Inc. 2010 www.xilinx.com +// +// XAPP xxx - 1:5 Differential Data De-serializer +// +////////////////////////////////////////////////////////////////////////////// +// +// File name : serdes_1_to_5_diff_data.v +// +// Description : This module instantiates IODELAY2 and ISERDES2 primitives +// to receive TMDS differential data in 1:5 format +// +// Note: +// +// Author : Bob Feng +////////////////////////////////////////////////////////////////////////////// +// +// Disclaimer: +// +// This disclaimer is not a license and does not grant any rights to the materials +// distributed herewith. Except as otherwise provided in a valid license issued to you +// by Xilinx, and to the maximum extent permitted by applicable law: +// (1) THESE MATERIALS ARE MADE AVAILABLE "AS IS" AND WITH ALL FAULTS, +// AND XILINX HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, EXPRESS, IMPLIED, OR STATUTORY, +// INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT, OR +// FITNESS FOR ANY PARTICULAR PURPOSE; and (2) Xilinx shall not be liable (whether in contract +// or tort, including negligence, or under any other theory of liability) for any loss or damage +// of any kind or nature related to, arising under or in connection with these materials, +// including for any direct, or any indirect, special, incidental, or consequential loss +// or damage (including loss of data, profits, goodwill, or any type of loss or damage suffered +// as a result of any action brought by a third party) even if such damage or loss was +// reasonably foreseeable or Xilinx had been advised of the possibility of the same. +// +// Critical Applications: +// +// Xilinx products are not designed or intended to be fail-safe, or for use in any application +// requiring fail-safe performance, such as life-support or safety devices or systems, +// Class III medical devices, nuclear facilities, applications related to the deployment of airbags, +// or any other applications that could lead to death, personal injury, or severe property or +// environmental damage (individually and collectively, "Critical Applications"). Customer assumes +// the sole risk and liability of any use of Xilinx products in Critical Applications, subject only +// to applicable laws and regulations governing limitations on product liability. +// +// THIS COPYRIGHT NOTICE AND DISCLAIMER MUST BE RETAINED AS PART OF THIS FILE AT ALL TIMES. +// +////////////////////////////////////////////////////////////////////////////// + +`timescale 1ps/1ps + +module serdes_1_to_5_diff_data # ( + parameter DIFF_TERM = "TRUE", + parameter SIM_TAP_DELAY = 49, + parameter BITSLIP_ENABLE = "FALSE" +)( + input wire use_phase_detector, // '1' enables the phase detector logic + input wire datain_p, // Input from LVDS receiver pin + input wire datain_n, // Input from LVDS receiver pin + input wire rxioclk, // IO Clock network + input wire rxserdesstrobe, // Parallel data capture strobe + input wire reset, // Reset line + input wire gclk, // Global clock + input wire bitslip, // Bitslip control line + output wire [4:0] data_out // Output data +); + + wire ddly_m; + wire ddly_s; + wire busys; + wire rx_data_in; + wire cascade; + wire pd_edge; + reg [8:0] counter; + reg [3:0] state; + reg cal_data_sint; + wire busy_data; + reg busy_data_d; + wire cal_data_slave; + reg enable; + reg cal_data_master; + reg rst_data; + reg inc_data_int; + wire inc_data; + reg ce_data; + reg valid_data_d; + reg incdec_data_d; + reg [4:0] pdcounter; + wire valid_data; + wire incdec_data; + reg flag; + reg mux; + reg ce_data_inta ; + wire [1:0] incdec_data_or; + wire incdec_data_im; + wire [1:0] valid_data_or; + wire valid_data_im; + wire [1:0] busy_data_or; + wire all_ce; + + wire [1:0] debug_in = 2'b00; + + assign busy_data = busys ; + + assign cal_data_slave = cal_data_sint ; + + ///////////////////////////////////////////////// + // + // IDELAY Calibration FSM + // + ///////////////////////////////////////////////// + always @ (posedge gclk or posedge reset) + begin + if (reset == 1'b1) begin + state <= 0 ; + cal_data_master <= 1'b0 ; + cal_data_sint <= 1'b0 ; + counter <= 9'h000 ; + enable <= 1'b0 ; + mux <= 1'h1 ; + end + else begin + counter <= counter + 9'h001 ; + if (counter[8] == 1'b1) begin + counter <= 9'h000 ; + end + if (counter[5] == 1'b1) begin + enable <= 1'b1 ; + end + if (state == 0 && enable == 1'b1) begin // Wait for IODELAY to be available + cal_data_master <= 1'b0 ; + cal_data_sint <= 1'b0 ; + rst_data <= 1'b0 ; + if (busy_data_d == 1'b0) begin + state <= 1 ; + end + end + else if (state == 1) begin // Issue calibrate command to both master and slave, needed for simulation, not for the silicon + cal_data_master <= 1'b1 ; + cal_data_sint <= 1'b1 ; + if (busy_data_d == 1'b1) begin // and wait for command to be accepted + state <= 2 ; + end + end + else if (state == 2) begin // Now RST master and slave IODELAYs needed for simulation, not for the silicon + cal_data_master <= 1'b0 ; + cal_data_sint <= 1'b0 ; + if (busy_data_d == 1'b0) begin + rst_data <= 1'b1 ; + state <= 3 ; + end + end + else if (state == 3) begin // Wait for IODELAY to be available + rst_data <= 1'b0 ; + if (busy_data_d == 1'b0) begin + state <= 4 ; + end + end + else if (state == 4) begin // Wait for occasional enable + if (counter[8] == 1'b1) begin + state <= 5 ; + end + end + else if (state == 5) begin // Calibrate slave only + if (busy_data_d == 1'b0) begin + cal_data_sint <= 1'b1 ; + state <= 6 ; + end + end + else if (state == 6) begin // Wait for command to be accepted + cal_data_sint <= 1'b0 ; + if (busy_data_d == 1'b1) begin + state <= 7 ; + end + end + else if (state == 7) begin // Wait for all IODELAYs to be available, ie CAL command finished + cal_data_sint <= 1'b0 ; + if (busy_data_d == 1'b0) begin + state <= 4 ; + end + end + end + end + +always @ (posedge gclk or posedge reset) // Per-bit phase detection state machine +begin +if (reset == 1'b1) begin + pdcounter <= 5'b1000 ; + ce_data_inta <= 1'b0 ; + flag <= 1'b0 ; // flag is there to only allow one inc or dec per cal (test) +end +else begin + busy_data_d <= busy_data_or[1] ; + if (use_phase_detector == 1'b1) begin // decide whther pd is used + incdec_data_d <= incdec_data_or[1] ; + valid_data_d <= valid_data_or[1] ; + if (ce_data_inta == 1'b1) begin + ce_data = mux ; + end + else begin + ce_data = 64'h0000000000000000 ; + end + if (state == 7) begin + flag <= 1'b0 ; + end + else if (state != 4 || busy_data_d == 1'b1) begin // Reset filter if state machine issues a cal command or unit is busy + pdcounter <= 5'b10000 ; + ce_data_inta <= 1'b0 ; + end + else if (pdcounter == 5'b11111 && flag == 1'b0) begin // Filter has reached positive max - increment the tap count + ce_data_inta <= 1'b1 ; + inc_data_int <= 1'b1 ; + pdcounter <= 5'b10000 ; + flag <= 1'b1 ; + end + else if (pdcounter == 5'b00000 && flag == 1'b0) begin // Filter has reached negative max - decrement the tap count + ce_data_inta <= 1'b1 ; + inc_data_int <= 1'b0 ; + pdcounter <= 5'b10000 ; + flag <= 1'b1 ; + end + else if (valid_data_d == 1'b1) begin // increment filter + ce_data_inta <= 1'b0 ; + if (incdec_data_d == 1'b1 && pdcounter != 5'b11111) begin + pdcounter <= pdcounter + 5'b00001 ; + end + else if (incdec_data_d == 1'b0 && pdcounter != 5'b00000) begin // decrement filter + pdcounter <= pdcounter + 5'b11111 ; + end + end + else begin + ce_data_inta <= 1'b0 ; + end + end + else begin + ce_data = all_ce ; + inc_data_int = debug_in[1] ; + end +end +end + +assign inc_data = inc_data_int ; + +assign incdec_data_or[0] = 1'b0 ; // Input Mux - Initialise generate loop OR gates +assign valid_data_or[0] = 1'b0 ; +assign busy_data_or[0] = 1'b0 ; + +assign incdec_data_im = incdec_data & mux; // Input muxes +assign incdec_data_or[1] = incdec_data_im | incdec_data_or; // AND gates to allow just one signal through at a tome +assign valid_data_im = valid_data & mux; // followed by an OR +assign valid_data_or[1] = valid_data_im | valid_data_or; // for the three inputs from each PD +assign busy_data_or[1] = busy_data | busy_data_or; // The busy signals just need an OR gate + +assign all_ce = debug_in[0] ; + +IBUFDS #( + .DIFF_TERM (DIFF_TERM)) +data_in ( + .I (datain_p), + .IB (datain_n), + .O (rx_data_in) +); + +// +// Master IDELAY +// +IODELAY2 #( + .DATA_RATE ("SDR"), + .IDELAY_VALUE (0), + .IDELAY2_VALUE (0), + .IDELAY_MODE ("NORMAL" ), + .ODELAY_VALUE (0), + .IDELAY_TYPE ("DIFF_PHASE_DETECTOR"), + .COUNTER_WRAPAROUND ("STAY_AT_LIMIT"), //("WRAPAROUND"), // this setting is for implementation +// .COUNTER_WRAPAROUND ("WRAPAROUND"), // this setting is for simulation + .DELAY_SRC ("IDATAIN"), + .SERDES_MODE ("MASTER"), + .SIM_TAPDELAY_VALUE (SIM_TAP_DELAY) +) iodelay_m ( + .IDATAIN (rx_data_in), // data from IBUFDS + .TOUT (), // tri-state signal to IOB + .DOUT (), // output data to IOB + .T (1'b1), // tri-state control from OLOGIC/OSERDES2 + .ODATAIN (1'b0), // data from OLOGIC/OSERDES2 + .DATAOUT (ddly_m), // Output data 1 to ILOGIC/ISERDES2 + .DATAOUT2 (), // Output data 2 to ILOGIC/ISERDES2 + .IOCLK0 (rxioclk), // High speed clock for calibration + .IOCLK1 (1'b0), // High speed clock for calibration + .CLK (gclk), // Fabric clock (GCLK) for control signals + .CAL (cal_data_master), // Calibrate control signal + .INC (inc_data), // Increment counter + .CE (ce_data), // Clock Enable + .RST (rst_data), // Reset delay line + .BUSY () // output signal indicating sync circuit has finished / calibration has finished +); + +// +// Slave IDELAY +// +IODELAY2 #( + .DATA_RATE ("SDR"), + .IDELAY_VALUE (0), + .IDELAY2_VALUE (0), + .IDELAY_MODE ("NORMAL" ), + .ODELAY_VALUE (0), + .IDELAY_TYPE ("DIFF_PHASE_DETECTOR"), + .COUNTER_WRAPAROUND ("STAY_AT_LIMIT"), // this lacked the note about impl/sim +// .COUNTER_WRAPAROUND ("WRAPAROUND"), + .DELAY_SRC ("IDATAIN"), + .SERDES_MODE ("SLAVE"), + .SIM_TAPDELAY_VALUE (SIM_TAP_DELAY) +) iodelay_s ( + .IDATAIN (rx_data_in), // data from IBUFDS + .TOUT (), // tri-state signal to IOB + .DOUT (), // output data to IOB + .T (1'b1), // tri-state control from OLOGIC/OSERDES2 + .ODATAIN (1'b0), // data from OLOGIC/OSERDES2 + .DATAOUT (ddly_s), // Slave output data to ILOGIC/ISERDES2 + .DATAOUT2 (), // + .IOCLK0 (rxioclk), // High speed IO clock for calibration + .IOCLK1 (1'b0), + .CLK (gclk), // Fabric clock (GCLK) for control signals + .CAL (cal_data_slave), // Calibrate control signal + .INC (inc_data), // Increment counter + .CE (ce_data), // Clock Enable + .RST (rst_data), // Reset delay line + .BUSY (busys) // output signal indicating sync circuit has finished / calibration has finished +); + +// +// Master ISERDES +// + +ISERDES2 #( + .DATA_WIDTH (5), + .DATA_RATE ("SDR"), + .BITSLIP_ENABLE (BITSLIP_ENABLE), + .SERDES_MODE ("MASTER"), + .INTERFACE_TYPE ("RETIMED")) +iserdes_m ( + .D (ddly_m), + .CE0 (1'b1), + .CLK0 (rxioclk), + .CLK1 (1'b0), + .IOCE (rxserdesstrobe), + .RST (reset), + .CLKDIV (gclk), + .SHIFTIN (pd_edge), + .BITSLIP (bitslip), + .FABRICOUT (), + .Q4 (data_out[4]), + .Q3 (data_out[3]), + .Q2 (data_out[2]), + .Q1 (data_out[1]), + .DFB (), + .CFB0 (), + .CFB1 (), + .VALID (valid_data), + .INCDEC (incdec_data), + .SHIFTOUT (cascade)); + +// +// Slave ISERDES +// + +ISERDES2 #( + .DATA_WIDTH (5), + .DATA_RATE ("SDR"), + .BITSLIP_ENABLE (BITSLIP_ENABLE), + .SERDES_MODE ("SLAVE"), + .INTERFACE_TYPE ("RETIMED") +) iserdes_s ( + .D (ddly_s), + .CE0 (1'b1), + .CLK0 (rxioclk), + .CLK1 (1'b0), + .IOCE (rxserdesstrobe), + .RST (reset), + .CLKDIV (gclk), + .SHIFTIN (cascade), + .BITSLIP (bitslip), + .FABRICOUT (), + .Q4 (data_out[0]), + .Q3 (), + .Q2 (), + .Q1 (), + .DFB (), + .CFB0 (), + .CFB1 (), + .VALID (), + .INCDEC (), + .SHIFTOUT (pd_edge)); + + +reg [7:0] rxpdcntr = 8'h7f; +always @ (posedge gclk or posedge reset) begin + if (reset) + rxpdcntr <= 8'h7f; + else if (ce_data) + if (inc_data) + rxpdcntr <= rxpdcntr + 1'b1; + else + rxpdcntr <= rxpdcntr - 1'b1; +end + +endmodule diff --git a/tx/convert_30to15_fifo.v b/tx/convert_30to15_fifo.v new file mode 100755 index 0000000..8ad8727 --- /dev/null +++ b/tx/convert_30to15_fifo.v @@ -0,0 +1,168 @@ +module convert_30to15_fifo( + input wire rst, // reset + input wire clk, // clock input + input wire clkx2, // 2x clock input + input wire [29:0] datain, // input data for 2:1 serialisation + output wire [14:0] dataout); // 5-bit data out + + //////////////////////////////////////////////////// + // Here we instantiate a 16x10 Dual Port RAM + // and fill first it with data aligned to + // clk domain + //////////////////////////////////////////////////// + wire [3:0] wa; // RAM read address + reg [3:0] wa_d; // RAM read address + wire [3:0] ra; // RAM read address + reg [3:0] ra_d; // RAM read address + wire [29:0] dataint; // RAM output + + parameter ADDR0 = 4'b0000; + parameter ADDR1 = 4'b0001; + parameter ADDR2 = 4'b0010; + parameter ADDR3 = 4'b0011; + parameter ADDR4 = 4'b0100; + parameter ADDR5 = 4'b0101; + parameter ADDR6 = 4'b0110; + parameter ADDR7 = 4'b0111; + parameter ADDR8 = 4'b1000; + parameter ADDR9 = 4'b1001; + parameter ADDR10 = 4'b1010; + parameter ADDR11 = 4'b1011; + parameter ADDR12 = 4'b1100; + parameter ADDR13 = 4'b1101; + parameter ADDR14 = 4'b1110; + parameter ADDR15 = 4'b1111; + + always@(wa) begin + case (wa) + ADDR0 : wa_d = ADDR1 ; + ADDR1 : wa_d = ADDR2 ; + ADDR2 : wa_d = ADDR3 ; + ADDR3 : wa_d = ADDR4 ; + ADDR4 : wa_d = ADDR5 ; + ADDR5 : wa_d = ADDR6 ; + ADDR6 : wa_d = ADDR7 ; + ADDR7 : wa_d = ADDR8 ; + ADDR8 : wa_d = ADDR9 ; + ADDR9 : wa_d = ADDR10; + ADDR10 : wa_d = ADDR11; + ADDR11 : wa_d = ADDR12; + ADDR12 : wa_d = ADDR13; + ADDR13 : wa_d = ADDR14; + ADDR14 : wa_d = ADDR15; + default : wa_d = ADDR0; + endcase + end + + FDC fdc_wa0 (.C(clk), .D(wa_d[0]), .CLR(rst), .Q(wa[0])); + FDC fdc_wa1 (.C(clk), .D(wa_d[1]), .CLR(rst), .Q(wa[1])); + FDC fdc_wa2 (.C(clk), .D(wa_d[2]), .CLR(rst), .Q(wa[2])); + FDC fdc_wa3 (.C(clk), .D(wa_d[3]), .CLR(rst), .Q(wa[3])); + + //Dual Port fifo to bridge data from clk to clkx2 + DRAM16XN #(.data_width(30)) + fifo_u ( + .DATA_IN(datain), + .ADDRESS(wa), + .ADDRESS_DP(ra), + .WRITE_EN(1'b1), + .CLK(clk), + .O_DATA_OUT(), + .O_DATA_OUT_DP(dataint)); + + ///////////////////////////////////////////////////////////////// + // Here starts clk2x domain for fifo read out + // FIFO read is set to be once every 2 cycles of clk2x in order + // to keep up pace with the fifo write speed + // Also FIFO read reset is delayed a bit in order to avoid + // underflow. + ///////////////////////////////////////////////////////////////// + + always@(ra) begin + case (ra) + ADDR0 : ra_d = ADDR1 ; + ADDR1 : ra_d = ADDR2 ; + ADDR2 : ra_d = ADDR3 ; + ADDR3 : ra_d = ADDR4 ; + ADDR4 : ra_d = ADDR5 ; + ADDR5 : ra_d = ADDR6 ; + ADDR6 : ra_d = ADDR7 ; + ADDR7 : ra_d = ADDR8 ; + ADDR8 : ra_d = ADDR9 ; + ADDR9 : ra_d = ADDR10; + ADDR10 : ra_d = ADDR11; + ADDR11 : ra_d = ADDR12; + ADDR12 : ra_d = ADDR13; + ADDR13 : ra_d = ADDR14; + ADDR14 : ra_d = ADDR15; + default : ra_d = ADDR0; + endcase + end + + wire rstsync, rstsync_q, rstp; + (* ASYNC_REG = "TRUE" *) FDP fdp_rst (.C(clkx2), .D(rst), .PRE(rst), .Q(rstsync)); + + FD fd_rstsync (.C(clkx2), .D(rstsync), .Q(rstsync_q)); + FD fd_rstp (.C(clkx2), .D(rstsync_q), .Q(rstp)); + + wire sync; + FDR sync_gen (.Q (sync), .C (clkx2), .R(rstp), .D(~sync)); + + FDRE fdc_ra0 (.C(clkx2), .D(ra_d[0]), .R(rstp), .CE(sync), .Q(ra[0])); + FDRE fdc_ra1 (.C(clkx2), .D(ra_d[1]), .R(rstp), .CE(sync), .Q(ra[1])); + FDRE fdc_ra2 (.C(clkx2), .D(ra_d[2]), .R(rstp), .CE(sync), .Q(ra[2])); + FDRE fdc_ra3 (.C(clkx2), .D(ra_d[3]), .R(rstp), .CE(sync), .Q(ra[3])); + + wire [29:0] db; + + FDE fd_db0 (.C(clkx2), .D(dataint[0]), .CE(sync), .Q(db[0])); + FDE fd_db1 (.C(clkx2), .D(dataint[1]), .CE(sync), .Q(db[1])); + FDE fd_db2 (.C(clkx2), .D(dataint[2]), .CE(sync), .Q(db[2])); + FDE fd_db3 (.C(clkx2), .D(dataint[3]), .CE(sync), .Q(db[3])); + FDE fd_db4 (.C(clkx2), .D(dataint[4]), .CE(sync), .Q(db[4])); + FDE fd_db5 (.C(clkx2), .D(dataint[5]), .CE(sync), .Q(db[5])); + FDE fd_db6 (.C(clkx2), .D(dataint[6]), .CE(sync), .Q(db[6])); + FDE fd_db7 (.C(clkx2), .D(dataint[7]), .CE(sync), .Q(db[7])); + FDE fd_db8 (.C(clkx2), .D(dataint[8]), .CE(sync), .Q(db[8])); + FDE fd_db9 (.C(clkx2), .D(dataint[9]), .CE(sync), .Q(db[9])); + FDE fd_db10 (.C(clkx2), .D(dataint[10]), .CE(sync), .Q(db[10])); + FDE fd_db11 (.C(clkx2), .D(dataint[11]), .CE(sync), .Q(db[11])); + FDE fd_db12 (.C(clkx2), .D(dataint[12]), .CE(sync), .Q(db[12])); + FDE fd_db13 (.C(clkx2), .D(dataint[13]), .CE(sync), .Q(db[13])); + FDE fd_db14 (.C(clkx2), .D(dataint[14]), .CE(sync), .Q(db[14])); + FDE fd_db15 (.C(clkx2), .D(dataint[15]), .CE(sync), .Q(db[15])); + FDE fd_db16 (.C(clkx2), .D(dataint[16]), .CE(sync), .Q(db[16])); + FDE fd_db17 (.C(clkx2), .D(dataint[17]), .CE(sync), .Q(db[17])); + FDE fd_db18 (.C(clkx2), .D(dataint[18]), .CE(sync), .Q(db[18])); + FDE fd_db19 (.C(clkx2), .D(dataint[19]), .CE(sync), .Q(db[19])); + FDE fd_db20 (.C(clkx2), .D(dataint[20]), .CE(sync), .Q(db[20])); + FDE fd_db21 (.C(clkx2), .D(dataint[21]), .CE(sync), .Q(db[21])); + FDE fd_db22 (.C(clkx2), .D(dataint[22]), .CE(sync), .Q(db[22])); + FDE fd_db23 (.C(clkx2), .D(dataint[23]), .CE(sync), .Q(db[23])); + FDE fd_db24 (.C(clkx2), .D(dataint[24]), .CE(sync), .Q(db[24])); + FDE fd_db25 (.C(clkx2), .D(dataint[25]), .CE(sync), .Q(db[25])); + FDE fd_db26 (.C(clkx2), .D(dataint[26]), .CE(sync), .Q(db[26])); + FDE fd_db27 (.C(clkx2), .D(dataint[27]), .CE(sync), .Q(db[27])); + FDE fd_db28 (.C(clkx2), .D(dataint[28]), .CE(sync), .Q(db[28])); + FDE fd_db29 (.C(clkx2), .D(dataint[29]), .CE(sync), .Q(db[29])); + + wire [14:0] mux; + assign mux = (~sync) ? db[14:0] : db[29:15]; + + FD fd_out0 (.C(clkx2), .D(mux[0]), .Q(dataout[0])); + FD fd_out1 (.C(clkx2), .D(mux[1]), .Q(dataout[1])); + FD fd_out2 (.C(clkx2), .D(mux[2]), .Q(dataout[2])); + FD fd_out3 (.C(clkx2), .D(mux[3]), .Q(dataout[3])); + FD fd_out4 (.C(clkx2), .D(mux[4]), .Q(dataout[4])); + FD fd_out5 (.C(clkx2), .D(mux[5]), .Q(dataout[5])); + FD fd_out6 (.C(clkx2), .D(mux[6]), .Q(dataout[6])); + FD fd_out7 (.C(clkx2), .D(mux[7]), .Q(dataout[7])); + FD fd_out8 (.C(clkx2), .D(mux[8]), .Q(dataout[8])); + FD fd_out9 (.C(clkx2), .D(mux[9]), .Q(dataout[9])); + FD fd_out10 (.C(clkx2), .D(mux[10]), .Q(dataout[10])); + FD fd_out11 (.C(clkx2), .D(mux[11]), .Q(dataout[11])); + FD fd_out12 (.C(clkx2), .D(mux[12]), .Q(dataout[12])); + FD fd_out13 (.C(clkx2), .D(mux[13]), .Q(dataout[13])); + FD fd_out14 (.C(clkx2), .D(mux[14]), .Q(dataout[14])); + +endmodule diff --git a/tx/dvi_encoder_top.v b/tx/dvi_encoder_top.v new file mode 100755 index 0000000..7f3c3d3 --- /dev/null +++ b/tx/dvi_encoder_top.v @@ -0,0 +1,311 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Xilinx, Inc. 2009 www.xilinx.com +// +// XAPP xyz +// +////////////////////////////////////////////////////////////////////////////// +// +// File name : dvi_encoder.v +// +// Description : dvi_encoder +// +// Date - revision : April 2009 - 1.0.0 +// +// Author : Bob Feng +// +// Disclaimer: LIMITED WARRANTY AND DISCLAMER. These designs are +// provided to you "as is". Xilinx and its licensors makeand you +// receive no warranties or conditions, express, implied, +// statutory or otherwise, and Xilinx specificallydisclaims any +// implied warranties of merchantability, non-infringement,or +// fitness for a particular purpose. Xilinx does notwarrant that +// the functions contained in these designs will meet your +// requirements, or that the operation of these designswill be +// uninterrupted or error free, or that defects in theDesigns +// will be corrected. Furthermore, Xilinx does not warrantor +// make any representations regarding use or the results ofthe +// use of the designs in terms of correctness, accuracy, +// reliability, or otherwise. +// +// LIMITATION OF LIABILITY. In no event will Xilinx or its +// licensors be liable for any loss of data, lost profits,cost +// or procurement of substitute goods or services, or forany +// special, incidental, consequential, or indirect damages +// arising from the use or operation of the designs or +// accompanying documentation, however caused and on anytheory +// of liability. This limitation will apply even if Xilinx +// has been advised of the possibility of such damage. This +// limitation shall apply not-withstanding the failure ofthe +// essential purpose of any limited remedies herein. +// +// Copyright © 2009 Xilinx, Inc. +// All rights reserved +// +////////////////////////////////////////////////////////////////////////////// +// Modifications copyright (c) 2011, Andrew "bunnie" Huang +// All rights reserved as permitted by law. +// +// 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 dvi_encoder_top ( + input wire pclk, // pixel clock + input wire pclkx2, // pixel clock x2 + input wire pclkx10, // pixel clock x2 + input wire serdesstrobe, // OSERDES2 serdesstrobe + input wire rstin, // reset + input wire [7:0] blue_din, // Blue data in + input wire [7:0] green_din, // Green data in + input wire [7:0] red_din, // Red data in + input wire hsync, // hsync data + input wire vsync, // vsync data + input wire de, // data enable + output wire [3:0] TMDS, + output wire [3:0] TMDSB, + input wire vid_pa, + input wire vid_gb, + input wire dat_pa, + input wire dat_gb, + input wire dat_ena, + input wire [9:0] dat_din, + input wire [3:0] ctl_code, + input wire [29:0] bypass_sdata, + input wire bypass_ena, + output reg byp_error, + input wire box_active +); + + wire [9:0] red ; + wire [9:0] green ; + wire [9:0] blue ; + + wire [9:0] red_t4 ; + wire [9:0] green_t4 ; + wire [9:0] blue_t4 ; + + wire [4:0] tmds_data0, tmds_data1, tmds_data2; + wire [2:0] tmdsint; + + // + // Forward TMDS Clock Using OSERDES2 block + // + reg [4:0] tmdsclkint = 5'b00000; + reg toggle = 1'b0; + + always @ (posedge pclkx2 or posedge rstin) begin + if (rstin) + toggle <= 1'b0; + else + toggle <= ~toggle; + end + + always @ (posedge pclkx2) begin + if (toggle) + tmdsclkint <= 5'b11111; + else + tmdsclkint <= 5'b00000; + end + + wire tmdsclk; + + serdes_n_to_1 #( + .SF (5)) + clkout ( + .iob_data_out (tmdsclk), + .ioclk (pclkx10), + .serdesstrobe (serdesstrobe), + .gclk (pclkx2), + .reset (rstin), + .datain (tmdsclkint)); + + OBUFDS TMDS3 (.I(tmdsclk), .O(TMDS[3]), .OB(TMDSB[3])) ;// clock + + // + // Forward TMDS Data: 3 channels + // + serdes_n_to_1 #(.SF(5)) oserdes0 ( + .ioclk(pclkx10), + .serdesstrobe(serdesstrobe), + .reset(rstin), + .gclk(pclkx2), + .datain(tmds_data0), + .iob_data_out(tmdsint[0])) ; + + serdes_n_to_1 #(.SF(5)) oserdes1 ( + .ioclk(pclkx10), + .serdesstrobe(serdesstrobe), + .reset(rstin), + .gclk(pclkx2), + .datain(tmds_data1), + .iob_data_out(tmdsint[1])) ; + + serdes_n_to_1 #(.SF(5)) oserdes2 ( + .ioclk(pclkx10), + .serdesstrobe(serdesstrobe), + .reset(rstin), + .gclk(pclkx2), + .datain(tmds_data2), + .iob_data_out(tmdsint[2])) ; + + OBUFDS TMDS0 (.I(tmdsint[0]), .O(TMDS[0]), .OB(TMDSB[0])) ; + OBUFDS TMDS1 (.I(tmdsint[1]), .O(TMDS[1]), .OB(TMDSB[1])) ; + OBUFDS TMDS2 (.I(tmdsint[2]), .O(TMDS[2]), .OB(TMDSB[2])) ; + + encodeb encb ( + .clkin (pclk), + .rstin (rstin), + .din (blue_din), + .c0 (hsync), + .c1 (vsync), + .de (de), + .dout (blue), + .vid_gb (vid_gb)) ; + + encodeg encg ( + .clkin (pclk), + .rstin (rstin), + .din (green_din), + .c0 (ctl_code[0]), // bit 0 + .c1 (ctl_code[1]), // bit 1 + .de (de), + .dout (green), + .vid_gb (vid_gb)) ; + + encoder encr ( + .clkin (pclk), + .rstin (rstin), + .din (red_din), + .c0 (ctl_code[2]), // bit 2 + .c1 (ctl_code[3]), // bit 3 + .de (de), + .dout (red), + .vid_gb (vid_gb)) ; + +encode_terc4 engb_t4 + ( .clkin (pclk), + .rstin (rstin), + .din ( {dat_din[9] | dat_gb, dat_din[8] | dat_gb, vsync, hsync} ), + .dout (blue_t4), + .dat_gb (1'b0) // gb is considered with sync + ); + +encode_terc4 encg_t4 + ( .clkin (pclk), + .rstin (rstin), + .din (dat_din[3:0]), + .dout (green_t4), + .dat_gb (dat_gb) + ); + +encode_terc4 encr_t4 + ( .clkin (pclk), + .rstin (rstin), + .din (dat_din[7:4]), + .dout (red_t4), + .dat_gb (dat_gb) + ); + + // pipe alignment + reg dat_ena_q, dat_ena_reg, dat_ena_r2; + reg dat_gb_q, dat_gb_reg, dat_gb_r2; + + always @(posedge pclk or posedge rstin) begin + if( rstin ) begin + dat_ena_q <= 1'b0; + dat_ena_reg <= 1'b0; + dat_ena_r2 <= 1'b0; + dat_gb_q <= 1'b0; + dat_gb_reg <= 1'b0; + dat_gb_r2 <= 1'b0; + end else begin + dat_ena_q <= dat_ena; + dat_ena_reg <= dat_ena_q; + dat_ena_r2 <= dat_ena_reg; + + dat_gb_q <= dat_gb; + dat_gb_reg <= dat_gb_q; + dat_gb_r2 <= dat_gb_reg; + end + end + + // insert four pipe stages to s_data override + reg [29:0] byp_sd1; + reg [29:0] byp_sd2; + reg [29:0] byp_sd3; + reg [29:0] byp_sd4; + reg [29:0] byp_sd5; + reg [4:0] box_active_q; + always @(posedge pclk or posedge rstin) begin + if( rstin ) begin + byp_sd1 <= 30'b0; + byp_sd2 <= 30'b0; + byp_sd3 <= 30'b0; + byp_sd4 <= 30'b0; + byp_sd5 <= 30'b0; + end else begin + byp_sd1 <= bypass_sdata; + byp_sd2 <= byp_sd1; + byp_sd3 <= byp_sd2; + byp_sd4 <= byp_sd3; + byp_sd5 <= byp_sd4; + + box_active_q[4] <= box_active_q[3]; + box_active_q[3] <= box_active_q[2]; + box_active_q[2] <= box_active_q[1]; + box_active_q[1] <= box_active_q[0]; + box_active_q[0] <= box_active; + end // else: !if( rstin ) + end // always @ (posedge pclk or posedge rstin) + +// wire [29:0] s_data_x = (dat_ena_r2 | dat_gb_r2) ? +// {red_t4[9:5], green_t4[9:5], blue_t4[9:5], +// red_t4[4:0], green_t4[4:0], blue_t4[4:0]} : +// {red[9:5], green[9:5], blue[9:5], +// red[4:0], green[4:0], blue[4:0]}; + + // this destroys our ability to bypass sound, but fixes the problem + // where HDMI data of Red = 0x55, Green = 0x55, blue = anything + // causes the stream to flip into TERC4 mode + wire [29:0] s_data_x = {red[9:5], green[9:5], blue[9:5], + red[4:0], green[4:0], blue[4:0]}; + + // was bypass_ena in here... + wire [29:0] s_data = !box_active_q[4] ? byp_sd5 : s_data_x; + + always @(posedge pclk or posedge rstin) begin + if( rstin ) begin + byp_error <= 1'b0; + end else begin + byp_error = byp_sd5 != s_data_x; + end + end + + convert_30to15_fifo pixel2x ( + .rst (rstin), + .clk (pclk), + .clkx2 (pclkx2), + .datain (s_data), + .dataout ({tmds_data2, tmds_data1, tmds_data0})); + +endmodule diff --git a/tx/encode.v b/tx/encode.v new file mode 100755 index 0000000..7158abc --- /dev/null +++ b/tx/encode.v @@ -0,0 +1,233 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Xilinx, Inc. 2008 www.xilinx.com +// +////////////////////////////////////////////////////////////////////////////// +// +// File name : encode.v +// +// Description : TMDS encoder +// +// Date - revision : Jan. 2008 - v 1.0 +// +// Author : Bob Feng +// +// Disclaimer: LIMITED WARRANTY AND DISCLAMER. These designs are +// provided to you "as is". Xilinx and its licensors make and you +// receive no warranties or conditions, express, implied, +// statutory or otherwise, and Xilinx specifically disclaims any +// implied warranties of merchantability, non-infringement,or +// fitness for a particular purpose. Xilinx does not warrant that +// the functions contained in these designs will meet your +// requirements, or that the operation of these designs will be +// uninterrupted or error free, or that defects in the Designs +// will be corrected. Furthermore, Xilinx does not warrantor +// make any representations regarding use or the results of the +// use of the designs in terms of correctness, accuracy, +// reliability, or otherwise. +// +// LIMITATION OF LIABILITY. In no event will Xilinx or its +// licensors be liable for any loss of data, lost profits,cost +// or procurement of substitute goods or services, or for any +// special, incidental, consequential, or indirect damages +// arising from the use or operation of the designs or +// accompanying documentation, however caused and on any theory +// of liability. This limitation will apply even if Xilinx +// has been advised of the possibility of such damage. This +// limitation shall apply not-withstanding the failure of the +// essential purpose of any limited remedies herein. +// +// Copyright © 2006 Xilinx, Inc. +// All rights reserved +// +////////////////////////////////////////////////////////////////////////////// +// Modifications copyright (c) 2011, Andrew "bunnie" Huang +// All rights reserved as permitted by law. +// +// 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 ps / 1ps + +module encode ( + input clkin, // pixel clock input + input rstin, // async. reset input (active high) + input [7:0] din, // data inputs: expect registered + input c0, // c0 input + input c1, // c1 input + input de, // de input + output reg [9:0] dout, // data outputs + input vid_pa // video preamble encoding select +); + + //////////////////////////////////////////////////////////// + // Counting number of 1s and 0s for each incoming pixel + // component. Pipe line the result. + // Register Data Input so it matches the pipe lined adder + // output + //////////////////////////////////////////////////////////// + reg [3:0] n1d; //number of 1s in din + reg [7:0] din_q; + + always @ (posedge clkin) begin + n1d <=#1 din[0] + din[1] + din[2] + din[3] + din[4] + din[5] + din[6] + din[7]; + + din_q <=#1 din; + end + + /////////////////////////////////////////////////////// + // Stage 1: 8 bit -> 9 bit + // Refer to DVI 1.0 Specification, page 29, Figure 3-5 + /////////////////////////////////////////////////////// + wire decision1; + + assign decision1 = (n1d > 4'h4) | ((n1d == 4'h4) & (din_q[0] == 1'b0)); +/* + reg [8:0] q_m; + always @ (posedge clkin) begin + q_m[0] <=#1 din_q[0]; + q_m[1] <=#1 (decision1) ? (q_m[0] ^~ din_q[1]) : (q_m[0] ^ din_q[1]); + q_m[2] <=#1 (decision1) ? (q_m[1] ^~ din_q[2]) : (q_m[1] ^ din_q[2]); + q_m[3] <=#1 (decision1) ? (q_m[2] ^~ din_q[3]) : (q_m[2] ^ din_q[3]); + q_m[4] <=#1 (decision1) ? (q_m[3] ^~ din_q[4]) : (q_m[3] ^ din_q[4]); + q_m[5] <=#1 (decision1) ? (q_m[4] ^~ din_q[5]) : (q_m[4] ^ din_q[5]); + q_m[6] <=#1 (decision1) ? (q_m[5] ^~ din_q[6]) : (q_m[5] ^ din_q[6]); + q_m[7] <=#1 (decision1) ? (q_m[6] ^~ din_q[7]) : (q_m[6] ^ din_q[7]); + q_m[8] <=#1 (decision1) ? 1'b0 : 1'b1; + end +*/ + wire [8:0] q_m; + assign q_m[0] = din_q[0]; + assign q_m[1] = (decision1) ? (q_m[0] ^~ din_q[1]) : (q_m[0] ^ din_q[1]); + assign q_m[2] = (decision1) ? (q_m[1] ^~ din_q[2]) : (q_m[1] ^ din_q[2]); + assign q_m[3] = (decision1) ? (q_m[2] ^~ din_q[3]) : (q_m[2] ^ din_q[3]); + assign q_m[4] = (decision1) ? (q_m[3] ^~ din_q[4]) : (q_m[3] ^ din_q[4]); + assign q_m[5] = (decision1) ? (q_m[4] ^~ din_q[5]) : (q_m[4] ^ din_q[5]); + assign q_m[6] = (decision1) ? (q_m[5] ^~ din_q[6]) : (q_m[5] ^ din_q[6]); + assign q_m[7] = (decision1) ? (q_m[6] ^~ din_q[7]) : (q_m[6] ^ din_q[7]); + assign q_m[8] = (decision1) ? 1'b0 : 1'b1; + + ///////////////////////////////////////////////////////// + // Stage 2: 9 bit -> 10 bit + // Refer to DVI 1.0 Specification, page 29, Figure 3-5 + ///////////////////////////////////////////////////////// + reg [3:0] n1q_m, n0q_m; // number of 1s and 0s for q_m + always @ (posedge clkin) begin + n1q_m <=#1 q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7]; + n0q_m <=#1 4'h8 - (q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7]); + end + + parameter CTRLTOKEN0 = 10'b1101010100; + parameter CTRLTOKEN1 = 10'b0010101011; + parameter CTRLTOKEN2 = 10'b0101010100; + parameter CTRLTOKEN3 = 10'b1010101011; + + reg [4:0] cnt; //disparity counter, MSB is the sign bit + wire decision2, decision3; + + assign decision2 = (cnt == 5'h0) | (n1q_m == n0q_m); + ///////////////////////////////////////////////////////////////////////// + // [(cnt > 0) and (N1q_m > N0q_m)] or [(cnt < 0) and (N0q_m > N1q_m)] + ///////////////////////////////////////////////////////////////////////// + assign decision3 = (~cnt[4] & (n1q_m > n0q_m)) | (cnt[4] & (n0q_m > n1q_m)); + + //////////////////////////////////// + // pipe line alignment + //////////////////////////////////// + reg de_q, de_reg; + reg c0_q, c1_q; + reg c0_reg, c1_reg; + reg [8:0] q_m_reg; + reg vid_pa_q, vid_pa_reg; + + + always @ (posedge clkin) begin + de_q <=#1 de; + de_reg <=#1 de_q; + + c0_q <=#1 c0; + c0_reg <=#1 c0_q; + c1_q <=#1 c1; + c1_reg <=#1 c1_q; + + q_m_reg <=#1 q_m; + + vid_pa_q <=#1 vid_pa; + vid_pa_reg <=#1 vid_pa_q; + end + + /////////////////////////////// + // 10-bit out + // disparity counter + /////////////////////////////// + always @ (posedge clkin or posedge rstin) begin + if(rstin) begin + dout <= 10'h0; + cnt <= 5'h0; + end else begin + if(vid_pa_reg) begin + // video preamble coding + dout[9:0] <= DNU; // this code shouldn't be used + // branch encode into rgb specific lines, since hdmi spec + // puts different code on each + // don't make it dynamically selectable because it will add logic + // to a critical path. Less maintainable to do it with separate encoders + // for each path, but on the other hand it makes the compiler's job easier + // to optimize each unit. + end else begin + if (de_reg) begin + if(decision2) begin + dout[9] <=#1 ~q_m_reg[8]; + dout[8] <=#1 q_m_reg[8]; + dout[7:0] <=#1 (q_m_reg[8]) ? q_m_reg[7:0] : ~q_m_reg[7:0]; + + cnt <=#1 (~q_m_reg[8]) ? (cnt + n0q_m - n1q_m) : (cnt + n1q_m - n0q_m); + end else begin + if(decision3) begin + dout[9] <=#1 1'b1; + dout[8] <=#1 q_m_reg[8]; + dout[7:0] <=#1 ~q_m_reg[7:0]; + + cnt <=#1 cnt + {q_m_reg[8], 1'b0} + (n0q_m - n1q_m); + end else begin + dout[9] <=#1 1'b0; + dout[8] <=#1 q_m_reg[8]; + dout[7:0] <=#1 q_m_reg[7:0]; + + cnt <=#1 cnt - {~q_m_reg[8], 1'b0} + (n1q_m - n0q_m); + end + end + end else begin + case ({c1_reg, c0_reg}) + 2'b00: dout <=#1 CTRLTOKEN0; + 2'b01: dout <=#1 CTRLTOKEN1; + 2'b10: dout <=#1 CTRLTOKEN2; + default: dout <=#1 CTRLTOKEN3; + endcase + + cnt <=#1 5'h0; + end // else: !if(de_reg) + end // if (vid_pa) + end // else: !if(rstin) + end // always @ (posedge clkin or posedge rstin) + + +endmodule diff --git a/tx/encode_terc4.v b/tx/encode_terc4.v new file mode 100755 index 0000000..e6c0e0e --- /dev/null +++ b/tx/encode_terc4.v @@ -0,0 +1,86 @@ +////////////////////////////////////////////////////////////////////////////// +// 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 ps / 1ps + +module encode_terc4 ( + input clkin, // pixel clock input + input rstin, // async. reset input (active high) + input [3:0] din, // data inputs: expect registered + output reg [9:0] dout, // data outputs + input dat_gb +); + + reg [3:0] din_q; // extra stages to match pipeline delays + reg [9:0] q_m_reg; // extra stages to match pipeline delays + + reg dat_gb_q, dat_gb_reg; + + always @(posedge clkin or posedge rstin) begin + if(rstin) begin + din_q <= 4'b0; + dout <= 10'b0; + dat_gb_q <= 1'b0; + dat_gb_reg <= 1'b0; + end else begin + din_q <= din; + dat_gb_q <= dat_gb; + dat_gb_reg <= dat_gb_q; + + if( dat_gb_reg ) begin + dout <= 10'b0100110011; + end else begin + dout <= q_m_reg; + end + end // else: !if(rstin) + end // always @ (posedge clkin or posedge rstin) + + always @(posedge clkin or posedge rstin) begin + if( rstin ) begin + q_m_reg[9:0] <= 10'h0; + end else begin + case ({din_q[3], din_q[2], din_q[1], din_q[0]}) + 4'b0000: q_m_reg[9:0] <= 10'b1010011100; + 4'b0001: q_m_reg[9:0] <= 10'b1001100011; + 4'b0010: q_m_reg[9:0] <= 10'b1011100100; + 4'b0011: q_m_reg[9:0] <= 10'b1011100010; + 4'b0100: q_m_reg[9:0] <= 10'b0101110001; + 4'b0101: q_m_reg[9:0] <= 10'b0100011110; + 4'b0110: q_m_reg[9:0] <= 10'b0110001110; + 4'b0111: q_m_reg[9:0] <= 10'b0100111100; + 4'b1000: q_m_reg[9:0] <= 10'b1011001100; + 4'b1001: q_m_reg[9:0] <= 10'b0100111001; + 4'b1010: q_m_reg[9:0] <= 10'b0110011100; + 4'b1011: q_m_reg[9:0] <= 10'b1011000110; + 4'b1100: q_m_reg[9:0] <= 10'b1010001110; + 4'b1101: q_m_reg[9:0] <= 10'b1001110001; + 4'b1110: q_m_reg[9:0] <= 10'b0101100011; + 4'b1111: q_m_reg[9:0] <= 10'b1011000011; + // no default since all cases are covered in this ROM. + endcase // case ({din_q[3], din_q[2], din_q[1], din_q[0]}) + end // else: !if( rstin ) + end // always @ (posedge clkin or posedge rstin) + +endmodule // encode_terc4 diff --git a/tx/encodeb.v b/tx/encodeb.v new file mode 100755 index 0000000..8bb4ce6 --- /dev/null +++ b/tx/encodeb.v @@ -0,0 +1,231 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Xilinx, Inc. 2008 www.xilinx.com +// +////////////////////////////////////////////////////////////////////////////// +// +// File name : encode.v +// +// Description : TMDS encoder +// +// Date - revision : Jan. 2008 - v 1.0 +// +// Author : Bob Feng +// +// Disclaimer: LIMITED WARRANTY AND DISCLAMER. These designs are +// provided to you "as is". Xilinx and its licensors make and you +// receive no warranties or conditions, express, implied, +// statutory or otherwise, and Xilinx specifically disclaims any +// implied warranties of merchantability, non-infringement,or +// fitness for a particular purpose. Xilinx does not warrant that +// the functions contained in these designs will meet your +// requirements, or that the operation of these designs will be +// uninterrupted or error free, or that defects in the Designs +// will be corrected. Furthermore, Xilinx does not warrantor +// make any representations regarding use or the results of the +// use of the designs in terms of correctness, accuracy, +// reliability, or otherwise. +// +// LIMITATION OF LIABILITY. In no event will Xilinx or its +// licensors be liable for any loss of data, lost profits,cost +// or procurement of substitute goods or services, or for any +// special, incidental, consequential, or indirect damages +// arising from the use or operation of the designs or +// accompanying documentation, however caused and on any theory +// of liability. This limitation will apply even if Xilinx +// has been advised of the possibility of such damage. This +// limitation shall apply not-withstanding the failure of the +// essential purpose of any limited remedies herein. +// +// Copyright © 2006 Xilinx, Inc. +// All rights reserved +// +////////////////////////////////////////////////////////////////////////////// +// Modifications copyright (c) 2011, Andrew "bunnie" Huang +// All rights reserved as permitted by law. +// +// 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 ps / 1ps + +module encodeb ( + input clkin, // pixel clock input + input rstin, // async. reset input (active high) + input [7:0] din, // data inputs: expect registered + input c0, // c0 input + input c1, // c1 input + input de, // de input + output reg [9:0] dout, // data outputs + input vid_gb // video preamble encoding select +); + + //////////////////////////////////////////////////////////// + // Counting number of 1s and 0s for each incoming pixel + // component. Pipe line the result. + // Register Data Input so it matches the pipe lined adder + // output + //////////////////////////////////////////////////////////// + reg [3:0] n1d; //number of 1s in din + reg [7:0] din_q; + + always @ (posedge clkin) begin + n1d <=#1 din[0] + din[1] + din[2] + din[3] + din[4] + din[5] + din[6] + din[7]; + + din_q <=#1 din; + end + + /////////////////////////////////////////////////////// + // Stage 1: 8 bit -> 9 bit + // Refer to DVI 1.0 Specification, page 29, Figure 3-5 + /////////////////////////////////////////////////////// + wire decision1; + + assign decision1 = (n1d > 4'h4) | ((n1d == 4'h4) & (din_q[0] == 1'b0)); +/* + reg [8:0] q_m; + always @ (posedge clkin) begin + q_m[0] <=#1 din_q[0]; + q_m[1] <=#1 (decision1) ? (q_m[0] ^~ din_q[1]) : (q_m[0] ^ din_q[1]); + q_m[2] <=#1 (decision1) ? (q_m[1] ^~ din_q[2]) : (q_m[1] ^ din_q[2]); + q_m[3] <=#1 (decision1) ? (q_m[2] ^~ din_q[3]) : (q_m[2] ^ din_q[3]); + q_m[4] <=#1 (decision1) ? (q_m[3] ^~ din_q[4]) : (q_m[3] ^ din_q[4]); + q_m[5] <=#1 (decision1) ? (q_m[4] ^~ din_q[5]) : (q_m[4] ^ din_q[5]); + q_m[6] <=#1 (decision1) ? (q_m[5] ^~ din_q[6]) : (q_m[5] ^ din_q[6]); + q_m[7] <=#1 (decision1) ? (q_m[6] ^~ din_q[7]) : (q_m[6] ^ din_q[7]); + q_m[8] <=#1 (decision1) ? 1'b0 : 1'b1; + end +*/ + wire [8:0] q_m; + assign q_m[0] = din_q[0]; + assign q_m[1] = (decision1) ? (q_m[0] ^~ din_q[1]) : (q_m[0] ^ din_q[1]); + assign q_m[2] = (decision1) ? (q_m[1] ^~ din_q[2]) : (q_m[1] ^ din_q[2]); + assign q_m[3] = (decision1) ? (q_m[2] ^~ din_q[3]) : (q_m[2] ^ din_q[3]); + assign q_m[4] = (decision1) ? (q_m[3] ^~ din_q[4]) : (q_m[3] ^ din_q[4]); + assign q_m[5] = (decision1) ? (q_m[4] ^~ din_q[5]) : (q_m[4] ^ din_q[5]); + assign q_m[6] = (decision1) ? (q_m[5] ^~ din_q[6]) : (q_m[5] ^ din_q[6]); + assign q_m[7] = (decision1) ? (q_m[6] ^~ din_q[7]) : (q_m[6] ^ din_q[7]); + assign q_m[8] = (decision1) ? 1'b0 : 1'b1; + + ///////////////////////////////////////////////////////// + // Stage 2: 9 bit -> 10 bit + // Refer to DVI 1.0 Specification, page 29, Figure 3-5 + ///////////////////////////////////////////////////////// + reg [3:0] n1q_m, n0q_m; // number of 1s and 0s for q_m + always @ (posedge clkin) begin + n1q_m <=#1 q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7]; + n0q_m <=#1 4'h8 - (q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7]); + end + + parameter CTRLTOKEN0 = 10'b1101010100; + parameter CTRLTOKEN1 = 10'b0010101011; + parameter CTRLTOKEN2 = 10'b0101010100; + parameter CTRLTOKEN3 = 10'b1010101011; + + parameter CTLBLUGB = 10'b1011001100; + parameter CTLGRNGB = 10'b0100110011; + parameter CTLREDGB = 10'b1011001100; + + reg [4:0] cnt; //disparity counter, MSB is the sign bit + wire decision2, decision3; + + assign decision2 = (cnt == 5'h0) | (n1q_m == n0q_m); + ///////////////////////////////////////////////////////////////////////// + // [(cnt > 0) and (N1q_m > N0q_m)] or [(cnt < 0) and (N0q_m > N1q_m)] + ///////////////////////////////////////////////////////////////////////// + assign decision3 = (~cnt[4] & (n1q_m > n0q_m)) | (cnt[4] & (n0q_m > n1q_m)); + + //////////////////////////////////// + // pipe line alignment + //////////////////////////////////// + reg de_q, de_reg; + reg c0_q, c1_q; + reg c0_reg, c1_reg; + reg [8:0] q_m_reg; + reg vid_gb_q, vid_gb_reg; + + + always @ (posedge clkin) begin + de_q <=#1 de; + de_reg <=#1 de_q; + + c0_q <=#1 c0; + c0_reg <=#1 c0_q; + c1_q <=#1 c1; + c1_reg <=#1 c1_q; + + q_m_reg <=#1 q_m; + + vid_gb_q <=#1 vid_gb; + vid_gb_reg <=#1 vid_gb_q; + end + + /////////////////////////////// + // 10-bit out + // disparity counter + /////////////////////////////// + always @ (posedge clkin or posedge rstin) begin + if(rstin) begin + dout <= 10'h0; + cnt <= 5'h0; + end else begin + if(vid_gb_reg) begin + // video preamble coding + dout[9:0] <= CTLBLUGB; + end else begin + if (de_reg) begin + if(decision2) begin + dout[9] <=#1 ~q_m_reg[8]; + dout[8] <=#1 q_m_reg[8]; + dout[7:0] <=#1 (q_m_reg[8]) ? q_m_reg[7:0] : ~q_m_reg[7:0]; + + cnt <=#1 (~q_m_reg[8]) ? (cnt + n0q_m - n1q_m) : (cnt + n1q_m - n0q_m); + end else begin + if(decision3) begin + dout[9] <=#1 1'b1; + dout[8] <=#1 q_m_reg[8]; + dout[7:0] <=#1 ~q_m_reg[7:0]; + + cnt <=#1 cnt + {q_m_reg[8], 1'b0} + (n0q_m - n1q_m); + end else begin + dout[9] <=#1 1'b0; + dout[8] <=#1 q_m_reg[8]; + dout[7:0] <=#1 q_m_reg[7:0]; + + cnt <=#1 cnt - {~q_m_reg[8], 1'b0} + (n1q_m - n0q_m); + end + end + end else begin + case ({c1_reg, c0_reg}) + 2'b00: dout <=#1 CTRLTOKEN0; + 2'b01: dout <=#1 CTRLTOKEN1; + 2'b10: dout <=#1 CTRLTOKEN2; + default: dout <=#1 CTRLTOKEN3; + endcase + + cnt <=#1 5'h0; + end // else: !if(de_reg) + end // if (vid_gb) + end // else: !if(rstin) + end // always @ (posedge clkin or posedge rstin) + + +endmodule diff --git a/tx/encodeg.v b/tx/encodeg.v new file mode 100755 index 0000000..128a07e --- /dev/null +++ b/tx/encodeg.v @@ -0,0 +1,231 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Xilinx, Inc. 2008 www.xilinx.com +// +////////////////////////////////////////////////////////////////////////////// +// +// File name : encode.v +// +// Description : TMDS encoder +// +// Date - revision : Jan. 2008 - v 1.0 +// +// Author : Bob Feng +// +// Disclaimer: LIMITED WARRANTY AND DISCLAMER. These designs are +// provided to you "as is". Xilinx and its licensors make and you +// receive no warranties or conditions, express, implied, +// statutory or otherwise, and Xilinx specifically disclaims any +// implied warranties of merchantability, non-infringement,or +// fitness for a particular purpose. Xilinx does not warrant that +// the functions contained in these designs will meet your +// requirements, or that the operation of these designs will be +// uninterrupted or error free, or that defects in the Designs +// will be corrected. Furthermore, Xilinx does not warrantor +// make any representations regarding use or the results of the +// use of the designs in terms of correctness, accuracy, +// reliability, or otherwise. +// +// LIMITATION OF LIABILITY. In no event will Xilinx or its +// licensors be liable for any loss of data, lost profits,cost +// or procurement of substitute goods or services, or for any +// special, incidental, consequential, or indirect damages +// arising from the use or operation of the designs or +// accompanying documentation, however caused and on any theory +// of liability. This limitation will apply even if Xilinx +// has been advised of the possibility of such damage. This +// limitation shall apply not-withstanding the failure of the +// essential purpose of any limited remedies herein. +// +// Copyright © 2006 Xilinx, Inc. +// All rights reserved +// +////////////////////////////////////////////////////////////////////////////// +// Modifications copyright (c) 2011, Andrew "bunnie" Huang +// All rights reserved as permitted by law. +// +// 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 ps / 1ps + +module encodeg ( + input clkin, // pixel clock input + input rstin, // async. reset input (active high) + input [7:0] din, // data inputs: expect registered + input c0, // c0 input + input c1, // c1 input + input de, // de input + output reg [9:0] dout, // data outputs + input vid_gb // video preamble encoding select +); + + //////////////////////////////////////////////////////////// + // Counting number of 1s and 0s for each incoming pixel + // component. Pipe line the result. + // Register Data Input so it matches the pipe lined adder + // output + //////////////////////////////////////////////////////////// + reg [3:0] n1d; //number of 1s in din + reg [7:0] din_q; + + always @ (posedge clkin) begin + n1d <=#1 din[0] + din[1] + din[2] + din[3] + din[4] + din[5] + din[6] + din[7]; + + din_q <=#1 din; + end + + /////////////////////////////////////////////////////// + // Stage 1: 8 bit -> 9 bit + // Refer to DVI 1.0 Specification, page 29, Figure 3-5 + /////////////////////////////////////////////////////// + wire decision1; + + assign decision1 = (n1d > 4'h4) | ((n1d == 4'h4) & (din_q[0] == 1'b0)); +/* + reg [8:0] q_m; + always @ (posedge clkin) begin + q_m[0] <=#1 din_q[0]; + q_m[1] <=#1 (decision1) ? (q_m[0] ^~ din_q[1]) : (q_m[0] ^ din_q[1]); + q_m[2] <=#1 (decision1) ? (q_m[1] ^~ din_q[2]) : (q_m[1] ^ din_q[2]); + q_m[3] <=#1 (decision1) ? (q_m[2] ^~ din_q[3]) : (q_m[2] ^ din_q[3]); + q_m[4] <=#1 (decision1) ? (q_m[3] ^~ din_q[4]) : (q_m[3] ^ din_q[4]); + q_m[5] <=#1 (decision1) ? (q_m[4] ^~ din_q[5]) : (q_m[4] ^ din_q[5]); + q_m[6] <=#1 (decision1) ? (q_m[5] ^~ din_q[6]) : (q_m[5] ^ din_q[6]); + q_m[7] <=#1 (decision1) ? (q_m[6] ^~ din_q[7]) : (q_m[6] ^ din_q[7]); + q_m[8] <=#1 (decision1) ? 1'b0 : 1'b1; + end +*/ + wire [8:0] q_m; + assign q_m[0] = din_q[0]; + assign q_m[1] = (decision1) ? (q_m[0] ^~ din_q[1]) : (q_m[0] ^ din_q[1]); + assign q_m[2] = (decision1) ? (q_m[1] ^~ din_q[2]) : (q_m[1] ^ din_q[2]); + assign q_m[3] = (decision1) ? (q_m[2] ^~ din_q[3]) : (q_m[2] ^ din_q[3]); + assign q_m[4] = (decision1) ? (q_m[3] ^~ din_q[4]) : (q_m[3] ^ din_q[4]); + assign q_m[5] = (decision1) ? (q_m[4] ^~ din_q[5]) : (q_m[4] ^ din_q[5]); + assign q_m[6] = (decision1) ? (q_m[5] ^~ din_q[6]) : (q_m[5] ^ din_q[6]); + assign q_m[7] = (decision1) ? (q_m[6] ^~ din_q[7]) : (q_m[6] ^ din_q[7]); + assign q_m[8] = (decision1) ? 1'b0 : 1'b1; + + ///////////////////////////////////////////////////////// + // Stage 2: 9 bit -> 10 bit + // Refer to DVI 1.0 Specification, page 29, Figure 3-5 + ///////////////////////////////////////////////////////// + reg [3:0] n1q_m, n0q_m; // number of 1s and 0s for q_m + always @ (posedge clkin) begin + n1q_m <=#1 q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7]; + n0q_m <=#1 4'h8 - (q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7]); + end + + parameter CTRLTOKEN0 = 10'b1101010100; + parameter CTRLTOKEN1 = 10'b0010101011; + parameter CTRLTOKEN2 = 10'b0101010100; + parameter CTRLTOKEN3 = 10'b1010101011; + + parameter CTLBLUGB = 10'b1011001100; + parameter CTLGRNGB = 10'b0100110011; + parameter CTLREDGB = 10'b1011001100; + + reg [4:0] cnt; //disparity counter, MSB is the sign bit + wire decision2, decision3; + + assign decision2 = (cnt == 5'h0) | (n1q_m == n0q_m); + ///////////////////////////////////////////////////////////////////////// + // [(cnt > 0) and (N1q_m > N0q_m)] or [(cnt < 0) and (N0q_m > N1q_m)] + ///////////////////////////////////////////////////////////////////////// + assign decision3 = (~cnt[4] & (n1q_m > n0q_m)) | (cnt[4] & (n0q_m > n1q_m)); + + //////////////////////////////////// + // pipe line alignment + //////////////////////////////////// + reg de_q, de_reg; + reg c0_q, c1_q; + reg c0_reg, c1_reg; + reg [8:0] q_m_reg; + reg vid_gb_q, vid_gb_reg; + + + always @ (posedge clkin) begin + de_q <=#1 de; + de_reg <=#1 de_q; + + c0_q <=#1 c0; + c0_reg <=#1 c0_q; + c1_q <=#1 c1; + c1_reg <=#1 c1_q; + + q_m_reg <=#1 q_m; + + vid_gb_q <=#1 vid_gb; + vid_gb_reg <=#1 vid_gb_q; + end + + /////////////////////////////// + // 10-bit out + // disparity counter + /////////////////////////////// + always @ (posedge clkin or posedge rstin) begin + if(rstin) begin + dout <= 10'h0; + cnt <= 5'h0; + end else begin + if(vid_gb_reg) begin + // video preamble coding + dout[9:0] <= CTLGRNGB; + end else begin + if (de_reg) begin + if(decision2) begin + dout[9] <=#1 ~q_m_reg[8]; + dout[8] <=#1 q_m_reg[8]; + dout[7:0] <=#1 (q_m_reg[8]) ? q_m_reg[7:0] : ~q_m_reg[7:0]; + + cnt <=#1 (~q_m_reg[8]) ? (cnt + n0q_m - n1q_m) : (cnt + n1q_m - n0q_m); + end else begin + if(decision3) begin + dout[9] <=#1 1'b1; + dout[8] <=#1 q_m_reg[8]; + dout[7:0] <=#1 ~q_m_reg[7:0]; + + cnt <=#1 cnt + {q_m_reg[8], 1'b0} + (n0q_m - n1q_m); + end else begin + dout[9] <=#1 1'b0; + dout[8] <=#1 q_m_reg[8]; + dout[7:0] <=#1 q_m_reg[7:0]; + + cnt <=#1 cnt - {~q_m_reg[8], 1'b0} + (n1q_m - n0q_m); + end + end + end else begin + case ({c1_reg, c0_reg}) + 2'b00: dout <=#1 CTRLTOKEN0; + 2'b01: dout <=#1 CTRLTOKEN1; + 2'b10: dout <=#1 CTRLTOKEN2; + default: dout <=#1 CTRLTOKEN3; + endcase + + cnt <=#1 5'h0; + end // else: !if(de_reg) + end // if (vid_gb) + end // else: !if(rstin) + end // always @ (posedge clkin or posedge rstin) + + +endmodule diff --git a/tx/encoder.v b/tx/encoder.v new file mode 100755 index 0000000..cf2f4f5 --- /dev/null +++ b/tx/encoder.v @@ -0,0 +1,231 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Xilinx, Inc. 2008 www.xilinx.com +// +////////////////////////////////////////////////////////////////////////////// +// +// File name : encode.v +// +// Description : TMDS encoder +// +// Date - revision : Jan. 2008 - v 1.0 +// +// Author : Bob Feng +// +// Disclaimer: LIMITED WARRANTY AND DISCLAMER. These designs are +// provided to you "as is". Xilinx and its licensors make and you +// receive no warranties or conditions, express, implied, +// statutory or otherwise, and Xilinx specifically disclaims any +// implied warranties of merchantability, non-infringement,or +// fitness for a particular purpose. Xilinx does not warrant that +// the functions contained in these designs will meet your +// requirements, or that the operation of these designs will be +// uninterrupted or error free, or that defects in the Designs +// will be corrected. Furthermore, Xilinx does not warrantor +// make any representations regarding use or the results of the +// use of the designs in terms of correctness, accuracy, +// reliability, or otherwise. +// +// LIMITATION OF LIABILITY. In no event will Xilinx or its +// licensors be liable for any loss of data, lost profits,cost +// or procurement of substitute goods or services, or for any +// special, incidental, consequential, or indirect damages +// arising from the use or operation of the designs or +// accompanying documentation, however caused and on any theory +// of liability. This limitation will apply even if Xilinx +// has been advised of the possibility of such damage. This +// limitation shall apply not-withstanding the failure of the +// essential purpose of any limited remedies herein. +// +// Copyright © 2006 Xilinx, Inc. +// All rights reserved +// +////////////////////////////////////////////////////////////////////////////// +// Modifications copyright (c) 2011, Andrew "bunnie" Huang +// All rights reserved as permitted by law. +// +// 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 ps / 1ps + +module encoder ( + input clkin, // pixel clock input + input rstin, // async. reset input (active high) + input [7:0] din, // data inputs: expect registered + input c0, // c0 input + input c1, // c1 input + input de, // de input + output reg [9:0] dout, // data outputs + input vid_gb // video preamble encoding select +); + + //////////////////////////////////////////////////////////// + // Counting number of 1s and 0s for each incoming pixel + // component. Pipe line the result. + // Register Data Input so it matches the pipe lined adder + // output + //////////////////////////////////////////////////////////// + reg [3:0] n1d; //number of 1s in din + reg [7:0] din_q; + + always @ (posedge clkin) begin + n1d <=#1 din[0] + din[1] + din[2] + din[3] + din[4] + din[5] + din[6] + din[7]; + + din_q <=#1 din; + end + + /////////////////////////////////////////////////////// + // Stage 1: 8 bit -> 9 bit + // Refer to DVI 1.0 Specification, page 29, Figure 3-5 + /////////////////////////////////////////////////////// + wire decision1; + + assign decision1 = (n1d > 4'h4) | ((n1d == 4'h4) & (din_q[0] == 1'b0)); +/* + reg [8:0] q_m; + always @ (posedge clkin) begin + q_m[0] <=#1 din_q[0]; + q_m[1] <=#1 (decision1) ? (q_m[0] ^~ din_q[1]) : (q_m[0] ^ din_q[1]); + q_m[2] <=#1 (decision1) ? (q_m[1] ^~ din_q[2]) : (q_m[1] ^ din_q[2]); + q_m[3] <=#1 (decision1) ? (q_m[2] ^~ din_q[3]) : (q_m[2] ^ din_q[3]); + q_m[4] <=#1 (decision1) ? (q_m[3] ^~ din_q[4]) : (q_m[3] ^ din_q[4]); + q_m[5] <=#1 (decision1) ? (q_m[4] ^~ din_q[5]) : (q_m[4] ^ din_q[5]); + q_m[6] <=#1 (decision1) ? (q_m[5] ^~ din_q[6]) : (q_m[5] ^ din_q[6]); + q_m[7] <=#1 (decision1) ? (q_m[6] ^~ din_q[7]) : (q_m[6] ^ din_q[7]); + q_m[8] <=#1 (decision1) ? 1'b0 : 1'b1; + end +*/ + wire [8:0] q_m; + assign q_m[0] = din_q[0]; + assign q_m[1] = (decision1) ? (q_m[0] ^~ din_q[1]) : (q_m[0] ^ din_q[1]); + assign q_m[2] = (decision1) ? (q_m[1] ^~ din_q[2]) : (q_m[1] ^ din_q[2]); + assign q_m[3] = (decision1) ? (q_m[2] ^~ din_q[3]) : (q_m[2] ^ din_q[3]); + assign q_m[4] = (decision1) ? (q_m[3] ^~ din_q[4]) : (q_m[3] ^ din_q[4]); + assign q_m[5] = (decision1) ? (q_m[4] ^~ din_q[5]) : (q_m[4] ^ din_q[5]); + assign q_m[6] = (decision1) ? (q_m[5] ^~ din_q[6]) : (q_m[5] ^ din_q[6]); + assign q_m[7] = (decision1) ? (q_m[6] ^~ din_q[7]) : (q_m[6] ^ din_q[7]); + assign q_m[8] = (decision1) ? 1'b0 : 1'b1; + + ///////////////////////////////////////////////////////// + // Stage 2: 9 bit -> 10 bit + // Refer to DVI 1.0 Specification, page 29, Figure 3-5 + ///////////////////////////////////////////////////////// + reg [3:0] n1q_m, n0q_m; // number of 1s and 0s for q_m + always @ (posedge clkin) begin + n1q_m <=#1 q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7]; + n0q_m <=#1 4'h8 - (q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7]); + end + + parameter CTRLTOKEN0 = 10'b1101010100; + parameter CTRLTOKEN1 = 10'b0010101011; + parameter CTRLTOKEN2 = 10'b0101010100; + parameter CTRLTOKEN3 = 10'b1010101011; + + parameter CTLBLUGB = 10'b1011001100; + parameter CTLGRNGB = 10'b0100110011; + parameter CTLREDGB = 10'b1011001100; + + reg [4:0] cnt; //disparity counter, MSB is the sign bit + wire decision2, decision3; + + assign decision2 = (cnt == 5'h0) | (n1q_m == n0q_m); + ///////////////////////////////////////////////////////////////////////// + // [(cnt > 0) and (N1q_m > N0q_m)] or [(cnt < 0) and (N0q_m > N1q_m)] + ///////////////////////////////////////////////////////////////////////// + assign decision3 = (~cnt[4] & (n1q_m > n0q_m)) | (cnt[4] & (n0q_m > n1q_m)); + + //////////////////////////////////// + // pipe line alignment + //////////////////////////////////// + reg de_q, de_reg; + reg c0_q, c1_q; + reg c0_reg, c1_reg; + reg [8:0] q_m_reg; + reg vid_gb_q, vid_gb_reg; + + + always @ (posedge clkin) begin + de_q <=#1 de; + de_reg <=#1 de_q; + + c0_q <=#1 c0; + c0_reg <=#1 c0_q; + c1_q <=#1 c1; + c1_reg <=#1 c1_q; + + q_m_reg <=#1 q_m; + + vid_gb_q <=#1 vid_gb; + vid_gb_reg <=#1 vid_gb_q; + end + + /////////////////////////////// + // 10-bit out + // disparity counter + /////////////////////////////// + always @ (posedge clkin or posedge rstin) begin + if(rstin) begin + dout <= 10'h0; + cnt <= 5'h0; + end else begin + if(vid_gb_reg) begin + // video preamble coding + dout[9:0] <= CTLREDGB; + end else begin + if (de_reg) begin + if(decision2) begin + dout[9] <=#1 ~q_m_reg[8]; + dout[8] <=#1 q_m_reg[8]; + dout[7:0] <=#1 (q_m_reg[8]) ? q_m_reg[7:0] : ~q_m_reg[7:0]; + + cnt <=#1 (~q_m_reg[8]) ? (cnt + n0q_m - n1q_m) : (cnt + n1q_m - n0q_m); + end else begin + if(decision3) begin + dout[9] <=#1 1'b1; + dout[8] <=#1 q_m_reg[8]; + dout[7:0] <=#1 ~q_m_reg[7:0]; + + cnt <=#1 cnt + {q_m_reg[8], 1'b0} + (n0q_m - n1q_m); + end else begin + dout[9] <=#1 1'b0; + dout[8] <=#1 q_m_reg[8]; + dout[7:0] <=#1 q_m_reg[7:0]; + + cnt <=#1 cnt - {~q_m_reg[8], 1'b0} + (n1q_m - n0q_m); + end + end + end else begin + case ({c1_reg, c0_reg}) + 2'b00: dout <=#1 CTRLTOKEN0; + 2'b01: dout <=#1 CTRLTOKEN1; + 2'b10: dout <=#1 CTRLTOKEN2; + default: dout <=#1 CTRLTOKEN3; + endcase + + cnt <=#1 5'h0; + end // else: !if(de_reg) + end // if (vid_gb) + end // else: !if(rstin) + end // always @ (posedge clkin or posedge rstin) + + +endmodule diff --git a/tx/gbgen.v b/tx/gbgen.v new file mode 100755 index 0000000..b7f0607 --- /dev/null +++ b/tx/gbgen.v @@ -0,0 +1,106 @@ +////////////////////////////////////////////////////////////////////////////// +// 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. +// +////////////////////////////////////////////////////////////////////////////// +module gbgen ( + input pclk, + input rstin, + input vsync, + input hsync, + input sync_pol, + input de, + + output gb, + output [3:0] code + ); + + reg [11:0] hpixel; + wire [11:0] papos; + reg [11:0] depos; + + reg hsync_v; // active when high + reg hsync_v2; + reg vsync_v; + reg vsync_v2; + reg de_d; + + wire hsync_rising; + wire vsync_rising; + + wire pa; + + always @(posedge pclk) begin + if( rstin ) begin + hpixel <= 0; + depos <= 0; + end else begin + if( hsync_rising ) begin + hpixel <= 0; + end else begin + hpixel <= hpixel + 12'b1; + end + + if( de && !de_d ) begin // de is rising + depos <= hpixel; + end else begin + depos <= depos; + end + end // else: !if( rstin ) + end // always @ (posedge pclk) + + assign papos = depos - 12'd10; // note: decimal 10, not binary 10 + // 0 1 2 3 4 5 6 7 + // | | | | | | | | + // ct pa gb gb v v v v + // + // depos = 4 + // gbpos = 2 + + assign gb = (hpixel >= (depos - 12'd2)) && (hpixel < depos); + assign pa = (hpixel >= papos) && (hpixel < depos); + + assign code[3:1] = 3'b0; // these never change + assign code[0] = pa; // this is the only bit that changes on pre-amble + + 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 + hsync_v <= hsync ^ !sync_pol; + vsync_v <= vsync ^ !sync_pol; + + de_d <= de; + 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; + +endmodule // gbgen diff --git a/tx/serdes_n_to_1.v b/tx/serdes_n_to_1.v new file mode 100755 index 0000000..bc384b6 --- /dev/null +++ b/tx/serdes_n_to_1.v @@ -0,0 +1,150 @@ +////////////////////////////////////////////////////////////////////////////// +// +// Xilinx, Inc. 2008 www.xilinx.com +// +////////////////////////////////////////////////////////////////////////////// +// +// File name : serdes_n_to_1.v +// +// Description : 1-bit generic n:1 transmitter module +// Takes in n bits of data and serialises this to 1 bit +// data is transmitted LSB first +// 0, 1, 2 ...... +// +// Date - revision : August 1st 2008 - v 1.0 +// +// Author : NJS +// +// Disclaimer: LIMITED WARRANTY AND DISCLAMER. These designs are +// provided to you "as is". Xilinx and its licensors make and you +// receive no warranties or conditions, express, implied, +// statutory or otherwise, and Xilinx specifically disclaims any +// implied warranties of merchantability, non-infringement,or +// fitness for a particular purpose. Xilinx does not warrant that +// the functions contained in these designs will meet your +// requirements, or that the operation of these designs will be +// uninterrupted or error free, or that defects in the Designs +// will be corrected. Furthermore, Xilinx does not warrantor +// make any representations regarding use or the results of the +// use of the designs in terms of correctness, accuracy, +// reliability, or otherwise. +// +// LIMITATION OF LIABILITY. In no event will Xilinx or its +// licensors be liable for any loss of data, lost profits,cost +// or procurement of substitute goods or services, or for any +// special, incidental, consequential, or indirect damages +// arising from the use or operation of the designs or +// accompanying documentation, however caused and on any theory +// of liability. This limitation will apply even if Xilinx +// has been advised of the possibility of such damage. This +// limitation shall apply not-withstanding the failure of the +// essential purpose of any limited remedies herein. +// +// Copyright © 2008 Xilinx, Inc. +// All rights reserved +// +////////////////////////////////////////////////////////////////////////////// +// + +`timescale 1ps/1ps + +module serdes_n_to_1 (ioclk, serdesstrobe, reset, gclk, datain, iob_data_out) ; + +parameter integer SF = 8 ; // Parameter to set the serdes factor 1..8 + +input ioclk ; // IO Clock network +input serdesstrobe ; // Parallel data capture strobe +input reset ; // Reset +input gclk ; // Global clock +input [SF-1 : 0] datain ; // Data for output +output iob_data_out ; // output data + +wire cascade_di ; // +wire cascade_do ; // +wire cascade_ti ; // +wire cascade_to ; // +wire [8:0] mdatain ; // + +genvar i ; // Pad out the input data bus with 0's to 8 bits to avoid errors +generate +for (i = 0 ; i <= (SF - 1) ; i = i + 1) +begin : loop0 +assign mdatain[i] = datain[i] ; +end +endgenerate +generate +for (i = (SF) ; i <= 8 ; i = i + 1) +begin : loop1 +assign mdatain[i] = 1'b0 ; +end +endgenerate + +OSERDES2 #( + .DATA_WIDTH (SF), // SERDES word width. This should match the setting is BUFPLL + .DATA_RATE_OQ ("SDR"), // , DDR + .DATA_RATE_OT ("SDR"), // , DDR + .SERDES_MODE ("MASTER"), // , MASTER, SLAVE + .OUTPUT_MODE ("DIFFERENTIAL")) +oserdes_m ( + .OQ (iob_data_out), + .OCE (1'b1), + .CLK0 (ioclk), + .CLK1 (1'b0), + .IOCE (serdesstrobe), + .RST (reset), + .CLKDIV (gclk), + .D4 (mdatain[7]), + .D3 (mdatain[6]), + .D2 (mdatain[5]), + .D1 (mdatain[4]), + .TQ (), + .T1 (1'b0), + .T2 (1'b0), + .T3 (1'b0), + .T4 (1'b0), + .TRAIN (1'b0), + .TCE (1'b1), + .SHIFTIN1 (1'b1), // Dummy input in Master + .SHIFTIN2 (1'b1), // Dummy input in Master + .SHIFTIN3 (cascade_do), // Cascade output D data from slave + .SHIFTIN4 (cascade_to), // Cascade output T data from slave + .SHIFTOUT1 (cascade_di), // Cascade input D data to slave + .SHIFTOUT2 (cascade_ti), // Cascade input T data to slave + .SHIFTOUT3 (), // Dummy output in Master + .SHIFTOUT4 ()) ; // Dummy output in Master + +OSERDES2 #( + .DATA_WIDTH (SF), // SERDES word width. This should match the setting is BUFPLL + .DATA_RATE_OQ ("SDR"), // , DDR + .DATA_RATE_OT ("SDR"), // , DDR + .SERDES_MODE ("SLAVE"), // , MASTER, SLAVE + .OUTPUT_MODE ("DIFFERENTIAL")) +oserdes_s ( + .OQ (), + .OCE (1'b1), + .CLK0 (ioclk), + .CLK1 (1'b0), + .IOCE (serdesstrobe), + .RST (reset), + .CLKDIV (gclk), + .D4 (mdatain[3]), + .D3 (mdatain[2]), + .D2 (mdatain[1]), + .D1 (mdatain[0]), + .TQ (), + .T1 (1'b0), + .T2 (1'b0), + .T3 (1'b0), + .T4 (1'b0), + .TRAIN (1'b0), + .TCE (1'b1), + .SHIFTIN1 (cascade_di), // Cascade input D from Master + .SHIFTIN2 (cascade_ti), // Cascade input T from Master + .SHIFTIN3 (1'b1), // Dummy input in Slave + .SHIFTIN4 (1'b1), // Dummy input in Slave + .SHIFTOUT1 (), // Dummy output in Slave + .SHIFTOUT2 (), // Dummy output in Slave + .SHIFTOUT3 (cascade_do), // Cascade output D data to Master + .SHIFTOUT4 (cascade_to)) ; // Cascade output T data to Master + +endmodule -- cgit v1.2.3