// Copyright © 2019-2023
// 
// 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.

`include "VX_platform.vh"

`TRACING_OFF
module VX_divider #(
    parameter N_WIDTH  = 1,
    parameter D_WIDTH  = 1,
    parameter Q_WIDTH  = 1,
    parameter R_WIDTH  = 1,
    parameter N_SIGNED = 0,
    parameter D_SIGNED = 0,
    parameter LATENCY  = 0
) (
    input wire                clk,
    input wire                enable, 
    input wire [N_WIDTH-1:0]  numer,
    input wire [D_WIDTH-1:0]  denom,
    output wire [Q_WIDTH-1:0] quotient,
    output wire [R_WIDTH-1:0] remainder
);

`ifdef QUARTUS

    wire [N_WIDTH-1:0] quotient_unqual;
    wire [D_WIDTH-1:0] remainder_unqual;

    lpm_divide divide (
        .clock    (clk),        
        .clken    (enable),
        .numer    (numer),
        .denom    (denom),
        .quotient (quotient_unqual),
        .remain   (remainder_unqual)
    );

    defparam
        divide.lpm_type     = "LPM_DIVIDE",
        divide.lpm_widthn   = N_WIDTH,        
        divide.lpm_widthd   = D_WIDTH,
        divide.lpm_nrepresentation = N_SIGNED ? "SIGNED" : "UNSIGNED",
        divide.lpm_drepresentation = D_SIGNED ? "SIGNED" : "UNSIGNED",
        divide.lpm_hint     = "MAXIMIZE_SPEED=6,LPM_REMAINDERPOSITIVE=FALSE",
        divide.lpm_pipeline = LATENCY;

    assign quotient  = quotient_unqual [Q_WIDTH-1:0];
    assign remainder = remainder_unqual [R_WIDTH-1:0];

`else

    reg [N_WIDTH-1:0] quotient_unqual;
    reg [D_WIDTH-1:0] remainder_unqual;

    always @(*) begin   
        begin
            if (N_SIGNED && D_SIGNED) begin
                quotient_unqual  = $signed(numer) / $signed(denom);
                remainder_unqual = $signed(numer) % $signed(denom);
            end 
            else if (N_SIGNED && !D_SIGNED) begin
                quotient_unqual  = $signed(numer) / denom;
                remainder_unqual = $signed(numer) % denom;
            end 
            else if (!N_SIGNED && D_SIGNED) begin
                quotient_unqual  = numer / $signed(denom);
                remainder_unqual = numer % $signed(denom);
            end 
            else begin
                quotient_unqual  = numer / denom;
                remainder_unqual = numer % denom;        
            end
        end
    end

    if (LATENCY == 0) begin
        assign quotient  = quotient_unqual [Q_WIDTH-1:0];
        assign remainder = remainder_unqual [R_WIDTH-1:0];
    end else begin
        reg [N_WIDTH-1:0] quotient_pipe [LATENCY-1:0];
        reg [D_WIDTH-1:0] remainder_pipe [LATENCY-1:0];

        for (genvar i = 0; i < LATENCY; ++i) begin
            always @(posedge clk) begin                
                if (enable) begin
                    quotient_pipe[i]  <= (0 == i) ? quotient_unqual  : quotient_pipe[i-1];
                    remainder_pipe[i] <= (0 == i) ? remainder_unqual : remainder_pipe[i-1];
                end
            end
        end

        assign quotient  = quotient_pipe[LATENCY-1][Q_WIDTH-1:0];
        assign remainder = remainder_pipe[LATENCY-1][R_WIDTH-1:0];
    end    

`endif

endmodule
`TRACING_ON
