////////////////////////////////////////////////////////////////////////////////////////////
// Company:         TAIR
// Engineer:        Chigrinskiy A.  
// 
// Create Date:     18/04/2024 
// Design Name: 
// Module Name:     FifoCtrl
// Project Name:    SB_TMSG44V1_FPGA
// Target Devices:  Board: SB_TMSG44v1. FPGA: GW1N-LV9PG256C6/I5
// Tool versions:
// Description:     This module is a controller for the FIFOs. It controls the read and write pointers of the FIFOs.
//
// Dependencies:  
// Revision: 
// Revision 1.0 - File Created
// Additional Comments: 
//
////////////////////////////////////////////////////////////////////////////////////////////
module FifoCtrl #(
    parameter PARITY_CHECK = 0, // 1 - parity check enabled, 0 - disabled
    parameter IN_WIDTH = 24,
    parameter WR_NUM = 1,
    parameter OUT_WIDTH = 24
)(
    input WrClk_i,
    input RdClk_i,
    input Rst_i,
    input [IN_WIDTH-1:0] Data_i,
    input Val_i,
    input BusySpiM_i,
    input FifoFull_i,
    input FifoEmpty_i,

    output [OUT_WIDTH-1:0] Data_o,
    output reg ReadEn_o,
    output reg  WriteEn_o,
    output reg ValRdData_o

);
//================================================================================
//	LOCAL PARAMETERS
//================================================================================
localparam DATA_WIDTH = WR_NUM*IN_WIDTH;

//================================================================================
//	REG/WIRE
//================================================================================
reg [DATA_WIDTH-1:0] dataReg;
reg [1:0]  wrCnt;
reg [9:0] parityErrors;      // Ошибки чётности для каждого байта
wire [9:0] byteParityBits;   // Вычисленные биты чётности
reg [9:0] storedParityBits;  // Сохранённые биты чётности
//================================================================================
//  ASSIGNMENTS
//================================================================================
assign Data_o = dataReg[OUT_WIDTH-1:0];

//================================================================================
//	CODING
//================================================================================
generate
    if (PARITY_CHECK) begin
    /* Parity check for each byte */
        assign byteParityBits[0] = ^dataReg[7:0];
        assign byteParityBits[1] = ^dataReg[15:8];
        assign byteParityBits[2] = ^dataReg[23:16];
        assign byteParityBits[3] = ^dataReg[31:24];
        assign byteParityBits[4] = ^dataReg[39:32];
        assign byteParityBits[5] = ^dataReg[47:40];
        assign byteParityBits[6] = ^dataReg[55:48];
        assign byteParityBits[7] = ^dataReg[63:56];
        assign byteParityBits[8] = ^dataReg[71:64];
        assign byteParityBits[9] = ^dataReg[79:72];

        always @(posedge WrClk_i) begin 
               if (Rst_i) begin 
                   parityErrors <= 0;
                    storedParityBits <= 0;
               end
               else begin 
                   if (WriteEn_o) begin
                       parityErrors <= (dataReg[95:86] ^ byteParityBits);
                       storedParityBits <= dataReg[95:86];
                   end
               end
        end
    end
endgenerate
        

always @(posedge WrClk_i) begin 
    if (Rst_i) begin 
        wrCnt <= 0;
    end
    else begin 
        if (Val_i) begin 
            wrCnt <= wrCnt + 1;
        end
        else if (wrCnt == WR_NUM) begin 
            wrCnt <= 0;
        end
    end
end

always @(posedge WrClk_i) begin 
    if (Rst_i) begin 
        dataReg <= 0;
    end
    else begin
        if (WR_NUM>1) begin
            case (WR_NUM)
            4: begin 
                case (wrCnt)  
                    0 : begin 
                        if (Val_i) begin 
                            dataReg[(4*IN_WIDTH)-1:(3*IN_WIDTH)] <= Data_i;
                        end
                    end
                    1 : begin 
                        if (Val_i) begin 
                            dataReg[(3*IN_WIDTH)-1:(2*IN_WIDTH)] <= Data_i;
                        end
                    end
                    2:  begin
                        if (Val_i) begin
                            dataReg[(2*IN_WIDTH)-1:IN_WIDTH] <= Data_i;
                        end
                    end
                    3: begin
                        if (Val_i) begin 
                        dataReg[IN_WIDTH-1:0] <= Data_i;
                        end
                    end
                endcase
            end
            3: begin
                case (wrCnt)
                0 : begin 
                    if (Val_i) begin 
                        dataReg[(3*IN_WIDTH)-1:(2*IN_WIDTH)] <= Data_i;
                    end
                end
                1 : begin 
                    if (Val_i) begin 
                        dataReg[(2*IN_WIDTH)-1:IN_WIDTH] <= Data_i;
                    end
                end
                2 : begin 
                    if (Val_i) begin 
                        dataReg[IN_WIDTH-1:0] <= Data_i;
                    end
                end
                endcase
            end
            2 : begin 
                case (wrCnt)
                0: begin 
                    if (Val_i) begin 
                        dataReg[(2*IN_WIDTH)-1:IN_WIDTH] <= Data_i;
                    end
                end
                1: begin 
                    if (Val_i) begin 
                        dataReg[IN_WIDTH-1:0] <= Data_i;
                    end
                end
                endcase
            end
            endcase
        end
        else begin
            if (Val_i) begin 
                dataReg[IN_WIDTH-1:0] <= Data_i;
            end
        end
    end
end

always @(posedge WrClk_i) begin
    if (Rst_i) begin 
        WriteEn_o <= 1'b0;
    end
    else begin  
        if (Val_i && wrCnt == WR_NUM-1 && !FifoFull_i) begin
            WriteEn_o <= 1'b1;
        end
        else begin 
            WriteEn_o <= 1'b0;
        end
    end
end

always @(posedge RdClk_i) begin
    if (Rst_i) begin 
        ReadEn_o <= 1'b0;
    end
    else begin  
        if (!FifoEmpty_i && !BusySpiM_i) begin 
            ReadEn_o <= 1'b1;
        end
        else begin 
            ReadEn_o <= 1'b0;
        end
    end
end

always @(posedge RdClk_i) begin 
    if (Rst_i) begin 
        ValRdData_o <= 1'b0;
    end
    else begin
        if (!FifoEmpty_i && !BusySpiM_i) begin 
            ValRdData_o <= 1'b1;
        end
        else begin 
            ValRdData_o <= 1'b0;
        end
    end
end

endmodule