mirror of
https://github.com/openhwgroup/cva6.git
synced 2025-04-22 21:27:10 -04:00
This is the third step for #1451. Many values are moved but not all values are moved yet * move NR_SB_ENTRIES & TRANS_ID_BITS * remove default rvfi_instr_t from spike.sv * fifo_v3: ariane_pkg::FPGA_EN becomes a param * move FPGA_EN * inline wt_cache_pkg::L15_SET_ASSOC * move wt_cache_pkg::L15_WAY_WIDTH * inline wt_cache_pkg::L1I_SET_ASSOC * inline wt_cache_pkg::L1D_SET_ASSOC * move wt_cache_pkg::DCACHE_CL_IDX_WIDTH * move ICACHE_TAG_WIDTH * move DCACHE_TAG_WIDTH * move ICACHE_INDEX_WIDTH * move ICACHE_SET_ASSOC * use ICACHE_SET_ASSOC_WIDTH instead of $clog2(ICACHE_SET_ASSOC) * move DCACHE_NUM_WORDS * move DCACHE_INDEX_WIDTH * move DCACHE_OFFSET_WIDTH * move DCACHE_BYTE_OFFSET * move DCACHE_DIRTY_WIDTH * move DCACHE_SET_ASSOC_WIDTH * move DCACHE_SET_ASSOC * move CONFIG_L1I_SIZE * move CONFIG_L1D_SIZE * move DCACHE_LINE_WIDTH * move ICACHE_LINE_WIDTH * move ICACHE_USER_LINE_WIDTH * move DCACHE_USER_LINE_WIDTH * DATA_USER_WIDTH = DCACHE_USER_WIDTH * move DCACHE_USER_WIDTH * move FETCH_USER_WIDTH * move FETCH_USER_EN * move LOG2_INSTR_PER_FETCH * move INSTR_PER_FETCH * move FETCH_WIDTH * transform SSTATUS_SD and SMODE_STATUS_READ_MASK into functions * move [SM]_{SW,TIMER,EXT}_INTERRUPT into a structure * move SV * move vm_mode_t to config_pkg * move MODE_SV * move VPN2 * move PPNW * move ASIDW * move ModeW * move XLEN_ALIGN_BYTES * move DATA_USER_EN * format: apply verible
279 lines
8.7 KiB
Systemverilog
279 lines
8.7 KiB
Systemverilog
// Copyright 2018 ETH Zurich and University of Bologna.
|
|
// Copyright and related rights are licensed under the Solderpad Hardware
|
|
// License, Version 0.51 (the "License"); you may not use this file except in
|
|
// compliance with the License. You may obtain a copy of the License at
|
|
// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
|
|
// or agreed to in writing, software, hardware and materials distributed under
|
|
// this 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.
|
|
//
|
|
// Author: Michael Schaffner <schaffner@iis.ee.ethz.ch>, ETH Zurich
|
|
// Andreas Traber <traber@iis.ee.ethz.ch>, ETH Zurich
|
|
//
|
|
// Date: 18.10.2018
|
|
// Description: simple 64bit serial divider
|
|
|
|
|
|
module serdiv
|
|
import ariane_pkg::*;
|
|
#(
|
|
parameter config_pkg::cva6_cfg_t CVA6Cfg = config_pkg::cva6_cfg_empty,
|
|
parameter WIDTH = 64,
|
|
parameter STABLE_HANDSHAKE = 0 // Guarantee a stable in_rdy_o during the input handshake. Keep it at 0 in CVA6
|
|
) (
|
|
// Subsystem Clock - SUBSYSTEM
|
|
input logic clk_i,
|
|
// Asynchronous reset active low - SUBSYSTEM
|
|
input logic rst_ni,
|
|
// Serdiv translation ID - Mult
|
|
input logic [CVA6Cfg.TRANS_ID_BITS-1:0] id_i,
|
|
// A operand - Mult
|
|
input logic [WIDTH-1:0] op_a_i,
|
|
// B operand - Mult
|
|
input logic [WIDTH-1:0] op_b_i,
|
|
// Serdiv operation - Mult
|
|
input logic [1:0] opcode_i, // 0: udiv, 2: urem, 1: div, 3: rem
|
|
// Serdiv instruction is valid - Mult
|
|
input logic in_vld_i,
|
|
// Serdiv FU is ready - Mult
|
|
output logic in_rdy_o,
|
|
// Flush - CONTROLLER
|
|
input logic flush_i,
|
|
// Serdiv result is valid - Mult
|
|
output logic out_vld_o,
|
|
// Serdiv is ready - Mult
|
|
input logic out_rdy_i,
|
|
// Serdiv transaction ID - Mult
|
|
output logic [CVA6Cfg.TRANS_ID_BITS-1:0] id_o,
|
|
// Serdiv result - Mult
|
|
output logic [WIDTH-1:0] res_o
|
|
);
|
|
|
|
/////////////////////////////////////
|
|
// signal declarations
|
|
/////////////////////////////////////
|
|
|
|
enum logic [1:0] {
|
|
IDLE,
|
|
DIVIDE,
|
|
FINISH
|
|
}
|
|
state_d, state_q;
|
|
|
|
logic [WIDTH-1:0] res_q, res_d;
|
|
logic [WIDTH-1:0] op_a_q, op_a_d;
|
|
logic [WIDTH-1:0] op_b_q, op_b_d;
|
|
logic op_a_sign, op_b_sign;
|
|
logic op_b_zero, op_b_zero_q, op_b_zero_d;
|
|
logic op_b_neg_one, op_b_neg_one_q, op_b_neg_one_d;
|
|
|
|
logic [CVA6Cfg.TRANS_ID_BITS-1:0] id_q, id_d;
|
|
|
|
logic rem_sel_d, rem_sel_q;
|
|
logic comp_inv_d, comp_inv_q;
|
|
logic res_inv_d, res_inv_q;
|
|
|
|
logic [WIDTH-1:0] add_mux;
|
|
logic [WIDTH-1:0] add_out;
|
|
logic [WIDTH-1:0] add_tmp;
|
|
logic [WIDTH-1:0] b_mux;
|
|
logic [WIDTH-1:0] out_mux;
|
|
|
|
logic [$clog2(WIDTH)-1:0] cnt_q, cnt_d;
|
|
logic cnt_zero;
|
|
|
|
logic [WIDTH-1:0] lzc_a_input, lzc_b_input, op_b;
|
|
logic [$clog2(WIDTH)-1:0] lzc_a_result, lzc_b_result;
|
|
logic [$clog2(WIDTH+1)-1:0] shift_a;
|
|
logic [ $clog2(WIDTH+1):0] div_shift;
|
|
|
|
logic a_reg_en, b_reg_en, res_reg_en, ab_comp, pm_sel, load_en;
|
|
logic lzc_a_no_one, lzc_b_no_one;
|
|
logic div_res_zero_d, div_res_zero_q;
|
|
|
|
|
|
/////////////////////////////////////
|
|
// align the input operands
|
|
// for faster division
|
|
/////////////////////////////////////
|
|
|
|
assign op_a_sign = op_a_i[$high(op_a_i)];
|
|
assign op_b_sign = op_b_i[$high(op_b_i)];
|
|
assign op_b_zero = lzc_b_no_one & ~op_b_sign;
|
|
assign op_b_neg_one = lzc_b_no_one & op_b_sign;
|
|
|
|
assign lzc_a_input = (opcode_i[0] & op_a_sign) ? {~op_a_i[$high(op_a_i)-1:0], 1'b1} : op_a_i;
|
|
assign lzc_b_input = (opcode_i[0] & op_b_sign) ? ~op_b_i : op_b_i;
|
|
|
|
lzc #(
|
|
.MODE (1), // count leading zeros
|
|
.WIDTH(WIDTH)
|
|
) i_lzc_a (
|
|
.in_i (lzc_a_input),
|
|
.cnt_o (lzc_a_result),
|
|
.empty_o(lzc_a_no_one)
|
|
);
|
|
|
|
lzc #(
|
|
.MODE (1), // count leading zeros
|
|
.WIDTH(WIDTH)
|
|
) i_lzc_b (
|
|
.in_i (lzc_b_input),
|
|
.cnt_o (lzc_b_result),
|
|
.empty_o(lzc_b_no_one)
|
|
);
|
|
|
|
assign shift_a = (lzc_a_no_one) ? WIDTH : {1'b0, lzc_a_result};
|
|
assign div_shift = {1'b0, lzc_b_result} - shift_a;
|
|
|
|
assign op_b = op_b_i <<< $unsigned(div_shift);
|
|
|
|
// the division is zero if |opB| > |opA| and can be terminated
|
|
assign div_res_zero_d = (load_en) ? div_shift[$high(div_shift)] : div_res_zero_q;
|
|
|
|
/////////////////////////////////////
|
|
// Datapath
|
|
/////////////////////////////////////
|
|
|
|
assign pm_sel = load_en & ~(opcode_i[0] & (op_a_sign ^ op_b_sign));
|
|
|
|
// muxes
|
|
assign add_mux = (load_en) ? op_a_i : op_b_q;
|
|
|
|
// attention: logical shift by one in case of negative operand B!
|
|
assign b_mux = (load_en) ? op_b : {comp_inv_q, (op_b_q[$high(op_b_q):1])};
|
|
|
|
// in case of bad timing, we could output from regs -> needs a cycle more in the FSM
|
|
assign out_mux = (rem_sel_q) ? (op_b_neg_one_q ? '0 : op_a_q) : (op_b_zero_q ? '1 : (op_b_neg_one_q ? op_a_q : res_q));
|
|
|
|
// invert if necessary
|
|
assign res_o = (res_inv_q) ? -$signed(out_mux) : out_mux;
|
|
|
|
// main comparator
|
|
assign ab_comp = ((op_a_q == op_b_q) | ((op_a_q > op_b_q) ^ comp_inv_q)) & ((|op_a_q) | op_b_zero_q);
|
|
|
|
// main adder
|
|
assign add_tmp = (load_en) ? 0 : op_a_q;
|
|
assign add_out = (pm_sel) ? add_tmp + add_mux : add_tmp - $signed(add_mux);
|
|
|
|
/////////////////////////////////////
|
|
// FSM, counter
|
|
/////////////////////////////////////
|
|
|
|
assign cnt_zero = (cnt_q == 0);
|
|
assign cnt_d = (load_en) ? div_shift[$clog2(WIDTH)-1:0] : (~cnt_zero) ? cnt_q - 1 : cnt_q;
|
|
|
|
always_comb begin : p_fsm
|
|
// default
|
|
state_d = state_q;
|
|
in_rdy_o = 1'b0;
|
|
out_vld_o = 1'b0;
|
|
load_en = 1'b0;
|
|
a_reg_en = 1'b0;
|
|
b_reg_en = 1'b0;
|
|
res_reg_en = 1'b0;
|
|
|
|
unique case (state_q)
|
|
IDLE: begin
|
|
in_rdy_o = 1'b1;
|
|
|
|
if (in_vld_i) begin
|
|
// CVA6: there is a cycle delay until the valid signal is asserted by the id stage
|
|
// Ara: we need a stable handshake
|
|
in_rdy_o = (STABLE_HANDSHAKE) ? 1'b1 : 1'b0;
|
|
a_reg_en = 1'b1;
|
|
b_reg_en = 1'b1;
|
|
load_en = 1'b1;
|
|
state_d = DIVIDE;
|
|
end
|
|
end
|
|
DIVIDE: begin
|
|
if (~(div_res_zero_q | op_b_zero_q | op_b_neg_one_q)) begin
|
|
a_reg_en = ab_comp;
|
|
b_reg_en = 1'b1;
|
|
res_reg_en = 1'b1;
|
|
end
|
|
// can end the division immediately if the result is known
|
|
if (div_res_zero_q | op_b_zero_q | op_b_neg_one_q) begin
|
|
out_vld_o = 1'b1;
|
|
state_d = FINISH;
|
|
if (out_rdy_i) begin
|
|
// in_rdy_o = 1'b1;// there is a cycle delay until the valid signal is asserted by the id stage
|
|
state_d = IDLE;
|
|
end
|
|
end else if (cnt_zero) begin
|
|
state_d = FINISH;
|
|
end
|
|
end
|
|
FINISH: begin
|
|
out_vld_o = 1'b1;
|
|
|
|
if (out_rdy_i) begin
|
|
// in_rdy_o = 1'b1;// there is a cycle delay until the valid signal is asserted by the id stage
|
|
state_d = IDLE;
|
|
end
|
|
end
|
|
default: state_d = IDLE;
|
|
endcase
|
|
|
|
if (flush_i) begin
|
|
in_rdy_o = 1'b0;
|
|
out_vld_o = 1'b0;
|
|
a_reg_en = 1'b0;
|
|
b_reg_en = 1'b0;
|
|
load_en = 1'b0;
|
|
state_d = IDLE;
|
|
end
|
|
end
|
|
|
|
/////////////////////////////////////
|
|
// regs, flags
|
|
/////////////////////////////////////
|
|
|
|
// get flags
|
|
assign rem_sel_d = (load_en) ? opcode_i[1] : rem_sel_q;
|
|
assign comp_inv_d = (load_en) ? opcode_i[0] & op_b_sign : comp_inv_q;
|
|
assign op_b_zero_d = (load_en) ? op_b_zero : op_b_zero_q;
|
|
assign op_b_neg_one_d = (load_en) ? op_b_neg_one : op_b_neg_one_q;
|
|
assign res_inv_d = (load_en) ? (~op_b_zero | opcode_i[1]) & opcode_i[0] & (op_a_sign ^ op_b_sign ^ op_b_neg_one) : res_inv_q;
|
|
|
|
// transaction id
|
|
assign id_d = (load_en) ? id_i : id_q;
|
|
assign id_o = id_q;
|
|
|
|
assign op_a_d = (a_reg_en) ? add_out : op_a_q;
|
|
assign op_b_d = (b_reg_en) ? b_mux : op_b_q;
|
|
assign res_d = (load_en) ? '0 : (res_reg_en) ? {res_q[$high(res_q)-1:0], ab_comp} : res_q;
|
|
|
|
always_ff @(posedge clk_i or negedge rst_ni) begin : p_regs
|
|
if (~rst_ni) begin
|
|
state_q <= IDLE;
|
|
op_a_q <= '0;
|
|
op_b_q <= '0;
|
|
res_q <= '0;
|
|
cnt_q <= '0;
|
|
id_q <= '0;
|
|
rem_sel_q <= 1'b0;
|
|
comp_inv_q <= 1'b0;
|
|
res_inv_q <= 1'b0;
|
|
op_b_zero_q <= 1'b0;
|
|
op_b_neg_one_q <= 1'b0;
|
|
div_res_zero_q <= 1'b0;
|
|
end else begin
|
|
state_q <= state_d;
|
|
op_a_q <= op_a_d;
|
|
op_b_q <= op_b_d;
|
|
res_q <= res_d;
|
|
cnt_q <= cnt_d;
|
|
id_q <= id_d;
|
|
rem_sel_q <= rem_sel_d;
|
|
comp_inv_q <= comp_inv_d;
|
|
res_inv_q <= res_inv_d;
|
|
op_b_zero_q <= op_b_zero_d;
|
|
op_b_neg_one_q <= op_b_neg_one_d;
|
|
div_res_zero_q <= div_res_zero_d;
|
|
end
|
|
end
|
|
|
|
endmodule
|