blob: a25a32ac85e62303f9696a7073cc5566b7abb9bc [file] [log] [blame]
`ifndef HDL_VERILOG_RVV_DESIGN_RVV_SVH
`include "rvv_backend.svh"
`endif
`ifndef DIV_DEFINE_SVH
`include "rvv_backend_div.svh"
`endif
`ifndef RVV_ASSERT__SVH
`include "rvv_backend_sva.svh"
`endif
module rvv_backend_div_unit_divider
(
clk,
rst_n,
div_valid,
opcode,
src2_dividend,
src1_divisor,
result_quotient,
result_remainder,
result_valid,
`ifdef TB_SUPPORT
res_reuse_valid_p1,
`endif
result_ready,
trap_flush_rvv
);
//
// parameter
//
parameter DIV_WIDTH = `WORD_WIDTH;
//
// interface signals
//
// global signals
input logic clk;
input logic rst_n;
// opcode
input DIV_SIGN_SRC_e opcode;
// operand
input logic div_valid;
input logic [DIV_WIDTH-1:0] src2_dividend;
input logic [DIV_WIDTH-1:0] src1_divisor;
// result
output logic [DIV_WIDTH-1:0] result_quotient;
output logic [DIV_WIDTH-1:0] result_remainder;
output logic result_valid;
`ifdef TB_SUPPORT
output logic res_reuse_valid_p1;
`endif
input logic result_ready;
// trap-flush
input logic trap_flush_rvv;
//
// internal signals
//
// FSM
enum logic [1:0] {DIV_IDLE, DIV_WORKING, DIV_PRINT, DIV_UNREACHABLE}
state, next_state;
// number of leading zero bits
logic [$clog2(DIV_WIDTH):0] cnt_clzb;
logic [$clog2(DIV_WIDTH):0] clzb;
logic [$clog2(DIV_WIDTH):0] count_shift;
// processing round for divider
logic [DIV_WIDTH-1:0] quotient_r1;
logic [DIV_WIDTH-1:0] quotient_r2;
logic [DIV_WIDTH-1:0] quotient_r3;
logic [DIV_WIDTH-1:0] remainder_r1;
logic [DIV_WIDTH-1:0] remainder_r2;
logic [DIV_WIDTH-1:0] remainder_r3;
logic [$clog2(DIV_WIDTH):0] count_r1;
logic [$clog2(DIV_WIDTH):0] count_r2;
logic [$clog2(DIV_WIDTH):0] count_r3;
// register signals
logic dividend_en;
logic [DIV_WIDTH-1:0] dividend_d;
logic [DIV_WIDTH-1:0] dividend_q;
logic divisor_en;
logic [DIV_WIDTH-1:0] divisor_d;
logic [DIV_WIDTH-1:0] divisor_q;
logic quotient_en;
logic [DIV_WIDTH-1:0] quotient_d;
logic [DIV_WIDTH-1:0] quotient_q;
logic remainder_en;
logic [DIV_WIDTH-1:0] remainder_d;
logic [DIV_WIDTH-1:0] remainder_q;
logic count_en;
logic [$clog2(DIV_WIDTH):0] count_d;
logic [$clog2(DIV_WIDTH):0] count_q;
logic q_sgn_en;
logic q_sgn_d;
logic q_sgn_q;
logic r_sgn_en;
logic r_sgn_d;
logic r_sgn_q;
`ifdef TB_SUPPORT
logic res_reuse_valid_p0;
`endif
//
// register
//
// dividend
cdffr
#(
.T (logic [DIV_WIDTH-1:0])
)
dividend
(
.clk (clk),
.rst_n (rst_n),
.e (dividend_en),
.c (trap_flush_rvv),
.d (dividend_d),
.q (dividend_q)
);
// divisor
cdffr
#(
.T (logic [DIV_WIDTH-1:0])
)
divisor
(
.clk (clk),
.rst_n (rst_n),
.e (divisor_en),
.c (trap_flush_rvv),
.d (divisor_d),
.q (divisor_q)
);
// quotient
cdffr
#(
.T (logic [DIV_WIDTH-1:0])
)
quotient
(
.clk (clk),
.rst_n (rst_n),
.e (quotient_en),
.c (trap_flush_rvv),
.d (quotient_d),
.q (quotient_q)
);
// remainder
cdffr
#(
.T (logic [DIV_WIDTH-1:0])
)
remainder
(
.clk (clk),
.rst_n (rst_n),
.e (remainder_en),
.c (trap_flush_rvv),
.d (remainder_d),
.q (remainder_q)
);
// count
edff
#(
.T (logic [$clog2(DIV_WIDTH):0])
)
count
(
.clk (clk),
.rst_n (rst_n),
.e (count_en),
.d (count_d),
.q (count_q)
);
// record quotient sgn-bit
cdffr
q_sgn
(
.clk (clk),
.rst_n (rst_n),
.e (q_sgn_en),
.c (trap_flush_rvv),
.d (q_sgn_d),
.q (q_sgn_q)
);
// record remainder sgn-bit
cdffr
r_sgn
(
.clk (clk),
.rst_n (rst_n),
.e (r_sgn_en),
.c (trap_flush_rvv),
.d (r_sgn_d),
.q (r_sgn_q)
);
`ifdef TB_SUPPORT
always_ff @(posedge clk, negedge rst_n) begin
if(rst_n=='b0)
res_reuse_valid_p1 = 'b0;
else if(next_state==DIV_IDLE)
res_reuse_valid_p1 = 'b0;
else if((state==DIV_IDLE)&div_valid)
res_reuse_valid_p1 = res_reuse_valid_p0;
else
res_reuse_valid_p1 = res_reuse_valid_p1;
end
`endif
//
// FSM
//
always_ff @(posedge clk, negedge rst_n) begin
if (rst_n=='b0)
state <= DIV_IDLE;
else
state <= next_state;
end
// state transition
always_comb begin
next_state = state;
case(state)
DIV_IDLE: begin
if(div_valid&(!trap_flush_rvv)) begin
if ((count_en=='b1)&(count_d=='b1))
next_state = DIV_PRINT;
else
next_state = DIV_WORKING;
end
end
DIV_WORKING: begin
if(trap_flush_rvv)
next_state = DIV_IDLE;
else if(div_valid) begin
if ((count_en=='b1)&(count_d=='b1))
next_state = DIV_PRINT;
else
next_state = DIV_WORKING;
end
end
DIV_PRINT: begin
if(trap_flush_rvv)
next_state = DIV_IDLE;
else if ((div_valid)&(result_ready))
next_state = DIV_IDLE;
else
next_state = DIV_PRINT;
end
// Necessary to suppress FSM_COMPLETE errors
DIV_UNREACHABLE: begin
next_state = DIV_IDLE;
end
default: begin
next_state = DIV_IDLE;
end
endcase
end
// computational logic in every state
generate
if (DIV_WIDTH==`WORD_WIDTH) begin
assign clzb = f_clzb32(dividend_d);
assign count_shift = 'd33 - clzb;
end
else if (DIV_WIDTH==`HWORD_WIDTH) begin
assign clzb = f_clzb16(dividend_d);
assign count_shift = 'd17 - clzb;
end
else if (DIV_WIDTH==`BYTE_WIDTH) begin
assign clzb = f_clzb8(dividend_d);
assign count_shift = 'd9 - clzb;
end
endgenerate
always_comb begin
// initial
dividend_en = 'b0;
dividend_d = 'b0;
divisor_en = 'b0;
divisor_d = 'b0;
quotient_en = 'b0;
quotient_d = 'b0;
remainder_en = 'b0;
remainder_d = 'b0;
count_en = 'b0;
count_d = 'b0;
q_sgn_en = 'b0;
q_sgn_d = 'b0;
r_sgn_en = 'b0;
r_sgn_d = 'b0;
cnt_clzb = 'b0;
quotient_r1 = 'b0;
quotient_r2 = 'b0;
quotient_r3 = 'b0;
remainder_r1 = 'b0;
remainder_r2 = 'b0;
remainder_r3 = 'b0;
count_r1 = 'b0;
count_r2 = 'b0;
count_r3 = 'b0;
result_quotient = 'b0;
result_remainder = 'b0;
result_valid = 'b0;
`ifdef TB_SUPPORT
res_reuse_valid_p0 = 'b0;
`endif
case(state)
DIV_IDLE: begin
if(div_valid) begin
// check whether divisor is 0
if(src1_divisor=='b0) begin
dividend_en = 'b1;
dividend_d = 'b0;
divisor_en = 'b1;
divisor_d = 'b0;
quotient_en = 'b1;
quotient_d = '1;
remainder_en = 'b1;
remainder_d = src2_dividend;
q_sgn_en = 'b1;
q_sgn_d = 'b0;
r_sgn_en = 'b1;
r_sgn_d = 'b0;
count_en = 'b1;
count_d = 'b1;
end
// check whether is -2^(WIDTH-1)/-1. The result will overflow.
else if ((opcode==DIV_SIGN)&(src2_dividend=={1'b1,{(DIV_WIDTH-1){1'b0}}})&(src1_divisor=='1)) begin
dividend_en = 'b1;
dividend_d = 'b0;
divisor_en = 'b1;
divisor_d = 'b0;
quotient_en = 'b1;
quotient_d = {1'b1,{(DIV_WIDTH-1){1'b0}}};
remainder_en = 'b1;
remainder_d = 'b0;
q_sgn_en = 'b1;
q_sgn_d = 'b0;
r_sgn_en = 'b1;
r_sgn_d = 'b0;
count_en = 'b1;
count_d = 'b1;
end
// start to initialize
else begin
if(opcode==DIV_SIGN) begin
// convert signed data to unsigned data
dividend_d = src2_dividend[DIV_WIDTH-1] ? (~(src2_dividend)+1) : src2_dividend;
divisor_d = src1_divisor[DIV_WIDTH-1] ? (~(src1_divisor )+1) : src1_divisor;
q_sgn_d = src2_dividend[DIV_WIDTH-1]^src1_divisor[DIV_WIDTH-1];
r_sgn_d = src2_dividend[DIV_WIDTH-1];
end
else begin
dividend_d = src2_dividend;
divisor_d = src1_divisor;
q_sgn_d = 'b0;
r_sgn_d = 'b0;
end
// check whether dividend and divisor equal to last source data
if ((dividend_d==dividend_q)&(divisor_d==divisor_q)&(q_sgn_d==q_sgn_q)&(r_sgn_d==r_sgn_q)) begin
dividend_en = 'b0;
divisor_en = 'b0;
q_sgn_en = 'b0;
r_sgn_en = 'b0;
count_en = 'b1;
count_d = 'b1;
`ifdef TB_SUPPORT
res_reuse_valid_p0 = 1'b1;
`endif
end
else begin
dividend_en = 'b1;
divisor_en = 'b1;
q_sgn_en = 'b1;
r_sgn_en = 'b1;
// count leading zero bits in dividend
cnt_clzb = clzb;
count_en = 'b1;
count_d = count_shift;
// initialize quotient and remainder
quotient_en = 'b1;
quotient_d = dividend_d<<cnt_clzb;
remainder_en = 'b1;
remainder_d = 'b0;
end
end
end
end
DIV_WORKING: begin
if(div_valid) begin
// processing
f_div_step (remainder_q ,quotient_q ,divisor_q,remainder_r1,quotient_r1);
f_div_step (remainder_r1,quotient_r1,divisor_q,remainder_r2,quotient_r2);
f_div_step (remainder_r2,quotient_r2,divisor_q,remainder_r3,quotient_r3);
count_r1 = count_q - 'd1;
count_r2 = count_q - 'd2;
count_r3 = count_q - 'd3;
quotient_en = 'b1;
remainder_en = 'b1;
count_en = 'b1;
case({{($clog2(DIV_WIDTH)-1){1'b0}},1'b1})
count_r1: begin
quotient_d = quotient_r1;
remainder_d = remainder_r1;
count_d = 'b1;
end
count_r2: begin
quotient_d = quotient_r2;
remainder_d = remainder_r2;
count_d = 'b1;
end
default: begin
quotient_d = quotient_r3;
remainder_d = remainder_r3;
count_d = count_r3;
end
endcase
end
end
// get result to ouput and wait for ready signal
DIV_PRINT: begin
result_valid = 'b1;
result_quotient = q_sgn_q ? ((~quotient_q )+'d1) : quotient_q;
result_remainder = r_sgn_q ? ((~remainder_q)+'d1) : remainder_q;
count_en = result_ready;
count_d = 'b0;
end
endcase
end
`ifdef TB_SUPPORT
`ifdef ASSERT_ON
`rvv_forbid(result_valid&res_reuse_valid_p1&result_ready&(result_quotient!=(src2_dividend/src1_divisor)))
else $warning("result_quotient(0x%h) should be 0x%h.\n",result_quotient,src2_dividend/src1_divisor);
`rvv_forbid(result_valid&res_reuse_valid_p1&result_ready&(result_remainder!=(src2_dividend%src1_divisor)))
else $warning("result_remainder(0x%h) should be 0x%h.\n",result_remainder,src2_dividend%src1_divisor);
`endif
`endif
//
// function unit
//
// count leading zero bits
function [1:0] f_clzb2
(
input logic [1:0] src
);
if (src==2'b00)
f_clzb2 = 2'b10;
else if (src==2'b01)
f_clzb2 = 2'b01;
else
f_clzb2 = 2'b00;
endfunction
function [2:0] f_clzb4
(
input logic [3:0] src
);
logic [1:0] hi;
logic [1:0] lo;
hi = f_clzb2(src[3:2]);
lo = f_clzb2(src[1:0]);
if ((hi[1]==1'b1)&(lo[1]==1'b1))
f_clzb4 = 3'b100;
else if (hi[1]==1'b0)
f_clzb4 = {1'b0,hi};
else
f_clzb4 = {2'b01,lo[0]};
endfunction
function [3:0] f_clzb8
(
input logic [7:0] src
);
logic [2:0] hi;
logic [2:0] lo;
hi = f_clzb4(src[7:4]);
lo = f_clzb4(src[3:0]);
if ((hi[2]==1'b1)&(lo[2]==1'b1))
f_clzb8 = 4'b1000;
else if (hi[2]==1'b0)
f_clzb8 = {1'b0,hi};
else
f_clzb8 = {2'b01,lo[1:0]};
endfunction
function [4:0] f_clzb16
(
input logic [15:0] src
);
logic [3:0] hi;
logic [3:0] lo;
hi = f_clzb8(src[15:8]);
lo = f_clzb8(src[7:0]);
if ((hi[3]==1'b1)&(lo[3]==1'b1))
f_clzb16 = 5'b1_0000;
else if (hi[3]==1'b0)
f_clzb16 = {1'b0,hi};
else
f_clzb16 = {2'b01,lo[2:0]};
endfunction
function [5:0] f_clzb32
(
input logic [31:0] src
);
logic [4:0] hi;
logic [4:0] lo;
hi = f_clzb16(src[31:16]);
lo = f_clzb16(src[15:0]);
if ((hi[4]==1'b1)&(lo[4]==1'b1))
f_clzb32 = 6'b10_0000;
else if (hi[4]==1'b0)
f_clzb32 = {1'b0,hi};
else
f_clzb32 = {2'b01,lo[3:0]};
endfunction
// div step: shift and subtract
function void f_div_step
(
input logic [DIV_WIDTH-1:0] remainder_in,
input logic [DIV_WIDTH-1:0] quotient_in,
input logic [DIV_WIDTH-1:0] divisor_in,
output logic [DIV_WIDTH-1:0] remainder_out,
output logic [DIV_WIDTH-1:0] quotient_out
);
logic [DIV_WIDTH-1:0] remainder_tmp;
logic [DIV_WIDTH :0] diff;
remainder_tmp = {remainder_in[DIV_WIDTH-2:0],quotient_in[DIV_WIDTH-1]};
diff = {1'b0,remainder_tmp} - {1'b0,divisor_in};
if (diff[DIV_WIDTH]) begin
remainder_out = remainder_tmp;
quotient_out = {quotient_in[DIV_WIDTH-2:0],1'b0};
end
else begin
remainder_out = diff[DIV_WIDTH-1:0];
quotient_out = {quotient_in[DIV_WIDTH-2:0],1'b1};
end
endfunction
endmodule