Small Examples Illustrating Bluespec SystemVerilog (BSV) Language Concepts
Copyright © 2008 Bluespec, Inc, All Rights Reserved
Document Revision: May 20, 2008.
This is a work-in-progress. Expected completion: Q3 2008
Introduction
This is a series of small BSV examples to illustrate various language concepts in Bluespec SystemVerilog (BSV). Each example is as small as possible, illustrating just one concept (goal: one page or less of BSV code).
This set of examples is a freely available reference resource for the Bluespec community (mainly users, but even the just curious are welcome to browse). The examples can be read linearly in the sequence given, or randomly according to interest, because each example is a complete, self-contained, executable BSV program.
Note: the focus here is on language concepts only, using which users can improve the level of abstraction at which they think and design. These examples are neutral with respect to quality of hardware, that is, they are not intended necessarily to improve one's skills at designing good microarchitectures (which takes training and experience, no matter what the medium of expression). Instead, the purpose of these examples is to improve one's skills at expressing a given microarchitecture more quickly, and in a style that is more abstract, clean, succinct, reusable, maintainable, and obviously correct. Of course, with improved skill in expression and thinking more abstractly, one may also improve one's microarchitecture design skills.
Instead of browsing this on the Web, you can also download SmallExamples.tar.gz, which is a complete set including this page and all the source files, Makefiles etc. (you will need a BSV license to actually execute the examples). It untars into a directory tree with 'SmallExamples' as the (relative) root directory. This page is the file 'index.html', and the hyperlinks point at the files in the subdirectories.
The Examples
"Hello World!"
A first example, just to practice building and running a program, and observing the generated Verilog. Contains comments, package, module, Empty interface, (* synthesize *) attribute, one rule, $finish(). Source code (with commentary): Tb.bsv
Another simple example: a testbench, a DUT module, an interface, all in one source file. Source code (with commentary): Tb.bsv
The same, except that the DUT and its interface are separated into a separate package and source file. Source code (with commentary): Tb.bsv, DeepThought.bsv
Rules, registers, and FIFOs
One register of type Reg#(int), a rule that updates it. Showing: local variable definition, non-blocking assignment, old and new values. Source code (with commentary): Tb.bsv
Two registers, and rules that update them. Showing:
Source code (with commentary): Tb.bsv
Composition of multiple Actions into a single atomic action (parallel composition)
vs.
Composition of multiple rules, i.e., multiple atomic actions-- (urgency).Multiple registers in a rigid synchronous pipeline. Use of 'let'. Source code (with commentary): Tb.bsv
Multiple registers in a rigid synchronous pipeline (same example), except reusing the Reg#() interface for the DUT and therefore reusing the syntax shorthands for register reads and writes when reading and writing to the DUT. Source code (with commentary): Tb.bsv
Multiple registers in a rigid synchronous pipeline (same example), now using Valid bits to deal with pipeline bubbles. Source code (with commentary): Tb.bsv
Elastic "asynchronous" pipeline using FIFOs. Source code (with commentary): Tb.bsv
Module hierarchy and interfaces
A simple module hierarchy with multiple levels and instances; static module parameters; static elaboration; scopes Source code (with commentary): Tb.bsv
Methods with implicit conditions. Source code (with commentary): Tb.bsv
ActionValue method: ‘<-’ notation to invoke and get its value. Source code (with commentary): Tb.bsv
ActionValue method: ‘<-’ notation to invoke and get its value. Same as previous example, except with a deliberate error in using ‘=’ instead of ‘<-’ to demonstrate the type error message. Source code (with commentary): Tb.bsv
ActionValue method: deliberate error (in type-checking) on attempt to use an ActionValue method in a rule condition. Source code (with commentary): Tb.bsv
Actions: defining an Action function, and invoking it from multiple rules. Source code (with commentary): Tb.bsv
Nested interfaces; definition of methods and subinterfaces directly, and by assignment. Source code (with commentary): Tb.bsv
Standard “TLM” interfaces Get, Put; standard mkConnection; standard interface transformers fifoToGet(), fifoToPut(). Source code (with commentary): Tb.bsv
RWires
Up-down counter with simultaneous up/down commands. Simple RWire example; Maybe types. Source code (with commentary): Tb.bsv
Same example (up-down counter) except using pattern-matching to query the Maybe types. Source code (with commentary): Tb.bsv
Same example (up-down counter) showing use of Wires instead of RWires. Source code (with commentary): Tb.bsv
Same example (up-down counter) showing use of DWires instead of RWires or Wires. Source code (with commentary): Tb.bsv
Variant of last example (up-down counter) where the decrement is by a constant (1), showing use of a PulseWire instead of an RWire. Source code (with commentary): Tb.bsv
Same as example 4d (up/down counter) showing use of a mkBypassWire constructor instead of mkDWire. Source code (with commentary): Tb.bsv
An example illustrating an "atomicity paradox" arising due to the introduction of RWires. Source code (with commentary): Tb.bsv
Scheduling
Scheduling error due to bad parallel composition (methods in same rule/method). Source code (with commentary): Tb.bsv
Attributes on rules: simple prioritization using 'descending_urgency'. Source code (with commentary): Tb.bsv
Attributes on rules: showing the difference between 'descending_urgency' and 'execution_order'. Source code (with commentary): Tb.bsv
Attributes on rules: mutually_exclusive. Source code (with commentary): Tb.bsv
Attributes on rules: conflict_free. Source code (with commentary): Tb.bsv
Attributes on rules: preempts. Source code (with commentary): Tb.bsv
Attributes on methods: always_ready, always_enabled. Source code (with commentary): Tb.bsv, PipelinedSyncROM_model.bsv, mem_init.data
Attributes on rules: fire_when_enabled, no_implicit_conditions. Source code (with commentary): Tb.bsv
Conditionals in rules, and effect on scheduling: '-aggressive-conditions'. Source code (with commentary): Tb.bsv
Conditionals in rules, and effect on scheduling: '-split-if' Source code (with commentary): Tb.bsv
Effect of scheduling (possible atomicity inequivalence) of separating an ActionValue method into Action and Value methods. Source code (with commentary): Tb.bsv
Effect on scheduling when using mkConfigReg instead of mkReg. Source code (with commentary): Tb.bsv
Variables, assignments and combinational circuits
Variable declaration and initialization, including:
- declaring a variable with its type, and initializing it
- declaring and initializing a variable using 'let' (inferring its type)
- variable declaration and initialization with '='
- interface variable declaration and initialization with '<-' (module instantiation)
- variable declaration and initialization with '<-' (ActionValue invocation)
Variable assignments describe combinational circuits, and not the updating of storage. Source code (with commentary): Tb.bsv
For-loops/While-loops (static elaboration) - showing what goes wrong if we use a dynamic value in loop termination. Source code (with commentary): Tb.bsv
if-then-else hardware (mux) vs. if-then-else static elaboration. Source code (with commentary): Tb.bsv
Expressions
Don't care expression (and difference from 'x' and 'z') - Improving hardware quality by using don't-cares Tb.bsv
if-then-else expressions (contrast with if-then-else statements)
case expressions (contrast with case statements) Tb.bsv
Simple function as an abstraction of an expression (combo circuit) Tb.bsv
Arrays
Arrays and square-bracket notation Tb.bsv
Arrays vs Vectors Tb.bsv
Bit-vectors and square-bracket notation Tb.bsv
Meaning of bit-range update of registers (whole register update) Tb.bsv
Array of registers Tb.bsv
Array of modules Tb.bsv
Static elaboration array vs. run-time value array Tb.bsv
Vector of subinterfaces Tb.bsv
Types
Simple Processor model, illustrating the use of enums, tagged unions, structs, arrays of registers, and pattern-matching. Pseudo-functions SizeOf#() and valueof(). Source code (with commentary): Tb.bsv, SimpleProcessor.bsv
Simple Processor model, illustrating the use of enums, tagged unions, structs, arrays of registers, and pattern-matching. Variant of example 9a demonstrating custom bit-encoding of processor instructions i.e., explicit 'instance Bits#()' declaration, defining explicit 'pack()' and 'unpack()' functions. Source code (with commentary): Tb.bsv SimpleProcessor.bsv
Integer type: unbounded, non-synthesizable Tb.bsv
Using abstract types (Bool, UInt#(), Int#()) instead of Bit#() Tb.bsv
String Tb.bsv
Enum with explicit value specification Tb.bsv
Struct of registers vs. register of struct Tb.bsv
Tuples Tb.bsv
Type synonyms vs. new typedefs
Using typed constants instead of `define
Size types - Size provisos - Using size types in value expressions (valueOf)
Static type-assertions when the compiler does not have enough type info (e.g., dynamic bit slice)
Use of the 'numeric' keyword to force type variables to be of numeric kind.
Static Elaboration
Instantiating an array of modules using a for-loop - Static expressions in for-loop conditions
Instantiating alternative modules using if-then-else - Static expressions in if-then-else conditions
Recursive instantiation
Use of the 'parameter' keyword in module parameters.
Using more than one interface parameter in module interface list.
Polymorphism
FSMs
FSM by explicitly using rules. Source code (with commentary): Tb.bsv
One-hot FSM by explicitly using rules. Source code (with commentary): Tb.bsv
StmtFSM: seq, par, if, while, repeat, etc. A template showing the basic components of the StmtFSM sublanguage: Tb1.bsv. Source code (with commentary): Tb.bsv
AutoFSM - start an FSM on reset. Source code (with commentary): Tb.bsv
Once - run a FSM only once. Source code (with commentary): Tb.bsv
Building your own FSM generator: a JTAG state machine Source code (with commentary): Tb.bsv, tap.bsv
Building your own FSM generator: FSM_from_list. Shows how to use the 'generate' facilities in BSV to create your own FSM generator. The FSM is specified by a list of 3-tuples (current-state, action, next-state). Since these components can be arbitrary expressions, in particular the next-state component, one can express arbitry control structures including unconditional and conditional jumps, loops, and so on. Source code (with commentary): Tb.bsv, FSMfromList.bsv
Synthesis boundaries, and controlling the generated Verilog
Where can we place a synthesis boundary, and why. Variant showing what can go wrong
Synthesizable wrapper for a polymorphic module
How a synthesis boundary can introduce resource (and therefore scheduling) constraints
Verilog port naming control
always_enabled, always_ready: effect on port list
Passing documentation thru to Verilog
noinline
Debugging by dumping information
VCD dumping
$display/$print
Probe
Using $format with Probe to get symbolic names in VCDs
Schdule dumps and interpretation
Using CAN_FIRE, WILL_FIRE
Specifying names for RTL ports
Clocks, Resets, Multiple Clock Domains
See lots of examples in the Reference Guide.
Importing Verilog
Simple example with default clock and reset
Simple example with non-default clock and reset
Importing C
Simple example using small scalar types
Simple example using non-scalar types (vector, struct)
Communicating a pointer to a dynamically allocated C object through BSV, by plumbing it as an opaque type from one BDPI method into another BDPI method.
Embedding in SystemC
Simple example with wire interfaces
Simple example with TLM interfaces
Advanced Types
Tagged Unions and pattern-matching: Maybe#() instead of explicit Valid bits (same example as 4b). Source code (with commentary): Tb.bsv
Tagged Unions and Pattern-Matching. See Examples 9a and 9b
Pattern-matching: '&&&' and difference from '&&'
Overloading: New typeclass, instance
Class Literal: example of fromInteger
Class Bits: explicit pack() and unpack() compared to "deriving". See example 9b.
Class Arith and Eq: extend the ops to fixpoint types
Class Connectable, extending mkConnection to a new pair of types
Provisos
Class Ord, Bounded
Higher-order programming
Higher-order function
map, zip, fold, scan, ... on Vectors
Action and ActionValue as first-class types
Rule as first-class type, joinRules
Currying
asReg, to supress the implicit ._read or ._write
Utilities
Random number generation: LFSR
Generating random objects of some data type (e.g., packets) using overloaded generators
Out-of-order processing: CompletionBuffer
Unique
ModuleCollect
Encapsulating tri-state buffers: ZBus
Configuration registers: CBus. Source code (with commentary): Tb.bsv, Block.bsv, CfgDefines.bsv
Config bus library file to be released: CBus.bsv
Tools
Detailed Makefile example - bsv.mk, with Bluesim, Verilog gen, Verilog sims (Modelsim/NCSim/VCS) - Development Workstation - expandPorts
Debug Workstation example usage
Compile-time errors and how to resolve them
TBD
Using major IP blocks (these may not be small examples)
"impedance matching" at a module boundary - unguarded fifos, interface transformater functions from fifos to bus-socket interfaces, removing RDY and ENA wires. Commentary. Source code: Tb.bsv, Bus_socket_ifc.bsv, EdgeFIFOs.bsv
AXI, AHB, OCP: see Azure IP documentation