From d71f8fcc731aaf08a49c1bdedf0ca5cc5f504180 Mon Sep 17 00:00:00 2001 From: wgulian3 Date: Tue, 18 Feb 2020 13:02:46 -0500 Subject: [PATCH] Fix divide edge case in verilator and move divide modules out of SYN_FUNC block within alu. --- rtl/VX_alu.v | 144 ++++++++++++++++++++++------------------- rtl/compat/VX_divide.v | 105 ++++++++++++++++++------------ 2 files changed, 144 insertions(+), 105 deletions(-) diff --git a/rtl/VX_alu.v b/rtl/VX_alu.v index 633a87ab..064651de 100644 --- a/rtl/VX_alu.v +++ b/rtl/VX_alu.v @@ -16,6 +16,80 @@ module VX_alu( localparam div_pipeline_len = 3; + wire[31:0] unsigned_div_result; + wire[31:0] unsigned_rem_result; + wire[31:0] signed_div_result; + wire[31:0] signed_rem_result; + + VX_divide #( + .WIDTHN(32), + .WIDTHD(32), + .SPEED("HIGHEST"), + .PIPELINE(div_pipeline_len) + ) unsigned_div ( + .clock(clk), + .aclr(0), + .clken(1), // TODO this could be disabled on inactive instructions + .numer(ALU_in1), + .denom(ALU_in2), + .quotient(unsigned_div_result), + .remainder(unsigned_rem_result) + ); + + VX_divide #( + .WIDTHN(32), + .WIDTHD(32), + .NREP("SIGNED"), + .DREP("SIGNED"), + .SPEED("HIGHEST"), + .PIPELINE(div_pipeline_len) + ) signed_div ( + .clock(clk), + .aclr(0), + .clken(1), // TODO this could be disabled on inactive instructions + .numer(ALU_in1), + .denom(ALU_in2), + .quotient(signed_div_result), + .remainder(signed_rem_result) + ); + + reg [15:0] curr_inst_delay; + reg [15:0] inst_delay; + reg inst_was_stalling; + + wire inst_delay_stall = inst_was_stalling ? inst_delay != 0 : curr_inst_delay != 0; + assign out_alu_stall = inst_delay_stall; + + always @(*) begin + case(in_alu_op) + `DIV, + `DIVU, + `REM, + `REMU: curr_inst_delay = div_pipeline_len; + default: curr_inst_delay = 0; + endcase // in_alu_op + end + + always @(posedge clk or posedge reset) begin + if (reset) begin + inst_delay <= 0; + inst_was_stalling <= 0; + end + else if (inst_delay_stall) begin + if (inst_was_stalling) begin + if (inst_delay > 0) + inst_delay <= inst_delay - 1; + end + else begin + inst_was_stalling <= 1; + inst_delay <= curr_inst_delay - 1; + end + end + else begin + inst_was_stalling <= 0; + end + end + `ifdef SYN_FUNC wire which_in2; @@ -24,15 +98,6 @@ module VX_alu( wire[63:0] ALU_in1_mult; wire[63:0] ALU_in2_mult; wire[31:0] upper_immed; - wire[31:0] unsigned_div_result; - wire[31:0] unsigned_rem_result; - wire[31:0] signed_div_result; - wire[31:0] signed_rem_result; - - reg [15:0] inst_delay; - reg [15:0] inst_delay_count; - - assign out_alu_stall = inst_delay != 0 || inst_delay_count != 0; assign which_in2 = in_rs2_src == `RS2_IMMED; @@ -41,39 +106,6 @@ module VX_alu( assign upper_immed = {in_upper_immed, {12{1'b0}}}; - VX_divide #( - .WIDTHN(32), - .WIDTHD(32), - .SPEED("HIGHEST"), - .PIPELINE(div_pipeline_len) - ) unsigned_div ( - .clock(clk), - .aclr(0), - .clken(1), // TODO this could be disabled on inactive instructions - .numer(ALU_in1), - .denom(ALU_in2), - .quotient(unsigned_div_result), - .remainder(unsigned_rem_result) - ); - - VX_divide #( - .WIDTHN(32), - .WIDTHD(32), - .NREP("SIGNED"), - .DREP("SIGNED"), - .SPEED("HIGHEST"), - .PIPELINE(div_pipeline_len) - ) signed_div ( - .clock(clk), - .aclr(0), - .clken(1), // TODO this could be disabled on inactive instructions - .numer(ALU_in1), - .denom(ALU_in2), - .quotient(signed_div_result), - .remainder(signed_rem_result) - ); - - //always @(posedge `MUL) begin @@ -116,25 +148,6 @@ module VX_alu( endcase // in_alu_op end - always @(*) begin - case(in_alu_op) - `DIV, - `DIVU, - `REM, - `REMU: inst_delay = div_pipeline_len; - default: inst_delay = 0; - endcase // in_alu_op - end - - always @(posedge clk or posedge reset) begin - if (reset) - inst_delay_count <= 0; - else if (inst_delay_count > 0) - inst_delay_count <= inst_delay_count - 1; - else if (inst_delay != 0) - inst_delay_count <= inst_delay - 1; - end - `else wire which_in2; @@ -186,10 +199,11 @@ module VX_alu( `MULH: out_alu_result = mult_signed_result[63:32]; `MULHSU: out_alu_result = mult_signed_un_result[63:32]; `MULHU: out_alu_result = mult_unsigned_result[63:32]; - `DIV: out_alu_result = (ALU_in2 == 0) ? 32'hffffffff : $signed($signed(ALU_in1) / $signed(ALU_in2)); - `DIVU: out_alu_result = (ALU_in2 == 0) ? 32'hffffffff : ALU_in1 / ALU_in2; - `REM: out_alu_result = (ALU_in2 == 0) ? ALU_in1 : $signed($signed(ALU_in1) % $signed(ALU_in2)); - `REMU: out_alu_result = (ALU_in2 == 0) ? ALU_in1 : ALU_in1 % ALU_in2; + // TODO profitable to roll these exceptional cases into inst_delay to avoid pipeline when possible? + `DIV: out_alu_result = (ALU_in2 == 0) ? 32'hffffffff : signed_div_result; + `DIVU: out_alu_result = (ALU_in2 == 0) ? 32'hffffffff : unsigned_div_result; + `REM: out_alu_result = (ALU_in2 == 0) ? ALU_in1 : signed_rem_result; + `REMU: out_alu_result = (ALU_in2 == 0) ? ALU_in1 : unsigned_rem_result; default: out_alu_result = 32'h0; endcase // in_alu_op end diff --git a/rtl/compat/VX_divide.v b/rtl/compat/VX_divide.v index ea5e8be7..15d5871e 100644 --- a/rtl/compat/VX_divide.v +++ b/rtl/compat/VX_divide.v @@ -13,8 +13,8 @@ module VX_divide input [WIDTHN-1:0] numer, input [WIDTHD-1:0] denom, - output [WIDTHN-1:0] quotient, - output [WIDTHD-1:0] remainder + output reg [WIDTHN-1:0] quotient, + output reg [WIDTHD-1:0] remainder ); // synthesis read_comments_as_HDL on @@ -53,61 +53,86 @@ module VX_divide ); end - else if (PIPELINE == 0) begin - if (NREP == "SIGNED") begin - assign quotient = $signed($signed(numer)/$signed(denom)); - assign remainder = $signed($signed(numer)%$signed(denom)); - end - else begin - assign quotient = numer/denom; - assign remainder = numer%denom; - end - end else begin - reg [WIDTHN-1:0] numer_pipe [0:PIPELINE-1]; - reg [WIDTHD-1:0] denom_pipe [0:PIPELINE-1]; + wire [WIDTHN-1:0] numer_pipe_end; + wire [WIDTHD-1:0] denom_pipe_end; + if (PIPELINE == 0) begin + assign numer_pipe_end = numer; + assign denom_pipe_end = denom; + end else begin + reg [WIDTHN-1:0] numer_pipe [0:PIPELINE-1]; + reg [WIDTHD-1:0] denom_pipe [0:PIPELINE-1]; + + genvar pipe_stage; + for (pipe_stage = 0; pipe_stage < PIPELINE-1; pipe_stage = pipe_stage+1) begin : pipe_stages + always @(posedge clock or posedge aclr) begin + if (aclr) begin + numer_pipe[pipe_stage+1] <= 0; + denom_pipe[pipe_stage+1] <= 0; + end + else if (clken) begin + numer_pipe[pipe_stage+1] <= numer_pipe[pipe_stage]; + denom_pipe[pipe_stage+1] <= denom_pipe[pipe_stage]; + end + end + end - genvar pipe_stage; - for (pipe_stage = 0; pipe_stage < PIPELINE-1; pipe_stage = pipe_stage+1) begin : pipe_stages always @(posedge clock or posedge aclr) begin if (aclr) begin - numer_pipe[pipe_stage+1] <= 0; - denom_pipe[pipe_stage+1] <= 0; + numer_pipe[0] <= 0; + denom_pipe[0] <= 0; end else if (clken) begin - numer_pipe[pipe_stage+1] <= numer_pipe[pipe_stage]; - denom_pipe[pipe_stage+1] <= denom_pipe[pipe_stage]; + numer_pipe[0] <= numer; + denom_pipe[0] <= denom; end end + + assign numer_pipe_end = numer_pipe[PIPELINE-1]; + assign denom_pipe_end = denom_pipe[PIPELINE-1]; end - always @(posedge clock or posedge aclr) begin - if (aclr) begin - numer_pipe[0] <= 0; - denom_pipe[0] <= 0; - end - else if (clken) begin - numer_pipe[0] <= numer; - denom_pipe[0] <= denom; - end - end - - wire [WIDTHN-1:0] numer_pipe_end; - assign numer_pipe_end = numer_pipe[PIPELINE-1]; - wire [WIDTHD-1:0] denom_pipe_end; - assign denom_pipe_end = denom_pipe[PIPELINE-1]; + /* * * * * * * * * * * * * * * * * * * * * * */ + /* Do the actual fallback computation here */ + /* * * * * * * * * * * * * * * * * * * * * * */ if (NREP == "SIGNED") begin - assign quotient = $signed($signed(numer_pipe_end)/$signed(denom_pipe_end)); - assign remainder = $signed($signed(numer_pipe_end)%$signed(denom_pipe_end)); + + /*VX_divide_internal_signed #( + .WIDTHN, + .WIDTHD + )div( + .numer(numer_pipe_end), + .denom(denom_pipe_end), + .quotient, + .remainder + );*/ + + always @(*) begin + if (denom_pipe_end == 0) begin + quotient = 32'hffffffff; + remainder = numer_pipe_end; + end + else if (denom_pipe_end == 32'hffffffff && numer_pipe_end == 32'h80000000) begin + // this edge case kills verilator in some cases by causing a division + // overflow exception. INT_MIN / -1 (on x86) + quotient = 0; + remainder = 0; + end + else begin + quotient = $signed($signed(numer_pipe_end)/$signed(denom_pipe_end)); + remainder = $signed($signed(numer_pipe_end)%$signed(denom_pipe_end)); + end + end + end else begin - assign quotient = numer_pipe_end/denom_pipe_end; - assign remainder = numer_pipe_end%denom_pipe_end; + assign quotient = (denom_pipe_end == 0) ? 32'hffffffff : numer_pipe_end/denom_pipe_end; + assign remainder = (denom_pipe_end == 0) ? numer_pipe_end : numer_pipe_end%denom_pipe_end; end end endgenerate -endmodule: VX_divide \ No newline at end of file +endmodule : VX_divide \ No newline at end of file