/*! * Module:clocks393m * @file clocks393m.v * @date 2015-07-17 * @author Andrey Filippov * * @brief Generating global clocks for x393 (excluding memcntrl and SATA) * * @copyright Copyright (c) 2015 Elphel, Inc . * * License: * * clocks393m.v is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * clocks393m.v is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Additional permission under GNU GPL version 3 section 7: * If you modify this Program, or any covered work, by linking or combining it * with independent modules provided by the FPGA vendor only (this permission * does not extend to any 3-rd party modules, "soft cores" or macros) under * different license terms solely for the purpose of generating binary "bitstream" * files and/or simulating the code, the copyright holders of this Program give * you the right to distribute the covered work without those independent modules * as long as the source code for them is available from the FPGA vendor free of * charge, and there is no dependence on any encrypted modules for simulating of * the combined code. This permission applies to you if the distributed code * contains all the components and scripts required to completely simulate it * with at least one of the Free Software programs. */ `timescale 1ns/1ps module clocks393m#( parameter CLK_ADDR = 'h728, // ..'h729 parameter CLK_MASK = 'h7fe, // parameter CLK_STATUS_REG_ADDR = 'h3a, // parameter CLK_CNTRL = 0, parameter CLK_STATUS = 1, parameter CLK_RESET = 'h0, // which clocks should stay reset after release of masrter reset {ff1,ff0,mem,sync,xclk,pclk,xclk} parameter CLK_PWDWN = 'h0, // which clocks should stay powered down after release of masrter reset {sync,xclk,pclk,xclk} // CLocks derived from external clock source (for sesnors parameter CLKIN_PERIOD_PCLK = 42, // 24MHz parameter DIVCLK_DIVIDE_PCLK = 1, parameter CLKFBOUT_MULT_PCLK = 40, // 960 MHz parameter CLKOUT_DIV_PCLK = 10, // 96MHz parameter BUF_CLK1X_PCLK = "BUFG", `ifdef USE_PCLK2X parameter CLKOUT_DIV_PCLK2X = 5, // 192 MHz parameter PHASE_CLK2X_PCLK = 0.000, parameter BUF_CLK1X_PCLK2X = "BUFG", `endif /* Mutltiple clocks derived from PS source (excluding memory controller) using a single PLL Fvco = 1200Mhz - maximal for spped grade -1 */ parameter MULTICLK_IN_PERIOD = 20, // 50MHz parameter MULTICLK_DIVCLK = 1, // parameter MULTICLK_MULT = 24, //1200MHz parameter MULTICLK_DIV_DLYREF = 6, // 6 - 200MHz I/O delay reference clock (4 - 300MHz) parameter MULTICLK_DIV_AXIHP = 8, // 150 MHz for AXI HP parameter MULTICLK_DIV_XCLK = 5, // 240 MHz for compressor (12 for 100 MHz) `ifdef USE_XCLK2X parameter MULTICLK_DIV_XCLK2X = 6, // 200 MHz for compressor (when MULTICLK_DIV_XCLK uses 100 MHz) `endif parameter MULTICLK_DIV_SYNC = 12, // 100 MHz for inter-camera synchronization and time keeping // Additional parameters for multi-clock PLL (phases and buffer types) parameter MULTICLK_PHASE_FB = 0.0, parameter MULTICLK_PHASE_DLYREF = 0.0, parameter MULTICLK_BUF_DLYREF = "BUFG", parameter MULTICLK_PHASE_AXIHP = 0.0, parameter MULTICLK_BUF_AXIHP = "BUFG", parameter MULTICLK_PHASE_XCLK = 0.0, parameter MULTICLK_BUF_XCLK = "BUFG", `ifdef USE_XCLK2X parameter MULTICLK_PHASE_XCLK2X = 0.0, parameter MULTICLK_BUF_XCLK2X = "BUFG", `endif parameter MULTICLK_PHASE_SYNC = 0.0, parameter MULTICLK_BUF_SYNC = "BUFG", parameter MEMCLK_CAPACITANCE = "DONT_CARE", parameter MEMCLK_IBUF_LOW_PWR = "TRUE", parameter MEMCLK_IOSTANDARD = "DEFAULT", parameter FFCLK0_CAPACITANCE = "DONT_CARE", parameter FFCLK0_DIFF_TERM = "FALSE", parameter FFCLK0_IBUF_LOW_PWR = "TRUE", parameter FFCLK0_IOSTANDARD = "DEFAULT", parameter FFCLK1_CAPACITANCE = "DONT_CARE", parameter FFCLK1_DIFF_TERM = "FALSE", parameter FFCLK1_IBUF_LOW_PWR = "TRUE", parameter FFCLK1_IOSTANDARD = "DEFAULT" )( input async_rst, // always reset MMCM/PLL input mclk, // global clock, comes from the memory controller (uses aclk generated here) input mrst, // command/status interface input [7:0] cmd_ad, // byte-serial command address/data (up to 6 bytes: AL-AH-D0-D1-D2-D3 input cmd_stb, // strobe (with first byte) for the command a/d output [7:0] status_ad, // status address/data - up to 5 bytes: A - {seq,status[1:0]} - status[2:9] - status[10:17] - status[18:25] output status_rq, // input request to send status downstream input status_start, // Acknowledge of the first status packet byte (address) input [3:0] fclk, // 4 clocks coming from the Zynq PS. Currently only [0] is used input memclk_pad, // connected to external clock generator (VDD=1.5V) input ffclk0p_pad, // differential clock (P) same power as sensors 0 and 1 (VCC_SENS01) input ffclk0n_pad, // differential clock (N) same power as sensors 0 and 1 (VCC_SENS01) input ffclk1p_pad, // differential clock (P) same power as sensors 0 and 1 (VCC_SENS01) input ffclk1n_pad, // differential clock (N) same power as sensors 0 and 1 (VCC_SENS01) output aclk, // global clock 50 MHz (used for maxi0) output hclk, // global clock 150MHz (used for afi*, saxi*) output pclk, // global clock for sensors (now 96MHz), based on external clock generator `ifdef USE_PCLK2X output pclk2x, // global clock for sensors, 2x frequency (now 192MHz) `endif output xclk, // global clock for compressor (now 100MHz) `ifdef USE_XCLK2X output xclk2x, // global clock for compressor, 2x frequency (now 200MHz) `endif output sync_clk, // global clock for camsync module (96 MHz for 353 compatibility - switch to 100MHz)? output time_ref, // non-global, just RTC (currently just mclk/8 = 25 MHz) output dly_ref_clk, // global clock for I/O delays calibration input [1:0] extra_status, // just extra two status bits from the top module output locked_sync_clk, output locked_xclk, output locked_pclk, output locked_hclk ); wire memclk; wire ffclk0; wire ffclk1; wire [8:0] status_data; wire [10:0] cmd_data; wire cmd_we; wire [0:0] cmd_a; wire set_ctrl_w = cmd_we & ((cmd_a && CLK_MASK) == CLK_CNTRL); wire set_status_w = cmd_we & ((cmd_a && CLK_MASK) == CLK_STATUS); wire [3:0] locked; reg [6:0] reset_clk = CLK_RESET; reg [3:0] pwrdwn_clk = CLK_PWDWN; reg [2:0] test_clk; // FF to test input clocks are running wire memclk_rst = reset_clk[4]; wire ffclk0_rst = reset_clk[5]; wire ffclk1_rst = reset_clk[6]; assign locked[3:2] = 3; // for compatibility with previous clocks393.v module assign locked_sync_clk = locked[3]; assign locked_xclk = locked[2]; assign locked_pclk = locked[1]; assign locked_hclk = locked[0]; always @ (posedge mclk) begin if (mrst) reset_clk <= CLK_RESET; else if (set_ctrl_w) reset_clk <= {cmd_data[10:8], cmd_data[3:0]}; if (mrst) pwrdwn_clk <= CLK_PWDWN; else if (set_ctrl_w) pwrdwn_clk <= cmd_data[7:4]; end assign status_data = {test_clk, locked, extra_status}; always @ (posedge memclk or posedge memclk_rst) if (async_rst || memclk_rst) test_clk[0] <= 0; else test_clk[0] <= ~test_clk[0]; always @ (posedge ffclk0 or posedge ffclk0_rst) if (async_rst || ffclk0_rst) test_clk[1] <= 0; else test_clk[1] <= ~test_clk[1]; always @ (posedge ffclk1 or posedge ffclk1_rst) if (async_rst || ffclk1_rst) test_clk[2] <= 0; else test_clk[2] <= ~test_clk[2]; cmd_deser #( .ADDR (CLK_ADDR), .ADDR_MASK (CLK_MASK), .NUM_CYCLES (4), .ADDR_WIDTH (1), .DATA_WIDTH (11) ) cmd_deser_32bit_i ( .rst (1'b0), // rst), // input .clk (mclk), // input .srst (mrst), // input .ad (cmd_ad), // input[7:0] .stb (cmd_stb), // input .addr (cmd_a), // output[3:0] .data (cmd_data), // output[31:0] .we (cmd_we) // output ); status_generate #( .STATUS_REG_ADDR (CLK_STATUS_REG_ADDR), .PAYLOAD_BITS (9), .REGISTER_STATUS (0) ) status_generate_i ( .rst (1'b0), // rst), // input .clk (mclk), // input .srst (mrst), // input .we (set_status_w), // input .wd (cmd_data[7:0]), // input[7:0] .status (status_data), // input[14:0] .ad (status_ad), // output[7:0] .rq (status_rq), // output .start (status_start) // input ); BUFG bufg_axi_aclk_i (.O(aclk), .I(fclk[0])); // PS clock, 50MHz // from external clock sourec dual_clock_source #( .CLKIN_PERIOD (CLKIN_PERIOD_PCLK), .DIVCLK_DIVIDE (DIVCLK_DIVIDE_PCLK), .CLKFBOUT_MULT (CLKFBOUT_MULT_PCLK), .CLKOUT_DIV_CLK1X (CLKOUT_DIV_PCLK), .BUF_CLK1X (BUF_CLK1X_PCLK) `ifdef USE_PCLK2X ,.CLKOUT_DIV_CLK2X (CLKOUT_DIV_PCLK2X), .PHASE_CLK2X (PHASE_CLK2X_PCLK), .BUF_CLK2X (BUF_CLK1X_PCLK2X) `else ,.BUF_CLK2X ("NONE") `endif ) dual_clock_pclk_i ( .rst (async_rst || reset_clk[1]), // input .clk_in (ffclk0), // input .pwrdwn (pwrdwn_clk[1]), // input .clk1x (pclk), // output `ifdef USE_PCLK2X .clk2x (pclk2x), // output `else .clk2x (), // output not connected `endif .locked (locked[1]) // output ); wire multi_clkfb; wire hclk_pre; wire dly_ref_clk_pre; wire xclk_pre; wire sync_clk_pre; `ifdef USE_PCLK2X wire xclk2x_pre; `endif pll_base #( .CLKIN_PERIOD (MULTICLK_IN_PERIOD), // 20 .BANDWIDTH ("OPTIMIZED"), .DIVCLK_DIVIDE (MULTICLK_DIVCLK), .CLKFBOUT_MULT (MULTICLK_MULT), // 2..64, // Fvco=Fclkin*CLKFBOUT_MULT_F/DIVCLK_DIVIDE, Fout=Fvco/CLKOUT#_DIVIDE .CLKFBOUT_PHASE (MULTICLK_PHASE_FB), .CLKOUT0_DIVIDE (MULTICLK_DIV_AXIHP), .CLKOUT0_PHASE (MULTICLK_PHASE_AXIHP), .CLKOUT1_DIVIDE (MULTICLK_DIV_XCLK), .CLKOUT1_PHASE (MULTICLK_PHASE_XCLK), `ifdef USE_XCLK2X .CLKOUT2_DIVIDE (MULTICLK_DIV_XCLK2X), .CLKOUT2_PHASE (MULTICLK_PHASE_XCLK2X), `endif .CLKOUT3_DIVIDE (MULTICLK_DIV_SYNC), .CLKOUT3_PHASE (MULTICLK_PHASE_SYNC), .CLKOUT5_DIVIDE (MULTICLK_DIV_DLYREF), .CLKOUT5_PHASE (MULTICLK_PHASE_DLYREF), .REF_JITTER1 (0.010), .STARTUP_WAIT ("FALSE") ) pll_base_i ( .clkin(aclk), // input .clkfbin (multi_clkfb), // input .rst (async_rst || reset_clk[0]), // input TODO: check resets/ .pwrdwn (pwrdwn_clk[0]), // input .clkout0 (hclk_pre), // output .clkout1 (xclk_pre), // output `ifdef USE_PCLK2X .clkout2 (xclk2x_pre), // output `else .clkout2 (), // output `endif .clkout3 (sync_clk_pre), // output .clkout4 (), // output .clkout5 (dly_ref_clk_pre), // output .clkfbout (multi_clkfb), // output .locked (locked[0]) // output ); // Buffering clocks outputs select_clk_buf #(.BUFFER_TYPE(MULTICLK_BUF_DLYREF)) dly_ref_clk_i (.o(dly_ref_clk), .i(dly_ref_clk_pre), .clr(async_rst)); select_clk_buf #(.BUFFER_TYPE(MULTICLK_BUF_AXIHP)) hclk_i (.o(hclk), .i(hclk_pre), .clr(async_rst)); select_clk_buf #(.BUFFER_TYPE(MULTICLK_BUF_XCLK)) xclk_i (.o(xclk), .i(xclk_pre), .clr(async_rst)); // locked[2],pwrdwn_clk[2],reset_clk[2] `ifdef USE_XCLK2X select_clk_buf #(.BUFFER_TYPE(MULTICLK_BUF_XCLK2X)) xclk2x_i (.o(xclk2x), .i(xclk2x_pre), .clr(async_rst)); `endif select_clk_buf #(.BUFFER_TYPE(MULTICLK_BUF_SYNC)) sync_clk_i (.o(sync_clk), .i(sync_clk_pre), .clr(async_rst)); // locked[3],pwrdwn_clk[3],reset_clk[3] ibuf_ibufg #( .CAPACITANCE (MEMCLK_CAPACITANCE), .IBUF_LOW_PWR (MEMCLK_IBUF_LOW_PWR), .IOSTANDARD (MEMCLK_IOSTANDARD) ) ibuf_ibufg_i ( .O (memclk), // output .I (memclk_pad) // input ); ibufds_ibufgds #( .CAPACITANCE (FFCLK0_CAPACITANCE), .DIFF_TERM (FFCLK0_DIFF_TERM), .IBUF_LOW_PWR (FFCLK0_IBUF_LOW_PWR), .IOSTANDARD (FFCLK0_IOSTANDARD) ) ibufds_ibufgds0_i ( .O (ffclk0), // output .I (ffclk0p_pad), // input .IB (ffclk0n_pad) // input ); ibufds_ibufgds #( .CAPACITANCE (FFCLK1_CAPACITANCE), .DIFF_TERM (FFCLK1_DIFF_TERM), .IBUF_LOW_PWR (FFCLK1_IBUF_LOW_PWR), .IOSTANDARD (FFCLK1_IOSTANDARD) ) ibufds_ibufgds10_i ( .O (ffclk1), // output .I (ffclk1p_pad), // input .IB (ffclk1n_pad) // input ); // RTC reference: integer number of microseconds, less than mclk/2. Not a global clock // temporary: reg [2:0] time_ref_r; always @ (posedge mclk) if (mrst) time_ref_r <= 0; else time_ref_r <= time_ref_r + 1; assign time_ref = time_ref_r[2]; endmodule