/******************************************************************************* * Module: sens_hispi_lane * Date:2015-10-13 * Author: andrey * Description: Decode a single lane of the HiSPi data assuming packetized-SP protocol * * Copyright (c) 2015 Elphel, Inc . * sens_hispi_lane.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_hispi_lane.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_hispi_lane#( parameter HISPI_MSB_FIRST = 0 )( input ipclk, // half HiSPi recovered clock (165 MHz for 660 bps of MT9F002) input irst, // reset sync to ipclk input [3:0] din, // @posedge ipclk, din[3] came first output reg [11:0] dout, // 12-bit data output output reg dv, // data valid - continuous marks line output reg embed, // valid @sol and up through all dv output reg sof, // always before first sol - not instead of output reg eof, // always after last eol (not instead of) output reg sol, // start of line - 1 cycle before dv output reg eol // end of line - last dv ); localparam [3:0] SYNC_SOF = HISPI_MSB_FIRST ? 4'h3 : 4'hc; localparam [3:0] SYNC_SOL = HISPI_MSB_FIRST ? 4'h1 : 4'h8; localparam [3:0] SYNC_EOF = HISPI_MSB_FIRST ? 4'h7 : 4'he; // localparam [3:0] SYNC_EOL = 6; localparam [3:0] SYNC_EMBED = HISPI_MSB_FIRST ? 4'h1 : 4'h8; // other nibble (bit 4) localparam LSB_INDEX = HISPI_MSB_FIRST ? 2 : 0; // nibble number in 12-bit word localparam MSB_INDEX = HISPI_MSB_FIRST ? 0 : 2; // nibble number in 12-bit word reg [3:0] d_r; // rehistered input data wire [2:0] num_trail_0_w; // number of trailing 0-s in the last nibble wire [2:0] num_lead_0_w; // number of leading 0-s in the last nibble wire [2:0] num_trail_1_w; // number of trailing 1-s in the last nibble wire [2:0] num_lead_1_w; // number of leading 1-s in the last nibble wire zero_after_ones_w; reg [3:0] num_running_ones; reg [4:0] num_running_zeros; // after sufficient ones reg prev4ones; // previous data nibble was 4'hf reg [1:0] num_first_zeros; // number of zeros in a first nibble after all ones reg [1:0] shift_val; // barrel shifter select (0 is 4!) wire got_sync_w = !num_running_zeros[3] && (&num_running_zeros_w[4:3]); reg got_sync; // Got 24 zeros after >=16 1-s reg [3:0] barrel; reg [3:0] sync_decode; // 1-hot decoding of the last sync word reg got_sof; reg got_eof; reg got_sol; // reg got_eol; reg got_embed; reg [2:0] pre_dv; wire [3:0] dout_w; wire [4:0] num_running_zeros_w = num_running_zeros + {1'b0, num_lead_0_w}; wire start_line = sync_decode[3] && (got_sol || got_sof); wire start_line_d; // delayed just to turn on pre_dv; assign num_trail_0_w = (|din) ? ((|din[2:0]) ? ((|din[1:0]) ? (din[0] ? 3'h0 : 3'h1) : 3'h2) : 3'h3) : 3'h4; assign num_lead_0_w = (|din) ? ((|din[3:1]) ? ((|din[3:2]) ? (din[3] ? 3'h0 : 3'h1) : 3'h2) : 3'h3) : 3'h4; assign num_trail_1_w = (&din) ? 3'h4 : ((&din[2:0]) ? 3'h3 : ((&din[1:0]) ? 3'h2 :((&din[0]) ? 3'h1 : 3'h0))); assign num_lead_1_w = (&din) ? 3'h4 : ((&din[3:1]) ? 3'h3 : ((&din[3:2]) ? 3'h2 :((&din[3]) ? 3'h1 : 3'h0))); // assign zero_after_ones_w = !((din[0] && !din[1]) || (din[1] && !din[2]) || (din[2] && !din[3]) || (d_r[3] && !din[0])); assign zero_after_ones_w = !((din[0] && !din[1]) || (din[1] && !din[2]) || (din[2] && !din[3]) || (din[3] && !d_r[0])); always @(posedge ipclk) begin d_r <= din; prev4ones <= num_trail_1_w[2]; if (prev4ones && !num_trail_1_w[2]) num_first_zeros <= num_trail_0_w[1:0]; // 4 will be 0 // first stage - get at least 12 consecutive 1-s, expecting many consecutive 0-s after, so any // 1 after zero should restart counting. if (irst) num_running_ones <= 0; else if ((num_running_ones == 0) || !zero_after_ones_w) num_running_ones <= {1'b0,num_trail_1_w}; // keep number of running 1-s saturated to 12 (to prevent roll over). Temporary there could be 13..15 // When running 1-s turn to running zeros, the count will not reset and stay on through counting // of 0-s (will only reset by 1 after 0) else num_running_ones <= (&num_running_ones[3:2]) ? 4'hc : (num_running_ones + {1'b0, num_lead_1_w}); // Now count consecutive 0-s after (>=12) 1-s. Not using zero_after_ones in the middle of the run - will // rely on the number of running ones being reset in that case // Saturate number with 24 (5'h18), but only first transition from <24 to >=24 is used for sync // detection. if (irst || !num_running_ones[3]) num_running_zeros <= 0; // else if (!num_running_ones[2]) num_running_zeros <= {2'b0,num_trail_0_w}; else if (prev4ones) num_running_zeros <= {2'b0,num_trail_0_w}; else num_running_zeros <= (&num_running_zeros[4:3])? 5'h18 : num_running_zeros_w; if (irst) got_sync <= 0; else got_sync <= got_sync_w; // got_sync should also abort data run - delayed by 10 clocks if (irst) shift_val <= 0; // else if (got_sync) shift_val <= num_first_zeros; else if (got_sync_w) shift_val <= num_first_zeros; case (shift_val) 2'h0: barrel <= din; // 2'h1: barrel <= {d_r[2:0], din[3]}; 2'h1: barrel <= {d_r[0], din[3:1]}; 2'h2: barrel <= {d_r[1:0], din[3:2]}; // 2'h3: barrel <= {d_r[0], din[3:1]}; 2'h3: barrel <= {d_r[2:0], din[3]}; endcase if (irst) sync_decode <= 0; else if (got_sync) sync_decode <= 4'h1; else sync_decode <= sync_decode << 1; if (got_sync) got_sof <= 0; else if (sync_decode[LSB_INDEX] && (barrel == SYNC_SOF)) got_sof <= 1; if (got_sync) got_eof <= 0; else if (sync_decode[LSB_INDEX] && (barrel == SYNC_EOF)) got_eof <= 1; if (got_sync) got_sol <= 0; else if (sync_decode[LSB_INDEX] && (barrel == SYNC_SOL)) got_sol <= 1; // if (got_sync) got_eol <= 0; // else if (sync_decode[LSB_INDEX] && (barrel == SYNC_EOL)) got_eol <= 1; if (got_sync) got_embed <= 0; else if (sync_decode[1] && (barrel == SYNC_EMBED)) got_embed <= 1; if (irst) dout[ 3:0] <= 0; else if (pre_dv[LSB_INDEX]) dout[ 3:0] <= dout_w; if (irst) dout[ 7:4] <= 0; else if (pre_dv[1]) dout[ 7:4] <= dout_w; if (irst) dout[11:8] <= 0; else if (pre_dv[MSB_INDEX]) dout[11:8] <= dout_w; if (irst || got_sync) pre_dv <= 0; else if (start_line_d) pre_dv <= 1; else pre_dv <= {pre_dv[1:0],pre_dv[2]}; if (irst) dv <= 0; else dv <= pre_dv[2]; if (irst) sol <= 0; else sol <= start_line_d; if (irst) eol <= 0; else eol <= got_sync && (|pre_dv); if (irst) sof <= 0; else sof <= sync_decode[3] && got_sof; if (irst) eof <= 0; else eof <= sync_decode[3] && got_eof; if (irst) embed <= 0; else if (sync_decode[3]) embed <= got_embed && (got_sof || got_sol); end dly_16 #( .WIDTH(4) ) dly_16_dout_i ( .clk (ipclk), // input .rst (1'b0), // input .dly (4'h8), // input[3:0] .din (HISPI_MSB_FIRST ? barrel :{barrel[0],barrel[1],barrel[2],barrel[3]}), // input[0:0] .dout (dout_w) // output[0:0] ); dly_16 #( .WIDTH(1) ) dly_16_pre_start_line_i ( .clk (ipclk), // input .rst (1'b0), // input .dly (4'h7), // input[3:0] .din (start_line), // input[0:0] .dout (start_line_d) // output[0:0] ); endmodule