Verilog code shown earlier—namely,
the complex control logic necessary
to manage parallel update to shared
state. It is easy to add more processes,
or more shared state, or more complex update conditions.
Rules matter even more when scaling up to systems organized into modules; we will return to this discussion after describing modularity mechanisms.
Organizing rules into modules. In
object-oriented programming, complex systems are organized into objects. The key behavioral construct is a
process or thread (just one in a sequential language). A process is rooted in
some module (perhaps “main”), but it
may span object boundaries through
method calls. Because methods may
themselves call methods, a process
may thus access an unbounded number of objects. Each method is semantically a fragment of a process.
Similarly, in BSV a syntactic rule
in one module can invoke a method in
another module. Methods can, in turn,
invoke other methods. Each method
is semantically a fragment of a Rule.
Thus, a method is more than just a
procedure—it is a guarded expression.
A semantic Rule is composed of syntactic components: a rule construct
and (transitively) all the methods it invokes; this semantic Rule is the unit of
parallelism and atomicity.
A small module that implements
a FIFO containing integers illustrates
this. First its interface declaration:
tor that can be instantiated multiple
times to create actual modules (hence
the stylistic choice of mk in the mod-
ule name mkFIFOint, suggesting the
word make). FIFOint in the header
specifies the interface. The next two
lines instantiate two registers: data,
containing an integer, initially un-
specified; and empty containing
a Boolean, initially True. The enq
method is guarded by the condition
if (empty)—any rule from which it
is invoked will be enabled only if the
guard is true. When invoked, it stores
its argument x in the data register
and sets empty to False. The first
and deq methods are guarded by the
condition if (! empty)—any rule
from which they are invoked will be
enabled only if this guard is true. The
first method returns data’s value
and does not change the FIFO. The
deq method has the effect of setting
empty to True.
figure 3. a fifo module.
module mkFIFOint (FIFOint);
Reg #(int) data <- mkRegU;
Reg #(Bool) empty <- mkReg (True);
method Action enq (int x) if (empty);
data <= x; empty <= False;
endmethod
method int first () if (! empty);
return data;
endmethod
method Action deq () if (! empty);
empty <= True;
endmethod
endmodule
figure 4. Butterfly crossbar switch
4×
4
upper 2×
2
interface FIFOint;
method Action enq (int x);
method int first ();
method Action deq ();
endinterface
Like a C++ virtual class, it merely
describes the externally visible methods of a module. The enq method enqueues an item x. Its Action type indicates that it returns no value and just
has an effect. The first method takes
no arguments and returns the value of
the element at the head of the queue.
The deq method is a pure Action—it
has no arguments or results; it just has
the effect of discarding the first element in the queue.
Figure 3 illustrates the code for one
possible module that implements this
interface. This is a module construc-
input ports
lower 2×
2
midports
output ports
Basic building blocks
2×
1 merge module
1×
2 routing rule
oCTobEr 2011 | voL. 54 | No. 10 | communications of the acm 41