Kuree / kratos

:crossed_swords: Debuggable hardware generator
https://kratos-doc.readthedocs.io
BSD 2-Clause "Simplified" License
66 stars 9 forks source link

Support for genvar and Verilog generate statement #162

Closed kartikp4892 closed 3 years ago

kartikp4892 commented 3 years ago

Is there any reason why kratos doesn't support verilog generate statement and genvar? This is very useful feature of verilog/systemverilog that makes code readable and easy to understand. Basically, I want to generate below code from kratos, is it possible?

module gray2bin
#(parameter SIZE = 8)
(
  input [SIZE-1:0] gray,
  output [SIZE-1:0] bin
)
  genvar gi;
  // generate and endgenerate is optional
  generate
  for (gi=0; gi < SIZE; gi=gi+1) begin
    assign bin[gi] = ^gray[SIZE-1:gi];
  end
  endgenerate
endmodule
Kuree commented 3 years ago

I think the major reason why kratos doesn't have verilog generate statement is that every object in kratos is mutable. You can modify any instances, signals, connections before hitting the code-gen stage. Adding generate statement seemed to be an implementation nightmare when it was designed.

That being said, I believe it is feasible to implement the feature with some efforts. If you want to hack around to make it work. I would suggest to introduce a generate IR statement block similar to how procedural block and for statement works. Then add a pass to unroll the loop if any inner loop object gets modified to ensure the logic is correct

The current way to implement the same logic is to use loops:

  1. Using loop with top-level assignment
    for gi in range(SIZE):
        mod.wire(bin[gi], gray[SIZE-1, gi].r_xor())

This will produce something like:

assign bin[0] = ^gray[SIZE-1:0];
assign bin[1] = ^gray[SIZE-1:1];
// ...

Notice that SIZE will be statically evaluated during compilation time.

  1. Use always_comb, which is semantically equivalent in most cases. It should produce most readable code given current implementation.
    @always_comb
    def logic():
      for gi in range(SIZE):
          bin[gi] = gray[SIZE-1, gi].r_xor()

This will produce something like

always_comb begin
    for (int gi = 0; gi < SIZE; gi++) begin
        bin[gi] = ^gray[SIZE-1:gi];
    end
end
kartikp4892 commented 3 years ago

Thanks for your inputs. The always_comb won't work if there are multiple instances of the module. In that case, the generate statement can't be re-implemented using always_comb. I would still like to see this feature implemented in the future versions of the kratos if that's feasible. Please suggest if there's a plan to add support for generate statement in future, I will keep this issue open for tracking purpose. If you think that's not possible, I will close this issue.

By the way, I am surprised to see how kratos generates nice systemverilog code. I was struggling to find python/scala based HDL which generates user readable verilog. I was disappointed by most of the popular modern HDLs like MyHDL, SpinalHDL and Chisel3. These are very powerful languages, but debugging in simulation is nightmare because of unreadable generated code. Finally I found kratos and happy to see it exceeds the expectation of generating user readable code.

Currently the limitation with scala based SpinalHDL that it can't generate parameters and loops. The challenge from the beginning is elaboration checks performed by SpinalHDL (combinatorial loop, latch detection, multiple drivers etc..) will not work on the parameters if that's generated in verilog code.

I would like to know if that is the limitation with kratos as well? I mean, when kratos generate parameters and loops, the various checks combinatorial loop, latch detection, multiple driver will still work or those are disabled when parameters and loops are generated in system Verilog?

Kuree commented 3 years ago

I would still like to see this feature implemented in the future versions of the kratos if that's feasible. Please suggest if there's a plan to add support for generate statement in future, I will keep this issue open for tracking purpose. If you think that's not possible, I will close this issue.

I'm current busy with some other work so don't have much free time right now. I can take a look and try to figure out the best way to implement this.

Finally I found kratos and happy to see it exceeds the expectation of generating user readable code.

Thanks! Debuggability is the first design goal for kratos.

I would like to know if that is the limitation with kratos as well? I mean, when kratos generate parameters and loops, the various checks combinatorial loop, latch detection, multiple driver will still work or those are disabled when parameters and loops are generated in system Verilog?

I can't think of any limitation except for some implementation bugs. Parameter values are realized when performing these checks so they are nothing but constants to IR passes. Please let me know if you run into any bugs, since the parameter is less tested.

Kuree commented 3 years ago

Added in acd95f94729ed0e767ed227b7cbfc5ee97a3977d

It uses a pass to automatically detect the array logic and convert it to a genvar construct. You have to turn it on when generate the verilog. You can see the usage here: https://github.com/Kuree/kratos/blob/acd95f94729ed0e767ed227b7cbfc5ee97a3977d/tests/test_generator.py#L2200-L2221

Feel free to open this issue or start a new one if you experience any bug. I haven't completely tested this implementation with other potential buggy constructs so I expect there are some bugs here and there.