calyx: Error for comparions that are always true/false

Problem

When I wanted to loop only 4 times using 2-bit number representation for loop counts and 2-bit le operator, I ran into a problem where after checking that the count is 2'b11, it overflows to 2'b00 on the next iteration, causing the loop to run forever and simulation to run until the maximum number of clock cycle is reached. Below is a simple example illustrating such problem. Here, I am attempting to simply update the value in dest with the value stored in register r0 that gets incremented by 1 in each iteration of the loop. When producing the outputs, the maximum number of clock cycles were configured to 500.

import "primitives/std.lib";
component main() -> () {
  cells {
    @external(1) dest = std_mem_d1(32, 1, 1);
    r0 = std_reg(32);
    const11 = std_const(32, 1);
    add = std_add(32);

    // Problem
    // i0 = std_reg(2);
    // le = std_le(2);
    // add_cnt = std_add(2);
    // const0 = std_const(2, 0);
    // const1 = std_const(2, 3);
    // const2 = std_const(2, 1);

    // Resolved by,
    i0 = std_reg(3);
    le = std_le(3);
    add_cnt = std_add(3);
    const0 = std_const(3, 0);
    const1 = std_const(3, 3);
    const2 = std_const(3, 1);
  }
  wires {
    group init {
      r0.write_en = 1'd1;
      r0.in = 32'd0;
      i0.write_en = 1'b1;
      i0.in = const0.out;
      init[done] = r0.done & i0.done ? 1'd1;
    }
    group cond {
      le.left = i0.out;
      le.right = const1.out;
      cond[done] = 1'd1;
    }
    group upd {
      dest.write_en = 1'd1;
      dest.addr0 = 1'd0;
      dest.write_data = r0.out;
      upd[done] = dest.done ? 1'd1;
    }
    group incr {
      add.left = r0.out;
      add.right = const11.out;
      r0.write_en = 1'd1;
      r0.in = add.out;
      add_cnt.left = i0.out;
      add_cnt.right = const2.out;
      i0.write_en = 1'd1;
      i0.in = add_cnt.out;
      incr[done] = r0.done & i0.done ? 1'd1;
    }
  }
  control {
    seq {
      init;
      while le.out with cond {
        seq {
          upd;
          incr;  
        }
      }
    }
  }
}

Output

Using the 2-bit configurations for primitive components, the output looks like such:

{
  "cycles": 500,
  "memories": {
    "dest": [
      81
    ]
  }
}

By increasing the width to 3 to take overflow issues into account, the output becomes more reasonable:

{
  "cycles": 35,
  "memories": {
    "dest": [
      3
    ]
  }
}

Thoughts

It seems that the current implementation of different primitives ignores overflow bits?

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 16 (9 by maintainers)

Most upvoted comments

A paper cut pass detects common mistakes found when writing a Calyx program. The current pass can be found here.

For example, https://github.com/cucapra/calyx/blob/9b76900d7f0e25df2aa09e55b5e868a02409b2ba/calyx/src/passes/papercut.rs#L19-L21

whenever you write to a std_reg, you need to drive the write_en(able) signal. This paper cut pass catches this case, and gives a descriptive compilation error. This makes the user’s life easier, since it is a small bug that is not immediately obvious.

// This will throw a paper cut error.
group write_to_x {
  x.in = 32'd42;
  // x.write_en = 1'd1;   <-- Commented out.
  write_to_x[done] = x.done;
}

Edit: Here’s an example program with the corresponding error message.

Perhaps something similar can be incorporated for simpler cases of overflow as described in your example program.