Skip to content
code0.HDL

Static and dynamic semantics: phases and scheduling relations

The three phases that give a BSV design its meaning, elaboration, scheduling analysis and runtime, and the C, CF, SB and SBR relations primitive modules declare between their methods, which decide which calls may share a cycle.

In this chapter

  • Place a compile error, a scheduling warning and a stalled design in the phase that produces it
  • Explain what elaboration fixes once and what is re-evaluated every cycle
  • Read a C, CF, SB or SBR relation and say whether two method calls may share a cycle or a rule
  • Derive the register rules, reads see the old value and a double write in one rule is an error, from the register's relations
  • Predict the state after a cycle in which several rules fire, by running them one at a time in schedule order
Concepts in this chapter
  • Static and dynamic semantics: phases and scheduling relations
Prerequisite concepts

The meaning of a BSV program is settled at three different moments. Compilation fixes types and structure, scheduling analysis decides which rules may run in the same cycle, and at runtime the current state decides which rules actually fire. Keeping those moments apart is what lets you tell a compile error, a scheduling warning, and a design that silently stalls from one another.

Earlier chapters met each moment on its own. Guards: hold and stop built a rule's enabling condition from its explicit condition and the readiness of every method it calls. Rules that share state showed atomic updates, conflicts between rules, and the G0010 and G0021 warnings. State machines with rules expressed a required order as phase state. This chapter names the three phases those behaviours belong to, and then explains the scheduling relations that primitive modules declare between their methods, which is where a conflict warning ultimately comes from.

1. Three phases of interpretation

1.1 What each phase decides

A BSV design is interpreted by static processing, then scheduling analysis, then execution. When a design misbehaves, look for the cause in that order.

PhaseWhenWhat it decidesProblems that show up here
Static checking and elaborationCompile timeTypes, provisos, widths, module instances, parameters, structural loopsType errors, unresolved provisos
SchedulingCompile timeWhich rules may share a cycle, method ordering, prioritiesConflict warnings, warnings about rules that can never fire
RuntimeEvery cycleWhich conditions are true now, which rules fire, the next stateDisabled rules, unexpected state transitions

Check

bsc prints a warning that two rules conflict and that one of them can never fire. Which phase produced that warning?

Your answer

1.2 Static checking and elaboration

Elaboration unfolds the hardware structure, starting at the top module and instantiating each child in turn. Parameters are passed down, and every loop and function that builds structure is expanded.

Vector#(4, Reg#(Bit#(8))) regs <- replicateM(mkReg(0));

for (Integer i = 0; i < 4; i = i + 1)
  rule clear_one (clearing);
    regs[i] <= 0;
  endrule
CodeWhat elaboration does with it
replicateM(mkReg(0))Creates four register instances
for (Integer i = 0; ...)Not a loop that runs in hardware; it unfolds into four separate rules

After elaboration the instance hierarchy and the set of rules never change. A type error is not a branch that might be taken at runtime; it is caught before any hardware exists.

Check

After elaboration of the snippet above, how many rules does the module contain?

Your answer

1.3 Scheduling analysis

Scheduling analysis works on that fixed design and follows every method each rule calls, including methods of other modules and nested calls. It runs once, at compile time, and produces a scheduler circuit that picks the rules to fire in each cycle.

1.4 Runtime

At runtime the current register values feed each rule's conditions. The chosen rules compute next-state values, and stored state changes all at once at the clock edge. Simulation and synthesized hardware follow the same state-transition rules.

The rules that fire together in one cycle must give the same result as running them one at a time, in the order the scheduler chose, each rule seeing the state the earlier ones left. That is how you predict a cycle: pick the order, then run the rules in your head one by one.

1.5 Example: one rule at a time

In OneRuleAtATime.bsv, x starts at 0 and y at 9. In cycle 0 both copy (y <= x) and incr (x <= x + 1) are enabled, and show prints the two registers in cycle 1. copy reads x and incr writes it; a register read is SB its write, so the only legal order is copy before incr.

Predict before you run. Run the two rules one at a time in that order and write down x and y.

Check

What does show print?

Your answer

OneRuleAtATime.bsv
package OneRuleAtATime;

(* synthesize *)
module mkTop(Empty);
  Reg#(Bit#(8)) x <- mkReg(0);
  Reg#(Bit#(8)) y <- mkReg(9);
  Reg#(Bit#(2)) cycle <- mkReg(0);

  // Both rules fire in cycle 0. copy reads x, and incr writes x.
  rule copy (cycle == 0);
    y <= x;
  endrule

  rule incr (cycle == 0);
    x <= x + 1;
  endrule

  rule advance;
    cycle <= cycle + 1;
  endrule

  rule show (cycle == 1);
    $display("x=%0d y=%0d", x, y);
    $finish(0);
  endrule
endmodule

endpackage

Check the result. x=1 y=0, with no scheduling warning: both rules fired in cycle 0, and the result is exactly copy followed by incr.

Try it yourself. Change incr to x <= y + 1; and predict again before you run.

Check

With incr changed to x <= y + 1;, what happens?

Your answer

2. Method scheduling relations

2.1 The relations

Primitive modules tell bsc whether two method calls can share a cycle by declaring a scheduling relation between them.

RelationMeaning
CThe calls conflict; they cannot fire in the same cycle
CFThe calls are conflict-free; any order gives the same result
SBThey can share a cycle, but must behave as if the first came before the second
SBRThe first comes before the second, and the two calls cannot appear in one rule

SA and SAR are the reverse-direction names for SB and SBR in scheduling documentation; they are not BSV source attributes.

Check

Which relation lets two method calls share a cycle in a fixed order but forbids both calls from appearing in the same rule?

Your answer

2.2 Register relations

Call pairRelationConsequence
_read and _readCFAny number of rules may read in the same cycle
_read and _writeSBReads are ordered before writes, which is why reads see the old value
_write and _writeSBRWrites from two rules need arbitration; two writes in one rule are an error

This table explains the phase.write versus phase.read text in the G0010 warning of the rule-conflict example in Rules that share state. For chooseOne to come before chooseTwo, chooseOne's write would have to precede chooseTwo's read, but reads must precede writes. The same holds in the other direction, so no order works.

The SBR row also explains two results from that chapter. Two writes to one register inside a single rule are the compile error G0004, because SBR forbids both calls in one rule. Two rules that each write the register can still share a cycle: bsc orders them, the write ordered later takes effect, and warning G0117 reports that it shadows the earlier one.

Check

Two different rules each write register r, and both are enabled in the same cycle. What does the SBR relation between _write and _write mean for them?

Your answer

Note. Modules with similar storage behavior can still have different scheduling relations; mkReg and mkConfigReg, for example, present different scheduling models. Choosing a primitive changes not only area and initialization but also which callers can share a cycle. Reach for the alternative only when you need its relation and have verified it.

3. Summary

TopicKey point
Three phasesElaboration fixes structure, scheduling fixes which rules may share a cycle, runtime computes the next state
Method relationsC, CF, SB, and SBR decide which calls may share a cycle
Register relationsReads are CF with each other and SB before writes; writes are SBR, so two writes in one rule are an error and two writing rules need arbitration

4. Check your understanding

  1. Name one thing elaboration fixes and one thing that is re-evaluated every cycle.

    Show answer

    Elaboration fixes the module instance hierarchy and the set of rules; a for loop over an Integer is unfolded at that point. Rule explicit conditions and method readiness conditions are re-evaluated every cycle.

  2. Why may any number of rules read a register in one cycle, while two rules that write it need arbitration?

    Show answer

    _read and _read are CF: the order of two reads never changes a result. _write and _write are SBR: the two writes must be ordered, so bsc has to decide which rule comes later, and that write is the one that takes effect.

Checkpoint

Answer the checks in this chapter

Sources

Licences and notices for the sources this site follows