2
\$\begingroup\$

I have a bunch of (System) Verilog code that uses initialization statements. This is code for an Altera FPGA. I test the code using automated testbenches in the version of modelsim that ships with quartus, and it all works fine. However, I have been trying to move some of our test benches to the cadence simulator, and it is complaining. A simple example of code that fails is this:

reg [15:0] counter = 0;

always_ff @(posedge clk) begin
    counter = counter + 1'b1;
    display("counter: %4X", counter);
end

This give an error in the cadence simulator that it is not allowed for counter to have multiple drivers because it is used in an always_ff block.

Is this error correct? Two tools say that code is OK, and one fails. The rules I have found for always_ff state that "a variable assigned in an always_ff, always_comb, or always_latch may not be assigned in by any other process". This makes sense, but it seems strange that the initial value would be considered a process, and in any case would make those language features mutually incompatible.

So is there a correct way to use initializers along side system verilog new style always_* processes? Does this behavior depend on the version of the system verilog standard such that I might be able to tell my tools which one to use? Or are my choices to use explicit reset lines only, or stick to old style always blocks?

\$\endgroup\$

4 Answers 4

1
\$\begingroup\$

I see that Cadence and Synopsys both produce an error for this code. I also see that Mentor and Aldec both allow driving initialized variables in always_ff blocks. This suggests that the correct behavior is not properly defined in the LRM.

Relying on power-up values for FPGA designs is perfectly viable. Altera's recommended coding guideline is to use always @(posedge clock), so I would suggest that you do this as a workaround. Alternatively you can use a different simulator.

Cadence's behavior arguably makes sense for ASIC designs, where you can't rely on initialization of registers. Adding an initialization value for a variable by mistake could hide missing reset bugs in simulation.

\$\endgroup\$
0
\$\begingroup\$

The LRM is not very clear about initializers as a process. Since you could call a function in an initialization, that could be be considered a process.

The purpose of the always_ff construct was strictly for design code not testbenches. Their purpose is to declare the intent of an always_* process up front in simulation so there are no surprises when you get to synthesis. However, the rules for what is synthesizable is a moving target.

If this code is indeed for a testbench, I would change the data types to bit instead of reg, then the default value becomes 0 anyways. Otherwise, I would use a simple always block.

\$\endgroup\$
3
  • \$\begingroup\$ I updated the question to make it clear I am concerned about code targeting the FPGA when run from the testbench, not specifically the testbench code itself. I guess I thought the purpose of the always_* restriction on multiple assignments was to avoid race conditions where it isn't clear which order the processes execute. But as I understand it, there can't be a race condition between an initializer and a regular process block. \$\endgroup\$
    – Evan
    Commented Jul 18, 2016 at 3:57
  • \$\begingroup\$ Most synthesis tools to not accept variable declarations with initializer that only execute once at time 0. Real hardware comes up in a random state and needs a reset signal to get to a known "good" state. \$\endgroup\$
    – dave_59
    Commented Jul 18, 2016 at 4:46
  • \$\begingroup\$ FPGAs, at least Xilinx and Altera FPGAs support and even encourage use of initializers, either instead of or in addition to reset nets, but I guess I should just not do that, or use a different simulator. \$\endgroup\$
    – Evan
    Commented Jul 19, 2016 at 15:31
0
\$\begingroup\$

Looks like the code infers logic for fpga. Instead of initializing the register during declaration, you should use a reset signal. That models the hardware more accurately.

In fpgas, the POR is implicit but you still should bring in external or internal reset into the logic. E.g. PLL locked output, etc.

reg [15:0] counter;
reg rst;

always_ff @(posedge clk or posedge rst)        
begin
   if(rst)
     counter <= '0;
   else
     begin
     counter <= counter + 1'b1;
     'ifdef SIM
        display("counter: %4X", counter);
      'endif 
      end
end

If you do not want to generate the reset internally, the fpga compilers have a default setting that sets the power up state of the registers. You can override this to any value.

\$\endgroup\$
1
  • \$\begingroup\$ Using compiler flags to set the initial values would make the simulation results wrong, so that isn't really an option. If sounds like adding a reset line is the only option. \$\endgroup\$
    – Evan
    Commented Jul 18, 2016 at 3:58
0
\$\begingroup\$

The LRM is quite clear on this, the code is not legal. The best reason to use always_ff is this assignment checking and the expanded automatic sensitivity list you get from the construct usage.

from 9.2.2.4 1800-2012/2017 The always_ff procedure imposes the restriction that it contains one and only one event control and no blocking timing controls. Variables on the left-hand side of assignments within an always_ff procedure, including variables from the contents of a called function, shall not be written to by any other process.

Just so you are convinced on the variable declaration initializations, Section 10.5 states the following (I add emphasis: The variable declaration assignment is a special case of procedural assignment as it assigns a value to a variable. It allows an initial value to be placed in a variable in the same statement that declares the variable (see 6.8). The assignment does not have duration; instead, the variable holds the value until the next assignment to that variable.

Some FPGA vendors require this statement for their setup (something for their toolset, not the language).

The Cadence tool has an internal/hidden switch, -warn_multiple_driver, to make it a warning instead of error for this illegal code, if that helps any.

Other workaround is to `ifdef the initializer for just the application that requires it, since it is illegal, rather than move to simulators that just do not care about these checks. It may require a separate statement in this case.

reg [15:0] counter;

ifdef FPGA initial counter = '0; endif

always_ff @(posedge clk) begin counter = counter + 1'b1; display("counter: %4X", counter); end

I prefer to control the code myself rather than rely on tool default behavior variations(simulator or FPGA tool), so explicitly write the resets.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.