Electronics > FPGA

Vivado: Synthesis crash (address violation) at "Start Cross Boundary" stage

(1/3) > >>

I am seeing Vivado crash at "Start Cross Boundary and Area Optimization" in synthesis, about a minute into starting synthesis.

With some liberal commenting of blocks out I was able to locate the fault to a custom IP of mine.  Inside this IP is a module that computes the address of a buffer using a few combinatorial inputs.  I cannot disclose the exact source but it has the structure:

--- Code: ---reg [31:0] addr;

always @* begin
    case (protocol)
        PROTOCOL1: begin
            if (yc_sel == 0) begin
                addr <= base_mask | (buff_y_base + (fram_arhd_segment * MEM_BUFF_HALF));
        PROTOCOL2: begin
            if (yc_sel == 0) begin
                addr <= base_mask | buff_y_base;
        PROTOCOL3: begin
            addr <= base_mask + MEM_COMP_BASE + (comp_use * MEM_COMP_SIZE);
        default : begin
            addr <= 32'hXXXXXXXX;
--- End code ---

My intention with the default assigning to dont-care was to say to the synthesis/implementation tool that "this won't happen, so optimise the logic for it not happening to be irrelevant".   

But later I noticed that I had mistakenly not included all conditions as yc_sel being nonzero would result in a non-assignment to addr.  (The intention was in the future versions of the IP to support these.)  However, adding in the missing conditions did not fix the crash.

I then changed the file to a SystemVerilog file and rewrote this as an always_comb block. This no longer crashes.  (I am still getting used to SV, so it was not my natural first choice.)

Has anyone seen this before?  Is there something wrong with what I've written, or is this just yet another Vivado bug?

I was misleading myself.  In testing always_comb I left a statement in place to the effect of replacing the combinatorial output of that.  Vivado still crashed with always_comb once I removed that statement.  However, I found the actual cause.  'fram_arhd_segment' was typoed in a downstream module, which was causing the disconnected net to be optimised out.  Why this led the synthesis process to crash is anyone's guess though, it's clearly a bug of some kind in the tool.

This code has non-blocking assignments in combinatorial block.

Have you considered:

--- Code: ---wire [31:0] addr = (protocol==PROTOCOL1) ? base_mask | (buff_y_base + (fram_arhd_segment * MEM_BUFF_HALF)) :
                   (protocol==PROTOCOL2) ? base_mask | buff_y_base :
                   (protocol==PROTOCOL3) ? base_mask + MEM_COMP_BASE + (comp_use * MEM_COMP_SIZE) :

--- End code ---
A simple wire, identical in both styles of verilog.

The problem is '(yc_sel == 0)', since you are not clocking anything, Im not sure your coding here with combinational logic is a good choice.  You could call this 'wire [31:0] address_select = .....' and later on say if (yc_sel == 0) addr <= address_select; where it is being clocked...

Also, if you want this addr to be driven by another se of logic, either tristate IO pins, or dangerous internal fpga logic, 32'hXXXXXXXX should be changed to 32'hZZZZZZZZ, though I do not know what you are trying to do.  I reserve the 'x' for displaying an erroneous or unknown state for those simulating my code, not for actual FPGA functionality.

Also, remember, if you are creating IP for release, or more than 1 vendor of FPGA, you will need to test your code in other environments as they all reveal different degree of nuances of error and warning checking during compile.


I did consider the ternary operator, but felt it looked messier than the alternative of an always_comb/always mux block.  I'll fix the non-blocking assignment, I've literally not used always_comb before this so still learning the ropes, as SystemVerilog is still new to me.  (However I do recall trying both and Vivado still crashed.)

For clarity base_mask, buff_y_base etc all change very infrequently (external control, <100Hz update rate) and are latched on the same clock as the logic using the addr after a register stage.  The purpose of this logic is to compute the base address of some array which is then incremented by another block until an end state is reached.

It only needs to work on a Xilinx device, so no vendor compatibility issues.

My thought with 32'hXXXXXXXX is that it tells the synthesis tool that I don't care what this output is.  That state is unreachable (within normal operation) and if that does somehow get reached then there's probably a serious bug so it doesn't really matter.  I therefore include it only to get the synthesis tool to stop worrying about a default condition and design the logic to output whatever makes the Karnaugh mapping smaller.  I've used the same 'trick' on the AXI register slave which controls the interface to this block, the result is reading back unassigned registers usually returns an adjacent register or part thereof.  It seems to simplify the multiplexing logic at no cost.


[0] Message Index

[#] Next page

There was an error while thanking
Go to full version