// Complex Multiplier with accumulation (pr+i.pi) = (ar+i.ai)*(br+i.bi)
// File: cmacc.v
// The RTL below describes a complex multiplier with accumulation
// which can be packed into 3 DSP blocks (Ultrascale architecture)
//Default parameters were changed because of slow test
//module cmacc # (parameter AWIDTH = 16, BWIDTH = 18, SIZEOUT = 40)
module cmacc # (parameter AWIDTH = 4, BWIDTH = 5, SIZEOUT = 9)
  (
   input clk,
   input sload,
   input signed [AWIDTH-1:0] ar,
   input signed [AWIDTH-1:0] ai,
  input signed [BWIDTH-1:0]   br,
  input signed [BWIDTH-1:0]   bi,
  output signed [SIZEOUT-1:0] pr,
  output signed [SIZEOUT-1:0] pi);

  reg signed [AWIDTH-1:0]  ai_d, ai_dd, ai_ddd, ai_dddd;
  reg signed [AWIDTH-1:0] ar_d, ar_dd, ar_ddd, ar_dddd;
  reg signed [BWIDTH-1:0] bi_d, bi_dd, bi_ddd, br_d, br_dd, br_ddd;
  reg signed [AWIDTH:0] addcommon;
  reg signed [BWIDTH:0] addr, addi;
  reg signed [AWIDTH+BWIDTH:0] mult0, multr, multi;
  reg signed [SIZEOUT-1:0] pr_int, pi_int, old_result_real, old_result_im;
  reg signed [AWIDTH+BWIDTH:0] common, commonr1, commonr2;

  reg sload_reg;

 `ifdef SIM
  initial
  begin
    ai_d = 0;
    ai_dd = 0;
    ai_ddd = 0;
    ai_dddd = 0;
    ar_d = 0;
    ar_dd = 0;
    ar_ddd = 0;
    ar_dddd = 0;
    bi_d = 0;
    bi_dd = 0;
    bi_ddd = 0;
    br_d = 0;
    br_dd = 0;
    br_ddd = 0;
  end
 `endif

  always @(posedge clk)
  begin
    ar_d      <= ar;
    ar_dd     <= ar_d;
    ai_d      <= ai;
    ai_dd     <= ai_d;
    br_d      <= br;
    br_dd     <= br_d;
    br_ddd    <= br_dd;
    bi_d      <= bi;
    bi_dd     <= bi_d;
    bi_ddd    <= bi_dd;
    sload_reg <= sload;
  end

  // Common factor (ar ai) x bi, shared for the calculations of the real and imaginary final products
  //
  always @(posedge clk)
  begin
    addcommon <= ar_d - ai_d;
    mult0     <= addcommon * bi_dd;
    common    <= mult0;
  end

  // Accumulation loop (combinatorial) for *Real*
  //
  always @(sload_reg or pr_int)
  if (sload_reg)
  old_result_real <= 0;
  else
  // 'sload' is now and opens the accumulation loop.
  // The accumulator takes the next multiplier output
  // in the same cycle.
  old_result_real <= pr_int;

  // Real product
  //
  always @(posedge clk)
  begin
    ar_ddd   <= ar_dd;
    ar_dddd  <= ar_ddd;
    addr     <= br_ddd - bi_ddd;
    multr    <= addr * ar_dddd;
    commonr1 <= common;
    pr_int   <= multr + commonr1 + old_result_real;
  end

  // Accumulation loop (combinatorial) for *Imaginary*
  //
  always @(sload_reg or pi_int)
  if (sload_reg)
  old_result_im <= 0;
  else
  // 'sload' is now and opens the accumulation loop.
  // The accumulator takes the next multiplier output
  // in the same cycle.
  old_result_im <= pi_int;

  // Imaginary product
  //
  always @(posedge clk)
  begin
    ai_ddd   <= ai_dd;
    ai_dddd  <= ai_ddd;
    addi     <= br_ddd + bi_ddd;
    multi    <= addi * ai_dddd;
    commonr2 <= common;
    pi_int   <= multi + commonr2 + old_result_im;
  end

  assign pr = pr_int;
  assign pi = pi_int;

endmodule // cmacc
