Small Examples Illustrating Bluespec SystemVerilog (BSV) Language Concepts

Copyright © 2008 Bluespec, Inc, All Rights Reserved

Document Revision: May 20, 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

  1. "Hello World!"

    1. 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

    2. Another simple example: a testbench, a DUT module, an interface, all in one source file. Source code (with commentary): Tb.bsv

    3. 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

  2. Rules, registers, and FIFOs

    1. 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

    2. Two registers, and rules that update them. Showing:
      Composition of multiple Actions into a single atomic action (parallel composition)
      vs.
      Composition of multiple rules, i.e., multiple atomic actions-- (urgency).

      Source code (with commentary): Tb.bsv
    3. Multiple registers in a rigid synchronous pipeline. Use of 'let'. Source code (with commentary): Tb.bsv

    4. 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

    5. Multiple registers in a rigid synchronous pipeline (same example), now using Valid bits to deal with pipeline bubbles. Source code (with commentary): Tb.bsv

    6. Elastic "asynchronous" pipeline using FIFOs. Source code (with commentary): Tb.bsv

  3. Module hierarchy and interfaces

    1. A simple module hierarchy with multiple levels and instances; static module parameters; static elaboration; scopes Source code (with commentary): Tb.bsv

    2. Methods with implicit conditions. Source code (with commentary): Tb.bsv

    3. ActionValue method: ‘<-’ notation to invoke and get its value. Source code (with commentary): Tb.bsv

    4. 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

    5. ActionValue method: deliberate error (in type-checking) on attempt to use an ActionValue method in a rule condition. Source code (with commentary): Tb.bsv

    6. Actions: defining an Action function, and invoking it from multiple rules. Source code (with commentary): Tb.bsv

    7. Nested interfaces; definition of methods and subinterfaces directly, and by assignment. Source code (with commentary): Tb.bsv

    8. Standard “TLM” interfaces Get, Put; standard mkConnection; standard interface transformers fifoToGet(), fifoToPut(). Source code (with commentary): Tb.bsv

    9. Simplified mkConnection example... Source code (with commentary): Tb.bsv

  4. RWires

    1. Up-down counter with simultaneous up/down commands. Simple RWire example; Maybe types. Source code (with commentary): Tb.bsv

    2. Same example (up-down counter) except using pattern-matching to query the Maybe types. Source code (with commentary): Tb.bsv

    3. Same example (up-down counter) showing use of Wires instead of RWires. Source code (with commentary): Tb.bsv

    4. Same example (up-down counter) showing use of DWires instead of RWires or Wires. Source code (with commentary): Tb.bsv

    5. 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

    6. Same as example 4d (up/down counter) showing use of a mkBypassWire constructor instead of mkDWire. Source code (with commentary): Tb.bsv

    7. An example illustrating an "atomicity paradox" arising due to the introduction of RWires. Source code (with commentary): Tb.bsv

  5. Scheduling

    1. Scheduling error due to bad parallel composition (methods in same rule/method). Source code (with commentary): Tb.bsv

    2. Attributes on rules: simple prioritization using 'descending_urgency'. Source code (with commentary): Tb.bsv

    3. Attributes on rules: showing the difference between 'descending_urgency' and 'execution_order'. Source code (with commentary): Tb.bsv

    4. Attributes on rules: mutually_exclusive. Source code (with commentary): Tb.bsv

    5. Attributes on rules: conflict_free. Source code (with commentary): Tb.bsv

    6. Attributes on rules: preempts. Source code (with commentary): Tb.bsv

    7. Attributes on methods: always_ready, always_enabled. Source code (with commentary): Tb.bsv, PipelinedSyncROM_model.bsv, mem_init.data

    8. Attributes on rules: fire_when_enabled, no_implicit_conditions. Source code (with commentary): Tb.bsv

    9. Conditionals in rules, and effect on scheduling: '-aggressive-conditions'. Source code (with commentary): Tb.bsv

    10. Conditionals in rules, and effect on scheduling: '-split-if' Source code (with commentary): Tb.bsv

    11. Effect of scheduling (possible atomicity inequivalence) of separating an ActionValue method into Action and Value methods. Source code (with commentary): Tb.bsv

    12. Effect on scheduling when using mkConfigReg instead of mkReg. Source code (with commentary): Tb.bsv

  6. Variables, assignments and combinational circuits

    1. 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)
      Source code (with commentary): Tb.bsv
    2. Variable assignments describe combinational circuits, and not the updating of storage. Source code (with commentary): Tb.bsv

    3. 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

    4. if-then-else hardware (mux) vs. if-then-else static elaboration. Source code (with commentary): Tb.bsv

  7. Expressions

    1. Don't care expression (and difference from 'x' and 'z') - Improving hardware quality by using don't-cares Tb.bsv

    2. if-then-else expressions (contrast with if-then-else statements)

    3. case expressions (contrast with case statements) Tb.bsv

    4. Simple function as an abstraction of an expression (combo circuit) Tb.bsv

  8. Arrays

    1. Arrays and square-bracket notation Tb.bsv

    2. Arrays vs Vectors Tb.bsv

    3. Bit-vectors and square-bracket notation Tb.bsv

    4. Meaning of bit-range update of registers (whole register update) Tb.bsv

    5. Array of registers Tb.bsv

    6. Array of modules Tb.bsv

    7. Static elaboration array vs. run-time value array Tb.bsv

    8. Vector of subinterfaces Tb.bsv

  9. Types

    1. 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

    2. 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

    3. Integer type: unbounded, non-synthesizable Tb.bsv

    4. Using abstract types (Bool, UInt#(), Int#()) instead of Bit#() Tb.bsv

    5. String Tb.bsv

    6. Enum with explicit value specification Tb.bsv

    7. Struct of registers vs. register of struct Tb.bsv

    8. Tuples Tb.bsv

    9. Type synonyms vs. new typedefs

    10. Using typed constants instead of `define

    11. Size types - Size provisos - Using size types in value expressions (valueOf)

    12. Static type-assertions when the compiler does not have enough type info (e.g., dynamic bit slice)

    13. Use of the 'numeric' keyword to force type variables to be of numeric kind.

  10. Static Elaboration

    1. Instantiating an array of modules using a for-loop - Static expressions in for-loop conditions

    2. Instantiating alternative modules using if-then-else - Static expressions in if-then-else conditions

    3. Recursive instantiation

    4. Use of the 'parameter' keyword in module parameters.

    5. Using more than one interface parameter in module interface list.

  11. Polymorphism

    1. Polymorphic function with and without provisos Tb.bsv

    2. Simple Polymorphic module with provisos Tb.bsv

    3. More complex polymorphic modules with provisos Tb.bsv

  12. FSMs

    1. FSM by explicitly using rules. Source code (with commentary): Tb.bsv

    2. One-hot FSM by explicitly using rules. Source code (with commentary): Tb.bsv

    3. 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

    4. AutoFSM - start an FSM on reset. Source code (with commentary): Tb.bsv

    5. Once - run a FSM only once. Source code (with commentary): Tb.bsv

    6. Building your own FSM generator: a JTAG state machine Source code (with commentary): Tb.bsv, tap.bsv

    7. 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

  13. Synthesis boundaries, and controlling the generated Verilog

    1. Where can we place a synthesis boundary, and why. Variant showing what can go wrong

    2. Synthesizable wrapper for a polymorphic module

    3. How a synthesis boundary can introduce resource (and therefore scheduling) constraints

    4. Verilog port naming control

    5. always_enabled, always_ready: effect on port list

    6. Passing documentation thru to Verilog

    7. noinline

  14. Debugging by dumping information

    1. VCD dumping

    2. $display/$print

    3. Probe

    4. Using $format with Probe to get symbolic names in VCDs

    5. Schdule dumps and interpretation

    6. Using CAN_FIRE, WILL_FIRE

    7. Specifying names for RTL ports

  15. Clocks, Resets, Multiple Clock Domains

    1. See lots of examples in the Reference Guide.

  16. Importing Verilog

    1. Simple example with default clock and reset

    2. Simple example with non-default clock and reset

  17. Importing C

    1. Simple example using small scalar types

    2. Simple example using non-scalar types (vector, struct)

    3. 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.

  18. Embedding in SystemC

    1. Simple example with wire interfaces

    2. Simple example with TLM interfaces

  19. Advanced Types

    1. Tagged Unions and pattern-matching: Maybe#() instead of explicit Valid bits (same example as 4b). Source code (with commentary): Tb.bsv

    2. Tagged Unions and Pattern-Matching. See Examples 9a and 9b

    3. Pattern-matching: '&&&' and difference from '&&'

    4. Overloading: New typeclass, instance

    5. Class Literal: example of fromInteger

    6. Class Bits: explicit pack() and unpack() compared to "deriving". See example 9b.

    7. Class Arith and Eq: extend the ops to fixpoint types

    8. Class Connectable, extending mkConnection to a new pair of types

    9. Provisos

    10. Class Ord, Bounded

  20. Higher-order programming

    1. Higher-order function

    2. map, zip, fold, scan, ... on Vectors

    3. Action and ActionValue as first-class types

    4. Rule as first-class type, joinRules

    5. Currying

    6. asReg, to supress the implicit ._read or ._write

  21. Utilities

    1. Random number generation: LFSR

    2. Generating random objects of some data type (e.g., packets) using overloaded generators

    3. Out-of-order processing: CompletionBuffer

    4. Unique

    5. ModuleCollect

    6. Encapsulating tri-state buffers: ZBus

    7. Configuration registers: CBus. Source code (with commentary): Tb.bsv, Block.bsv, CfgDefines.bsv
      Config bus library file to be released: CBus.bsv

  22. Tools

    1. Detailed Makefile example - bsv.mk, with Bluesim, Verilog gen, Verilog sims (Modelsim/NCSim/VCS) - blueview - expandPorts

    2. Debug Workstation example usage

  23. Compile-time errors and how to resolve them

    1. TBD

  24. Using major IP blocks (these may not be small examples)

    1. "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

    2. AXI, AHB, OCP: see Azure IP documentation