Skip to content
code0.HDL

Packages

One file is one package; export decides which names leave it, import brings published names in, and a package boundary is a visibility boundary, not a hardware boundary.

In this chapter

  • Declare a package whose name matches its file and compile a two-file design with bsc -u
  • Choose the public names of a package with export, including the (..) suffix an interface needs
  • Resolve an unbound-variable error by checking declaration, export and import in that order
  • Explain why a method's readiness crosses a package boundary while the boundary creates no hardware
Concepts in this chapter
  • Packages
Prerequisite concepts

Every BSV file is one package. A package is the unit bsc compiles separately, and it is also the unit that decides which names are public. Once a design spans several files, packages answer the question "who may use which name?" This chapter covers §3 of the BSV Language Reference Guide. You should know modules, rules, and registers from A module and its interface and Registers and one rule, the identifier rules from Lexical elements, and methods with their readiness from Methods: value, Action, ActionValue and Readiness, module arguments, and synthesis boundaries.

1. Declaring a package

1.1 The basic form

A package starts with package and ends with endpackage. The form below puts export before import, but the compiler also accepts an import followed by an export, as in the re-export example. This is not a required ordering between the two declarations.

Syntax

package PackageName;
  export publicName, ...;       // optional
  import OtherPackage::*;       // optional
  // top-level definitions: types, interfaces, modules, functions, constants
endpackage: PackageName
package Counter;

import FIFO::*;

Bit#(8) limit = 10;

module mkCounter(Empty);
  Reg#(Bit#(8)) count <- mkReg(0);
endmodule

endpackage: Counter
PartMeaning
package Counter;Names the package; the file must be Counter.bsv
import FIFO::*;Brings in the names published by library package FIFO
Bit#(8) limit = 10;A package-level constant
endpackage: CounterCloses the package; the name after the colon is optional

1.2 Package name and file name

The package name must match the file name, because bsc resolves import Tally::*; by looking for Tally.bsv. If the two disagree, a dependency- following -u compile stops with S0001.

You may omit the opening package line and the closing endpackage line together. The file name then supplies the package name.

1.3 Order of definitions

Package-level definitions may appear in any order. A module can use a function or type that is defined further down the file.

2. export: choosing public names

2.1 The default rule

Without any export declaration, a package publishes every name it defines itself, and none of the names it imported. As soon as one export declaration appears, only the listed items are published.

export declarationsPublished names
noneevery name this package defines
one or moreonly the listed names

2.2 The export form and (..)

Syntax

export name1, name2, TypeName(..);
export TallyIfc(..), mkTally;
ItemWhat is published
mkTallythe module name
TallyIfcthe type name only; importers see an abstract type with no visible insides
TallyIfc(..)the type name and its representation: an interface's methods, a struct's fields, a tagged union's tags

Exporting an interface without (..) leaves importers unable to call its methods. Current bsc also requires (..) to expose enum labels and interface methods.

Note. The BSV Language Reference Guide describes (..) only for structs and unions. This chapter and its example follow the current compiler's behavior.

2.3 Re-exporting

An imported name listed in export is published again. export P::*; republishes everything package P publishes.

import Tally::*;
export Tally::*;   // importers of this package need not import Tally themselves

Apply the default rule to two packages with no export at all.

package PkgA;
import FIFO::*;
module mkA(Empty);
  FIFO#(Bit#(8)) q <- mkFIFO;
endmodule
endpackage

package PkgB;
import PkgA::*;
(* synthesize *)
module mkTop(Empty);
  FIFO#(Bit#(8)) q <- mkFIFO;
endmodule
endpackage

Check

PkgA.bsv and PkgB.bsv are compiled with bsc -u. What happens to PkgB?

Your answer

3. import: bringing names in

Syntax

import PackageName::*;
import Tally::*;   // every name Tally publishes is now in scope

The ::* is mandatory. BSV has no form of import that leaves out what to take, so import Tally; fails to parse with P0005, pointing at the place where :: should be.

An import only makes names usable; it creates no hardware. Hardware appears when a module is instantiated.

4. Scope, clashes, and qualified names

4.1 Lexical scope

BSV uses lexical scope: a name refers to the nearest enclosing declaration in the source text, so an inner declaration hides an outer one with the same name.

Bit#(8) limit = 10;           // package scope

module mkTop(Empty);
  rule show;
    Bit#(8) limit = 3;        // rule scope: hides the outer limit
    $display("%0d", limit);   // displays 3
    $finish(0);
  endrule
endmodule

The package is the outermost scope. Modules, interfaces, functions, methods, rules, action blocks, and loops introduce nested scopes.

4.2 Qualified names

Every reference must resolve to exactly one declaration. If a local definition and an imported one make x ambiguous, the qualified name P::x selects the x published by package P.

Syntax

PackageName::name
Tally::TallyIfc c <- Tally::mkTally;   // say exactly which package each name comes from

5. The Standard Prelude

The Standard Prelude is imported into every package automatically, with no import line. It supplies basics such as Bit, Bool, Reg, mkReg, and Empty. Other library packages, such as FIFO and Vector, need an explicit import.

Note. If you define a name that the Prelude already uses, every use of it needs qualification and the code gets harder to read. Pick names that do not clash with the Prelude.

6. Example: a design in two files

6.1 Read the code

The example has two files. Tally.bsv defines a counter interface and module; Top.bsv imports and uses them. In Tally.bsv, look for:

  • the two public names in the export list;
  • the package-level constant limit, which is not in that list;
  • the condition if (count < limit) in the header of method increment.
Tally.bsv
// The package name must match the file name: Tally.bsv holds package Tally.
package Tally;

// Only what is exported can be seen from outside. Without an export
// clause everything defined here is visible.
export TallyIfc(..), mkTally;

interface TallyIfc;
  method Action increment;
  method Bit#(8) value;
endinterface

// Not exported: visible inside this package only.
Bit#(8) limit = 5;

module mkTally(TallyIfc);
  Reg#(Bit#(8)) count <- mkReg(0);

  method Action increment if (count < limit);
    count <= count + 1;
  endmethod

  method Bit#(8) value = count;
endmodule

endpackage

In Top.bsv, find the import, the mkTally instance, and the two rules. drive has no explicit condition; stop is ready when c.value == 5.

Top.bsv
package Top;

// Brings every exported name of Tally into scope. The ::* is required.
import Tally::*;

(* synthesize *)
module mkTop(Empty);

  TallyIfc c <- mkTally;

  rule drive;
    c.increment;
    $display("value = %0d", c.value);
  endrule

  rule stop (c.value == 5);
    $display("stopped at %0d", c.value);
    $finish(0);
  endrule

endmodule

endpackage

Practice this concept

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

The command compiles only Top.bsv with bsc -u. The -u flag makes bsc follow each import, find the package it needs (Tally.bsv), and compile that first.

6.2 Predict before you run

  1. Which file defines the TallyIfc and mkTally that Top uses?
  2. How many times can drive call increment?
  3. Does the value drive displays come from before or after that cycle's write?

Check

drive has no condition in its header. How many value = lines does it print before stop ends the run?

Your answer

6.3 Check the result

Compare the output with this state trace.

count at cycle startincrement ready?Rule that firesLine printedcount after the edge
0yesdrivevalue = 01
1–3yesdrivevalue = 1–32–4
4yesdrivevalue = 45
5nostopstopped at 5simulation ends
ObservationMeaning
five value = linesdrive fired five times; c.value in the same rule reads the value from before the write
drive stops once count is 5the implicit condition of increment became false, so the whole drive rule was no longer ready
stopped at 5stop observed 5 and ended the run

Two independent rules are at work. First, Top can use only the names Tally publishes. Second, a called method's readiness reaches across the package boundary and blocks the rule that calls it. drive has no condition of its own, yet the condition on increment stops it.

Note. mkTally has no (* synthesize *), so it is inlined into whatever instantiates it, and only mkTop becomes a Verilog module of its own. Hardware boundaries come from (* synthesize *), not from packages.

6.4 Try it yourself

ChangeExpected result
Set limit in Tally.bsv to 7On the supplied toolchain, the output is unchanged: stop ends the run at count = 5, even though increment is still ready there
Set limit in Tally.bsv to 3count stops at 3, stop never becomes ready, and the simulation never ends
Remove mkTally from the export listCompiling Top.bsv fails with T0004 Unbound variable `mkTally'

Check

In Tally.bsv you change the export list to export TallyIfc, mkTally; (no (..)). What happens when Top.bsv is compiled?

Your answer

Because limit is private, you can change the counter's policy without touching Top.bsv. To make the run end for any limit, stop must stop depending on the literal 5 and instead detect that the counter no longer advances.

7. Common mistakes

7.1 A package name that differs from the file name

// Wrong: the file is Tally.bsv
package Counter;

// Right
package Tally;

A -u compile stops with S0001 File name `Tally' does not match package name `Counter'. Rename the file or the package so they agree.

7.2 import without ::*

// Wrong
import Tally;

// Right
import Tally::*;

Without ::*, parsing fails with P0005, and the message says :: was expected.

7.3 Using a name that was never published

// Tally.bsv, wrong
export TallyIfc(..);            // mkTally is missing

// Tally.bsv, right
export TallyIfc(..), mkTally;

Top.bsv fails with T0004 Unbound variable `mkTally'. The symptom looks like an unknown name, but the cause is visibility, not module syntax, and adding another import to Top.bsv cannot help: no import can bring in a name Tally never published. For the same reason, reading the private constant limit from Top.bsv also fails with T0004.

7.4 Exporting an interface without (..)

// Wrong: the interface becomes an abstract type
export TallyIfc, mkTally;

// Right: publish the methods too
export TallyIfc(..), mkTally;

c.increment and c.value in Top.bsv fail with T0139, whose message explains that Tally::TallyIfc is abstract, so its fields are not visible.

8. Summary

TopicKey point
DeclarationOne package per file; package and file names must match
exportNo declaration publishes everything defined here; any declaration publishes only what it lists
(..)Publishes a type's representation; needed to call an interface's methods
importOnly the import P::*; form exists; it creates no hardware
ScopeThe nearest declaration wins; use P::x when a name is ambiguous
PreludeImported into every package automatically
Hardware boundarySet by (* synthesize *), not by packages

9. Check your understanding

  1. Why does an import not create hardware?

    Show answer

    An import only makes another package's published names usable in the current scope. Hardware is created when a module such as mkTally is instantiated.

  2. What changes when a package that had no export declarations gets its first one?

    Show answer

    Before, every name the package defined was public. Now only the names listed in export are public, and everything else is invisible outside the package.

  3. drive has no condition in its header. Why does it stop firing once count reaches 5?

    Show answer

    It calls increment, whose implicit condition is count < limit. When that condition is false, increment is not ready, and so the whole rule that calls it cannot fire.

  4. Top.bsv reports that mkTally is unbound. Which file do you check first?

    Show answer

    Tally.bsv, starting with its export list. Check where the name is declared, then whether it is published, and only then the import in the importing file.

  5. When is exporting TypeName safer than exporting TypeName(..)?

    Show answer

    When users should be able to pass the type around without depending on its insides. For example, exporting a struct abstractly lets you change its fields later without breaking code in other packages.

The Practice problems of this chapter all deal with the contract between a package's public interface and its hidden state. In the first, the value method fails to carry the private register outward, so its output cannot be trusted. In the second, a method's readiness compares against a hard-coded number instead of the private constant, so changing the constant has no effect. In the third, a package-level constant is left unspecified, and the methods built on it no longer agree with each other.

Checkpoint

Run the example and compare it with your prediction

Sources

Licences and notices for the sources this site follows