//======================================================================
//
// 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