//======================================================================
//
// 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_recombinator_cell
(
clk,
ce, clr,
din, dout, doutw
);
//
// Headers
//
`include "modexpng_parameters.vh"
`include "modexpng_dsp48e1.vh"
`include "modexpng_dsp_slice_primitives.vh"
//
// Ports
//
input clk;
input ce;
input clr;
input [ MAC_W -1:0] din;
output [WORD_W -1:0] dout;
output [WORD_W :0] doutw;
//
// din <=> {z[13:0], y[15:0], x[15:0]}
//
wire [WORD_W -1:0] din_z = {2'b00, din[3 * WORD_W -3 : 2 * WORD_W]}; // [47:46][45:32]
wire [WORD_W -1:0] din_y = { din[2 * WORD_W -1 : WORD_W]}; // [31:16]
wire [WORD_W -1:0] din_x = { din[ WORD_W -1 : 0]}; // [15: 0]
//
// Delayed Clock Enables
//
reg ce_dly1 = 1'b0, ce_dly2 = 1'b0, ce_dly3 = 1'b0, ce_dly4 = 1'b0, ce_dly5 = 1'b0, ce_dly6 = 1'b0;
always @(posedge clk) {ce_dly1, ce_dly2, ce_dly3, ce_dly4, ce_dly5, ce_dly6} <= {ce, ce_dly1, ce_dly2, ce_dly3, ce_dly4, ce_dly5};
//
// Delayed Clear
//
reg clr_dly1, clr_dly2, clr_dly3, clr_dly4;
always @(posedge clk) {clr_dly1, clr_dly2, clr_dly3, clr_dly4} <= {clr, clr_dly1, clr_dly2, clr_dly3};
//
// Phase Flip-Flop
//
reg phase_ff, phase_ff_dly1, phase_ff_dly2, phase_ff_dly3, phase_ff_dly4, phase_ff_dly5;
always @(posedge clk)
if (ce) phase_ff <= ~phase_ff;
else if (clr) phase_ff <= 1'b0;
always @(posedge clk)
{phase_ff_dly1, phase_ff_dly2, phase_ff_dly3, phase_ff_dly4, phase_ff_dly5} <= {phase_ff, phase_ff_dly1, phase_ff_dly2, phase_ff_dly3, phase_ff_dly4};
//
// Shift Registers
//
reg [WORD_W-1:0] din_x_dly1;
reg [WORD_W-1:0] din_y_dly1;
reg [WORD_W-1:0] din_z_dly1;
reg [WORD_W-1:0] din_z_dly2;
always @(posedge clk) begin
//
if (ce) {din_x_dly1, din_y_dly1, din_z_dly1} <= {din_x, din_y, din_z};
else if (clr) {din_x_dly1, din_y_dly1, din_z_dly1} <= {WORD_ZERO, WORD_ZERO, WORD_ZERO};
//
if (ce) {din_z_dly2} <= {din_z_dly1};
else if (clr) {din_z_dly2} <= {WORD_ZERO};
//
end
//
// DSP Input Registers
//
reg [2 * WORD_W-1:0] master_ab_reg;
reg [2 * WORD_W-1:0] master_c_reg;
reg [ WORD_W+1:0] slave_ab_reg;
reg [ WORD_W+1:0] slave_ab_next_reg;
//
// DSP Cascade Bus
//
wire [DSP48E1_P_W-1:0] master_slave_p_int;
//
// DSP Output Buses
//
wire [DSP48E1_P_W-1:0] master_p_int;
wire [DSP48E1_P_W-1:0] slave_p_int;
//
// DSP Input Mapping
//
wire [DSP48E1_C_W-1:0] master_ab_int = {{(DSP48E1_C_W - 2 * WORD_W){1'b0}}, master_ab_reg};
wire [DSP48E1_C_W-1:0] master_c_int = {{(DSP48E1_C_W - 2 * WORD_W){1'b0}}, master_c_reg};
wire [DSP48E1_C_W-1:0] slave_ab_int = {{(DSP48E1_C_W - (WORD_W+3)){1'b0}}, slave_ab_reg[WORD_W+1:WORD_W], 1'b1, slave_ab_reg[WORD_W-1:0]};
wire [DSP48E1_C_W-1:0] slave_c_int = {DSP48E1_C_W{1'b0}};
//
// Master DSP Input Logic
//
always @(posedge clk)
//
if (ce) begin
master_ab_reg <= !phase_ff ? {din_y, din_y_dly1} : {din_x, din_x_dly1};
master_c_reg <= !phase_ff ? {din_z_dly1, din_z_dly2} : {WORD_DNC, WORD_DNC};
end else begin
master_ab_reg <= {WORD_DNC, WORD_DNC};
master_c_reg <= {WORD_DNC, WORD_DNC};
end
//
// Slave DSP Input Logic
//
always @(posedge clk) begin
//
slave_ab_reg <= {(WORD_W+2){1'bX}};
slave_ab_next_reg <= {(WORD_W+2){1'bX}};
//
if (ce_dly3 && phase_ff_dly3) slave_ab_next_reg <= {master_p_int[2*WORD_W+1:WORD_W]};
//
if (ce_dly3 && phase_ff_dly3) slave_ab_reg <= {2'b00, master_p_int[WORD_W-1:0]};
if (ce_dly4 && phase_ff_dly4) slave_ab_reg <= slave_ab_next_reg;
//
end
//
// OPMODE Logic
//
reg [DSP48E1_OPMODE_W-1:0] master_opmode;
reg [DSP48E1_OPMODE_W-1:0] slave_opmode;
always @(posedge clk) begin
//
if (ce) master_opmode <= !phase_ff ? DSP48E1_OPMODE_Z0_YC_XAB : DSP48E1_OPMODE_ZP_Y0_XAB;
else master_opmode <= DSP48E1_OPMODE_DNC;
//
if (ce_dly4) slave_opmode <= clr_dly4 ? DSP48E1_OPMODE_Z0_Y0_XAB : DSP48E1_OPMODE_ZP17_Y0_XAB;
else slave_opmode <= DSP48E1_OPMODE_DNC;
//
end
//
// DSP Slice Instances
//
`MODEXPNG_DSP_SLICE_ADDSUB dsp_master_inst
(
.clk (clk),
.ce_abc (ce_dly1),
.ce_p (ce_dly2),
.ce_ctrl (ce_dly1),
.ab (master_ab_int),
.c (master_c_int),
.p (master_p_int),
.op_mode (master_opmode),
.alu_mode (DSP48E1_ALUMODE_Z_PLUS_X_AND_Y_AND_CIN),
.carry_in_sel (DSP48E1_CARRYINSEL_CARRYIN),
.casc_p_in (),
.casc_p_out (),
.carry_out ()
);
`MODEXPNG_DSP_SLICE_ADDSUB dsp_slave_inst
(
.clk (clk),
.ce_abc (ce_dly5),
.ce_p (ce_dly6),
.ce_ctrl (ce_dly5),
.ab (slave_ab_int),
.c (slave_c_int),
.p (slave_p_int),
.op_mode (slave_opmode),
.alu_mode (DSP48E1_ALUMODE_Z_PLUS_X_AND_Y_AND_CIN),
.carry_in_sel (DSP48E1_CARRYINSEL_CARRYIN),
.casc_p_in (),
.casc_p_out (),
.carry_out ()
);
//
// Output Register
//
assign dout = {slave_p_int[WORD_W-1:0]};
assign doutw = {slave_p_int[WORD_W+1], dout};
endmodule