//======================================================================
//
// 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_reductor
(
clk, rst_n,
ena, rdy,
word_index_last,
sel_wide_out, sel_narrow_out,
/*rd_wide_xy_addr_aux, rd_wide_xy_bank_aux,*/ rd_wide_x_din_aux, rd_wide_y_din_aux,
rcmb_final_xy_bank, rcmb_final_xy_addr, rcmb_final_x_din, rcmb_final_y_din, rcmb_final_xy_valid,
rdct_wide_xy_bank, rdct_wide_xy_addr, rdct_wide_x_dout, rdct_wide_y_dout, rdct_wide_xy_valid,
rdct_narrow_xy_bank, rdct_narrow_xy_addr, rdct_narrow_x_dout, rdct_narrow_y_dout, rdct_narrow_xy_valid
);
//
// Headers
//
`include "modexpng_parameters.vh"
input clk;
input rst_n;
input ena;
output rdy;
/*
input [FSM_STATE_WIDTH-1:0] fsm_state_next;*/
input [7:0] word_index_last;/*
input dsp_xy_ce_p;
*/
input [2:0] sel_wide_out;
input [2:0] sel_narrow_out;
/*
input [9*47-1:0] dsp_x_p;
input [9*47-1:0] dsp_y_p;
input [ 4:0] col_index;
input [ 4:0] col_index_last;
*//*
input [ 7:0] rd_narrow_xy_addr;
input [ 1:0] rd_narrow_xy_bank;
*/
//input [ BANK_ADDR_W -1:0] rd_wide_xy_bank_aux;
//input [ 7:0] rd_wide_xy_addr_aux;
input [ 17:0] rd_wide_x_din_aux;
input [ 17:0] rd_wide_y_din_aux;
//
input [ BANK_ADDR_W -1:0] rcmb_final_xy_bank;
input [ 7:0] rcmb_final_xy_addr;
input [ 17:0] rcmb_final_x_din;
input [ 17:0] rcmb_final_y_din;
input rcmb_final_xy_valid;
output [ 2:0] rdct_wide_xy_bank;
output [ 7:0] rdct_wide_xy_addr;
output [ 17:0] rdct_wide_x_dout;
output [ 17:0] rdct_wide_y_dout;
output rdct_wide_xy_valid;
output [ 2:0] rdct_narrow_xy_bank;
output [ 7:0] rdct_narrow_xy_addr;
output [ 17:0] rdct_narrow_x_dout;
output [ 17:0] rdct_narrow_y_dout;
output rdct_narrow_xy_valid;
//
// Ready
//
reg rdy_reg = 1'b1;
wire busy_now;
assign rdy = rdy_reg;
always @(posedge clk or negedge rst_n)
//
if (!rst_n) 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 [7:0] rcmb_xy_addr_dly1;
reg [7:0] rcmb_xy_addr_dly2;
reg [7:0] rcmb_xy_addr_dly3;
reg [17:0] rcmb_x_dout_dly1;
reg [17:0] rcmb_x_dout_dly2;
reg [17:0] rcmb_x_dout_dly3;
reg [17:0] rcmb_y_dout_dly1;
reg [17:0] rcmb_y_dout_dly2;
reg [17:0] rcmb_y_dout_dly3;
always @(posedge clk or negedge rst_n)
//
if (!rst_n) 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_din;
rcmb_y_dout_dly1 <= rcmb_final_y_din;
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
reg [ 1:0] rcmb_x_lsb_carry;
reg [15:0] rcmb_x_lsb_dummy;
reg [17:0] rcmb_x_lsb_dout;
reg [ 1:0] rcmb_y_lsb_carry;
reg [15:0] rcmb_y_lsb_dummy;
reg [17:0] rcmb_y_lsb_dout;
//reg [17:0] reductor_fat_bram_x_msb_dout;
//reg reductor_fat_bram_x_msb_dout_valid = 1'b0;
//reg [ 7:0] reductor_fat_bram_x_msb_addr;
//
// Carry Computation
//
always @(posedge clk)
//
if (ena) begin
rcmb_x_lsb_carry <= 2'b00;
rcmb_y_lsb_carry <= 2'b00;
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_din_aux + rcmb_x_lsb_carry;
{rcmb_y_lsb_carry, rcmb_y_lsb_dummy} <= rcmb_y_dout_dly3 + rd_wide_y_din_aux + rcmb_y_lsb_carry;
end
BANK_RCMB_MH:
if (rcmb_xy_addr_dly3 == 8'd0) begin
{rcmb_x_lsb_carry, rcmb_x_lsb_dummy} <= rcmb_x_dout_dly3 + rd_wide_x_din_aux + rcmb_x_lsb_carry;
{rcmb_y_lsb_carry, rcmb_y_lsb_dummy} <= rcmb_y_dout_dly3 + rd_wide_y_din_aux + rcmb_y_lsb_carry;
end
endcase
//
// Reduction
//
reg [ 2:0] wide_xy_bank;
reg [ 7:0] wide_xy_addr;
reg [ 17:0] wide_x_dout;
reg [ 17:0] wide_y_dout;
reg wide_xy_valid = 1'b0;
reg [ 2:0] narrow_xy_bank;
reg [ 7:0] narrow_xy_addr;
reg [ 17:0] narrow_x_dout;
reg [ 17:0] narrow_y_dout;
reg narrow_xy_valid = 1'b0;
assign rdct_wide_xy_bank = wide_xy_bank;
assign rdct_wide_xy_addr = wide_xy_addr;
assign rdct_wide_x_dout = wide_x_dout;
assign rdct_wide_y_dout = wide_y_dout;
assign rdct_wide_xy_valid = wide_xy_valid;
assign rdct_narrow_xy_bank = narrow_xy_bank;
assign rdct_narrow_xy_addr = narrow_xy_addr;
assign rdct_narrow_x_dout = narrow_x_dout;
assign rdct_narrow_y_dout = narrow_y_dout;
assign rdct_narrow_xy_valid = narrow_xy_valid;
task _update_rdct_wide;
input [ 2:0] bank;
input [ 7:0] addr;
input [17:0] dout_x;
input [17:0] dout_y;
input valid;
begin
wide_xy_bank <= bank;
wide_xy_addr <= addr;
wide_x_dout <= dout_x;
wide_y_dout <= dout_y;
wide_xy_valid <= valid;
end
endtask
task _update_rdct_narrow;
input [ 2:0] bank;
input [ 7:0] addr;
input [17:0] dout_x;
input [17:0] dout_y;
input valid;
begin
narrow_xy_bank <= bank;
narrow_xy_addr <= addr;
narrow_x_dout <= dout_x;
narrow_y_dout <= dout_y;
narrow_xy_valid <= valid;
end
endtask
task set_rdct_wide;
input [ 2:0] bank;
input [ 7:0] addr;
input [17:0] dout_x;
input [17:0] dout_y;
begin
_update_rdct_wide(bank, addr, dout_x, dout_y, 1'b1);
end
endtask
task set_rdct_narrow;
input [ 2:0] bank;
input [ 7:0] addr;
input [17:0] dout_x;
input [17:0] dout_y;
begin
_update_rdct_narrow(bank, addr, dout_x, dout_y, 1'b1);
end
endtask
task clear_rdct_wide;
begin
_update_rdct_wide(3'bXXX, 8'hXX, {18{1'bX}}, {18{1'bX}}, 1'b0);
end
endtask
task clear_rdct_narrow;
begin
_update_rdct_narrow(3'bXXX, 8'hXX, {18{1'bX}}, {18{1'bX}}, 1'b0);
end
endtask
//
//
//
wire [17:0] sum_rdct_x = rcmb_x_dout_dly3 + rd_wide_x_din_aux;
wire [17:0] sum_rdct_y = rcmb_y_dout_dly3 + rd_wide_y_din_aux;
wire [17:0] sum_rdct_x_carry = sum_rdct_x + {16'h0000, rcmb_x_lsb_carry};
wire [17:0] sum_rdct_y_carry = sum_rdct_y + {16'h0000, rcmb_y_lsb_carry};
//
//
//
always @(posedge clk or negedge rst_n)
//
if (!rst_n) begin
clear_rdct_wide;
clear_rdct_narrow;
end else begin
//
clear_rdct_wide;
clear_rdct_narrow;
//
if (rcmb_xy_valid_dly3)
//
case (rcmb_xy_bank_dly3)
BANK_RCMB_MH:
if (rcmb_xy_addr_dly3 == 8'd1) begin
set_rdct_wide (sel_wide_out, 8'd0, sum_rdct_x_carry, sum_rdct_y_carry);
set_rdct_narrow(sel_narrow_out, 8'd0, sum_rdct_x_carry, sum_rdct_y_carry);
end else if (rcmb_xy_addr_dly3 > 8'd1) begin
set_rdct_wide (sel_wide_out, rcmb_xy_addr_dly3 - 1'b1, sum_rdct_x, sum_rdct_y);
set_rdct_narrow(sel_narrow_out, rcmb_xy_addr_dly3 - 1'b1, sum_rdct_x, sum_rdct_y);
end
BANK_RCMB_EXT: begin
set_rdct_wide (sel_wide_out, word_index_last, rcmb_x_dout_dly3, rcmb_y_dout_dly3);
set_rdct_narrow(sel_narrow_out, word_index_last, rcmb_x_dout_dly3, rcmb_y_dout_dly3);
end
endcase
//
end
//
// Busy
//
reg busy_next = 1'b0;
reg [2:0] busy_now_shreg = {3{1'b0}};
assign busy_now = busy_now_shreg[2];
always @(posedge clk or negedge rst_n)
//
if (!rst_n) busy_now_shreg <= {3{1'b0}};
else begin
if (rdy && ena) busy_now_shreg <= {3{1'b1}};
else busy_now_shreg <= {busy_now_shreg[1:0], busy_next};
end
always @(posedge clk or negedge rst_n)
//
if (!rst_n) busy_next <= 1'b0;
else begin
if (rdy && ena) busy_next <= 1'b1;
if (!rdy && rcmb_xy_valid_dly3 && (rcmb_xy_bank_dly3 == BANK_RCMB_EXT)) busy_next <= 1'b0;
end
endmodule