aboutsummaryrefslogblamecommitdiff
path: root/rtl/modexpng_dsp_slice_addsub_wrapper_generic.v
blob: 1c4ca001040a5465ca5a1085c7d526d379fb1f2a (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_dsp_slice_addsub_wrapper_generic
(
    clk,
    ce_abc,
    ce_p,
    ce_ctrl,
    ab, c, p,
    op_mode,
    alu_mode,
    carry_in_sel,
    casc_p_in,
    casc_p_out,
    carry_out
);

    `include "modexpng_dsp48e1.vh"

    input                              clk;
    input                              ce_abc;
    input                              ce_p;
    input                              ce_ctrl;
    input  [         DSP48E1_C_W -1:0] ab;
    input  [         DSP48E1_C_W -1:0] c;
    output [         DSP48E1_P_W -1:0] p;
    input  [    DSP48E1_OPMODE_W -1:0] op_mode;
    input  [   DSP48E1_ALUMODE_W -1:0] alu_mode;
    input  [DSP48E1_CARRYINSEL_W -1:0] carry_in_sel;
    input  [         DSP48E1_P_W -1:0] casc_p_in;
    output [         DSP48E1_P_W -1:0] casc_p_out;
    output                             carry_out;


    //
    // Internal Registers
    //
    reg [         DSP48E1_C_W -1:0] reg_ab;
    reg [         DSP48E1_C_W -1:0] reg_c;

    reg [    DSP48E1_OPMODE_W -1:0] reg_op_mode;
    reg [   DSP48E1_ALUMODE_W -1:0] reg_alu_mode;
    reg [DSP48E1_CARRYINSEL_W -1:0] reg_carry_in_sel;

    always @(posedge clk)
        //
        if (ce_abc) begin
            reg_ab <= ab;
            reg_c <= c;
        end
        
    always @(posedge clk)
        //
        if (ce_ctrl) begin
            reg_op_mode      <= op_mode;
            reg_alu_mode     <= alu_mode;
            reg_carry_in_sel <= carry_in_sel;
        end


    //
    // Generic Math Slice Model
    //
    function  [DSP48E1_C_W-1:0] calc_mux_x;
        input [            1:0] opmode_x;
        input [DSP48E1_C_W-1:0] ab_value;
        case (opmode_x)
            DSP48E1_OPMODE_X_0:  calc_mux_x = {DSP48E1_C_W{1'b0}};
            DSP48E1_OPMODE_X_AB: calc_mux_x = ab_value;
            default: begin
                $display("ERROR: Bad X opmode (%b)!", opmode_x);
                $finish;
            end
        endcase
    endfunction

    function  [DSP48E1_C_W-1:0] calc_mux_y;
        input [            1:0] opmode_y;
        input [DSP48E1_C_W-1:0] c_value;
        case (opmode_y)
            DSP48E1_OPMODE_Y_0: calc_mux_y = {DSP48E1_C_W{1'b0}};
            DSP48E1_OPMODE_Y_C: calc_mux_y = c_value;
            default: begin
                $display("ERROR: Bad Y opmode (%b)!", opmode_y);
                $finish;
            end
        endcase
    endfunction

    function  [DSP48E1_C_W-1:0] calc_mux_z;
        input [            2:0] opmode_z;
        input [DSP48E1_P_W-1:0] p_value;
        input [DSP48E1_C_W-1:0] c_value;
        input [DSP48E1_P_W-1:0] pcin_value;
        case (opmode_z)
            DSP48E1_OPMODE_Z_0:      calc_mux_z = {DSP48E1_C_W{1'b0}};                        // 000
            DSP48E1_OPMODE_Z_PCIN:   calc_mux_z = pcin_value;                                 // 001
            DSP48E1_OPMODE_Z_P:      calc_mux_z = p_value;                                    // 010
            DSP48E1_OPMODE_Z_C:      calc_mux_z = c_value;                                    // 011
            DSP48E1_OPMODE_Z_PCIN17: calc_mux_z = {{17{1'b0}}, pcin_value[DSP48E1_C_W-1:17]}; // 101
            DSP48E1_OPMODE_Z_P17:    calc_mux_z = {{17{1'b0}}, p_value[DSP48E1_C_W-1:17]};    // 110
            default: begin
                $display("ERROR: Bad Z opmode (%b)!", opmode_z);
                $finish;
            end
        endcase
    endfunction

    function  [         DSP48E1_C_W -1:0] calc_mux_cin;
        input [DSP48E1_CARRYINSEL_W -1:0] carryinsel_value;
        input [   DSP48E1_ALUMODE_W -1:0] alumode_value;
        input                             carry_value;
        case (carryinsel_value)
            DSP48E1_CARRYINSEL_CARRYIN: calc_mux_cin = 1'b0; // 000
            DSP48E1_CARRYINSEL_CARRYCASCOUT: // 100
                case (alumode_value)
                    DSP48E1_ALUMODE_Z_PLUS_X_AND_Y_AND_CIN:  calc_mux_cin = carry_value; 
                    DSP48E1_ALUMODE_Z_MINUS_X_AND_Y_AND_CIN: calc_mux_cin = ~carry_value;
                    default: begin
                        $display("ERROR: Invalid ALUMODE (%b)!", alumode_value);
                        $finish;
                    end
                endcase
            default: begin
                $display("ERROR: Bad CARRYINSEL (%b)!", carryinsel_value);
                $finish;
            end
        endcase
    endfunction
    
    function  [         DSP48E1_P_W   :0] calc_p;
        input [   DSP48E1_ALUMODE_W -1:0] alumode_value;
        input [    DSP48E1_OPMODE_W -1:0] opmode_value;
        input [DSP48E1_CARRYINSEL_W -1:0] carryinsel_value;
        input [         DSP48E1_P_W -1:0] p_value;
        input [         DSP48E1_C_W -1:0] ab_value;
        input [         DSP48E1_C_W -1:0] c_value;
        input                             carry_value;
        input [         DSP48E1_P_W -1:0] pcin_value;
        reg   [         DSP48E1_C_W -1:0] mux_x;
        reg   [         DSP48E1_C_W -1:0] mux_y;
        reg   [         DSP48E1_C_W -1:0] mux_z;
        reg                               mux_cin;
        reg   [         DSP48E1_P_W   :0] int_p;
        begin
            mux_x   = calc_mux_x(opmode_value[1:0], ab_value);
            mux_y   = calc_mux_y(opmode_value[3:2], c_value);
            mux_z   = calc_mux_z(opmode_value[6:4], p_value, c_value, pcin_value);
            mux_cin = calc_mux_cin(carryinsel_value, alumode_value, carry_value);
            case (alumode_value)
                //
                DSP48E1_ALUMODE_Z_PLUS_X_AND_Y_AND_CIN: begin
                    int_p = {1'b0, mux_z} + {1'b0, mux_x} + {1'b0, mux_y} + {{DSP48E1_P_W{1'b0}}, mux_cin};
                    calc_p = {int_p[DSP48E1_P_W], int_p[DSP48E1_P_W-1:0]};
                end
                //  
                DSP48E1_ALUMODE_Z_MINUS_X_AND_Y_AND_CIN: begin
                    int_p = {1'b0, mux_z} - {1'b0, mux_x} - {1'b0, mux_y} - {{DSP48E1_P_W{1'b0}}, mux_cin};
                    calc_p = {~int_p[DSP48E1_P_W], int_p[DSP48E1_P_W-1:0]};
                end
                //
                default: begin
                    $display("ERROR: Invalid ALUMODE (%b)!", alumode_value);
                    $finish;
                end
            endcase
        end
    endfunction


    //
    // Output Registers
    //
    reg [DSP48E1_P_W -1:0] reg_p;
    reg                    reg_carry_out;
    
    always @(posedge clk)
        //
        if (ce_p) {reg_carry_out, reg_p} <=
            calc_p(reg_alu_mode, reg_op_mode, reg_carry_in_sel, reg_p, reg_ab, reg_c, reg_carry_out, casc_p_in);


    //
    // Output Buses
    //
    assign p = reg_p;
    assign carry_out = reg_carry_out;
    

    //
    // Cascade Bus
    //
    assign casc_p_out = reg_p;
    

endmodule