hardcaml icon indicating copy to clipboard operation
hardcaml copied to clipboard

Add "debug" signals to circuits

Open askvortsov1 opened this issue 3 years ago • 1 comments

One of the few downsides of Hardcaml as opposed to Verilog+Vivado is the inability to simulate the internals of a circuit. Currently, Circuit.t allows accessing input and output signals, but not anything in between. As a result, only inputs and outputs of circuits can be monitored in waveforms. This can make it difficult to both debug why an implementation isn't working during development, and to create integration tests for some part of a design that can't easily be expressed as an input or output of the total system.

For an example of the latter, see the datapath and CPU components of our Hardcaml MIPS project. writeback_data and writeback_pc are used in integration tests to confirm that some representative instruction sequences are processed correctly. However, they are not used directly higher up in the design, and so aren't really outputs, but we needed to make them outputs anyways so we could test them.

I would like to propose that Hardcaml add optional "debug signals" to circuits. These could be arbitrary signals (or functions of signals), as long as all their leaf dependencies are inputs to the circuit's computation graph. They would be evaluated during simulation and included in waveforms, but pruned during RTL generation to avoid any performance implications. There could also be an option to keep them in the generated RTL in case a different simulation engine is used.

I'm not 100% sure as to an ergonomic way of supporting this in hierarchical circuits, but several ideas are:

  • Registering them to the scope argument that's provided to all implementations of hierarchical functions.
  • Providing a new optional debug argument to implementation functions.
  • Adding an interface for them as an additional functor argument, and having hierarchical circuit implementations return a tuple of (output, debug). This would break current code, or require separate functions/functors, which would be messy.
  • Using some global log function, then inspecting the dependency tree to figure out between which input/output signals it corresponds to. This is my least favorite option, since its very magicky, and I suspect it wouldn't be possible to uniquely identify this.

Beyond this, there could also be a print function that, when given a signal, clock cycle number, and optional message, would print the signal value and message at the given clock cycle in expect tests. I'm not sure how useful this would be if we had debug signals though.

I think this feature could significantly speed up development of new Hardcaml designs by allowing "printf" debugging of sorts, and help increase meaningful test coverage of existing ones.

askvortsov1 avatar May 25 '22 06:05 askvortsov1

Initial thoughts;

When you name a signal with the (--) operator (or similarly when you use Scoped.naming in a hierarchical context) those named signals can be printed in the waveform without needing them to be outputs.

With Cyclesim you can also lookup_signal or lookup_reg to peek at the values of internal things.

I will think a bit more about your other points.

andrewray avatar May 25 '22 07:05 andrewray