Designing a circuit that calculates integer cube root

Good day everyone. In this article I will tell you how to make a circuit on Verilog HDL on FPGA which will calculate cube root from integer number.

I will write code in Quartus Prime Lite. My device is Cyclone IV E.

Pins and declarations

Firstly, let«s create our top level module and specify which input/output pins we will use:

module cube_root(

   input clk,              // Clock signal 50Mhz

   input [7:0] number,     // Input value

   output reg [3:0] Anode_Activate,  // Setter for activating segments

   output reg [7:0] LED_out          // Value on the segment

);

Number — value from which we will extract cube root.

Anode_Activate will specify currently activated segment of display. I will use only 3 segments: 1st for integer part of result, 2 last for fractional. Last segment is not used but specified in order to make updating working properly.

LED_out specifies which number will be shown one current segment.

clk — clock signal for updating segments.

There are also other terms which can be declared in program:

reg [4:0] result1;   // First segments's value

reg [4:0] result2;   // Second segments's value

reg [4:0] result3;   // Third segments's value

reg [4:0] LED_BCD;   // Current segments's value (not used)

reg [19:0] refresh_counter;         // Segmens update counter

wire [1:0] LED_activating_counter;  // Segment activation counter

result1 — result3 — for storing result of calculations.

LED_BCD stores which of digits will be shown on activated segment.

About refresh_counter and LED_activating_counter I will talk in the next part.

Showing results on 7-segment display

Cyclone IV can«t show several digits activated simultaneously, therefore I had to implement segments updater for it:

always @(posedge clk)

    begin

        refresh_counter <= refresh_counter + 1;

    end

// Setting LED_activating_counter as 2 last bits of refresh_counter

// in order to update segments each 5.2 ms

assign LED_activating_counter = refresh_counter[19:18];

LED_activating_counter will store 2 last bits of refresh_counter. These bits will change their value from 00 to 11 each 2^18 / 50×10^6 s = 5.2 ms.

Here is the code for setting currently activated segment according to LED_activating_counter:

// Setting one segment activated accorfing to LED_activating_counter

always @(*)

    begin

        case(LED_activating_counter)

        2'b00: begin

            Anode_Activate = 4'b0111;

            LED_BCD = result1;

            LED_BCD[4] = 1;  // This bit is responsible for showing dot

        end

        2'b01: begin

            Anode_Activate = 4'b1011;

            LED_BCD = result2;

            LED_BCD[4] = 0;

        end

        2'b10: begin

            Anode_Activate = 4'b1101;

            LED_BCD = result3;

            LED_BCD[4] = 0;

        end

        2'b11:  begin

            Anode_Activate = 4'b1110;

            LED_BCD = 5'b01011;

        end

    endcase

end

// Setting value for activated segment

always @(*)

    begin

        case(LED_BCD)

             5'b00000: LED_out = 8'b00000011; // "0"    

             5'b00001: LED_out = 8'b10011111; // "1"

             5'b00010: LED_out = 8'b00100101; // "2"

             5'b00011: LED_out = 8'b00001101; // "3"

             5'b00100: LED_out = 8'b10011001; // "4"

             5'b00101: LED_out = 8'b01001001; // "5"

             5'b00110: LED_out = 8'b01000001; // "6"

             5'b00111: LED_out = 8'b00011111; // "7"

             5'b01000: LED_out = 8'b00000001; // "8"    

             5'b01001: LED_out = 8'b00001001; // "9"

             5'b01011: LED_out = 8'b11111111; // " "

             5'b10000: LED_out = 8'b00000010; // "0."    

             5'b10001: LED_out = 8'b10011110; // "1."

             5'b10010: LED_out = 8'b00100100; // "2."

             5'b10011: LED_out = 8'b00001100; // "3."

             5'b10100: LED_out = 8'b10011000; // "4."

             5'b10101: LED_out = 8'b01001000; // "5."

             5'b10110: LED_out = 8'b01000000; // "6."

             5'b10111: LED_out = 8'b00011110; // "7."

             5'b11000: LED_out = 8'b00000000; // "8."    

             5'b11001: LED_out = 8'b00001000; // "9."

             default:  LED_out = 8'b00000000; // "8."

    endcase

end

Calculating cube root

Our number value will be input though pins using this scheme:

f3b31d293db6ef53a7e24d090f7ba14a.jpg

Maximum value of a number is 255, minimum — 0.

There already exists algorithm in a book Hacker«s Delight which can calculate cube root (code is on Java):

public int cube_root(int val){

            int s = 0;

            int y = 0;

            int b = 0;

            for (s=30;s>=0;s=s-3){

               y = 2*y;

               b = (3*y*(y+1)+1) << s;

               if (x>=b){

                x = x-b;

                y = y+1;

               }

            }

     return y;

}

But it can only output integer results. In order to overcome this restriction we need to multiply our input value on 10^(3*n), n — natural number, and then split results into digits. Our n last digits will be fractional part. I decided to show results with 2 digits after point. It means that we need to multiply input value on 1 000 000.

Here is the code on Verilog:

// Calculating cubic root of number

always@(*) begin : block_0

            reg [31:0] x;

            integer s;

   integer y;

   integer b;

   integer i;

            x = number;

            x = x * 1_000_000;

    y = 0;

    for (s=30;s>=0;s=s-3)

          begin : block_calc

          y=y*2;

          b = (3*y*(y+1)+1) << s;

          if (x>=b)

          begin : block_1

              x = x-b;

              y=y+1;

          end

      end

    result1 = y / 100;            // First digit

    result2 = (y % 100)/10;       // Second digit

    result3 = y % 10;             // Third digit

end

The whole code of my project:

module cube_root(

   input clk,       // Clock signal 50Mhz

   input [7:0] number,    // Input value

   output reg [3:0] Anode_Activate,  // Setter for activating segments

   output reg [7:0] LED_out          // Value on the segment

);

reg [4:0] result1;            // First segments's value

reg [4:0] result2;            // Second segments's value

reg [4:0] result3;           // Third segments's value

reg [4:0] LED_BCD;         // Current segments's value (not used)

reg [19:0] refresh_counter;   // Segmens update counter

wire [1:0] LED_activating_counter;  // Segment activation counter

// Calculating cubic root of number

always@(*) begin : block_0

   reg [31:0] x;

   integer s;

   integer y;

   integer b;

   integer i;

   x = number;

   x = x * 1_000_000;

    y = 0;

    for (s=30;s>=0;s=s-3)

         begin : block_calc

          y=y*2;

          b = (3*y*(y+1)+1) << s;

          if (x>=b)

          begin : block_1

              x = x-b;

              y=y+1;

          end

      end

    result1 = y / 100;

    result2 = (y % 100)/10;

    result3 = y % 10;

end

// Changing refresh_counter to update segments

always @(posedge clk)

    begin

        refresh_counter <= refresh_counter + 1;

    end

// Setting LED_activating_counter as 2 last bits of refresh_counter

// in order to update segments each 5.2 ms

assign LED_activating_counter = refresh_counter[19:18];

// Setting one segment activated accorfing to LED_activating_counter

always @(*)

    begin

        case(LED_activating_counter)

        2'b00: begin

            Anode_Activate = 4'b0111;

            LED_BCD = result1;

            LED_BCD[4] = 1;  // This bit is responsible for showing dot

        end

        2'b01: begin

            Anode_Activate = 4'b1011;

            LED_BCD = result2;

            LED_BCD[4] = 0;

               end

        2'b10: begin

            Anode_Activate = 4'b1101;

            LED_BCD = result3;

            LED_BCD[4] = 0;

        end

        2'b11:  begin

            Anode_Activate = 4'b1110;

            LED_BCD = 5'b01011;

        end

    endcase

end

// Setting value for activated segment

always @(*)

    begin

        case(LED_BCD)

            5'b00000: LED_out = 8'b00000011; // "0"     

            5'b00001: LED_out = 8'b10011111; // "1"

            5'b00010: LED_out = 8'b00100101; // "2"

            5'b00011: LED_out = 8'b00001101; // "3"

            5'b00100: LED_out = 8'b10011001; // "4"

            5'b00101: LED_out = 8'b01001001; // "5"

            5'b00110: LED_out = 8'b01000001; // "6"

            5'b00111: LED_out = 8'b00011111; // "7"

            5'b01000: LED_out = 8'b00000001; // "8"    

            5'b01001: LED_out = 8'b00001001; // "9"

            5'b01011: LED_out = 8'b11111111; // " "

            5'b10000: LED_out = 8'b00000010; // "0."    

            5'b10001: LED_out = 8'b10011110; // "1."

            5'b10010: LED_out = 8'b00100100; // "2."

            5'b10011: LED_out = 8'b00001100; // "3."

            5'b10100: LED_out = 8'b10011000; // "4."

            5'b10101: LED_out = 8'b01001000; // "5."

            5'b10110: LED_out = 8'b01000000; // "6."

            5'b10111: LED_out = 8'b00011110; // "7."

            5'b11000: LED_out = 8'b00000000; // "8."    

            5'b11001: LED_out = 8'b00001000; // "9."

            default:  LED_out = 8'b00000000; // "8."

        endcase

end

endmodule

Pin assignments.

Now, I have to specify which pins will be connected with declared ones in module. We can do it in pin planner:

ed7d03113a43445b965102a971e74db5.png

Results of our work.

After compiling our project we can run it on FPGA. Here are photos with results of a program:

76c970fe77aac01f77c4cd21f874dc0c.jpg9d02e4b9032b29843a7c8ac1aec35322.jpgc7f48b58343dc4ea81b83cbdc184e0f5.jpg233de0376f0d086601547b8fc524cbdc.jpg

Useful links:

Lesson on how to deal with 7-segment display on Cyclone IV- [FPGA Tutorial] Seven-Segment LED Display on Basys 3 FPGA — FPGA4student.com

Algorithm for calculating cube root on C — Пролистал 2-е издание Hacker«s Delight в поисках занятных задач для лабника по Verilog & FPGA — Silicon Russia & Ukraine (silicon-russia.com)

© Habrahabr.ru