abstractaccelerator/uriscv/src/uriscv_muldiv.v

247 lines
6.5 KiB
Verilog

//-----------------------------------------------------------------
// uRISC-V CPU
// V0.5.0
// github.com/ultraembedded/core_uriscv
// Copyright 2015-2021
//
// admin@ultra-embedded.com
//
// License: Apache 2.0
//-----------------------------------------------------------------
// Copyright 2015-2021 github.com/ultraembedded
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//-----------------------------------------------------------------
module uriscv_muldiv
(
input clk_i,
input rst_i,
// Operation select
input valid_i,
input inst_mul_i,
input inst_mulh_i,
input inst_mulhsu_i,
input inst_mulhu_i,
input inst_div_i,
input inst_divu_i,
input inst_rem_i,
input inst_remu_i,
// Operands
input [31:0] operand_ra_i,
input [31:0] operand_rb_i,
// Result
output stall_o,
output ready_o,
output [31:0] result_o
);
//-----------------------------------------------------------------
// Includes
//-----------------------------------------------------------------
`include "uriscv_defs.v"
//-------------------------------------------------------------
// Multiplier
//-------------------------------------------------------------
reg [32:0] mul_operand_a_q;
reg [32:0] mul_operand_b_q;
reg mulhi_sel_q;
//-------------------------------------------------------------
// Multiplier
//-------------------------------------------------------------
wire [64:0] mult_result_w;
reg [32:0] operand_b_r;
reg [32:0] operand_a_r;
reg [31:0] mul_result_r;
wire mult_inst_w = inst_mul_i |
inst_mulh_i |
inst_mulhsu_i |
inst_mulhu_i;
always @ *
begin
if (inst_mulhsu_i)
operand_a_r = {operand_ra_i[31], operand_ra_i[31:0]};
else if (inst_mulh_i)
operand_a_r = {operand_ra_i[31], operand_ra_i[31:0]};
else // MULHU || MUL
operand_a_r = {1'b0, operand_ra_i[31:0]};
end
always @ *
begin
if (inst_mulhsu_i)
operand_b_r = {1'b0, operand_rb_i[31:0]};
else if (inst_mulh_i)
operand_b_r = {operand_rb_i[31], operand_rb_i[31:0]};
else // MULHU || MUL
operand_b_r = {1'b0, operand_rb_i[31:0]};
end
// Pipeline flops for multiplier
always @(posedge clk_i )
if (rst_i)
begin
mul_operand_a_q <= 33'b0;
mul_operand_b_q <= 33'b0;
mulhi_sel_q <= 1'b0;
end
else if (valid_i && mult_inst_w)
begin
mul_operand_a_q <= operand_a_r;
mul_operand_b_q <= operand_b_r;
mulhi_sel_q <= ~inst_mul_i;
end
else
begin
mul_operand_a_q <= 33'b0;
mul_operand_b_q <= 33'b0;
mulhi_sel_q <= 1'b0;
end
assign mult_result_w = {{ 32 {mul_operand_a_q[32]}}, mul_operand_a_q}*{{ 32 {mul_operand_b_q[32]}}, mul_operand_b_q};
always @ *
begin
mul_result_r = mulhi_sel_q ? mult_result_w[63:32] : mult_result_w[31:0];
end
reg mul_busy_q;
always @(posedge clk_i )
if (rst_i)
mul_busy_q <= 1'b0;
else
mul_busy_q <= valid_i & mult_inst_w;
//-------------------------------------------------------------
// Divider
//-------------------------------------------------------------
wire div_rem_inst_w = inst_div_i ||
inst_divu_i ||
inst_rem_i ||
inst_remu_i;
wire signed_operation_w = inst_div_i || inst_rem_i;
wire div_operation_w = inst_div_i || inst_divu_i;
reg [31:0] dividend_q;
reg [62:0] divisor_q;
reg [31:0] quotient_q;
reg [31:0] q_mask_q;
reg div_inst_q;
reg div_busy_q;
reg invert_res_q;
wire div_start_w = valid_i & div_rem_inst_w & !stall_o;
wire div_complete_w = !(|q_mask_q) & div_busy_q;
always @ (posedge clk_i )
if (rst_i)
begin
div_busy_q <= 1'b0;
dividend_q <= 32'b0;
divisor_q <= 63'b0;
invert_res_q <= 1'b0;
quotient_q <= 32'b0;
q_mask_q <= 32'b0;
div_inst_q <= 1'b0;
end
else if (div_start_w)
begin
div_busy_q <= 1'b1;
div_inst_q <= div_operation_w;
if (signed_operation_w && operand_ra_i[31])
dividend_q <= -operand_ra_i;
else
dividend_q <= operand_ra_i;
if (signed_operation_w && operand_rb_i[31])
divisor_q <= {-operand_rb_i, 31'b0};
else
divisor_q <= {operand_rb_i, 31'b0};
invert_res_q <= (inst_div_i && (operand_ra_i[31] != operand_rb_i[31]) && |operand_rb_i) ||
(inst_rem_i && operand_ra_i[31]);
quotient_q <= 32'b0;
q_mask_q <= 32'h80000000;
end
else if (div_complete_w)
begin
div_busy_q <= 1'b0;
end
else if (div_busy_q)
begin
if (divisor_q <= {31'b0, dividend_q})
begin
dividend_q <= dividend_q - divisor_q[31:0];
quotient_q <= quotient_q | q_mask_q;
end
divisor_q <= {1'b0, divisor_q[62:1]};
q_mask_q <= {1'b0, q_mask_q[31:1]};
end
reg [31:0] div_result_r;
always @ *
begin
div_result_r = 32'b0;
if (div_inst_q)
div_result_r = invert_res_q ? -quotient_q : quotient_q;
else
div_result_r = invert_res_q ? -dividend_q : dividend_q;
end
//-------------------------------------------------------------
// Shared logic
//-------------------------------------------------------------
// Stall if divider logic is busy and new multiplier or divider op
assign stall_o = (div_busy_q & (mult_inst_w | div_rem_inst_w)) ||
(mul_busy_q & div_rem_inst_w);
reg [31:0] result_q;
reg ready_q;
always @ (posedge clk_i )
if (rst_i)
ready_q <= 1'b0;
else if (mul_busy_q)
ready_q <= 1'b1;
else if (div_complete_w)
ready_q <= 1'b1;
else
ready_q <= 1'b0;
always @ (posedge clk_i )
if (rst_i)
result_q <= 32'b0;
else if (div_complete_w)
result_q <= div_result_r;
else if (mul_busy_q)
result_q <= mul_result_r;
assign result_o = result_q;
assign ready_o = ready_q;
endmodule