Skip to content
code0.HDL

A module and its interface

Run the smallest BSV program, a package with one module and one rule, and predict what it prints; then read a module with one register and one value method and the testbench that reads it, run it, and change one thing.

In this chapter

  • Read a BSV file as a package of modules and name the interface each module provides
  • Run a one-rule program, predict its output, and explain why its unguarded rule can fire in every cycle
  • Recognise a value method by its return type and treat it as an observation
  • Instantiate a module with <- and read its value method from a testbench rule
  • Predict what a design prints before running it, and tell elaboration from execution
Concepts in this chapter
  • A small module and a readable interface
Prerequisite concepts

1. Run the smallest BSV program first

Before any syntax, run something. The smallest complete BSV design has three parts you will see in every chapter:

PartWhat it is
packagethe file; one package per file, named after it
modulea piece of hardware; mkTop is the top of the design
rulewhat the hardware does in a clock cycle; a rule with no condition is ready in every cycle

FirstRule.bsv has one rule, greet, that prints a line and ends the simulation. Read the rule and decide what will appear before you press Run.

Check

How many times does Hello, BSV appear in the output?

FirstRule.bsv
package FirstRule;

(* synthesize *)
module mkTop(Empty);
  // A rule with no condition: it is ready in every clock cycle.
  rule greet;
    $display("Hello, BSV");
    $finish(0);
  endrule
endmodule

endpackage

Press Run. The first two lines come from the compiler: Verilog file created: mkTop.v means bsc turned the BSV module into a Verilog module, and Verilog binary file created: sim.out means a simulator was built from it. Only the last line, Hello, BSV, comes from the design.

Structure versus simulation. Two different things happened. bsc built hardware: a module mkTop whose rule greet has an enable signal that is true in every cycle. Then the simulator ran that hardware clock by clock. $display and $finish are simulation-only system tasks; in a chip the rule would still fire every cycle, but nothing would print and nothing would stop.

Now change the program and predict each result before you run it.

EditPredict
Change the text "Hello, BSV" to your own messageonly the printed line changes
Add $display("still the same cycle"); on the line before $finish(0);how many lines the design prints, and in which order

Check

With the second $display added before $finish(0), what does the design print?

Your answer

Check

Predict without running it: you delete the line $finish(0);. What happens?

Your answer

2. A BSV file is a package of modules

A BSV source file is one package, named after the file: ValueMethod.bsv opens with package ValueMethod; and closes with endpackage. Inside the package, the unit of hardware is the module. By convention module names start with mk, short for "make", because a module definition is a recipe; hardware exists only once something builds an instance from it.

Form

package PackageName;

(* synthesize *)
module mkModuleName(InterfaceType);
  // state declarations
  // rules
endmodule

endpackage

The type in parentheses is the interface the module offers to the outside. In FirstRule.bsv every line now has a name:

CodeMeaning
package FirstRule; … endpackageOpens and closes the package; the file must be named FirstRule.bsv
(* synthesize *)An attribute asking bsc to emit this module as its own Verilog module
module mkTop(Empty);Starts module mkTop; the parenthesized type is the interface it offers
EmptyAn interface with no methods, typical for a top-level module that only drives a simulation
rule greet; … endruleA rule with no condition, so it may fire every cycle
$display, $finish(0)System tasks that print and end the simulation

System tasks, the names starting with $, exist for simulation. bsc keeps them in the generated Verilog between translate_off markers so that synthesis skips them; they never become hardware. Every example marks its top module (* synthesize *), and the Run command names that module with -g mkTop. The full package syntax, including import and export, is in Packages.

Run performs three steps: bsc -verilog -g mkTop compiles the BSV source to Verilog, bsc -e mkTop -vsim iverilog links a simulator with Icarus Verilog, and vvp runs it.

3. An interface says what a module offers

3.1 Declaring an interface

Verilog describes a module from outside by its ports. BSV describes it by its methods. An interface is a type made of method prototypes, each with a name, argument types and a result type, and nothing about how the module works inside. It is declared at the top level of a package, and several modules may provide the same interface.

Form

interface InterfaceName;
  method ReturnType methodName(arguments);
  ...
endinterface
interface Counter;
  method Bit#(8) value;
endinterface

interface Accumulator;
  method Action add(Bit#(8) x);
  method Bit#(8) total;
endinterface
CodeMeaning
interface Counter;Declares an interface type named Counter; type names start uppercase
method Bit#(8) value;A method with no arguments that returns an 8-bit value; empty parentheses may be omitted
method Action add(Bit#(8) x);A method that takes an 8-bit argument and changes state
endinterfaceCloses the declaration

The interface lists only names and types. Implementations, and the readiness conditions a method may carry, belong to the method definitions inside a module.

3.2 First, value methods

A method's return type decides its kind. This chapter needs only one kind: a method that returns a value, such as Bit#(8) or Bool, is a value method. Calling it reads the module's state and returns a result; it never changes anything, so reading it twice is the same as reading it once.

value in Counter returns Bit#(8), so it is a value method. Methods whose return type is Action or ActionValue#(t) change state; add in Accumulator above is one. What those two kinds promise, how a rule calls them, and how to choose among the three for a given contract are the subject of Methods: value, Action, ActionValue, after you have written registers and rules yourself.

4. State comes only from module instances

4.1 Instantiation binds an interface to a name

In BSV, anything that remembers a value from one cycle to the next exists only because a module was instantiated. Registers, FIFOs, memories and your own larger components are all created the same way.

Form

InterfaceType instanceName <- moduleName(arguments);
Reg#(Bit#(8))  count <- mkReg(42);
FIFO#(Bit#(8)) queue <- mkFIFO;
CodeMeaning
Reg#(Bit#(8))The interface type the instance provides: an 8-bit register interface
countThe name bound to that interface; you call its methods through count
<-Runs the module to create an instance and binds the resulting interface
mkReg(42)The module to instantiate and its argument: a register that starts at 42

A module with no arguments, such as mkFIFO, may omit the parentheses. mkReg(42) is itself a module, the register, and Reg#(Bit#(8)) is the interface it provides.

Note. Ordinary variables are not state. Local variables, function arguments, names bound by patterns and loop variables are names used within one computation. In let x = queue.first;, x creates no storage; it simply names the value queue is presenting right now.

4.2 Definitions versus instances

A module definition is a recipe for building hardware. A module instance is the hardware built from it. Instantiating one definition twice gives two independent copies of its state.

Reg#(Bit#(8)) a <- mkReg(0);
Reg#(Bit#(8)) b <- mkReg(0);   // a second register, independent of a

Instances form a strict hierarchy: every instance except the top one has exactly one parent. The mk prefix on definition names is what lets you tell the recipe from the instances.

4.3 The module keeps state and defines the method

mkCounter provides Counter. Its body has two lines, and they are two different kinds of thing.

LineWhat it is
Reg#(Bit#(8)) count <- mkReg(42);state: a register holding eight bits, built with the initial value 42
method Bit#(8) value = count;wiring: the method's result is the register's current value

Using count in an expression reads the value the register holds at the start of the current cycle. Nothing in this design ever writes count, so that value is 42 in every cycle. Writing a register, and the rule that decides when, is the subject of Registers and one rule.

5. The testbench instantiates the module and reads the method

A module does nothing on its own. mkTop is the testbench. It builds an instance with Counter c <- mkCounter; and then declares one rule. A rule is a block of actions that may run in a clock cycle; this one has no condition, so it is ready from the first cycle. Its body reads c.value, the dot notation for instance.method, prints the result with $display and ends the simulation with $finish(0).

ValueMethod.bsv is about the smallest module possible: one value method. Read it in this order:

  • The Counter interface has a single method, value.
  • In mkCounter, the register count starts at 42 and nothing ever writes it.
  • mkTop instantiates mkCounter with <- and reads c.value in rule show.

Predict before you run.

Check

What number does the value = line print?

Check

How many state transitions does count go through before the simulation ends?

ValueMethod.bsv
package ValueMethod;

// An interface declares what a module offers. A value method reads state and
// returns a result without changing anything.
interface Counter;
  method Bit#(8) value;
endinterface

module mkCounter(Counter);
  Reg#(Bit#(8)) count <- mkReg(42);

  method Bit#(8) value = count;
endmodule

(* synthesize *)
module mkTop(Empty);
  // The module is instantiated with <-, and the result is its interface.
  Counter c <- mkCounter;

  rule show;
    $display("value = %0d", c.value);
    $finish(0);
  endrule
endmodule

endpackage

Practice this concept

Use this concept in a short BSV problem as your next step.

Press Run. The lab compiles the package to Verilog with bsc, links a simulator with Icarus Verilog and runs it.

Output lineWhat it shows
Compiler messagesThe lines reporting mkTop.v and sim.out mean bsc compiled and linked successfully
value = 42The initial value appears unchanged. c.value is just an expression over state, so there were zero state transitions: the testbench observed the initial value and stopped

Caution. Output means the design compiled and simulated with bsc 2026.01 and Icarus Verilog 11. Synthesis is not run in this course. (* synthesize *) only decides which modules become separate Verilog modules; it does not prove the design maps to a chip.

6. Change one thing

Make two edits in the lab's editor, one at a time, and run after each.

EditWhat to expect
mkReg(42) to mkReg(7)Only the printed value changes, to 7; the structure is unchanged and there are still zero transitions
Counter c <- mkCounter; to Counter c = mkCounter;Compilation stops with T0020: the expected type is Counter, but mkCounter is a module computation that produces one

Check

After the second edit, when does the mistake show up?

Your answer

The second edit is a common mistake, worth reading in the compiler's words.

// Wrong
Counter c = mkCounter;

// Right
Counter c <- mkCounter;

mkCounter is not an interface value; it is a module computation that produces one. With =, error T0020 reports that it expected Counter but inferred a__#(Counter), the type of a module that yields a Counter once it is run. Only <- runs it.

<- appears again later with a second meaning, running an action that returns a value. Both uses share one idea: run something, then bind its result.

7. Types and elaboration

Widths are part of types. Reg#(Bit#(8)) is a register interface for 8-bit values, and a width mismatch is caught by type checking before any hardware exists. Types, widths and explicit conversion covers this in detail.

Compilation distinguishes two moments:

MomentWhat is decided
ElaborationStarting from the top module, submodules are instantiated and the hardware structure is fixed
ExecutionIn that fixed hardware, rules become ready and change state cycle by cycle

<- belongs to the first moment: Counter c <- mkCounter; and mkReg(42) run once, while the structure is being built. Firing a rule never creates a module. The structure is settled during elaboration.

Note. Every package automatically imports the Standard Prelude, which supplies basics such as Reg, Bool and Empty. FIFOs, vectors, memories and similar components come from bsc library packages that you import. BSV and BH (Bluespec Haskell, also called Bluespec Classic) differ in syntax but share semantics, and bsc accepts .bsv and .bs packages in one design.

Note. Rules have a formal semantics based on Term Rewriting Systems. Invariants you prove in the one-rule-at-a-time model still hold for the parallel hardware that bsc generates. Static and dynamic semantics makes that model precise.

8. Apply it: a value method must report the state

In the Practice problem below, mkCounter has a second method, set, which writes the register, and value is defined as count + 1. The testbench loads 8'h2a and reads back 43 instead of 42. A value method observes without changing state; it may compute a result from that state. This problem specifically requires the stored count unchanged. Repair value so that every representable count is reported unchanged, then run.

The checkpoint for this chapter is running the example and comparing the printed value with your prediction; the Practice problem is the first exercise of the course.

9. Summary

ElementMeaning
package … endpackageone file, one package, same name
interface … endinterfacea type listing method prototypes; implementations live in modules
rule name; … endrulewhat happens in a clock cycle; with no explicit condition it may fire every cycle, subject to method readiness and scheduling
value methoda method returning a value type; it only observes. Action and ActionValue methods come later
module mkName(Ifc); … endmodulea definition providing interface Ifc; a recipe, not hardware
Reg#(t) r <- mkReg(init);a register instance holding a t, starting at init; state exists only through instances
method t name = expr;a value method: reads, never writes
Ifc x <- mkName;an instance built with <-, used as x.method; = binds without running and fails with T0020
(* synthesize *)emit this module as its own Verilog module
elaboration, executioninstances are built once while the structure is fixed; rules then fire cycle by cycle

10. Check your understanding

  1. Why do two instances of the same module definition hold separate state?

    Show answer

    A module definition is only a recipe. State is created each time the definition is instantiated with <-, so each instance owns its own registers.

  2. Name one thing that happens during elaboration and one that happens every cycle.

    Show answer

    During elaboration, modules such as mkReg and mkCounter are instantiated and the hardware structure is fixed. Every cycle, rule readiness is evaluated and the selected rules' register writes are stored at the clock edge.

Checkpoint

Run the example and compare it with your prediction

Sources

Licences and notices for the sources this site follows