module modexpng_reductor
(
clk, rst,
ena, rdy,
word_index_last,
rd_wide_xy_addr_aux, rd_wide_xy_bank_aux, rd_wide_x_dout_aux, rd_wide_y_dout_aux,
rcmb_final_xy_bank, rcmb_final_xy_addr, rcmb_final_x_dout, rcmb_final_y_dout, rcmb_final_xy_valid,
rdct_final_xy_addr, rdct_final_x_dout, rdct_final_y_dout, rdct_final_xy_valid
);
//
// Headers
//
`include "../rtl/modexpng_parameters.vh"
input clk;
input rst;
//
input ena;
output rdy;
//
input [ OP_ADDR_W -1:0] word_index_last;
//
input [BANK_ADDR_W -1:0] rd_wide_xy_bank_aux;
input [ OP_ADDR_W -1:0] rd_wide_xy_addr_aux;
input [ WORD_EXT_W -1:0] rd_wide_x_dout_aux;
input [ WORD_EXT_W -1:0] rd_wide_y_dout_aux;
//
input [BANK_ADDR_W -1:0] rcmb_final_xy_bank;
input [ OP_ADDR_W -1:0] rcmb_final_xy_addr;
input [ WORD_EXT_W -1:0] rcmb_final_x_dout;
input [ WORD_EXT_W -1:0] rcmb_final_y_dout;
input rcmb_final_xy_valid;
//
output [ OP_ADDR_W -1:0] rdct_final_xy_addr;
output [ WORD_EXT_W -1:0] rdct_final_x_dout;
output [ WORD_EXT_W -1:0] rdct_final_y_dout;
output rdct_final_xy_valid;
//
// Ready
//
reg rdy_reg = 1'b1;
reg busy_now = 1'b0;
assign rdy = rdy_reg;
always @(posedge clk)
//
if (rst) rdy_reg <= 1'b1;
else begin
if (rdy && ena) rdy_reg <= 1'b0;
if (!rdy && !busy_now) rdy_reg <= 1'b1;
end
//
// Pipeline (Delay Match)
//
reg rcmb_xy_valid_dly1 = 1'b0;
reg rcmb_xy_valid_dly2 = 1'b0;
reg rcmb_xy_valid_dly3 = 1'b0;
reg [BANK_ADDR_W -1:0] rcmb_xy_bank_dly1;
reg [BANK_ADDR_W -1:0] rcmb_xy_bank_dly2;
reg [BANK_ADDR_W -1:0] rcmb_xy_bank_dly3;
reg [ OP_ADDR_W -1:0] rcmb_xy_addr_dly1;
reg [ OP_ADDR_W -1:0] rcmb_xy_addr_dly2;
reg [ OP_ADDR_W -1:0] rcmb_xy_addr_dly3;
reg [ WORD_EXT_W -1:0] rcmb_x_dout_dly1;
reg [ WORD_EXT_W -1:0] rcmb_x_dout_dly2;
reg [ WORD_EXT_W -1:0] rcmb_x_dout_dly3;
reg [ WORD_EXT_W -1:0] rcmb_y_dout_dly1;
reg [ WORD_EXT_W -1:0] rcmb_y_dout_dly2;
reg [ WORD_EXT_W -1:0] rcmb_y_dout_dly3;
always @(posedge clk)
//
if (rst) begin
rcmb_xy_valid_dly1 <= 1'b0;
rcmb_xy_valid_dly2 <= 1'b0;
rcmb_xy_valid_dly3 <= 1'b0;
end else begin
rcmb_xy_valid_dly1 <= rcmb_final_xy_valid;
rcmb_xy_valid_dly2 <= rcmb_xy_valid_dly1;
rcmb_xy_valid_dly3 <= rcmb_xy_valid_dly2;
end
always @(posedge clk) begin
//
if (rcmb_final_xy_valid) begin
rcmb_xy_bank_dly1 <= rcmb_final_xy_bank;
rcmb_xy_addr_dly1 <= rcmb_final_xy_addr;
rcmb_x_dout_dly1 <= rcmb_final_x_dout;
rcmb_y_dout_dly1 <= rcmb_final_y_dout;
end
//
if (rcmb_xy_valid_dly1) begin
rcmb_xy_bank_dly2 <= rcmb_xy_bank_dly1;
rcmb_xy_addr_dly2 <= rcmb_xy_addr_dly1;
rcmb_x_dout_dly2 <= rcmb_x_dout_dly1;
rcmb_y_dout_dly2 <= rcmb_y_dout_dly1;
end
//
if (rcmb_xy_valid_dly2) begin
rcmb_xy_bank_dly3 <= rcmb_xy_bank_dly2;
rcmb_xy_addr_dly3 <= rcmb_xy_addr_dly2;
rcmb_x_dout_dly3 <= rcmb_x_dout_dly2;
rcmb_y_dout_dly3 <= rcmb_y_dout_dly2;
end
//
end
//
// Carry Logic
//
reg [RDCT_CARRY_W -1:0] rcmb_x_lsb_carry;
reg [WORD_W -1:0] rcmb_x_lsb_dummy;
reg [WORD_EXT_W -1:0] rcmb_x_lsb_dout;
reg [RDCT_CARRY_W -1:0] rcmb_y_lsb_carry;
reg [WORD_W -1:0] rcmb_y_lsb_dummy;
reg [WORD_EXT_W -1:0] rcmb_y_lsb_dout;
//
// Carry Computation
//
always @(posedge clk)
//
if (ena) begin
rcmb_x_lsb_carry <= RDCT_CARRY_ZEROES;
rcmb_y_lsb_carry <= RDCT_CARRY_ZEROES;
end else if (rcmb_xy_valid_dly3)
//
case (rcmb_xy_bank_dly3)
BANK_RCMB_ML: begin
{rcmb_x_lsb_carry, rcmb_x_lsb_dummy} <= rcmb_x_dout_dly3 + rd_wide_x_dout_aux + rcmb_x_lsb_carry;
{rcmb_y_lsb_carry, rcmb_y_lsb_dummy} <= rcmb_y_dout_dly3 + rd_wide_y_dout_aux + rcmb_y_lsb_carry;
end
BANK_RCMB_MH:
if (rcmb_xy_addr_dly3 == OP_ADDR_ZERO) begin
{rcmb_x_lsb_carry, rcmb_x_lsb_dummy} <= rcmb_x_dout_dly3 + rd_wide_x_dout_aux + rcmb_x_lsb_carry;
{rcmb_y_lsb_carry, rcmb_y_lsb_dummy} <= rcmb_y_dout_dly3 + rd_wide_y_dout_aux + rcmb_y_lsb_carry;
end
endcase
//
// Reduction
//
reg [ OP_ADDR_W -1:0] rdct_xy_addr;
reg [WORD_EXT_W -1:0] rdct_x_dout;
reg [WORD_EXT_W -1:0] rdct_y_dout;
reg rdct_xy_valid = 1'b0;
assign rdct_final_xy_addr = rdct_xy_addr;
assign rdct_final_x_dout = rdct_x_dout;
assign rdct_final_y_dout = rdct_y_dout;
assign rdct_final_xy_valid = rdct_xy_valid;
task _update_rdct;
input [ OP_ADDR_W -1:0] addr;
input [WORD_EXT_W -1:0] dout_x;
input [WORD_EXT_W -1:0] dout_y;
input valid;
begin
rdct_xy_addr <= addr;
rdct_x_dout <= dout_x;
rdct_y_dout <= dout_y;
rdct_xy_valid <= valid;
end
endtask
task set_rdct;
input [ OP_ADDR_W -1:0] addr;
input [WORD_EXT_W -1:0] dout_x;
input [WORD_EXT_W -1:0] dout_y;
begin
_update_rdct(addr, dout_x, dout_y, 1'b1);
end
endtask
task clear_rdct;
begin
_update_rdct(OP_ADDR_DONT_CARE, WORD_EXT_DONT_CARE, WORD_EXT_DONT_CARE, 1'b0);
end
endtask
//
// Helper Wires
//
wire [WORD_EXT_W -1:0] sum_rdct_x = rcmb_x_dout_dly3 + rd_wide_x_dout_aux;
wire [WORD_EXT_W -1:0] sum_rdct_y = rcmb_y_dout_dly3 + rd_wide_y_dout_aux;
wire [WORD_EXT_W -1:0] sum_rdct_x_carry = sum_rdct_x + {WORD_NULL, rcmb_x_lsb_carry};
wire [WORD_EXT_W -1:0] sum_rdct_y_carry = sum_rdct_y + {WORD_NULL, rcmb_y_lsb_carry};
//
//
//
always @(posedge clk)
//
if (rst) clear_rdct;
else begin
//
clear_rdct;
//
if (busy_now && rcmb_xy_valid_dly3)
//
case (rcmb_xy_bank_dly3)
BANK_RCMB_MH:
if (rcmb_xy_addr_dly3 == OP_ADDR_ONE)
set_rdct(OP_ADDR_ZERO, sum_rdct_x_carry, sum_rdct_y_carry);
else if (rcmb_xy_addr_dly3 > OP_ADDR_ONE)
set_rdct(rcmb_xy_addr_dly3 - 1'b1, sum_rdct_x, sum_rdct_y);
BANK_RCMB_EXT:
set_rdct(word_index_last, rcmb_x_dout_dly3, rcmb_y_dout_dly3);
endcase
//
end
//
// Busy
//
always @(posedge clk)
//
if (rst) busy_now <= 1'b0;
else begin
if (rdy && ena) busy_now <= 1'b1;
//if (!rdy && !busy_now) rdy <= 1'b1;
end
endmodule