Electronic – Using arithmetic operations in systemverilog

fpgasystem-verilogverilogvivado

I was trying to create a module for using the sensors that I recently bought. My module works well in simulation , synthesis and implementation. but when I used my module inside the top leveled module, I saw that while synthesizing it, it creates some registers outside it's module. With trial and error I found out that the problem is from a sequence of arithmetic operations.

module Sensor_handle_dev1(
    input logic i_clk , i_reset , i_echo,
    output logic o_trg, 
    output logic[7:0] o_distance , sensorNum
    );
    assign sensorNum = 8'b1;
    logic[1:0] IdleState , CatchEchoState , CountState , PackUpState;
    assign IdleState = 2'b00; 
    assign CatchEchoState  = 2'b01; 
    assign CountState  = 2'b10; 
    assign PackUpState = 2'b11; 

    logic next_trg;

    logic[1:0] curState , next_state;
    logic[31:0] l_count , next_count;
    logic[7:0] next_distance; 
    always @( posedge i_clk )
        begin
            if ( i_reset == 1'b1 )
                begin
                    curState <= IdleState;
                    o_trg <= 0;
                    o_distance <= 0;
                    l_count <= 0;
                end
            else
                begin
                    curState <= next_state;
                    l_count <= next_count;
                    o_distance <= next_distance;
                    o_trg <= next_trg;
                end
        end
     always_comb 
            begin
                    next_trg = o_trg; // <-- default value is flopped value
                    next_distance = o_distance; // <-- default value is flopped value
                    next_count = l_count;
                       case(curState)
                             IdleState:
                                begin
                                        next_count = 0;
                                        next_trg = 1;
                                        next_count = 0;
                                        next_distance = 0;
                                        //# trig wait 
                                        next_state = CatchEchoState;
                                end
                           CatchEchoState:
                                begin
                                        next_trg = 0;
                                        if ( i_echo == 1 )
                                        begin
                                            if( l_count < 32'b101101110001101100000000 )
                                                begin  
                                                    next_count = l_count + 1;
                                                    next_state = CatchEchoState;
                                                end
                                            else 
                                                begin
                                                    next_state = IdleState;
                                                end
                                        end
                                        else
                                            begin
                                                next_state = CountState;
                                            end
                                end
                           CountState:
                                begin
                                     // here is the problem
                                    //next_distance =  ((l_count * 68) / 100000) + 1;
                                     next_state = IdleState;
                                end
                           default:
                                next_state = IdleState; 
                           endcase                     

            end

endmodule

and the schematic looks like thisenter image description here

how can I implement to evade this situation ?

Best Answer

My guess is it requires more than one clock to calculate ((l_count * 68) / 100000) + 1. There should be a warning about this in the synthesis/timing report.

First thing that can be done is reduce the 68/100000 to 17/25000. The synthesizer should have done the same optimization.

Then split the calculation across multiple clocks. Notice in the CatchEchoState state when next_state = CountState;, next_count is not being updated. Some of the distance calculation can be move to this condition.

In the example below I moved the multiplication to the CatchEchoState and kept the division in the CountState. Check the timing report. If the timing can be meet for partial calc and not the final calc, then include a division of factor of 25000 (ex 40 is a factor of 25000, which then becomes next_count = (l_count * 17) / 40; ... next_distance = (l_count / 625) + 1;). If timing still cannot be meet, consider to increment next_count by 17 instead of 1, thereby eliminating the dedicated multiplication logic.

...
CatchEchoState:
  begin
    next_trg = 0;
    if ( i_echo == 1 )
      ...
    else
      begin
        next_count = l_count * 17; // <-- partial calc
        next_state = CountState;
      end
  end
CountState:
  begin
    next_distance =  (l_count / 25000) + 1; // <-- final calc
    next_state = IdleState;
  end
...   

If there is still an issue, then it likely has to do with your top level module or your synthesis configurations.


FYI: o_distance and next_distance need to be at least 24-bits wide to store the maximum unsigned value (assuming max l_count value is 11,999,999). Looks like this is what is going on in the screenshot, but it is 8-bits in the provided RTL.