/******************************************************************************* * Module: gtx_comma_align * Date: 2015-07-11 * Author: Alexey * Description: comma aligner implementation * * Copyright (c) 2015 Elphel, Inc. * gtx_comma_align.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. * * gtx_comma_align.v file 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 . *******************************************************************************/ module gtx_comma_align( input wire rst, input wire clk, // input data comes this way (standart 8/10 bit notation) // cycle 0: {[hgfedcba]=1st byte,[hgfedcba]=0st byte} // cycle 1: {[hgfedcba]=3rd byte,[hgfedcba]=2dn byte} // => {[cycle1 data], [cycle0 data]} = as if we were reading by dwords input wire [19:0] indata, output wire [19:0] outdata, // outdata contains comma output wire comma, // pulse, indicating that stream was once again adjusted to a comma // if asserted after link was down - OK // if asserted during a work - most likely indicates an error in a stream output wire realign // asserted when input stream looks like comma, but it is not // later on after 10/8 it would get something link NOTINTHETABLE error anyways // output wire error ); // only comma character = K28.5, has 5 '1's or 5 '0's in a row. // after we met it, call it a comma group, we could compare other symbols /* // create a window reg [19:0] indata_r; wire [23:0] window; always @ (posedge clk) indata_r <= indata; assign window = {indata_r[17:0], indata[19:14]}; // search for a comma group - parallel 24-bit window into 20 5-bit words // transposed -> 5 x 20-bit words wire [19:0] lane0; wire [19:0] lane1; wire [19:0] lane2; wire [19:0] lane3; wire [19:0] lane4; assign lane0 = window[19:0]; assign lane1 = window[20:1]; assign lane2 = window[21:2]; assign lane3 = window[22:3]; assign lane4 = window[23:4]; // calcute at what position in a window comma group is detected, // so the position in actual {indata_r, indata} would be +2 from the left side wire [19:0] comma_pos; assign comma_pos = lane0 & lane1 & lane2 & lane3 & lane4; */ // seach for a comma // TODO make it less expensive reg [19:0] indata_r; wire [38:0] window; always @ (posedge clk) indata_r <= indata; assign window = {indata[18:0], indata_r}; // there is only 1 matched subwindow due to 20-bit comma's non-repetative pattern wire [19:0] subwindow [19:0]; wire [19:0] comma_match; wire [19:0] comma_match_p; reg [19:0] aligned_data; reg [19:0] comma_match_prev; wire comma_detected; wire [19:0] comma_p = 20'b10101010100101111100; wire [19:0] comma_n = 20'b10101010101010000011; genvar ii; generate for (ii = 0; ii < 20; ii = ii + 1) begin: look_for_comma assign subwindow[ii] = window[ii + 19:ii]; // assign comma_match[ii] = subwindow[ii] == 20'b01010101010011111010 | subwindow[ii] == 20'b01010101011100000101; // stream comes inverted assign comma_match_p[ii] = subwindow[ii] == comma_p; assign comma_match[ii] = comma_match_p[ii] | subwindow[ii] == comma_n; end endgenerate assign comma_detected = |comma_match; // save the shift count always @ (posedge clk) comma_match_prev <= rst ? 20'h1 : comma_detected ? comma_match : comma_match_prev; // shift /* TODO wire [38:0] shifted_window; assign shifted_window = comma_detected ? {window >> (comma_match - 1)} : {window >> (comma_match_prev - 1)}; */ // temp shift wire [19:0] shifted_window; wire [19:0] ored_subwindow [19:0]; wire [19:0] ored_subwindow_comdet [19:0]; assign ored_subwindow_comdet[0] = {20{comma_match_p[0]}} & comma_p | {20{~comma_match_p[0] & comma_match[0]}} & comma_n; assign ored_subwindow[0] = {20{comma_match_prev[0]}} & subwindow[0]; generate for (ii = 1; ii < 20; ii = ii + 1) begin: or_all_possible_windows assign ored_subwindow_comdet[ii] = {20{comma_match_p[ii]}} & comma_p | {20{~comma_match_p[ii] & comma_match[ii]}} & comma_n | ored_subwindow_comdet[ii-1]; // SuppressThisWarning VEditor -warning would be fixed in future releases assign ored_subwindow[ii] = {20{comma_match_prev[ii]}} & subwindow[ii] | ored_subwindow[ii-1]; // SuppressThisWarning VEditor -warning would be fixed in future releases end endgenerate assign shifted_window = comma_detected ? ored_subwindow_comdet[19] : ored_subwindow[19]; always @ (posedge clk) // aligned_data <= comma_detected ? {window >> (comma_match - 1)}[19:0] : {window >> (comma_match_prev - 1)}[19:0]; aligned_data <= shifted_window[19:0]; // form outputs assign comma = comma_detected; assign realign = comma_detected & |(comma_match_prev ^ comma_match); assign outdata = aligned_data; endmodule