YosysHQ / yosys

Yosys Open SYnthesis Suite
https://yosyshq.net/yosys/
ISC License
3.45k stars 881 forks source link

Attribute to force a signal to be used "as-is" (like if coming from a blackbox). #2996

Open smunaut opened 3 years ago

smunaut commented 3 years ago

What I'm looking for is a way to force yosys to use a signal without looking at "where / how" that signal is generated when optimizing.

So for instance, take that snippet of code :

wire, a, b, c, d;
wire mysig;
wire derived;

assign mysig = a & b & c & d;
assign derived = ~mysig & b;

I'd like yosys to generate mysig however it likes, using all the optimization it can. And then when generating derived, it should not look at where mysig comes from, it should consider it a "fixed point" / "blackbox" / ... and not try to combine it.

I tried using (* keep *) but it's not doing the trick unfortunately.

The use case for this is guiding yosys to generate better logic. Take for instance the module below :

module dec(
    input  wire [7:0] start,
    output reg  [7:0] val,
    input  wire       load,
    input wire        clk
);

    wire load_n; 

//`define USE_BBOX
`ifdef USE_BBOX
    bbox_buf bb_I (load_n, ~load);
`else
    assign load_n = ~load;
`endif

    always @(posedge clk)
        val <= load_n ? ( val + {8{load_n}} ) : start;

endmodule

(* blackbox *)
module bbox_buf(output wire x, input wire a);
    assign x = a;
endmodule

If you synthesize it for ice40 as is, you get :

     SB_CARRY                        7
     SB_DFF                          8
     SB_LUT4                        17

if you enable the USE_BLACKBOX which sort of illustrate what I mean (except it creates extraneous instances that need to be stripped as a post processing step). :

     SB_CARRY                        7
     SB_DFF                          8
     SB_LUT4                         9
     bbox_buf                        1

Which is much better logic.

msinger commented 3 years ago

I was asking myself the same thing, if there is a way to do something like that, but since I don't have much experience with HDLs in general, I was afraid that this could be a noob question and I didn't want to bother people with it.

I was wondering if there is a way to describe pure asynchronous logic (without flipflops) and being able to control how it is optimized with an attribute like that. So that you can prevent unwanted hazards at the output for certain input state changes. You know, stuff you try to figure out with Karnaugh Maps. I understand synchronous logic (flipflops) is the "right" way to design stuff with FPGAs, which eliminates the problem of hazards. But I'd like to implement a Game Boy and do stuff close to the original, like generating (hazard free) chip select signals based on the address coming from the CPU without the need to feed the outputs through additional flipflops which would delay them by a tick. I haven't thought that all through yet, so it might just be stupid talk. Sorry for that. But since @smunaut opened this issue, I just wanted to express that he's not the only one who is interested in an attribute like that.

It somehow reminds me of a compiler barrier, like writing asm volatile ("" : : : "memory") in GCC.

smunaut commented 3 years ago

Some work around I found is actually to use the (* keep_hierarchy *) attribute instead of (* black_box *). It prevents optimization across hierarchy and you don't need an extra step to remove it like for the black box because nextpnr can load the multi level JSON without issue and see that the internal one is just a simple net.

Not as convenient as a dedicated attribute, but much better than having a specialized synthesis flow and playing with adding / rmeoving blackbox dummy modules.

msinger commented 3 years ago

Oh, good to know. Thank you.