247 lines
6.5 KiB
Verilog
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
|