Ways to implement recipricals on Verilog

2019-09-03 09:25发布

问题:

I want to implement a reciprical block on Verilog that will later be synthesized on an FPGA. The input should be a signed 32 bit wordlength with a 16 bit fraction length. The output should have the same format.

Example

input : x ---> output ---> 1/x

I have solved the problem using the inbuilt IP core divider. I'm wondering if there is an elegant/altenative way of solving this by for example by bit shifting or 2's complement with some xor grinds.


I have used the IP core to implement the inverse as it says in the manual but for some reason that i don't really understand the result is wrong and it needs to be shifted to the left by 1. For example; Reciprical of 1 gives 0.5 . Reciprical of 2 gives 1.

Below is a section from the manual and my testbench code


Test bench

module reciprical_tb;

    // Inputs
    reg clk;
    reg [1:0] dividend;
    reg [31:0] divisor;

    // Outputs
    wire rfd;
    wire [1:0] quotient;
    wire [31:0] fractional;

    // Instantiate the Unit Under Test (UUT)
    reciprical uut (
        .rfd(rfd), 
        .clk(clk), 
        .dividend(dividend), 
        .quotient(quotient), 
        .divisor(divisor), 
        .fractional(fractional)
    );

    // clock
    always begin
        #5 clk = ~clk;
    end

    initial begin
        // Initialize Inputs
        clk = 0;
        dividend = 2'b1; // 1
        divisor = 2**16;; // = 1  when fraction length is 16bit

        // Wait 100 ns for global reset to finish
        #100;

        // Add stimulus here :: Inverse of 2 should give 0.5
        //$display("inv(%g) =>  %g || inv = %b",$itor(divisor)*2.0**-16, $itor(fractional)*2.0**-16, fractional); //gives zero
        $monitor("inv(%d) => q = %d || inv = %b", divisor>>>16,fractional>>>16, fractional);  //gives a wrong answer by a factor of 2
        // Using the monitor i get inv(1) = 0.5 instead of 1.
        #100;
    end

endmodule

Manual section (page 4):

... The divider can be used to implement the reciprocal of X; that is the 1/X function. To do this, the dividend bit width is set to 2 and fractional mode is selected. The dividend input is then tied to 01 for both unsigned or signed operation, and the X value is provided via the divisor input.

Ip Core used

回答1:

Trying to debug part of the question:

can you try :

    // Wait 10 clock cycles
    repeat (10) begin
      @(posedge clk);
    end

    // Add stimulus here :: Inverse of 2 should give 0.5
    $display("dsiplay inv(%g) =>  %g || inv = %b",$itor(divisor)*2.0**-16, $itor(fractional)*2.0**-16, fractional); //gives zero
    $monitor("inv(%d) => q = %d || inv = %b", divisor>>>16,fractional>>>16, fractional);  //gives a wrong answer by a factor of 2
    // Using the monitor i get inv(1) = 0.5 instead of 1.
    // Wait 10 clock cycles
    repeat (10) begin
      @(posedge clk);
    end
    $display("dsiplay inv(%g) =>  %g || inv = %b",$itor(divisor)*2.0**-16, $itor(fractional)*2.0**-16, fractional);

    //End simulation
    $finish();

As monitor is only issued once, it might be after 200ns that it is actually firing and outputting the updated value.



回答2:

IP cores are normally pretty efficient, but they will use fabric multipliers and a reasonable amount of logic. Depending on the accuracy that you need it could also be done with a look-up-table stored in block RAM. A RAM that used the raw input as the address would be massively too big for a device. But you can store a reciprocal curve and pre-scale the input and apply shifts at the output. You can reduce the ram usage further by linearly interpolating between two points on the curve.

It depends on what resources you have and what accuracy you need.