module jelly_float_reciprocal
        #(
            parameter   EXP_WIDTH       = 8,
            parameter   EXP_OFFSET      = (1 << (EXP_WIDTH-1)) - 1,
            parameter   FRAC_WIDTH      = 23,
            parameter   FLOAT_WIDTH     = 1 + EXP_WIDTH + FRAC_WIDTH,   // sign + exp + frac
            
            parameter   USER_WIDTH      = 0,
            
            parameter   D_WIDTH         = 8,                            // interpolation table addr bits
            parameter   K_WIDTH         = FRAC_WIDTH - D_WIDTH,
            parameter   GRAD_WIDTH      = FRAC_WIDTH,
            
            parameter   MASTER_IN_REGS  = 1,
            parameter   MASTER_OUT_REGS = 1,
            
            parameter   RAM_TYPE        = "block",
            
            parameter   MAKE_TABLE      = 1,
            parameter   WRITE_TABLE     = 0,
            parameter   READ_TABLE      = 0,
            parameter   FILE_NAME       = "float_reciprocal.hex",
            
            parameter   USER_BITS       = USER_WIDTH > 0 ? USER_WIDTH : 1
        )
        (
            input   wire                        reset,
            input   wire                        clk,
            input   wire                        cke,
            
            input   wire    [USER_BITS-1:0]     s_user,
            input   wire    [FLOAT_WIDTH-1:0]   s_float,
            input   wire                        s_valid,
            output  wire                        s_ready,
            
            output  wire    [USER_BITS-1:0]     m_user,
            output  wire    [FLOAT_WIDTH-1:0]   m_float,
            output  wire                        m_valid,
            input   wire                        m_ready
        );
    
    localparam  PIPELINE_STAGES = 5;
    
    wire    [PIPELINE_STAGES-1:0]   stage_cke;
    wire    [PIPELINE_STAGES-1:0]   stage_valid;
    
    wire    [USER_BITS-1:0]         src_user;
    wire                            src_sign;
    wire    [EXP_WIDTH-1:0]         src_exp;
    wire    [FRAC_WIDTH-1:0]        src_frac;
    
    wire    [USER_BITS-1:0]         sink_user;
    wire                            sink_sign;
    wire    [EXP_WIDTH-1:0]         sink_exp;
    wire    [FRAC_WIDTH-1:0]        sink_frac;
    
    jelly_pipeline_control
            #(
                .PIPELINE_STAGES    (PIPELINE_STAGES),
                .S_DATA_WIDTH       (USER_BITS+FLOAT_WIDTH),
                .M_DATA_WIDTH       (USER_BITS+FLOAT_WIDTH),
                .AUTO_VALID         (1),
                .MASTER_IN_REGS     (MASTER_IN_REGS),
                .MASTER_OUT_REGS    (MASTER_OUT_REGS)
            )
        i_pipeline_control
            (
                .reset              (reset),
                .clk                (clk),
                .cke                (cke),
                
                .s_data             ({s_user, s_float}),
                .s_valid            (s_valid),
                .s_ready            (s_ready),
                
                .m_data             ({m_user, m_float}),
                .m_valid            (m_valid),
                .m_ready            (m_ready),
                
                .stage_cke          (stage_cke),
                .stage_valid        (stage_valid),
                .next_valid         ({PIPELINE_STAGES{1'bx}}),
                .src_data           ({src_user, src_sign, src_exp, src_frac}),
                .src_valid          (),
                .sink_data          ({sink_user, sink_sign, sink_exp, sink_frac}),
                .buffered           ()
            );
    
    wire    [FRAC_WIDTH-1:0]    st1_frac;
    wire    [FRAC_WIDTH-1:0]    st1_grad;
    
    generate
    if ( FRAC_WIDTH == 23 && D_WIDTH == 6 && GRAD_WIDTH == 23 && !MAKE_TABLE && !WRITE_TABLE && !READ_TABLE ) begin
        // どんな合成器でも１種類は動くようにテーブル化
        jelly_float_reciprocal_frac23_d6
            i_float_reciprocal_frac23_d6
                (
                    .clk            (clk),
                    
                    .cke            (stage_cke[1:0]),
                    
                    .in_d           (src_frac[FRAC_WIDTH-1 -: D_WIDTH]),
                    
                    .out_frac       (st1_frac),
                    .out_grad       (st1_grad)
                );
    end
    else begin
        // テーブル生成
        jelly_float_reciprocal_table
                #(
                    .FRAC_WIDTH     (FRAC_WIDTH),
                    .D_WIDTH        (D_WIDTH),
                    .K_WIDTH        (K_WIDTH),
                    .GRAD_WIDTH     (GRAD_WIDTH),
                    .OUT_REGS       (1),
                    .RAM_TYPE       (RAM_TYPE),
                    
                    .WRITE_TABLE    (WRITE_TABLE),
                    .READ_TABLE     (READ_TABLE),
                    .FILE_NAME      (FILE_NAME)
                )
            i_float_reciprocal_table
                (
                    .reset          (reset),
                    .clk            (clk),
                    .cke            (stage_cke[1:0]),
                    
                    .in_d           (src_frac[FRAC_WIDTH-1 -: D_WIDTH]),
                    
                    .out_frac       (st1_frac),
                    .out_grad       (st1_grad)
                );
    end
    endgenerate
    
    reg     [USER_BITS-1:0]     st0_user;
    reg                         st0_sign;
    reg     [EXP_WIDTH-1:0]     st0_exp;
    reg                         st0_frac_one;
    reg     [K_WIDTH-1:0]       st0_k;
    
    reg     [USER_BITS-1:0]     st1_user;
    reg                         st1_sign;
    reg     [EXP_WIDTH-1:0]     st1_exp;
    reg     [K_WIDTH-1:0]       st1_k;
    
    reg     [USER_BITS-1:0]     st2_user;
    reg                         st2_sign;
    reg     [EXP_WIDTH-1:0]     st2_exp;
    reg     [FRAC_WIDTH-1:0]    st2_frac;
    reg     [K_WIDTH-1:0]       st2_k;
    reg     [GRAD_WIDTH-1:0]    st2_grad;
    
    reg     [USER_BITS-1:0]     st3_user;
    reg                         st3_sign;
    reg     [EXP_WIDTH-1:0]     st3_exp;
    reg     [FRAC_WIDTH-1:0]    st3_frac;
    reg     [GRAD_WIDTH-1:0]    st3_diff;
    
    reg     [USER_BITS-1:0]     st4_user;
    reg                         st4_sign;
    reg     [EXP_WIDTH-1:0]     st4_exp;
    reg     [FRAC_WIDTH-1:0]    st4_frac;
    
    always @(posedge clk) begin
        if ( stage_cke[0] ) begin
            st0_user     <= src_user;
            st0_sign     <= src_sign;
            st0_exp      <= src_exp;
            st0_frac_one <= (src_frac == {FRAC_WIDTH{1'b0}});
            st0_k        <= src_frac[0 +: K_WIDTH];
        end
        
        if ( stage_cke[1] ) begin
            st1_user     <= st0_user;
            st1_sign     <= st0_sign;
            st1_exp      <= -(st0_exp - EXP_OFFSET) + st0_frac_one + EXP_OFFSET - 1;
            st1_k        <= st0_k;
        end
        
        if ( stage_cke[2] ) begin
            st2_user <= st1_user;
            st2_sign <= st1_sign;
            st2_exp  <= st1_exp;
            st2_frac <= st1_frac;
            st2_grad <= st1_grad;
            st2_k    <= st1_k;
        end
        
        if ( stage_cke[3] ) begin
            st3_user <= st2_user;
            st3_sign <= st2_sign;
            st3_exp  <= st2_exp;
            st3_frac <= st2_frac;
            st3_diff <= (({{GRAD_WIDTH{1'b0}}, st2_grad} * {{K_WIDTH{1'b0}}, st2_k}) >> K_WIDTH);
        end
        
        if ( stage_cke[4] ) begin
            st4_user <= st3_user;
            st4_sign <= st3_sign;
            st4_exp  <= st3_exp;
            st4_frac <= st3_frac - st3_diff;
        end
    end
    
    assign sink_user = st4_user;
    assign sink_sign = st4_sign;
    assign sink_exp  = st4_exp;
    assign sink_frac = st4_frac;
    
endmodule