/*! * Module:sens_hispi12l4 * @file sens_hispi12l4.v * @date 2015-10-13 * @author Andrey Filippov * * @brief Decode HiSPi 4-lane, 12 bits Packetized-SP data from the sensor * * @copyright Copyright (c) 2015 Elphel, Inc . * * License: * * sens_hispi12l4.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. * * sens_hispi12l4.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 sens_hispi12l4#( parameter IODELAY_GRP = "IODELAY_SENSOR", parameter integer IDELAY_VALUE = 0, parameter real REFCLK_FREQUENCY = 200.0, parameter HIGH_PERFORMANCE_MODE = "FALSE", parameter SENS_PHASE_WIDTH= 8, // number of bits for te phase counter (depends on divisors) // parameter SENS_PCLK_PERIOD = 3.000, // input period in ns, 0..100.000 - MANDATORY, resolution down to 1 ps parameter SENS_BANDWIDTH = "OPTIMIZED", //"OPTIMIZED", "HIGH","LOW" parameter CLKIN_PERIOD_SENSOR = 3.000, // input period in ns, 0..100.000 - MANDATORY, resolution down to 1 ps parameter CLKFBOUT_MULT_SENSOR = 3, // 330 MHz --> 990 MHz parameter CLKFBOUT_PHASE_SENSOR = 0.000, // CLOCK FEEDBACK phase in degrees (3 significant digits, -360.000...+360.000) parameter IPCLK_PHASE = 0.000, parameter IPCLK2X_PHASE = 0.000, parameter BUF_IPCLK = "BUFR", parameter BUF_IPCLK2X = "BUFR", parameter SENS_DIVCLK_DIVIDE = 1, // Integer 1..106. Divides all outputs with respect to CLKIN parameter SENS_REF_JITTER1 = 0.010, // Expected jitter on CLKIN1 (0.000..0.999) parameter SENS_REF_JITTER2 = 0.010, parameter SENS_SS_EN = "FALSE", // Enables Spread Spectrum mode parameter SENS_SS_MODE = "CENTER_HIGH",//"CENTER_HIGH","CENTER_LOW","DOWN_HIGH","DOWN_LOW" parameter SENS_SS_MOD_PERIOD = 10000, // integer 4000-40000 - SS modulation period in ns parameter DEFAULT_LANE_MAP = 8'b11100100, // one-to-one map (or make it 8'b00111001 ?) parameter HISPI_MSB_FIRST = 0, parameter HISPI_NUMLANES = 4, parameter HISPI_DELAY_CLK = "FALSE", parameter HISPI_MMCM = "TRUE", parameter HISPI_KEEP_IRST = 5, // number of cycles to keep irst on after release of prst (small number - use 1 hot) parameter HISPI_WAIT_ALL_LANES = 4'h8, // number of output pixel cycles to wait after the earliest lane parameter HISPI_FIFO_DEPTH = 4, parameter HISPI_FIFO_START = 7, parameter HISPI_CAPACITANCE = "DONT_CARE", parameter HISPI_DIFF_TERM = "TRUE", parameter HISPI_UNTUNED_SPLIT = "FALSE", // Very power-hungry parameter HISPI_DQS_BIAS = "TRUE", parameter HISPI_IBUF_DELAY_VALUE = "0", parameter HISPI_IBUF_LOW_PWR = "TRUE", parameter HISPI_IFD_DELAY_VALUE = "AUTO", parameter HISPI_IOSTANDARD = "DIFF_SSTL18_I" //"DIFF_SSTL18_II" for high current (13.4mA vs 8mA), )( input pclk, // global clock input, pixel rate (220MHz for MT9F002) input prst, // reset @pclk (add sensor reset here) // I/O pads input [HISPI_NUMLANES-1:0] sns_dp, input [HISPI_NUMLANES-1:0] sns_dn, input sns_clkp, input sns_clkn, // output // output reg [11:0] pxd_out, output [11:0] pxd_out, // output reg vact_out, output hact_out, output sof, // @pclk output reg eof, // @pclk // delay control inputs input mclk, input mrst, input [HISPI_NUMLANES * 8-1:0] dly_data, // delay value (3 LSB - fine delay) - @posedge mclk input set_lanes_map, // set number of physical lane for each logical one input set_fifo_dly, input [HISPI_NUMLANES-1:0] set_idelay, // mclk synchronous load idelay value input ld_idelay, // mclk synchronous set idealy value input set_clk_phase, // mclk synchronous set idealy value input rst_mmcm, input ignore_embedded, // ignore lines with embedded data // input wait_all_lanes, // when 0 allow some lanes missing sync (for easier phase adjustment) // MMCP output status output ps_rdy, // output output [7:0] ps_out, // output[7:0] reg output locked_pxd_mmcm, output clkin_pxd_stopped_mmcm, // output output clkfb_pxd_stopped_mmcm // output ); wire ipclk; // re-generated half HiSPi clock (165 MHz) wire ipclk2x;// re-generated HiSPi clock (330 MHz) wire [HISPI_NUMLANES * 4-1:0] sns_d; // localparam WAIT_ALL_LANES = 4'h8; // number of output pixel cycles to wait after the earliest lane // localparam FIFO_DEPTH = 4; reg [HISPI_KEEP_IRST-1:0] irst_r; wire irst = irst_r[0]; reg [HISPI_NUMLANES * 2-1:0] lanes_map; reg [HISPI_NUMLANES * 4-1:0] logical_lanes4; reg [HISPI_FIFO_DEPTH-1:0] fifo_out_dly_mclk; reg [HISPI_FIFO_DEPTH-1:0] fifo_out_dly; always @ (posedge mclk) begin if (mrst) lanes_map <= DEFAULT_LANE_MAP; //{2'h3,2'h2,2'h1,2'h0}; // 1-to-1 default map else if (set_lanes_map) lanes_map <= dly_data[HISPI_NUMLANES * 2-1:0]; if (mrst) fifo_out_dly_mclk <= HISPI_FIFO_START; else if (set_fifo_dly) fifo_out_dly_mclk <= dly_data[HISPI_FIFO_DEPTH-1:0]; end //non-parametrized lane switch (4x4) always @(posedge ipclk) begin logical_lanes4[ 3: 0] <= sns_d[{lanes_map[1:0],2'b0} +:4]; logical_lanes4[ 7: 4] <= sns_d[{lanes_map[3:2],2'b0} +:4]; logical_lanes4[11: 8] <= sns_d[{lanes_map[5:4],2'b0} +:4]; logical_lanes4[15:12] <= sns_d[{lanes_map[7:6],2'b0} +:4]; end always @(posedge ipclk) begin fifo_out_dly <= fifo_out_dly_mclk; end sens_hispi_clock #( .SENS_PHASE_WIDTH (SENS_PHASE_WIDTH), .SENS_BANDWIDTH (SENS_BANDWIDTH), .CLKIN_PERIOD_SENSOR (CLKIN_PERIOD_SENSOR), .CLKFBOUT_MULT_SENSOR (CLKFBOUT_MULT_SENSOR), .CLKFBOUT_PHASE_SENSOR (CLKFBOUT_PHASE_SENSOR), .IPCLK_PHASE (IPCLK_PHASE), .IPCLK2X_PHASE (IPCLK2X_PHASE), .BUF_IPCLK (BUF_IPCLK), .BUF_IPCLK2X (BUF_IPCLK2X), .SENS_DIVCLK_DIVIDE (SENS_DIVCLK_DIVIDE), .SENS_REF_JITTER1 (SENS_REF_JITTER1), .SENS_REF_JITTER2 (SENS_REF_JITTER2), .SENS_SS_EN (SENS_SS_EN), .SENS_SS_MODE (SENS_SS_MODE), .SENS_SS_MOD_PERIOD (SENS_SS_MOD_PERIOD), .IODELAY_GRP (IODELAY_GRP), .IDELAY_VALUE (IDELAY_VALUE), .REFCLK_FREQUENCY (REFCLK_FREQUENCY), .HIGH_PERFORMANCE_MODE (HIGH_PERFORMANCE_MODE), .HISPI_DELAY_CLK (HISPI_DELAY_CLK), .HISPI_MMCM (HISPI_MMCM), .HISPI_CAPACITANCE (HISPI_CAPACITANCE), .HISPI_DIFF_TERM (HISPI_DIFF_TERM), .HISPI_DQS_BIAS (HISPI_DQS_BIAS), .HISPI_IBUF_DELAY_VALUE (HISPI_IBUF_DELAY_VALUE), .HISPI_IBUF_LOW_PWR (HISPI_IBUF_LOW_PWR), .HISPI_IFD_DELAY_VALUE (HISPI_IFD_DELAY_VALUE), .HISPI_IOSTANDARD (HISPI_IOSTANDARD) ) sens_hispi_clock_i ( .mclk (mclk), // input .mrst (mrst), // input .phase (dly_data[7:0]), // input[7:0] .set_phase (set_clk_phase), // input .load (ld_idelay), // input .rst_mmcm (rst_mmcm), // input .clp_p (sns_clkp), // input .clk_n (sns_clkn), // input .ipclk (ipclk), // output .ipclk2x (ipclk2x), // output .ps_rdy (ps_rdy), // output .ps_out (ps_out), // output[7:0] .locked_pxd_mmcm (locked_pxd_mmcm), // output .clkin_pxd_stopped_mmcm (clkin_pxd_stopped_mmcm), // output .clkfb_pxd_stopped_mmcm (clkfb_pxd_stopped_mmcm) // output ); sens_hispi_din #( .IODELAY_GRP (IODELAY_GRP), .IDELAY_VALUE (IDELAY_VALUE), .REFCLK_FREQUENCY (REFCLK_FREQUENCY), .HIGH_PERFORMANCE_MODE (HIGH_PERFORMANCE_MODE), .HISPI_NUMLANES (HISPI_NUMLANES), .HISPI_CAPACITANCE (HISPI_CAPACITANCE), .HISPI_DIFF_TERM (HISPI_DIFF_TERM), .HISPI_UNTUNED_SPLIT (HISPI_UNTUNED_SPLIT), .HISPI_DQS_BIAS (HISPI_DQS_BIAS), .HISPI_IBUF_DELAY_VALUE (HISPI_IBUF_DELAY_VALUE), .HISPI_IBUF_LOW_PWR (HISPI_IBUF_LOW_PWR), .HISPI_IFD_DELAY_VALUE (HISPI_IFD_DELAY_VALUE), .HISPI_IOSTANDARD (HISPI_IOSTANDARD) ) sens_hispi_din_i ( .mclk (mclk), // input .mrst (mrst), // input .dly_data (dly_data), // input[31:0] .set_idelay (set_idelay), // input[3:0] .ld_idelay (ld_idelay), // input .ipclk (ipclk), // input .ipclk2x (ipclk2x), // input .irst (irst), // input //`ifdef REVERSE_LANES // .din_p ({sns_dp[0],sns_dp[1],sns_dp[2],sns_dp[3]}), // input[3:0] // .din_n ({sns_dn[0],sns_dn[1],sns_dn[2],sns_dn[3]}), // input[3:0] //`else .din_p (sns_dp), // input[3:0] .din_n (sns_dn), // input[3:0] //`endif .dout (sns_d) // output[15:0] ); wire [HISPI_NUMLANES * 12-1:0] hispi_aligned; wire [HISPI_NUMLANES-1:0] hispi_dv; wire [HISPI_NUMLANES-1:0] hispi_embed; wire [HISPI_NUMLANES-1:0] hispi_sof; wire [HISPI_NUMLANES-1:0] hispi_eof; wire [HISPI_NUMLANES-1:0] hispi_sol; wire [HISPI_NUMLANES-1:0] hispi_eol; // TODO - try to make that something will be recorded even if some lanes are bad (to simplify phase adjust // possibly - extra control bit (wait_all_lanes) // use earliest SOF reg vact_ipclk; reg [1:0] vact_pclk_strt; wire [HISPI_NUMLANES-1:0] rd_run; reg rd_line; // combine all lanes reg rd_line_r; wire sol_all_dly; reg [HISPI_NUMLANES-1:0] rd_run_d; reg sof_pclk; // wire [HISPI_NUMLANES-1:0] sol_pclk = rd_run & ~rd_run_d; wire sol_pclk = |(rd_run & ~rd_run_d); // possibly multi-cycle reg start_fifo_re; // start reading FIFO - single-cycle reg [HISPI_NUMLANES-1:0] good_lanes; // lanes that started active line OK reg [HISPI_NUMLANES-1:0] fifo_re; reg [HISPI_NUMLANES-1:0] fifo_re_r; reg hact_r; wire [HISPI_NUMLANES * 12-1:0] fifo_out; wire hact_on; wire hact_off; reg ignore_embedded_ipclk; reg [1:0] vact_pclk; wire [11:0] pxd_out_pre = ({12 {fifo_re_r[0] & rd_run[0]}} & fifo_out[0 * 12 +:12]) | ({12 {fifo_re_r[1] & rd_run[1]}} & fifo_out[1 * 12 +:12]) | ({12 {fifo_re_r[2] & rd_run[2]}} & fifo_out[2 * 12 +:12]) | ({12 {fifo_re_r[3] & rd_run[3]}} & fifo_out[3 * 12 +:12]); reg start_only; // time window at the beginning of each line, can not end here assign hact_out = hact_r; assign sof = sof_pclk; // async reset always @ (posedge ipclk or posedge prst) begin if (prst) irst_r <= {HISPI_KEEP_IRST{1'b1}}; // HISPI_KEEP_IRST-1 else irst_r <= irst_r >> 1; end always @(posedge ipclk) begin // irst_r <= {irst_r[1:0], prst}; if (irst || (|hispi_eof)) vact_ipclk <= 0; // extend output if hact active else if (|hispi_sof) vact_ipclk <= 1; ignore_embedded_ipclk <= ignore_embedded; end always @(posedge pclk) begin if (prst || !vact_ipclk) vact_pclk_strt <= 0; else vact_pclk_strt <= {vact_pclk_strt[0], 1'b1}; rd_run_d <= rd_run; start_fifo_re <= sol_pclk && !rd_line; // sol_pclk may be multi-cycle sof_pclk <= vact_pclk_strt[0] && ! vact_pclk_strt[1]; if (prst || sof_pclk || sol_all_dly) start_only <= 0; else if (sol_pclk) start_only <= 1; if (prst || sof_pclk) rd_line <= 0; else if (sol_pclk) rd_line <= 1; else rd_line <= rd_line & (start_only || (&(~good_lanes | rd_run))); // Off when first of the good lanes goes off rd_line_r <= rd_line; if (sol_pclk && !rd_line) good_lanes <= ~rd_run_d; // should be off before start else if (sol_all_dly) good_lanes <= good_lanes & rd_run; // and now they should be on fifo_re_r <= fifo_re & rd_run; // when data out is ready, mask if not running // not using HISPI_NUMLANES here - fix? Will be 0 (not possible in hispi) when no data /* pxd_out <= ({12 {fifo_re_r[0] & rd_run[0]}} & fifo_out[0 * 12 +:12]) | ({12 {fifo_re_r[1] & rd_run[1]}} & fifo_out[1 * 12 +:12]) | ({12 {fifo_re_r[2] & rd_run[2]}} & fifo_out[2 * 12 +:12]) | ({12 {fifo_re_r[3] & rd_run[3]}} & fifo_out[3 * 12 +:12]); */ if (prst) fifo_re <= 0; // else if (sol_pclk || (rd_line && fifo_re[HISPI_NUMLANES - 1])) fifo_re <= 1; else if (start_fifo_re || (rd_line && fifo_re[HISPI_NUMLANES - 1])) fifo_re <= 1; else fifo_re <= fifo_re << 1; // if (prst || (hact_off && (|(good_lanes & ~rd_run)))) hact_r <= 0; if (prst || (hact_off && (!rd_line || (good_lanes[3] & ~rd_run[3])))) hact_r <= 0; else if (hact_on) hact_r <= 1; vact_pclk <= {vact_pclk[0],vact_pclk_strt [0] || hact_r}; eof <= vact_pclk[1] && !vact_pclk[0]; // vact_out <= vact_pclk_strt [0] || hact_r; end dly_16 #( .WIDTH(1) ) dly_16_start_line_i ( .clk (pclk), // input .rst (1'b0), // input .dly (HISPI_WAIT_ALL_LANES), // input[3:0] .din (rd_line && !rd_line_r), // input[0:0] .dout (sol_all_dly) // output[0:0] ); dly_16 #( .WIDTH(1) ) dly_16_hact_on_i ( .clk (pclk), // input .rst (1'b0), // input // .dly (4'h2), // input[3:0] // .dly (4'h3), // input[3:0] .dly (4'h1), // input[3:0] // .dly (4'h2), // input[3:0] .din (sol_pclk), // input[0:0] .dout (hact_on) // output[0:0] ); dly_16 #( .WIDTH(1) ) dly_16_hact_off_i ( .clk (pclk), // input .rst (1'b0), // input // .dly (4'h2), // input[3:0] // .dly (4'h0), // input[3:0] .dly (4'h1), // input[3:0] // .dly (4'h2), // input[3:0] .din (fifo_re[HISPI_NUMLANES - 1]), // input[0:0] .dout (hact_off) // output[0:0] ); dly_16 #( .WIDTH(12) ) dly_16_pxd_out_i ( .clk (pclk), // input .rst (1'b0), // input // .dly (4'h2), // input[3:0] .dly (4'h0), // input[3:0] // .dly (4'h1), // input[3:0] .din (pxd_out_pre), // input[0:0] .dout (pxd_out) // output[0:0] ); generate genvar i; for (i=0; i < 4; i=i+1) begin: hispi_lane sens_hispi_lane #( .HISPI_MSB_FIRST(HISPI_MSB_FIRST) ) sens_hispi_lane_i ( .ipclk (ipclk), // input .irst (irst), // input .din (logical_lanes4[4*i +: 4]), // input[3:0] .dout (hispi_aligned[12*i +: 12]), // output[3:0] reg .dv (hispi_dv[i]), // output reg .embed (hispi_embed[i]), // output reg .sof (hispi_sof[i]), // output reg .eof (hispi_eof[i]), // output reg .sol (hispi_sol[i]), // output reg .eol (hispi_eol[i]) // output reg ); sens_hispi_fifo #( // .COUNT_START (HISPI_FIFO_START), .DATA_WIDTH (12), .DATA_DEPTH (HISPI_FIFO_DEPTH) ) sens_hispi_fifo_i ( .ipclk (ipclk), // input .irst (irst), // input .we (hispi_dv[i]), // input .sol (hispi_sol[i] && !(hispi_embed[i] && ignore_embedded_ipclk)), // input .eol (hispi_eol[i]), // input .din (hispi_aligned[12*i +: 12]), // input[11:0] .out_dly (fifo_out_dly), // input[3:0] .pclk (pclk), // input .prst (prst), // input .re (fifo_re[i]), // input .dout (fifo_out[12*i +: 12]), // output[11:0] reg .run (rd_run[i]) // output ); end endgenerate endmodule