////////////////////////////////////////////////////////////////////////////////
//
// Company:         GYROOPTICS Ltd.
// Engineer:        Ilchenko Sergey Vladimirovich
//
// Create Date:     14:29 02.05.2024
// Design Name:
// Module Name:     bublesort
// Project Name:
// Target Devices:  Cyclone V
// Tool versions:
// Description:     unsigned number sort
//
////////////////////////////////////////////////////////////////////////////////

`timescale 1 ns/100 ps
`default_nettype none

/*
bublesort
    bublesort_inst
    (
        .RESET                  (),                     //  in, u[ 1], Async reset
        .CLK                    (),                     //  in, u[ 1], System clock

        .START                  (),                     //  in, u[ 1], Start of sort

        .N                      (),                     //  in, u[32], num_elements

        .DI                     (),                     //  in, u[32], data from memory

        .EN                     (),                     // out, u[ 1], clock enable to memory
        .WE                     (),                     // out, u[ 1], write enable to memory
        .ADDRESS                (),                     // out, u[32], address to memory
        .DO                     (),                     // out, u[32], data to memory

        .RDY                    ()                      // out, u[ 1], Ready
    );
*/

module bublesort
    (
        input   wire                RESET,              //  in, u[ 1], Async reset
        input   wire                CLK,                //  in, u[ 1], System clock

        input   wire                START,              //  in, u[ 1], Start of sort

        input   wire    [ 31 :  0 ] N,                  //  in, u[32], num_elements

        input   wire    [ 31 :  0 ] DI,                 //  in, u[32], data from memory

        output  wire                EN,                 // out, u[ 1], clock enable to memory
        output  wire                WE,                 // out, u[ 1], write enable to memory
        output  wire    [ 31 :  0 ] ADDRESS,            // out, u[32], address to memory
        output  wire    [ 31 :  0 ] DO,                 // out, u[32], data to memory

        output  wire                RDY                 // out, u[ 1], Ready
    );

parameter state_00 = 7'b000_0001;
parameter state_01 = 7'b000_0010;
parameter state_02 = 7'b000_0100;
parameter state_03 = 7'b000_1000;
parameter state_04 = 7'b001_0000;
parameter state_05 = 7'b010_0000;
parameter state_06 = 7'b100_0000;

(* syn_encoding = "one-hot" *) reg     [  6 :  0 ] r_fsm;
//(* FSM_ENCODING="ONE-HOT", SAFE_IMPLEMENTATION="NO" *) reg     [ 06 :  0 ] r_fsm;

reg                 r_rdy;
reg                 r_t;
reg     [ 31 :  0 ] r_i;
reg     [ 31 :  0 ] r_j;
reg     [ 31 :  0 ] r_temp;

wire                w_en_r_rdy;
wire                w_rdy_value;
wire                w_en_r_t;
wire                w_t_value;
wire                w_en_r_i;
wire                w_sclr_r_i;
wire                w_en_r_j;
wire                w_sclr_r_j;
wire                w_en_temp;
wire                w_check = ((N == 32'b0) || (N == 32'b1)) ? 1'b0 : 1'b1;
wire                w_en;
wire                w_we;
wire                w_sel_address;
wire                w_less = (DI < r_temp) ? 1'b1 : 1'b0;
wire                w_for_j_end = (r_j < (N - r_i - 32'd1)) ? 1'b0 : 1'b1;
wire                w_sel_do;

assign EN = w_en;
assign WE = w_we;
assign ADDRESS = (w_sel_address) ? (r_j + 32'd1) : r_j;
assign DO = (w_sel_do) ? DI : r_temp;
assign RDY = r_rdy;

assign w_en_r_rdy       = ((r_fsm == state_00) || (r_fsm == state_01 && w_check && START)) ? 1'b1 : 1'b0;
assign w_rdy_value      = (r_fsm == state_00) ? 1'b1 : 1'b0;

assign w_en_r_t         = ((r_fsm == state_00) || (r_fsm == state_02 && r_t) ||
                           (r_fsm == state_06)) ? 1'b1 : 1'b0;
assign w_t_value        = (r_fsm == state_00 || r_fsm == state_06) ? 1'b1 : 1'b0;

assign w_en_r_i         = (r_fsm == state_00 || (r_fsm == state_03 && w_for_j_end)) ? 1'b1 : 1'b0;
assign w_sclr_r_i       = (r_fsm == state_00) ? 1'b1 : 1'b0;

assign w_en_r_j         = ((r_fsm == state_02 && r_t) || (r_fsm == state_05 && w_less) ||
                            r_fsm == state_06) ? 1'b1 : 1'b0;
assign w_sclr_r_j       = (r_fsm == state_02 && r_t) ? 1'b1 : 1'b0;

assign w_en_temp        = (r_fsm == state_04 || (r_fsm == state_05 && ~w_less)) ? 1'b1 : 1'b0;

assign w_en             = ((r_fsm == state_03 && ~w_for_j_end) || r_fsm == state_04 ||
                           (r_fsm == state_05 && ~w_less) || r_fsm == state_06) ? 1'b1 : 1'b0;
assign w_we             = ((r_fsm == state_05 && ~w_less) || r_fsm == state_06) ? 1'b1 :1'b0;

assign w_sel_address    = ((r_fsm == state_03 && ~w_for_j_end) || r_fsm == state_06) ? 1'b1 : 1'b0;

assign w_sel_do         = (r_fsm == state_06) ? 1'b1 :1'b0;

    always @(posedge CLK or posedge RESET)
        if(RESET)
            r_fsm                   <= state_00;
        else
        begin
//            (* FULL_CASE, PARALLEL_CASE *) case(r_fsm)
            (* syn_encoding = "one-hot" *) case(r_fsm)
                state_00:
                    r_fsm   <= state_01;
                state_01:
                    if(w_check && START)
                        r_fsm   <= state_02;
                state_02:
                    if(r_t)
                        r_fsm   <= state_03;
                    else
                        r_fsm   <= state_00;
                state_03:
                    if(w_for_j_end)
                        r_fsm   <= state_02;
                    else
                        r_fsm   <= state_04;
                state_04:
                    r_fsm   <= state_05;
                state_05:
                    if(w_less)
                        r_fsm   <= state_03;
                    else
                        r_fsm   <= state_06;
                state_06:
                    r_fsm   <= state_03;
                default:
                    r_fsm   <= state_00;
            endcase
        end

    always @(posedge CLK or posedge RESET)
        if(RESET)
            r_rdy         <= 1'b0;
        else
            if(w_en_r_rdy)
                r_rdy         <= w_rdy_value;

    always @(posedge CLK or posedge RESET)
        if(RESET)
            r_t         <= 1'b0;
        else
            if(w_en_r_t)
                r_t         <= w_t_value;

    always @(posedge CLK or posedge RESET)
        if(RESET)
            r_i         <= 16'b0;
        else
            if(w_en_r_i)
                if(w_sclr_r_i)
                    r_i         <= 16'b0;
                else
                    r_i         <= r_i + 16'b1;

    always @(posedge CLK or posedge RESET)
        if(RESET)
            r_j         <= 16'b0;
        else
            if(w_en_r_j)
                if(w_sclr_r_j)
                    r_j         <= 16'b0;
                else
                    r_j         <= r_j + 16'b1;

    always @(posedge CLK or posedge RESET)
        if(RESET)
            r_temp         <= 32'b0;
        else
            if(w_en_temp)
                r_temp         <= DI;

endmodule