/*!
 * <b>Module:</b>bit_stuffer_raw_metadata
 * @file bit_stuffer_raw_metadata.v
 * @date 2015-10-25  
 * @author Andrey Filippov     
 *
 * @brief Bit stuffer combines variable length fragments (up to 16 bits long)
 * from the Huffman encoder to a byte stream, escapes every 0xff byte with 
 * 0x00 and adds file length and timestamp metadata
 * Modified from bit_stuffer_metadata to include raw byte stream as alternative
 *
 * @copyright Copyright (c) 2015 Elphel, Inc .
 *
 * <b>License:</b>
 *
 * bit_stuffer_raw_metadata.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.
 *
 *  bit_stuffer_raw_metadata.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 <http://www.gnu.org/licenses/> .
 *
 * 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  bit_stuffer_raw_metadata(
    input              mclk,
    input              mrst,       // @posedge mclk, sync reset
    input              xclk,
    input              xrst,       // @posedge xclk, sync reset
    input              last_block, //  use it to copy timestamp from fifo // TODO: leading edge is Need for raw also
    
    
// time stamping - will copy time at the end of color_first (later than the first hact after vact in the current frame, but before the next one
// and before the data is needed for output 
    input              ts_pre_stb,  // @mclk - 1 cycle before receiving 8 bytes of timestamp data
    input        [7:0] ts_data,     // timestamp data (s0,s1,s2,s3,us0,us1,us2,us3==0)
    input              color_first, // @fradv_clk only used for timestamp

    input       [31:0] din,         // input data, MSB aligned
    input        [1:0] bytes_in,    // number of bytes, valid @ ds (0 means 4)
    input              in_stb,      // input data/bytes_in strobe
    input              flush,       // end of input data 
    input              abort,       // @ any, extracts 0->1 and flushes (for both modes!)

    // RAW mode ports, all @ xclk
    input              compressed_mode,    // operating in raw mode (uncompressed)
    input              raw_mode,    // operating in raw mode (uncompressed)
    input              raw_be16,    // swap byte pairs to outut 16-bit big endian data
    input        [7:0] raw_bytes,   // raw bypass byte data in little endian order 
    input              raw_start,   // single-cycle set "running"
    input              raw_prefb,   // 1 cycle before sequence of 4 bytes
    input              raw_ts_copy, // single-cycle copy timestamp (some time before flush)
    input              raw_flush,   // flush remaining data, length and timestamp

    // outputs @ negedge clk
    output reg  [31:0] data_out,      // [31:0] output data
    output reg         data_out_valid,// output data valid
    output reg         done,        // reset by !en, goes high after some delay after flushing
    output reg         running      // from registering timestamp until done
`ifdef DEBUG_RING
,   output reg [3:0]  dbg_etrax_dma
   ,output            dbg_ts_rstb
   ,output [7:0]      dbg_ts_dout

`endif        
);
// Processing raw
    reg    [31:0] raw32;
    reg           raw_stb;
    reg     [3:0] raw_bs;
    always @ (posedge xclk) begin
        if (xrst) raw_bs <= 0;
        else raw_bs <= raw_be16?
                       {raw_bs[0], raw_bs[3], raw_prefb, raw_bs[1]}:
                       {raw_bs[2], raw_bs[1], raw_bs[0], raw_prefb};
        if (xrst) raw_stb <= 0;
        else      raw_stb <= raw_be16 ? raw_bs[2] : raw_bs[3];
        if (raw_bs[0]) raw32[31:24] <= raw_bytes;
        if (raw_bs[1]) raw32[23:16] <= raw_bytes;
        if (raw_bs[2]) raw32[15: 8] <= raw_bytes;
        if (raw_bs[3]) raw32[ 7: 0] <= raw_bytes;
    end

    reg     [7:0] time_ram0[0:3]; // 0 - seconds, 1 - microseconds MSB in the output 32-bit word, byt LSB of the sec/usec
    reg     [7:0] time_ram1[0:3];
    reg     [7:0] time_ram2[0:3];
    reg     [7:0] time_ram3[0:3];
    reg     [3:0] ts_in=8;         
    reg           last_block_d; // last_block delayed by one clock
    reg           color_first_r;   
    reg     [2:0] abort_r;
    reg           force_flush;
    
//    reg           color_first_r; // registered with the same clock as color_first to extract leading edge
    
// stb_time[2] - single-cycle pulse after color_first goes low 
//    reg    [19:0] imgsz32; // current image size in multiples of 32-bytes
    reg    [21:0] imgsz4; // current image size in multiples of 4-bytes
    reg           last_stb_4; // last stb_in was 4 bytes
    reg           trailer;
    reg           meta_out; 
    reg     [1:0] meta_word;
    reg           raw_flush_d; // raw_flush delayed by 1 cysle
    reg           zeros_out; // output of 32 bytes (8 words) of zeros 
    wire          trailer_done = (imgsz4[2:0] == 7) && zeros_out;
    wire          meta_last = (imgsz4[2:0] == 7) &&    meta_out;
    // re-clock enable to this clock
    
//    wire          ts_rstb= raw_mode ?  raw_ts_copy: (last_block && !last_block_d);  // enough time to have timestamp data; // one cycle before getting timestamp data from FIFO
    wire          ts_rstb= (raw_mode && raw_ts_copy) || ( compressed_mode && last_block && !last_block_d);  // enough time to have timestamp data; // one cycle before getting timestamp data from FIFO
    wire    [7:0] ts_dout; // timestamp data, byte at a time
    wire          write_size = (in_stb && (bytes_in != 0)) || (flush && last_stb_4) || raw_flush_d; // raw_flush; // TODO: never in raw mode?
//    wire          stb_start = raw_mode?  raw_start: (!color_first && color_first_r) ;
    wire          stb_start = (raw_mode && raw_start) ||  (compressed_mode && !color_first && color_first_r) ;
    wire          stb =  compressed_mode && in_stb && !trailer && !force_flush;
    wire          stb1 = raw_mode && raw_stb && !trailer && !force_flush;
    
    always @ (posedge xclk) begin
        raw_flush_d <= raw_mode && raw_flush;
        
        if (xrst ||trailer_done) imgsz4 <= 0;
        else if (stb || stb1 || trailer) imgsz4 <= imgsz4 + 1; // raw included
    
        if (stb || stb1) last_stb_4 <= !raw_mode && (bytes_in == 0);
        
        last_block_d <=  last_block;
        color_first_r <= color_first;
    
        if      (xrst)       ts_in <= 8;
        else if (ts_rstb)    ts_in <= 0;
        else if (!ts_in[3])  ts_in <= ts_in + 1;
        
        if ((!ts_in[3] && (ts_in[1:0] == 0)) || write_size) time_ram0[ts_in[3:2]] <= ts_in[3]? ({imgsz4[5:0],(flush || raw_mode)?2'b0:bytes_in}):ts_dout; //ts_in[3:2] == 2'b10 when write_size
        if ((!ts_in[3] && (ts_in[1:0] == 1)) || write_size) time_ram1[ts_in[3:2]] <= ts_in[3]? (imgsz4[13:6]):ts_dout;
        if ((!ts_in[3] && (ts_in[1:0] == 2)) || write_size) time_ram2[ts_in[3:2]] <= ts_in[3]? (imgsz4[21:14]):ts_dout;
        if ((!ts_in[3] && (ts_in[1:0] == 3)) || write_size) time_ram3[ts_in[3:2]] <= ts_in[3]? (8'hff):ts_dout;
        
        if      (xrst)                                                                 trailer <= 0;
//        else if (force_flush || (raw_mode ? raw_flush : flush)) trailer <= 1;
        else if (force_flush || (raw_mode && raw_flush) || (compressed_mode && flush)) trailer <= 1;
        else if (trailer_done)                                                         trailer <= 0;

        if      (xrst)                                       meta_out <= 0;
        else if (trailer && (imgsz4[2:0] == 4) &&!zeros_out) meta_out <= 1;
        else if (meta_last)                                  meta_out <= 0;
        
        if (!meta_out) meta_word <= 0;
        else           meta_word <= meta_word + 1;
        
        if      (xrst)         zeros_out <= 0;
        else if (meta_last)    zeros_out <= 1;
        else if (trailer_done) zeros_out <= 0;
        
        data_out <= ({32{stb1}} & raw32) | ({32{stb}} & din) | ({32{meta_out}} & {time_ram0[meta_word],time_ram1[meta_word],time_ram2[meta_word],time_ram3[meta_word]});
        data_out_valid <= stb1 || stb || trailer;
        
        
///        if      (xrst || trailer) running <= 0; // FIXME: Was this for quite a while? Was in intentional?
        if      (xrst || trailer_done) running <= 0;
        
        else if (stb_start)       running <= 1; // for raw need color_first trailing edge
        
        done <= trailer_done;
        // re-clock abort, extract leading edge
        abort_r <= {abort_r[0] & ~abort_r[1], abort_r[0], abort & ~trailer};
        
        if      (xrst || trailer)  force_flush <= 0;
        else if (abort_r)          force_flush <= 1;
        
    end
    
    // just for testing
`ifdef DEBUG_RING
    assign dbg_ts_rstb = ts_rstb;
    assign dbg_ts_dout = ts_dout;

    always @ (posedge xclk) begin
       dbg_etrax_dma <= imgsz4[3:0];
    end   
`endif        
    
//color_first && color_first_r
    timestamp_fifo timestamp_fifo_i (
        .sclk     (mclk),         // input
        .srst     (mrst),         // input
        .pre_stb  (ts_pre_stb),   // input
        .din      (ts_data),      // input[7:0] 
        .aclk     (xclk),         //fradv_clk),   // input
        .arst     (xrst),         //fradv_clk),   // input
        .advance  (stb_start),    // triggers at the 0->1
        .rclk     (xclk),         // input
        .rrst     (xrst),         //fradv_clk),   // input
        .rstb     (ts_rstb),      // input
        .dout     (ts_dout)       // output[7:0] reg 
    );

endmodule
