Search code examples
mathverilogsynthesisquartus

Using a non-constant value inside "while", gives me this error, what can I do?


I'm trying to make addition and subtraction of floating point. My guide is a book "Computer Arithmetic and Verilog HDL Fundamentals" by Cavanagh. Inside the module he use a code for aligning exponents as I show as follows.

  always @ (oper_1 or oper_2)
  begin 


  exp_a = oper_1 [31:24];
  exp_b = oper_2 [31:24];


  fract_a = oper_1 [23:0];
  fract_b = oper_2 [23:0];


  // bias exponents 


  exp_a_bias = exp_a + 8'b0111_1111;
  exp_b_bias = exp_b + 8'b0111_1111;


 // align fractions 


 if (exp_a_bias < exp_b_bias)
 ctrl_align = exp_b_bias - exp_a_bias;

 while (ctrl_align)
 begin
 fract_a = fract_a >> 1;
 exp_a_bias = exp_a_bias + 1;
 ctrl_align = ctrl_align - 1;
 end


  if (exp_b_bias < exp_a_bias)
  ctrl_align = exp_a_bias - exp_b_bias;

  while (ctrl_align)  // heres comes the troubles
  begin
  fract_b = fract_b >> 1;
  exp_b_bias = exp_b_bias + 1;
  ctrl_align = ctrl_align - 1;
  end

Quartus II give me the following error:

Error (10119): Verilog HDL Loop Statement error at ADD_SUB_FLO.v(40): loop with non-constant loop condition must terminate within 250 iterations

I searched and it seems that since Quartus can't be sure what will be the size of "ctrl_align" resultant it won't synthesize. Quartus site says I can edit the .qsf file, But I couldn't find in my file any

set_global_assignment -name VERILOG_NON_CONSTANT_LOOP_LIMIT 300

Moreover, I'm somehow impressed that the book teach that step with a wrong code, not syntesizable, I also wish to actually fix the problem not to workaround, what can I do?

Update: I tried changing to 'For' but it gives error: Error (10170): Verilog HDL syntax error at ADD_SUB_FLO.v(44) near text ">"; expecting "=" I think for or while it wouldn't matter because it seems the problem is that system requires to pre+know the bound, but that's for being calculated.

    for ( i=ctrl_align,i>0,i=i-1)
        begin
            fract_a = fract_a >> 1;
            exp_a_bias = exp_a_bias + 1;
        end

Solution

  • As a previous comment stated, this isn't synthesizable Verilog. You're getting caught up in thinking sequentially. You don't need a for loop or a while loop, basically, you want fract_* to shift, exp_*_bias to increment, and ctrl_align to decrement after an initial loading.

    There are three states:

    1. A loading state, where ctrl_align = exp_b_bias - exp_a_bias
    2. A counting state, where ctrl_align is decremented and the correct fract_ is shifted
    3. A done state, where ctr_align == 0 and the output is valid

    Use a simple state machine to keep track of this and then use a case statement to select the correct assignments for each net.

    For example:

    always @(posedge clk) begin
        // Should include a reset
        case(state):
        LOAD:begin
            ctr_align <= (exp_a_bias < exp_b_bias)? exp_b_bias - exp_a_bias : exp_a_bias - exp_b_bias;
        end
        COUNT:begin
            ctrl_align <= ctrl_align - 8'd1;
            fract_a <= (exp_a_bias < exp_b_bias)? fract_a >> 1 : fract_a;
            fract_b <= (exp_b_bias < exp_a_bias)? fract_b >> 1 : fract_b;
        end
        DONE: ctrl_align <= ctrl_align
        default: ctrl_align <= 0; // latches inferred for fract_*
    

    Haven't checked for syntax, and there's some inferred latches, but this should give you the gist of it. The state machine should be trivial to implement, just a few if statements.

    Hope that helps!