that update state. The classic style in
Verilog (all of these remarks apply to
VHDL as well) is state-centric. For each
state element x:
recognize that when cond2 is true,
process 1 cannot update x, thus open-
ing an opportunity for process 0 to do
so. Therefore, the designer might “im-
prove” the last two lines to:
always (posedge CLK)
if (...cond1...)
x <= ... value1 ...
if (cond0 && (!cond1 || cond2))
x <= x + 1;
else if (...cond2...)
x <= ... value2 ...
else if ...
...
else
x <= ... valueN ...
More generally, multiple state elements may be updated in each conditional arm, the conditional could be
written as a case statement. In synthesizable code, however, each state
element may be updated in only one
always block and may be updated at
most once in each conditional arm.
This is how the problem of parallel
updates to a shared resource is solved
in (synthesizable) Verilog: it is updated
in only one “process” (always block)
and, for every clock, exactly one
possible new value for x is specified. It is
almost a direct, explicit specification
of the hardware that is generated: the
conditional represents a multiplexer
on the input of the x register; the conditions specify how one of the multiplexer inputs is selected to be clocked
into the register; and the conditions
may also specify whether the register
is updated at all. Collectively, all this
logic is referred to as control logic.
This organization of behavior in
terms of states is, unfortunately, or-
thogonal to how behavior is typically
conceptualized: as a collection of steps,
or transactions. Each step of behavior
may involve updates (perhaps condi-
tionally) to multiple state elements.
This “transpose” from the transaction-
centric dimension to the state-centric
dimension is at the heart of the prob-
lem with Verilog. The state-centric
view does not scale well as the num-
ber of parallel activities increases and
becomes more complex in regard to
how the activities compete for shared
state. Consider the problem in Figure
2, which illustrates two registers x and
y updated by three parallel processes
under certain conditions. Assuming
that process 0 has lower priority than
process 1 for the update of x, and pro-
cess 1 has lower priority than process 2
for the update of y, the Verilog solution
may look like this:
always (posedge CLK) begin
if (cond2)
y <= y - 1;
else if (cond1) begin
y <= y + 1;
x <= x - 1;
end
if (cond0 && !cond1)
x <= x + 1;
This Verilog code could be written
in many styles, but they are all state-
centric. The “transpose” from problem
statement into state-centric code has
intertwined and obscured the original
transactions. Also, the priorities are
wired into the code structure—the pri-
ority of process 2 over process 1 is ex-
pressed in the sequentiality of if ...
else. The priority of process 1 over
process 0 is expressed in the !cond1
term. In fact, a clever designer may
Note the transitivity of control: Pro-
cess 0’s update of x is affected by the
condition of the nonadjacent Process
2. Third, some of the process condi-
tions are duplicated in multiple claus-
es, with the usual risks of incorrect
duplication.
rule proc0 (cond0);
x <= x + 1;
endrule
rule proc1 (cond1);
y <= y + 1;
x <= x - 1;
endrule
figure 2. two registers updated by three parallel processes.
cond0
cond1
cond2
rule proc2 (cond2);
y <= y - 1;
endrule
(*descending _ urgency = “proc2,
proc1, proc0” *)
+ 1
– 1
+ 1
– 1
Process 0
x
Process 1
y
Process 1
update priority: Process 0 < Process 1 < Process 2
The rules are a direct expression of
the process descriptions (i.e., they are
transaction-centric, not state-centric),
and the final line expresses the scheduling priority. Synthesis from this
code produces essentially the same