aboutsummaryrefslogblamecommitdiff
path: root/rtl/modexpng_uop_engine.v
blob: ba2d4c8485df3535bff20966c448211069f4ff5d (plain) (tree)































                                                                           


                          
          



        
                     









































































                                     
                                     



                                   
                                                




























































































































































































































                                                                                                                                                                     

                                   

































                                                                                                                    
                                                       
                                                       

                                                                                     


















                                                                                
                                          
          
                         









































































































                                                                                                                                       
                                                                                                                         







































































































































                                                                                                                                                     


                      
                                          
          

                                                        






















                                                                                                                      
                                          
          
                                                






                                                              
//======================================================================
//
// Copyright (c) 2019, NORDUnet A/S All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
// - Redistributions of source code must retain the above copyright
//   notice, this list of conditions and the following disclaimer.
//
// - Redistributions in binary form must reproduce the above copyright
//   notice, this list of conditions and the following disclaimer in the
//   documentation and/or other materials provided with the distribution.
//
// - Neither the name of the NORDUnet nor the names of its contributors may
//   be used to endorse or promote products derived from this software
//   without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
// TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//======================================================================

module modexpng_uop_engine
(
    clk,
    rst_n,

    ena,
    rdy,
 
    uop_decoded_stop,
    
    crt_mode,
    
    word_index_last_n,
    word_index_last_pq,

    bit_index_last_n,
    bit_index_last_pq,
    
    io_mgr_ena,
    io_mgr_rdy,
    io_mgr_sel_crt,
    io_mgr_sel_aux,
    io_mgr_sel_in,
    io_mgr_sel_out,
    io_mgr_word_index_last,
    io_mgr_opcode,
    io_mgr_ladder_steps,
    io_mgr_ladder_d,
    io_mgr_ladder_p,
    io_mgr_ladder_q,
    io_mgr_ladder_done,
    
    mmm_ena_x,
    mmm_ena_y,  
    mmm_rdy_x,
    mmm_rdy_y,
    mmm_word_index_last_x,
    mmm_word_index_last_y,
    mmm_word_index_last_minus1_x,
    mmm_word_index_last_minus1_y,
    mmm_ladder_mode_x,
    mmm_ladder_mode_y,
    mmm_sel_wide_in_x,
    mmm_sel_wide_in_y,
    mmm_sel_narrow_in_x,
    mmm_sel_narrow_in_y,
    mmm_force_unity_b_x,
    mmm_force_unity_b_y,
    mmm_only_reduce_x,
    mmm_only_reduce_y,
    mmm_just_multiply_x,
    mmm_just_multiply_y,

    rdct_word_index_last_x,
    rdct_word_index_last_y,
    rdct_sel_wide_out_x,
    rdct_sel_narrow_out_x,
    rdct_sel_wide_out_y,
    rdct_sel_narrow_out_y,

    wrk_ena,
    wrk_rdy,
    wrk_sel_wide_in,
    wrk_sel_wide_out,
    wrk_sel_narrow_in,
    wrk_sel_narrow_out,
    wrk_word_index_last,
    wrk_word_index_last_half,
    wrk_opcode
);


    //
    // Headers
    //
    `include "modexpng_parameters.vh"
    `include "modexpng_microcode.vh"


    //
    // Ports
    //
    input                      clk;
    input                      rst_n;
    
    input                      ena;
    output                     rdy;

    output                     uop_decoded_stop;
    
    input                      crt_mode;
    
    input  [OP_ADDR_W    -1:0] word_index_last_n;
    input  [OP_ADDR_W    -1:0] word_index_last_pq;

    input  [BIT_INDEX_W  -1:0] bit_index_last_n;
    input  [BIT_INDEX_W  -1:0] bit_index_last_pq;

    output                     io_mgr_ena;
    input                      io_mgr_rdy;
    output [UOP_CRT_W    -1:0] io_mgr_sel_crt;
    output [UOP_AUX_W    -1:0] io_mgr_sel_aux;
    output [BANK_ADDR_W  -1:0] io_mgr_sel_in;
    output [BANK_ADDR_W  -1:0] io_mgr_sel_out;
    output [OP_ADDR_W    -1:0] io_mgr_word_index_last;
    output [UOP_OPCODE_W -1:0] io_mgr_opcode;
    output [BIT_INDEX_W  -1:0] io_mgr_ladder_steps;
    input                      io_mgr_ladder_d;
    input                      io_mgr_ladder_p;
    input                      io_mgr_ladder_q;
    input                      io_mgr_ladder_done;
    
    output                     mmm_ena_x;
    output                     mmm_ena_y;  
    input                      mmm_rdy_x;
    input                      mmm_rdy_y; 
    output [OP_ADDR_W    -1:0] mmm_word_index_last_x;
    output [OP_ADDR_W    -1:0] mmm_word_index_last_y;
    output [OP_ADDR_W    -1:0] mmm_word_index_last_minus1_x;
    output [OP_ADDR_W    -1:0] mmm_word_index_last_minus1_y;
    output                     mmm_ladder_mode_x;
    output                     mmm_ladder_mode_y;
    output [BANK_ADDR_W  -1:0] mmm_sel_wide_in_x;
    output [BANK_ADDR_W  -1:0] mmm_sel_wide_in_y;
    output [BANK_ADDR_W  -1:0] mmm_sel_narrow_in_x;
    output [BANK_ADDR_W  -1:0] mmm_sel_narrow_in_y;
    output                     mmm_force_unity_b_x;
    output                     mmm_force_unity_b_y;
    output                     mmm_only_reduce_x;
    output                     mmm_only_reduce_y;
    output                     mmm_just_multiply_x;
    output                     mmm_just_multiply_y;
   
    output [OP_ADDR_W    -1:0] rdct_word_index_last_x;
    output [OP_ADDR_W    -1:0] rdct_word_index_last_y;
    output [BANK_ADDR_W  -1:0] rdct_sel_wide_out_x;
    output [BANK_ADDR_W  -1:0] rdct_sel_narrow_out_x;
    output [BANK_ADDR_W  -1:0] rdct_sel_wide_out_y;
    output [BANK_ADDR_W  -1:0] rdct_sel_narrow_out_y;
   
    output                     wrk_ena;
    input                      wrk_rdy;
    output [BANK_ADDR_W  -1:0] wrk_sel_wide_in;
    output [BANK_ADDR_W  -1:0] wrk_sel_wide_out;
    output [BANK_ADDR_W  -1:0] wrk_sel_narrow_in;
    output [BANK_ADDR_W  -1:0] wrk_sel_narrow_out;
    output [OP_ADDR_W    -1:0] wrk_word_index_last;
    output [OP_ADDR_W    -1:0] wrk_word_index_last_half;
    output [UOP_OPCODE_W -1:0] wrk_opcode;


    //
    // Registers
    //
    reg                     io_mgr_ena_r = 1'b0;
    reg [UOP_CRT_W    -1:0] io_mgr_sel_crt_r;
    reg [UOP_AUX_W    -1:0] io_mgr_sel_aux_r;
    reg [BANK_ADDR_W  -1:0] io_mgr_sel_in_r;
    reg [BANK_ADDR_W  -1:0] io_mgr_sel_out_r;
    reg [OP_ADDR_W    -1:0] io_mgr_word_index_last_r;
    reg [UOP_OPCODE_W -1:0] io_mgr_opcode_r;
    reg [BIT_INDEX_W  -1:0] io_mgr_ladder_steps_r;
    
    reg                     mmm_ena_x_r = 1'b0;
    reg                     mmm_ena_y_r = 1'b0;  
    reg [OP_ADDR_W    -1:0] mmm_word_index_last_x_r;
    reg [OP_ADDR_W    -1:0] mmm_word_index_last_y_r;
    reg [OP_ADDR_W    -1:0] mmm_word_index_last_minus1_x_r;
    reg [OP_ADDR_W    -1:0] mmm_word_index_last_minus1_y_r;
    reg                     mmm_ladder_mode_x_r;
    reg                     mmm_ladder_mode_y_r;
    reg [BANK_ADDR_W  -1:0] mmm_sel_wide_in_x_r;
    reg [BANK_ADDR_W  -1:0] mmm_sel_wide_in_y_r;
    reg [BANK_ADDR_W  -1:0] mmm_sel_narrow_in_x_r;
    reg [BANK_ADDR_W  -1:0] mmm_sel_narrow_in_y_r;
    reg                     mmm_force_unity_b_x_r;
    reg                     mmm_force_unity_b_y_r;
    reg                     mmm_only_reduce_x_r;
    reg                     mmm_only_reduce_y_r;
    reg                     mmm_just_multiply_x_r;
    reg                     mmm_just_multiply_y_r;
   
    reg [OP_ADDR_W    -1:0] rdct_word_index_last_x_r;
    reg [OP_ADDR_W    -1:0] rdct_word_index_last_y_r;
    reg [BANK_ADDR_W  -1:0] rdct_sel_wide_out_x_r;
    reg [BANK_ADDR_W  -1:0] rdct_sel_narrow_out_x_r;
    reg [BANK_ADDR_W  -1:0] rdct_sel_wide_out_y_r;
    reg [BANK_ADDR_W  -1:0] rdct_sel_narrow_out_y_r;

    reg                     wrk_ena_r = 1'b0;
    reg [BANK_ADDR_W  -1:0] wrk_sel_wide_in_r;
    reg [BANK_ADDR_W  -1:0] wrk_sel_wide_out_r;
    reg [BANK_ADDR_W  -1:0] wrk_sel_narrow_in_r;
    reg [BANK_ADDR_W  -1:0] wrk_sel_narrow_out_r;
    reg [OP_ADDR_W    -1:0] wrk_word_index_last_r;
    reg [OP_ADDR_W    -1:0] wrk_word_index_last_half_r;
    reg [UOP_OPCODE_W -1:0] wrk_opcode_r;
    
    
    //
    // Mapping
    //
    assign io_mgr_ena                   = io_mgr_ena_r;
    assign io_mgr_sel_crt               = io_mgr_sel_crt_r;
    assign io_mgr_sel_aux               = io_mgr_sel_aux_r;
    assign io_mgr_sel_in                = io_mgr_sel_in_r;
    assign io_mgr_sel_out               = io_mgr_sel_out_r;
    assign io_mgr_word_index_last       = io_mgr_word_index_last_r;
    assign io_mgr_opcode                = io_mgr_opcode_r;
    assign io_mgr_ladder_steps          = io_mgr_ladder_steps_r;
   
    assign mmm_ena_x                    = mmm_ena_x_r;
    assign mmm_ena_y                    = mmm_ena_y_r;
    assign mmm_word_index_last_x        = mmm_word_index_last_x_r;
    assign mmm_word_index_last_y        = mmm_word_index_last_y_r;
    assign mmm_word_index_last_minus1_x = mmm_word_index_last_minus1_x_r;
    assign mmm_word_index_last_minus1_y = mmm_word_index_last_minus1_y_r;
    assign mmm_ladder_mode_x            = mmm_ladder_mode_x_r;
    assign mmm_ladder_mode_y            = mmm_ladder_mode_y_r;
    assign mmm_sel_wide_in_x            = mmm_sel_wide_in_x_r;
    assign mmm_sel_wide_in_y            = mmm_sel_wide_in_y_r;
    assign mmm_sel_narrow_in_x          = mmm_sel_narrow_in_x_r;
    assign mmm_sel_narrow_in_y          = mmm_sel_narrow_in_y_r;
    assign mmm_force_unity_b_x          = mmm_force_unity_b_x_r;
    assign mmm_force_unity_b_y          = mmm_force_unity_b_y_r;
    assign mmm_only_reduce_x            = mmm_only_reduce_x_r;
    assign mmm_only_reduce_y            = mmm_only_reduce_y_r;
    assign mmm_just_multiply_x          = mmm_just_multiply_x_r;
    assign mmm_just_multiply_y          = mmm_just_multiply_y_r;
   
    assign rdct_word_index_last_x       = rdct_word_index_last_x_r;
    assign rdct_word_index_last_y       = rdct_word_index_last_y_r;
    assign rdct_sel_wide_out_x          = rdct_sel_wide_out_x_r;
    assign rdct_sel_wide_out_y          = rdct_sel_wide_out_y_r;
    assign rdct_sel_narrow_out_x        = rdct_sel_narrow_out_x_r;
    assign rdct_sel_narrow_out_y        = rdct_sel_narrow_out_y_r;

    assign wrk_ena                      = wrk_ena_r;
    assign wrk_sel_wide_in              = wrk_sel_wide_in_r;
    assign wrk_sel_wide_out             = wrk_sel_wide_out_r;
    assign wrk_sel_narrow_in            = wrk_sel_narrow_in_r;
    assign wrk_sel_narrow_out           = wrk_sel_narrow_out_r;
    assign wrk_word_index_last          = wrk_word_index_last_r;
    assign wrk_word_index_last_half     = wrk_word_index_last_half_r;
    assign wrk_opcode                   = wrk_opcode_r;

    
    //
    // UOP_FSM
    //
    localparam [1:0] UOP_FSM_STATE_IDLE     = 2'b00;
    localparam [1:0] UOP_FSM_STATE_FETCH    = 2'b01;
    localparam [1:0] UOP_FSM_STATE_DECODE   = 2'b10;
    localparam [1:0] UOP_FSM_STATE_BUSY     = 2'b11;

    reg [1:0] uop_fsm_state = UOP_FSM_STATE_IDLE;
    reg [1:0] uop_fsm_state_next;
    
    
    //
    // UOP ROM
    //
    reg  [UOP_ADDR_W   -1:0] uop_addr;
    wire [UOP_W        -1:0] uop_data;
    
    modexpng_uop_rom uop_rom
    (
        .clk    (clk),
        .addr   (uop_addr),
        .data   (uop_data)
    );
    
    
    //
    // UOP ROM Data Decoder
    //
    wire [UOP_OPCODE_W -1:0] uop_data_opcode         = uop_data[UOP_W                                                                             -1-: UOP_OPCODE_W];
    wire [UOP_CRT_W    -1:0] uop_data_crt            = uop_data[UOP_W -UOP_OPCODE_W                                                               -1-: UOP_CRT_W   ];
    wire [UOP_NPQ_W    -1:0] uop_data_npq            = uop_data[UOP_W -UOP_OPCODE_W -UOP_CRT_W                                                    -1-: UOP_NPQ_W   ];
    wire [UOP_AUX_W    -1:0] uop_data_aux            = uop_data[UOP_W -UOP_OPCODE_W -UOP_CRT_W -UOP_NPQ_W                                         -1-: UOP_AUX_W   ];
    wire [UOP_LADDER_W -1:0] uop_data_ladder         = uop_data[UOP_W -UOP_OPCODE_W -UOP_CRT_W -UOP_NPQ_W -UOP_AUX_W                              -1-: UOP_LADDER_W];
    wire [BANK_ADDR_W  -1:0] uop_data_sel_wide_in    = uop_data[UOP_W -UOP_OPCODE_W -UOP_CRT_W -UOP_NPQ_W -UOP_AUX_W -UOP_LADDER_W                -1-: BANK_ADDR_W ];
    wire [BANK_ADDR_W  -1:0] uop_data_sel_narrow_in  = uop_data[UOP_W -UOP_OPCODE_W -UOP_CRT_W -UOP_NPQ_W -UOP_AUX_W -UOP_LADDER_W -1*BANK_ADDR_W -1-: BANK_ADDR_W ];
    wire [BANK_ADDR_W  -1:0] uop_data_sel_wide_out   = uop_data[UOP_W -UOP_OPCODE_W -UOP_CRT_W -UOP_NPQ_W -UOP_AUX_W -UOP_LADDER_W -2*BANK_ADDR_W -1-: BANK_ADDR_W ];
    wire [BANK_ADDR_W  -1:0] uop_data_sel_narrow_out = uop_data[UOP_W -UOP_OPCODE_W -UOP_CRT_W -UOP_NPQ_W -UOP_AUX_W -UOP_LADDER_W -3*BANK_ADDR_W -1-: BANK_ADDR_W ];
    
    wire uop_opcode_is_stop   =  uop_data_opcode == UOP_OPCODE_STOP                ;
    wire uop_opcode_is_in     = (uop_data_opcode == UOP_OPCODE_INPUT_TO_WIDE       ) ||
                                (uop_data_opcode == UOP_OPCODE_INPUT_TO_NARROW     ) ;
    wire uop_opcode_is_out    =  uop_data_opcode == UOP_OPCODE_OUTPUT_FROM_NARROW  ;
    wire uop_opcode_is_wrk    = (uop_data_opcode == UOP_OPCODE_COPY_CRT_Y2X        ) ||
                                (uop_data_opcode == UOP_OPCODE_COPY_LADDERS_X2Y    ) ||
                                (uop_data_opcode == UOP_OPCODE_CROSS_LADDERS_X2Y   ) ||
                                (uop_data_opcode == UOP_OPCODE_MODULAR_SUBTRACT    ) ||
                                (uop_data_opcode == UOP_OPCODE_MODULAR_REDUCE_INIT ) ||
                                (uop_data_opcode == UOP_OPCODE_PROPAGATE_CARRIES   ) ||
                                (uop_data_opcode == UOP_OPCODE_MERGE_LH            ) ||
                                (uop_data_opcode == UOP_OPCODE_REGULAR_ADD_UNEVEN  ) ;
    wire uop_opcode_is_mmm    = (uop_data_opcode == UOP_OPCODE_MODULAR_MULTIPLY    ) ||
                                (uop_data_opcode == UOP_OPCODE_MODULAR_REDUCE_PROC ) ||
                                (uop_data_opcode == UOP_OPCODE_REGULAR_MULTIPLY    ) ;
    wire uop_opcode_is_ladder = (uop_data_opcode == UOP_OPCODE_LADDER_INIT         ) ||
                                (uop_data_opcode == UOP_OPCODE_LADDER_STEP         ) ;


    //
    // Debug Signal
    //
    `ifdef MODEXPNG_ENABLE_DEBUG
    assign uop_decoded_stop = (uop_fsm_state == UOP_FSM_STATE_DECODE) && uop_opcode_is_stop;
    `else
    assign uop_decoded_stop = 1'b0;
    `endif
    
        

    //
    // UOP ROM Address Increment Logic
    //
    wire                   uop_loop_now;
    
    wire [UOP_ADDR_W -1:0] uop_addr_plus1  = uop_addr + 1'b1;
    wire [UOP_ADDR_W -1:0] uop_addr_minus1 = uop_addr - 1'b1;
    
    wire [UOP_ADDR_W -1:0] uop_addr_next   = uop_loop_now ? uop_addr_minus1           : uop_addr_plus1             ;
    wire [UOP_ADDR_W -1:0] uop_addr_offset = crt_mode     ? UOP_ADDR_OFFSET_USING_CRT : UOP_ADDR_OFFSET_WITHOUT_CRT;

    always @(posedge clk)
        //
        if (uop_fsm_state_next == UOP_FSM_STATE_FETCH)
            uop_addr <= (uop_fsm_state == UOP_FSM_STATE_IDLE) ? uop_addr_offset : uop_addr_next;


    //
    // Handy Signals 
    //
    wire mmm_ena = mmm_ena_x & mmm_ena_y;
    wire mmm_rdy = mmm_rdy_x & mmm_rdy_y;
    
    assign uop_loop_now = (uop_data_opcode == UOP_OPCODE_LADDER_STEP) && !io_mgr_ladder_done;
    
    reg [1:0] uop_data_ladder_mux; 
    
    always @(uop_data_ladder, io_mgr_ladder_p, io_mgr_ladder_q, io_mgr_ladder_d)
        //
        case (uop_data_ladder)
            UOP_LADDER_00: uop_data_ladder_mux = 2'b00;
            UOP_LADDER_11: uop_data_ladder_mux = 2'b11;
            UOP_LADDER_D:  uop_data_ladder_mux = {~io_mgr_ladder_d, io_mgr_ladder_d};
            UOP_LADDER_PQ: uop_data_ladder_mux = { io_mgr_ladder_p, io_mgr_ladder_q};
        endcase

    reg [OP_ADDR_W-1:0] word_index_last_mux;
    
    always @(uop_data_npq, word_index_last_n, word_index_last_pq)
        //
        if (uop_data_npq == UOP_NPQ_N) word_index_last_mux = word_index_last_n;
        else                           word_index_last_mux = word_index_last_pq;
    
    reg [BIT_INDEX_W-1:0] bit_index_last_mux;
    
    always @(crt_mode, bit_index_last_pq, bit_index_last_n)
        if (crt_mode) bit_index_last_mux = bit_index_last_pq;
        else          bit_index_last_mux = bit_index_last_n;


    //
    // UOP Trigger Logic
    //
    always @(posedge clk or negedge rst_n)
        //
        if (!rst_n) begin
            io_mgr_ena_r <= 1'b0;
            mmm_ena_x_r  <= 1'b0;
            mmm_ena_y_r  <= 1'b0;
            wrk_ena_r    <= 1'b0;
        end else begin
            io_mgr_ena_r <= uop_fsm_state == UOP_FSM_STATE_DECODE ? (uop_opcode_is_in    ||
                                                                     uop_opcode_is_out   ||
                                                                     uop_opcode_is_ladder): 1'b0;
            mmm_ena_x_r  <= uop_fsm_state == UOP_FSM_STATE_DECODE ?  uop_opcode_is_mmm    : 1'b0;
            mmm_ena_y_r  <= uop_fsm_state == UOP_FSM_STATE_DECODE ?  uop_opcode_is_mmm    : 1'b0;
            wrk_ena_r    <= uop_fsm_state == UOP_FSM_STATE_DECODE ? (uop_opcode_is_wrk   ||
                                                                     uop_opcode_is_out   ): 1'b0;
        end


    //
    // UOP Completion Detector 
    //
    reg uop_exit_from_busy;

    always @* begin
        //
        uop_exit_from_busy = 0;
        //
        if (uop_opcode_is_in    ) uop_exit_from_busy =  ~io_mgr_ena & io_mgr_rdy  ;
        if (uop_opcode_is_out   ) uop_exit_from_busy = (~io_mgr_ena & io_mgr_rdy) &
                                                       (~wrk_ena    & wrk_rdy   ) ;
        if (uop_opcode_is_mmm   ) uop_exit_from_busy =  ~mmm_ena    & mmm_rdy     ;
        if (uop_opcode_is_wrk   ) uop_exit_from_busy =  ~wrk_ena    & wrk_rdy     ;
        if (uop_opcode_is_ladder) uop_exit_from_busy =  ~io_mgr_ena & io_mgr_rdy  ;
        //
    end


    //
    // UOP Parameters
    //
    task update_io_mgr_params;
        input [UOP_CRT_W   -1:0] sel_crt;
        input [UOP_AUX_W   -1:0] sel_aux;
        input [BANK_ADDR_W -1:0] sel_in;
        input [BANK_ADDR_W -1:0] sel_out;
        input [UOP_OPCODE_W-1:0] opcode;
        begin
            io_mgr_sel_crt_r      <= sel_crt;
            io_mgr_sel_aux_r      <= sel_aux;
            io_mgr_sel_in_r       <= sel_in;
            io_mgr_sel_out_r      <= sel_out;
            io_mgr_opcode_r       <= opcode;
        end
    endtask
    
    task update_wrk_params;
        input [BANK_ADDR_W -1:0] wide_in;
        input [BANK_ADDR_W -1:0] narrow_in;
        input [BANK_ADDR_W -1:0] wide_out;
        input [BANK_ADDR_W -1:0] narrow_out;
        input [UOP_OPCODE_W-1:0] opcode;
        begin
            wrk_sel_wide_in_r    <= wide_in;
            wrk_sel_narrow_in_r  <= narrow_in;
            wrk_sel_wide_out_r   <= wide_out;
            wrk_sel_narrow_out_r <= narrow_out;
            wrk_opcode_r         <= opcode;
        end
    endtask
    
    task update_mmm_params;
        input [            1:0] ladder_mode;
        input [BANK_ADDR_W-1:0] sel_wide_in;
        input [BANK_ADDR_W-1:0] sel_narrow_in;
        input                   force_unity_b;
        input                   only_reduce;
        input                   just_multiply;
        begin
            {mmm_ladder_mode_x_r,   mmm_ladder_mode_y_r  } <=    ladder_mode;
            {mmm_sel_wide_in_x_r,   mmm_sel_wide_in_y_r  } <= {2{sel_wide_in  }};
            {mmm_sel_narrow_in_x_r, mmm_sel_narrow_in_y_r} <= {2{sel_narrow_in}};
            {mmm_force_unity_b_x_r, mmm_force_unity_b_y_r} <= {2{force_unity_b}};
            {mmm_only_reduce_x_r,   mmm_only_reduce_y_r  } <= {2{only_reduce  }};            
            {mmm_just_multiply_x_r, mmm_just_multiply_y_r} <= {2{just_multiply}};
        end
    endtask
        
    task update_rdct_params;
        input [BANK_ADDR_W-1:0] sel_wide_out;
        input [BANK_ADDR_W-1:0] sel_narrow_out;
        begin
            {rdct_sel_wide_out_x_r,   rdct_sel_wide_out_y_r  } <= {2{sel_wide_out}};
            {rdct_sel_narrow_out_x_r, rdct_sel_narrow_out_y_r} <= {2{sel_narrow_out}};
        end
    endtask
        
    always @(posedge clk)
        //
        if (uop_fsm_state == UOP_FSM_STATE_DECODE)
            //
            case (uop_data_opcode)
                //
                UOP_OPCODE_INPUT_TO_WIDE:
                    update_io_mgr_params(uop_data_crt, uop_data_aux, uop_data_sel_narrow_in, uop_data_sel_wide_out, uop_data_opcode);
                //
                UOP_OPCODE_INPUT_TO_NARROW:
                    update_io_mgr_params(uop_data_crt, uop_data_aux, uop_data_sel_narrow_in, uop_data_sel_narrow_out, uop_data_opcode);
                //
                UOP_OPCODE_OUTPUT_FROM_NARROW: begin
                    update_io_mgr_params(uop_data_crt, uop_data_aux, BANK_DNC, uop_data_sel_narrow_out, uop_data_opcode);
                    update_wrk_params(BANK_DNC, uop_data_sel_narrow_in, BANK_DNC, BANK_DNC, uop_data_opcode);                
                end
                //
                UOP_OPCODE_COPY_CRT_Y2X,
                UOP_OPCODE_COPY_LADDERS_X2Y,
                UOP_OPCODE_CROSS_LADDERS_X2Y:
                    update_wrk_params(uop_data_sel_wide_in, uop_data_sel_narrow_in, uop_data_sel_wide_out, uop_data_sel_narrow_out, uop_data_opcode);
                //
                UOP_OPCODE_MODULAR_MULTIPLY: begin
                    update_mmm_params(uop_data_ladder_mux, uop_data_sel_wide_in, uop_data_sel_narrow_in, uop_data_aux, 1'b0, 1'b0);
                    update_rdct_params(uop_data_sel_wide_out, uop_data_sel_narrow_out);
                end
                //
                UOP_OPCODE_MODULAR_SUBTRACT:
                    update_wrk_params(BANK_DNC, uop_data_sel_narrow_in, uop_data_sel_wide_out, uop_data_sel_narrow_out, uop_data_opcode);
                //
                UOP_OPCODE_MODULAR_REDUCE_INIT:
                    update_wrk_params(BANK_DNC, uop_data_sel_narrow_in, BANK_DNC, BANK_DNC, uop_data_opcode);
                //
                UOP_OPCODE_MODULAR_REDUCE_PROC: begin
                    update_mmm_params(2'bXX, BANK_DNC, BANK_DNC, 1'b0, 1'b1, 1'b0);
                    update_rdct_params(uop_data_sel_wide_out, uop_data_sel_narrow_out);                                    
                end
                //
                UOP_OPCODE_PROPAGATE_CARRIES:
                    update_wrk_params(BANK_DNC, uop_data_sel_narrow_in, BANK_DNC, uop_data_sel_narrow_out, uop_data_opcode);
                //
                UOP_OPCODE_MERGE_LH:
                    update_wrk_params(BANK_DNC, BANK_DNC, BANK_DNC, uop_data_sel_narrow_out, uop_data_opcode);
                //
                UOP_OPCODE_REGULAR_MULTIPLY: begin
                    update_mmm_params(2'b11, uop_data_sel_wide_in, uop_data_sel_narrow_in, 1'b0, 1'b0, 1'b1);
                    update_rdct_params(uop_data_sel_wide_out, uop_data_sel_narrow_out);
                end
                //
                UOP_OPCODE_REGULAR_ADD_UNEVEN:
                    update_wrk_params(uop_data_sel_wide_in, uop_data_sel_narrow_in, BANK_DNC, uop_data_sel_narrow_out, uop_data_opcode);
                //
                UOP_OPCODE_LADDER_INIT,
                UOP_OPCODE_LADDER_STEP:
                    update_io_mgr_params(UOP_CRT_DNC, UOP_AUX_DNC, BANK_DNC, BANK_DNC, uop_data_opcode);
                //
            endcase
     
            
    //
    // UOP Lengths
    //
    task update_io_mgr_length;
        input [OP_ADDR_W  -1:0] word_index_last;
        input [BIT_INDEX_W-1:0] ladder_steps;
        begin
            io_mgr_word_index_last_r <= word_index_last;
            io_mgr_ladder_steps_r    <= ladder_steps;
        end
    endtask
    
    task update_wrk_length;
        input [OP_ADDR_W-1:0] word_index_last;
        input [OP_ADDR_W-1:0] word_index_last_half;
        begin
            wrk_word_index_last_r      <= word_index_last;
            wrk_word_index_last_half_r <= word_index_last_half;
        end
    endtask
    
    task update_mmm_length;
        input [OP_ADDR_W-1:0] word_index_last;
        begin
            mmm_word_index_last_x_r        <= word_index_last;
            mmm_word_index_last_y_r        <= word_index_last;
            mmm_word_index_last_minus1_x_r <= word_index_last - 1'b1;
            mmm_word_index_last_minus1_y_r <= word_index_last - 1'b1;
        end
    endtask
    
    task update_rdct_length;
        input [OP_ADDR_W-1:0] word_index_last;
        begin
            rdct_word_index_last_x_r <= word_index_last;
            rdct_word_index_last_y_r <= word_index_last;
        end
    endtask
    
    always @(posedge clk)
        //
        if (uop_fsm_state == UOP_FSM_STATE_DECODE)
            //
            case (uop_data_opcode)
                //
                UOP_OPCODE_INPUT_TO_WIDE,    
                UOP_OPCODE_INPUT_TO_NARROW,
                UOP_OPCODE_OUTPUT_FROM_NARROW:
                    update_io_mgr_length(word_index_last_mux, BIT_INDEX_DNC);
                //
                UOP_OPCODE_COPY_CRT_Y2X,
                UOP_OPCODE_COPY_LADDERS_X2Y,
                UOP_OPCODE_CROSS_LADDERS_X2Y:
                    update_wrk_length(word_index_last_mux, OP_ADDR_DNC);
                //
                UOP_OPCODE_MODULAR_MULTIPLY: begin
                    update_mmm_length(word_index_last_mux);
                    update_rdct_length(word_index_last_mux);
                end
                //            
                UOP_OPCODE_MODULAR_SUBTRACT:
                    update_wrk_length(word_index_last_mux, OP_ADDR_DNC);
                //
                UOP_OPCODE_MODULAR_REDUCE_INIT:
                    update_wrk_length(word_index_last_n, word_index_last_pq);
                //
                UOP_OPCODE_MODULAR_REDUCE_PROC: begin
                    update_mmm_length(word_index_last_pq);
                    update_rdct_length(word_index_last_pq);
                end
                //
                UOP_OPCODE_PROPAGATE_CARRIES:
                    update_wrk_length(word_index_last_mux, OP_ADDR_DNC);
                //
                UOP_OPCODE_MERGE_LH:
                    update_wrk_length(word_index_last_n, word_index_last_pq);
                //
                UOP_OPCODE_REGULAR_MULTIPLY: begin
                    update_mmm_length(word_index_last_pq);
                    update_rdct_length(word_index_last_pq);
                end
                //
                UOP_OPCODE_REGULAR_ADD_UNEVEN:
                    update_wrk_length(word_index_last_n, word_index_last_pq);
                //
                UOP_OPCODE_LADDER_INIT,
                UOP_OPCODE_LADDER_STEP:
                    update_io_mgr_length(OP_ADDR_LADDER_LAST, bit_index_last_mux);
                //
            endcase
            
    //
    // UOP FSM Process
    //
    always @(posedge clk or negedge rst_n)
        //
        if (!rst_n) uop_fsm_state <= UOP_FSM_STATE_IDLE;
        else        uop_fsm_state <= uop_fsm_state_next;
            
    
    //
    // UOP FSM Transition Logic
    //
    always @* begin
        //
        case (uop_fsm_state)
            UOP_FSM_STATE_IDLE:   uop_fsm_state_next = ena                ? UOP_FSM_STATE_FETCH  : UOP_FSM_STATE_IDLE;
            UOP_FSM_STATE_FETCH:  uop_fsm_state_next =                      UOP_FSM_STATE_DECODE ;
            UOP_FSM_STATE_DECODE: uop_fsm_state_next = uop_opcode_is_stop ? UOP_FSM_STATE_IDLE   : UOP_FSM_STATE_BUSY;
            UOP_FSM_STATE_BUSY:   uop_fsm_state_next = uop_exit_from_busy ? UOP_FSM_STATE_FETCH  : UOP_FSM_STATE_BUSY;
        endcase
        //
    end


    //
    // Ready Flag Logic
    //
    reg rdy_r = 1'b1;
    assign rdy = rdy_r;

    always @(posedge clk or negedge rst_n)
        //
        if (!rst_n)               rdy_r <= 1'b1;
        else case (uop_fsm_state)
            UOP_FSM_STATE_IDLE:   rdy_r <= ~ena;
            UOP_FSM_STATE_DECODE: rdy_r <= uop_opcode_is_stop;
        endcase


endmodule