Reference-Guide Bluespec
Reference-Guide Bluespec
Reference Guide
1
Reference Guide Bluespec SystemVerilog
Contents
Table of Contents 3
1 Introduction 12
1.1 Meta notation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
2 Lexical elements 12
2.1 Whitespace and comments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.2 Identifiers and keywords . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.3 Integer literals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.3.1 Type conversion of integer literals . . . . . . . . . . . . . . . . . . . . . . . . 14
2.4 Real literals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.5 String literals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.6 Don’t-care values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.7 Compiler directives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.7.1 File inclusion: ‘include and ‘line . . . . . . . . . . . . . . . . . . . . . . . . . 15
2.7.2 Macro definition and substitution: ‘define and related directives . . . . . . . 16
2.7.3 Conditional compilation: ‘ifdef and related directives . . . . . . . . . . . . . . 17
4 Types 20
4.1 Polymorphism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
4.2 Provisos (brief intro) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
4.2.1 The pseudo-function valueof (or valueOf) . . . . . . . . . . . . . . . . . . . . 22
4.3 A brief introduction to deriving clauses . . . . . . . . . . . . . . . . . . . . . . . . . 23
9 Expressions 63
9.1 Don’t-care expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
9.2 Conditional expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
9.3 Unary and binary operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
9.4 Bit concatenation and selection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
9.5 Begin-end expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
9.6 Actions and action blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
9.7 Actionvalue blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
9.8 Function calls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
9.9 Method calls . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
9.10 Static type assertions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
9.11 Struct and union expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
9.11.1 Struct expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
9.11.2 Struct member selection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
9.11.3 Tagged union expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
9.11.4 Tagged union member selection . . . . . . . . . . . . . . . . . . . . . . . . . . 73
9.12 Interface expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
9.12.1 Differences between interfaces and structs . . . . . . . . . . . . . . . . . . . . 75
9.13 Rule expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
10 Pattern matching 77
10.1 Case statements with pattern matching . . . . . . . . . . . . . . . . . . . . . . . . . 79
10.2 Case expressions with pattern matching . . . . . . . . . . . . . . . . . . . . . . . . . 80
10.3 Pattern matching in if statements and other contexts . . . . . . . . . . . . . . . . . . 81
10.4 Pattern matching assignment statements . . . . . . . . . . . . . . . . . . . . . . . . . 82
12 Important primitives 83
12.1 The types bit and Bit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
12.1.1 Bit-width compatibility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
12.2 UInt, Int, int and Integer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
12.3 String . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
12.4 Tuples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
12.5 Registers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
12.6 FIFOs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
12.7 FIFOFs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
12.8 System tasks and functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
A Keywords 141
Index 376
1 Introduction
Bluespec SystemVerilog (BSV) is aimed at hardware designers who are using or expect to use
Verilog [IEE01], VHDL [IEE02], or SystemVerilog [Acc04] to design ASICs or FPGAs. BSV is
based on a synthesizable subset of SystemVerilog, including SystemVerilog types, modules, module
instantiation, interfaces, interface instantiation, parameterization, static elaboration, and “generate”
elaboration. BSV can significantly improve the hardware designer’s productivity with some key
innovations:
• It expresses synthesizable behavior with Rules instead of synchronous always blocks. Rules
are powerful concepts for achieving correct concurrency and eliminating race conditions. Each
rule can be viewed as a declarative assertion expressing a potential atomic state transition.
Although rules are expressed in a modular fashion, a rule may span multiple modules, i.e., it
can test and affect the state in multiple modules. Rules need not be disjoint, i.e., two rules
can read and write common state elements. The BSV compiler produces efficient RTL code
that manages all the potential interactions between rules by inserting appropriate arbitration
and scheduling logic, logic that would otherwise have to be designed and coded manually. The
atomicity of rules gives a scalable way to avoid unwanted concurrency (races) in large designs.
• It enables more powerful generate-like elaboration. This is made possible because in BSV,
actions, rules, modules, interfaces and functions are all first-class objects. BSV also has more
general type parameterization (polymorphism). These enable the designer to “compute with
design fragments,” i.e., to reuse designs and to glue them together in much more flexible ways.
This leads to much greater succinctness and correctness.
• It provides formal semantics, enabling formal verification and formal design-by-refinement.
BSV rules are based on Term Rewriting Systems, a clean formalism supported by decades
of theoretical research in the computer science community [Ter03]. This, together with a
judicious choice of a design subset of SystemVerilog, makes programs in BSV amenable to
formal reasoning.
This manual is meant to be a stand-alone reference for BSV, i.e., it fully describes the subset of
Verilog and SystemVerilog used in BSV. It is not intended to be a tutorial for the beginner. A reader
with a working knowledge of Verilog 1995 or Verilog 2001 should be able to read this manual easily.
Prior knowledge of SystemVerilog is not required.
2 Lexical elements
BSV has the same basic lexical elements as Verilog.
Spaces, tabs, newlines, formfeeds, and carriage returns all constitute whitespace. They may be used
freely between all lexical tokens.
A comment is treated as whitespace (it can only occur between, and never within, any lexical token).
A one-line comment starts with // and ends with a newline. A block comment begins with /* and
ends with */ and may span any number of lines.
Comments do not nest. In a one-line comment, the character sequences //, /* and */ have no special
significance. In a block comment, the character sequences // and /* have no special significance.
An identifier in BSV consists of any sequence of letters, digits, dollar signs $ and underscore char-
acters (_). Identifiers are case-sensitive: glurph, gluRph and Glurph are three distinct identifiers.
The first character cannot be a digit.
BSV currently requires a certain capitalization convention for the first letter in an identifier. Identi-
fiers used for package names, type names, enumeration labels, union members and type classes must
begin with a capital letter. In the syntax, we use the non-terminal Identifier to refer to these. Other
identifiers (including names of variables, modules, interfaces, etc.) must begin with a lowercase letter
and, in the syntax, we use the non-terminal identifier to refer to these.
As in Verilog, identifiers whose first character is $ are reserved for so-called system tasks and functions
(see Section 12.8).
If the first character of an instance name is an underscore, (_), the compiler will not generate
this instance in the Verilog hierarchy name. This can be useful for removing submodules from the
hierarchical naming.
There are a number of keywords that are essentially reserved identifiers, i.e., they cannot be used by
the programmer as identifiers. Keywords generally do not use uppercase letters (the only exception
is the keyword valueOf). BSV includes all keywords in SystemVerilog. All keywords are listed in
Appendix A.
The types Action and ActionValue are special, and cannot be redefined.
Integer literals are written with the usual Verilog and C notations:
intLiteral ::= ’0 | ’1 | decLiteral | hexLiteral | octLiteral | binLiteral
decLiteral ::= [ - ] decDigits
| [ bitWidth ] ( ’d | ’D ) decDigits
hexLiteral ::= [ bitWidth ] ( ’h | ’H ) hexDigits
octLiteral ::= [ bitWidth ] ( ’o | ’O ) octDigits
binLiteral ::= [ bitWidth ] ( ’b | ’B ) binDigits
bitWidth ::= decDigits
decDigits ::= 1 or more consecutive characters from the set 0...9
hexDigits ::= 1 or more consecutive characters from the sets 0...9, a...f, A...F
octDigits ::= 1 or more consecutive characters from the set 0...7
binDigits ::= 1 or more consecutive characters from the set 0...1
With the exception of plain decimal literals (that have neither a bit width or a base), there is no
leading + or - in the syntax for integer literals. Instead, we provide unary prefix + or - operators
that can be used in front of any integer expression, including literals (see Section 9). An optional -
is part of the syntax for plain decimal literals so that it is possible to construct negative constants
whose negation is not in the range of the type being constructed (e.g. Int#(4) x = -8; since 8 is
not a valid Int#(4), but -8 is).
Examples:
125
-16
’h48454a
32’h48454a
8’o255
12’b101010
Integer literals can be used to specify values for various integer types and even for user-defined
types. BSV uses its systematic overloading resolution mechanism to perform these type conversions.
Overloading resolution is described in more detail in Section 14.1.
In an integer literal, if a specific width w is given (e.g., 8’o255), the literal is assumed to have type
bit [w − 1:0]. The compiler implicitly applies the overloaded function unpack to the literal to
convert it to the type required by the context. Thus, sized literals can be used for any type on which
the overloaded function unpack is defined, i.e., for any type in the Bits type class.
If a specific width is not given, the literal is assumed to have type Integer. The compiler implicitly
applies the overloaded function fromInteger to the literal to convert it to the type required by
the context. Thus, unsized literals can be used for any type on which the overloaded function
fromInteger is defined.
The literal ’0 just stands for 0. The literal ’1 stands for a value in which all bits are 1 (the width
depends on the context).
Support for real (Verilog 2001) and shortreal (SystemVerilog) will be added to BSV in the future.
String literals are written enclosed in double quotes "· · ·" and must be contained on a single source
line.
stringLiteral ::= " · · · string characters · · · "
Special characters may be inserted in string literals with the following backslash escape sequences:
\n newline
\t tab
\\ backslash
\" double quote
\v vertical tab
\f form feed
\a bell
\OOO exactly 3 octal digits (8-bit character code)
\xHH exactly 2 hexadecimal digits (8-bit character code)
A lone question mark ? is treated as a special don’t-care value. For example, one may return ?
from an arm of a case statement that is known to be unreachable.
Example - Using ? as a don’t care value
module mkExample (Empty);
Reg#(Bit#(8)) r <- mkReg(?); // don’t care is used for the
rule every; // reset value of the Reg
$display("value is %h", r); // the value of r is displayed
endrule
endmodule
The following compiler directives permit file inclusion, macro definition and substitution, and condi-
tional compilation. They follow the specifications given in the Verilog 2001 LRM plus the extensions
given in the SystemVerilog 3.1a LRM.
In general, these compiler directives can appear anywhere in the source text. In particular, they do
not need to be on lines by themselves, and they need not begin in the first column. Of course, they
should not be inside strings or comments, where the text remains uninterpreted.
it to regard the next line onwards as coming from the given source file and line number. It is
generally not necessary to use this directive explicitly; it is mainly intended to be generated by other
preprocessors that may themselves need to alter the source files before passing them through the
BSV compiler; this mechanism allows proper references to the original source.
The level specifier is either 0, 1 or 2:
• ‘" Indicates that a double-quote (") should be placed in the expanded text.
• ‘\‘" Indicates that a backslash and a double-quote (\") should be placed in the expanded
text.
• ‘‘ Indicates that there should be no whitespace between the preceding and following
text. This allows construction of identifiers from the macro arguments.
A minimal amount of lexical analysis of macroText is done to identify comments, string literals,
identifiers representing macro formals, and macro invocations. As described earlier, one-line com-
ments are removed. The text inside string literals is not interpreted except for the usual string
escape sequences described in Section 2.5.
There are two define macros in the define environment initially; ‘bluespec and ‘BLUESPEC.
Once defined, a macro can be invoked anywhere in the source text (including within other macro
definitions) using the following syntax.
The macroName must refer to a macro definition available at expansion time. The macroActuals,
if present, consist of substitution text substText that is arbitrary text, possibly spread over multiple
lines, excluding commas. A minimal amount of parsing of this substitution text is done, so that
commas that are not at the top level are not interpreted as the commas separating macroActuals.
Examples of such “inner” uninterpreted commas are those within strings and within comments.
The ‘undef directive’s effect is that the specified macro (with or without formal arguments) is no
longer defined for the subsequent source text. Of course, it can be defined again with ‘define in the
subsequent text. The ‘resetall directive has the effect of undefining all currently defined macros,
i.e., there are no macros defined in the subsequent source text.
These directives are used together in either an ‘ifdef-endif sequence or an ifndef-endif sequence.
In either case, the sequence can contain zero or more elsif directives followed by zero or one else
directives. These sequences can be nested, i.e., each ‘ifdef or ifndef introduces a new, nested
sequence until a corresponding endif.
In an ‘ifdef sequence, if the macroName is currently defined, the subsequent text is processed until
the next corresponding elsif, else or endif. All text from that next corresponding elsif or else
is ignored until the endif.
If the macroName is currently not defined, the subsequent text is ignored until the next corresponding
‘elsif, ‘else or ‘endif. If the next corresponding directive is an ‘elsif, it is treated just as if it
were an ‘ifdef at that point.
If the ‘ifdef and all its corresponding ‘elsifs fail (macros were not defined), and there is an ‘else
present, then the text between the ‘else and ‘endif is processed.
An ‘ifndef sequence is just like an ‘ifdef sequence, except that the sense of the first test is
inverted, i.e., its following text is processes if the macroName is not defined, and its ‘elsif and
‘else arms are considered only if the macro is defined.
‘ifdef USE_16_BITS
Reg#(Bit#(16)) a_reg <- mkReg(0);
‘else
Reg#(Bit#(8)) a_reg <- mkReg(0);
‘endif
type, but not its members, are visible outside the package. This is a way to define abstract data
types, i.e., types whose internal structure is hidden.
Each import item specifies a package from which to import identifiers, i.e., to make them visible
locally within this package. For each imported package, all identifiers exported from that package
are made locally visible.
Example:
package Foo;
export x;
export y;
import Bar::*;
endpackage: Foo
Here, Foo is the name of this package. The identifiers x and y, which must be defined by the top-level
definitions in this package are names exported from this package. From package Bar we import all
its definitions.
BSV uses standard static scoping (also known as lexical scoping). Many constructs introduce new
scopes nested inside their surrounding scopes. Identifiers can be declared inside nested scopes. Any
use of an identifier refers to its declaration in the nearest textually surrounding scope. Thus, an
identifier x declared in a nested scope “shadows”, or hides, any declaration of x in surrounding scopes
(however, we recommend that the programmer avoids such shadowing, because it often makes code
more difficult to read.)
Packages form the the outermost scopes. Examples of nested scopes include modules, interfaces,
functions, methods, rules, action and actionvalue blocks, begin-end statements and expressions,
bodies of for and while loops, and seq and par blocks.
When used in any scope, an identifier must have an unambiguous meaning. If there is name clash
for an identifier x because it is defined in the current package and/or it is available from one or more
imported packages, then the ambiguity can be resolved by using a qualified name of the form P :: x
to refer to the version of x contained in package P .
The Standard Prelude is a predefined package that is imported implicitly into every BSV package,
i.e., it does not need an explicit import statement. It contains a number of useful predefined entities
(types, values, functions, modules, etc.). The Standard Prelude package is described in more detail
in appendix B. Reusing the name of Prelude entity when defining other entities, which would require
the entity’s name to be qualified with the package name, is strongly discouraged.
4 Types
Every variable and every expression in BSV has a type. Almost all variables must be declared with
their type.
The syntax of types (type expressions) is given below:
type ::= typePrimary
| typePrimary ( type { , type } ) Function type
typePrimary ::= typeIde [ # ( type { , type } ) ]
| typeNat
| bit [ typeNat : typeNat ]
Type expressions of the form X#(t1 ,· · ·,tN ) are called parameterized types. X is called a type
constructor and the types t1 ,· · ·,tN are the parameters of X. Examples:
Type parameters can be natural numbers (also known as size types). These usually indicate some
aspect of the size of the type, such as a bit-width or a table capacity. Examples:
Currently the second index n in a bit[m:n] type must be 0. The type bit[m:0] represents the
type of bit vectors, with bits indexed from m (msb/left) down through 0 (lsb/right), for m ≥ 0.
4.1 Polymorphism
A type can be polymorphic. This is indicated by using type variables as parameters. Examples:
The type variables represent unknown (but specific) types. In other words, List#(a) represents
the type of a list containing items all of which have some type a. It does not mean that different
elements of a list can have different types.
Provisos are described in detail in Section 14.1.1, and the general facility of type classes (overload-
ing groups), of which provisos form a part, is described in Section 14.1. Here we provide a brief
description, which is adequate for most uses and for continuity in a serial reading of this manual.
A proviso is a static condition attached to certain constructs, to impose certain restrictions on the
types involved in the construct. The restrictions are of two kinds:
• Require instance of a type class (overloading group): this kind of proviso states that certain
types must be instances of certain type classes, i.e., that certain overloaded functions are
defined on this type.
• Require size relationships: this kind of proviso expresses certain constraints between the sizes
of certain types.
Example:
Reg#(a) value_reg <- mkReg(?); // requires that type "a" be in the Bits typeclass.
rule every;
value_reg <= value_reg + 1; // requires that type "a" be in the Arith typeclass.
endrule
Example:
This defines a function pad0101 that takes a bit vector x and pads it to the right with the four bits
“0101” using the standard bit-concatenation notation. The types and proviso express the idea that
the function takes a bit vector of length n and returns a bit vector of length m, where n + 4 = m.
These provisos permit the BSV compiler to statically verify that entities (values, variables, registers,
memories, FIFOs, and so on) have the correct bit-width.
To get the value that corresponds to a size type, there is a special pseudo-function, valueof, that
takes a size type and gives the corresponding Integer value. The pseudo-function is also sometimes
written as valueOf; both are considered correct.
exprPrimary ::= valueof ( type )
| valueOf ( type )
In other words, it converts from a numeric type expression into an ordinary value. These mechanisms
can be used to do arithmetic to derive dependent sizes. Example:
This function takes a vector of length n as an argument. The proviso fixes k to be the (ceiling of
the) logarithm of n. The variable index has bit-width k, which will be adequate to hold an index
into the list. The variable is initialized to the maximum index.
Note that the function foo may be invoked in multiple contexts, each with a different vector length.
The compiler will statically verify that each use is correct (e.g., the index has the correct width).
The pseudo-function valueof, which converts a numeric type to a value, should not be confused
with the pseudo-function SizeOf, described in Section 14.1.5, which converts a type to a numeric
type.
The deriving clause is a part of the general facility of type classes (overloading groups), which is
described in detail in Section 14.1. Here we provide a brief description, which is adequate for most
uses and for continuity in a serial reading of this manual.
It is possible to attach a deriving clause to a type definition (Section 7), thereby directing the
compiler to define automatically certain overloaded functions for that type. The most common
forms of these clauses are:
Example:
In Verilog and SystemVerilog RTL, one simply declares variables, and a synthesis tool “infers” how
these variables actually map into state elements in hardware using, for example, their lifetimes
relative to events. A variable may map into a bus, a latch, a flip-flop, or even nothing at all. This
ambiguity is acknowledged in the Verilog 2001 and SystemVerilog LRMs.1
BSV removes this ambiguity and places control over state instantiation explicitly in the hands of
the designer. From the smallest state elements (such as registers) to the largest (such as memories),
all state instances are specified explicitly using module instantiation.
Conversely, an ordinary declared variable in BSV never implies state, i.e., it never holds a value
over time. Ordinary declared variables are always just convenient names for intermediate values in
a computation. Ordinary declared variables include variables declared in blocks, formal parameters,
pattern variables, loop iterators, and so on. Another way to think about this is that ordinary
variables play a role only in static elaboration, not in the dynamic semantics. This is one of the
aspects of BSV style that may initially appear unusual to the Verilog or SystemVerilog programmer.
Example:
1 In the Verilog 2001 LRM, Section 3.2.2, Variable declarations, says: “A variable is an abstraction of a data storage
element.· · ·NOTE In previous versions of the Verilog standard, the term register was used to encompass both the reg,
integer, time, real and realtime types; but that term is no longer used as a Verilog data type.”
In the SystemVerilog LRM, Section 5.1 says: “Since the keyword reg no longer describes the user’s intent in many
cases,· · ·Verilog-2001 has already deprecated the use of the term register in favor of variable.”
rule pop;
let value = fifo.first(); // value is a ordinary declared variable
// no state is implied or created
value_reg <= fifo.first(); // value_reg is state variable
fifo.deq();
endrule
endmodule
In BSV an interface contains members that are called methods (an interface may also contain subin-
terfaces, which are described in Section 5.2.1). To first order, a method can be regarded exactly
like a function, i.e., it is a procedure that takes zero or more arguments and returns a result. Thus,
method declarations inside interface declarations look just like function prototypes, the only differ-
ence being the use of the keyword method instead of the keyword function. Each method represents
one kind of transaction between a module and its clients. When translated into RTL, each method
becomes a bundle of wires.
The fundamental difference between a method and a function is that a method also carries with it a
so-called implicit condition. These will be described later along with method definitions and rules.
An interface declaration also looks similar to a struct declaration. One can think of an interface
declaration as declaring a new type similar to a struct type (Section 7), where the members all
happen to be method prototypes. A method prototype is essentially the header of a method definition
(Section 5.5).
interfaceDecl ::= [ attributeInstances ]
interface typeDefType ;
{ interfaceMemberDecl }
endinterface [ : typeIde ]
typeDefType ::= typeIde [ typeFormals ]
typeFormals ::= # ( typeFormal { , typeFormal })
typeFormal ::= [ numeric ] type typeIde
interfaceMemberDecl ::= methodProto | subinterfaceDecl
methodProto ::= [ attributeInstances ]
method type identifier ( [ methodProtoFormals ] ) ;
methodProtoFormals ::= methodProtoFormal { , methodProtoFormal }
methodProtoFormal ::= [ attributeInstances ] type identifier
Example: a stack of integers:
interface IntStack;
method Action push (int x);
method Action pop;
method int top;
endinterface: IntStack
This describes an interface to a circuit that implements a stack (LIFO) of integers. The push method
takes an int argument, the item to be pushed onto the stack. Its output type is Action, namely it
returns an enable wire which, when asserted, will carry out the pushing action.2 The pop method
2 The type Action is discussed in more detail in Section 9.6.
takes no arguments, and simply returns an enable wire which, when asserted, will discard the element
from the top of the stack. The top method takes no arguments, and returns a value of type int,
i.e., the element at the top of the stack.
What if the stack is empty? In that state, it should be illegal to use the pop and top methods.
This is exactly where the difference between methods and functions arises. Each method has an
implicit ready wire, which governs when it is legal to use it, and these wires for the pop and top
methods will presumably be de-asserted if the stack is empty. Exactly how this is accomplished is
an internal detail of the module, and is therefore not visible as part of the interface declaration. (We
can similarly discuss the case where the stack has a fixed, finite depth; in this situation, it should
be illegal to use the push method when the stack is full.)
One of the major advantages of BSV is that the compiler automatically generates all the control
circuitry needed to ensure that a method (transaction) is only used when it is legal to use it.
Interface types can be polymorphic, i.e., parameterized by other types. For example, the following
declaration describes an interface for a stack containing an arbitrary but fixed type:
We have replaced the previous specific type int with a type variable a. By “arbitrary but fixed” we
mean that a particular stack will specify a particular type for a, and all items in that stack will have
that type. It does not mean that a particular stack can contain items of different types.
For example, using this more general definition, we can also define the IntStack type as follows:
i.e., we simply specialize the more general type with the particular type int. All items in a stack of
this type will have the int type.
Usually there is information within the interface declaration which indicates whether a polymorphic
interface type is numeric or nonnumeric. The optional numeric is required before the type when
the interface type is polymorphic and must be numeric but there is no information in the interface
declaration which would indicate that the type is numeric.
For example, in the following polymorphic interface, count_size must be numeric because it is
defined as a parameter to Bit#().
From this use, it can be deduced that Counter’s parameter count_size must be numeric. However,
sometimes you might want to encode a size in an interface type which isn’t visible in the methods,
but is used by the module implementing the interface. For instance:
In this interface, the depth of the buffer is encoded in the type. For instance, SizedBuffer#(8,
Bool) would be a buffer of depth 8 with elements of type Bool. The depth is not visible in the
interface, but is used by the module to know how much storage to instantiate.
Because the parameter is not mentioned anywhere else in the interface, there is no information
to determine whether the parameter is a numeric type or a non-numeric type. In this situation,
the default is to assume that the parameter is non-numeric. The user can override this default by
specifying numeric in the interface declaration.
The Standard Prelude defines a standard interface called Empty which contains no methods, i.e., its
definition is:
interface Empty;
endinterface
This is often used for top-level modules that integrate a testbench and a design-under-test, and for
modules like mkConnection(C.6.2) that just take interface arguments and do not themselves offer
any interesting interface.
5.2.1 Subinterfaces
interface ILookup;
interface Server#( RequestType, ResponseType ) mif;
interface RAMclient#( AddrType, DataType ) ram;
method Bool initialized;
endinterface: ILookup
This declares an interface ILookup module that consists of three members: a Server subinterface
called mif, a RAMClient subinterface called ram, and a boolean method called initialized (the
Server and RAMClient interface types are defined in the libraries, see Appendix C). Methods of
subinterfaces are accessed using dot notation to select the desired component, e.g.,
ilookup.mif.request.put(...);
Since Clock and Reset are both interface types, they can be used in interface declarations. Example:
interface ClockTickIfc ;
method Action tick() ;
interface Clock new_clk ;
endinterface
A module definition begins with a module header containing the module keyword, the module name,
parameters, arguments, interface type and provisos. The header is followed by zero or more module
statements. Finally we have the closing endmodule keyword, optionally labelled again with the
module name.
moduleDef ::= moduleProto
{ moduleStmt }
endmodule [ : identifier ]
moduleProto ::= module [ [ type ] ] identifier
[ moduleFormalParams ] ( [ moduleFormalArgs ] ) [ provisos ];
moduleFormalParams ::= # (moduleFormalParam { , moduleFormalParam })
moduleFormalParam ::= [ parameter ] type identifier
moduleFormalArgs ::= type
| type identifier { , type identifier }
As a stylistic convention, many BSV examples use module names like mkFoo, i.e., beginning with
the letters mk, suggesting the word make. This serves as a reminder that a module definition is not
a module instance. When the module is instantiated, one invokes mkFoo to actually create a module
instance.
The optional moduleFormalParams are exactly as in Verilog and SystemVerilog, i.e., they represent
module parameters that must be supplied at each instantiation of this module, and are resolved at
elaboration time. The optional keyword parameter specifies a Verilog parameter is to be generated;
without the keyword a Verilog port is generated. A Verilog parameter requires that the value is a
constant at elaboration. When the module is instantiated, the actual expression provided for the
parameter must be something that can be computed using normal Verilog elaboration rules. The
bluespec compiler will check for this. The parameter keyword is only relevant when the module is
marked with the *synthesize* attribute.
Inside the module, the parameter keyword can be used for a parameter n that is used, for example,
for constants in expressions, register initialization values, and so on. However, n cannot be used
for structural variations in the module, such as declaring an array of n registers. Such structural
decisions (generate decisions) are taken by the Bluespec compiler, and cannot currently be postponed
into the Verilog.
The optional moduleFormalArgs represent the interfaces used by the module, such as clocks or wires.
The final argument is a single interface provided by the module instead of Verilog’s port list. The
interpretation is that this module will define and offer an interface of that type to its clients. If
the only argument is the interface, only the interface type is required. If there are other arguments,
both a type and an identifier must be specified for consistency, but the final interface name will not
be used in the body. Omitting the interface type completely is equivalent to using the pre-defined
Empty interface type, which is a trivial interface containing no methods.
The arguments and parameters may be enclosed in a single set of parentheses, in which case the #
would be omitted.
Provisos, which are optional, come next. These are part of an advanced feature called type classes
(overloading groups), and are discussed in more detail in Section 14.1.
Examples
A module with parameters and an interface.
The above module definition may also be written with the arguments and parameters combined in
a single set of parentheses.
Module instances form a hierarchy. A module definition can contain specifications for instantiating
other modules, and in the process, instantiating their interfaces. A single module definition may be
instantiated multiple times within a module.
//declare the interface instance gcdIFC, instantiate the module mkGCD, set N=5
module mkTest ();
...
ArithIO#(bit [31:0]) gcdIfc <- mkGCD (5, clocked_by dClkIn);
...
endmodule: mkTest
interface Design_IFC;
method Action start(Bit#(3) in_data1, Bit#(3) in_data2, Bool select);
interface Clock clk_out;
method Bit#(4) out_data();
endinterface : Design_IFC
A module instantiation can also be written in its full form on two consecutive lines, as typical
in SystemVerilog. The full form specifies names for both the interface instance and the module
instance. In the shorthand described above, there is no name provided for the module instance
and the compiler infers one based on the interface name. This is often acceptable because module
instance names are only used occasionally in debugging and in hierarchical names.
moduleInst ::= type identifier ( ) ;
moduleApp2 identifier ( [ moduleActualArgs ] ) ;
moduleApp2 ::= identifier [ # ( moduleActualParam { , moduleActualParam } ) ]
moduleActualParam ::= expression
moduleActualArgs ::= moduleActualArg { , moduleActualArg }
moduleActualArg ::= expression
| clocked_by expression
| reset_by expression
The first line declares an identifier with an interface type. The second line actually instantiates
the module and defines the interface. The moduleApp2 is the module (definition) identifier, and
it must be applied to actual parameters (in #(..)) if it had been defined to have parameters.
After the moduleApp, the first identifier names the new module instance. This may be followed
by one or more moduleActualArg which define the arguments being used by the module. The
last identifier (in parentheses) of the moduleActualArg must be the same as the interface identifier
declared immediately above. It may be followed by a clocked_by or reset_by statement.
The following examples show the complete form of the module instantiations of the examples shown
above.
A module definition contains a definition of its interface. Typically this takes the form of a collection
of definitions, one for each method in its interface. Each method definition begins with the keyword
method, followed optionally by the return-type of the method, then the method name, its formal
parameters, and an optional implicit condition. After this comes the method body which is exactly
like a function body. It ends with the keyword endmethod, optionally labelled again with the method
name.
and
The only syntactic difference is the position of the semicolon. In the first case, if (expr) is an
implicit condition on the method. In the second case the method has no implicit condition, and if
(expr) starts a conditional statement inside the method. In the first case, if the expression is false,
any rule that invokes this method cannot fire, i.e., no action in the rule or the rest of this method
is performed. In the second case, the method does not prevent an invoking rule from firing, and if
the rule does fire, the conditional statement is not executed but other actions in the rule and the
method may be performed.
The method body is exactly like a function body, which is discussed in Section 8.8 on function
definitions.
See also Section 9.12 for the more general concepts of interface expressions and expressions as first-
class objects.
Example:
If a method has type Action, then the following shorthand syntax may be used. Section 9.6 describes
action blocks in more detail.
methodDef ::= method Action identifier ( methodFormals ) [ implicitCond ] ;
{ actionStmt }
endmethod [ : identifier ]
i.e., if the type Action is used after the method keyword, then the method body can directly contain
a sequence of actionStmts without the enclosing action and endaction keywords.
Similarly, if a method has type ActionValue(t) (Section 9.7), the following shorthand syntax may
be used:
methodDef ::= method ActionValue #( type ) identifier ( methodFormals )
[ implicitCond ; ]
{ actionValueStmt }
endmethod [ : identifier ]
i.e., if the type ActionValue(t) is used after the method keyword, then the method body can
directly contain a sequence of actionStmts without the enclosing actionvalue and endactionvalue
keywords.
Example: The long form definition of an Action method:
module ...
...
...
interface Server mif;
endinterface: mif
...
endmodule
//in this module, there is an instanciated FIFO, and the Put interface
//of the "mkSameInterface" module is the same interface as the fifo’s:
interface IFC1 ;
interface Put#(int) in0 ;
endinterface
(*synthesize*)
module mkSameInterface (IFC1);
FIFO#(int) myFifo <- mkFIFO;
interface Put in0 = fifoToPut(myFifo);
endmodule
5.7 Examples
It is polymorphic, i.e., it can contain values of any type a. It has two methods. The _write()
method takes an argument x1 of type a and returns an Action, i.e., an enable-wire that, when
asserted, will deposit the value into the register. The _read() method takes no arguments and
returns the value that is in the register.
The principal predefined module definition for a register has the following header:
The module parameter v of type a is specified when instantiating the module (creating the register),
and represents the initial value of the register. The module defines an interface of type Reg #(a).
The proviso specifies that the type a must be convertible into an sa-bit value. Provisos are discussed
in more detail in Sections 4.2 and 14.1.
Here is a module to compute the GCD (greatest common divisor) of two numbers using Euclid’s
algorithm.
module mkGCD(ArithIO#(Bit#(size_t)));
endmodule: mkGCD
The interface type is called ArithIO because it expresses the interactions of modules that do any kind
of two-input, one-output arithmetic. Computing the GCD is just one example of such arithmetic.
We could define other modules with the same interface that do other kinds of arithmetic.
The module contains two rules, flip and sub, which implement Euclid’s algorithm. In other words,
assuming the registers x and y have been initialized with the input values, the rules repeatedly
update the registers with transformed values, terminating when the register y contains zero. At that
point, the rules stop firing, and the GCD result is in register x. Rule flip uses standard Verilog
non-blocking assignments to express an exchange of values between the two registers. As in Verilog,
the symbol <= is used both for non-blocking assignment as well as for the less-than-or-equal operator
(e.g., in rule sub’s explicit condition), and as usual these are disambiguated by context.
The start method takes two arguments num1 and num2 representing the numbers whose GCD is
sought, and loads them into the registers x and y, respectively. The result method returns the
result value from the x register. Both methods have an implicit condition (y == 0) that prevents
them from being used while the module is busy computing a GCD result.
A test bench for this module might look like this:
rule getInputs;
... read next num1 and num2 from file ...
the_gcd.start (num1, num2); // start the GCD computation
endrule
rule putOutput;
$display("Output is %d", the_gcd.result()); // print result
endrule
endmodule: mkTest
The first two lines instantiate a GCD module. The getInputs rule gets the next two inputs from
a file, and then initiates the GCD computation by calling the start method. The putOutput rule
prints the result. Note that because of the semantics of implicit conditions and enabling of rules,
the getInputs rule will not fire until the GCD module is ready to accept input. Similarly, the
putOutput rule will not fire until the output method is ready to deliver a result.3
The mkGCD module is trivial in that the rule conditions ((x > y) and (x <= y)) are mutually
exclusive, so they can never fire together. Nevertheless, since they both write to register y, the
compiler will insert the appropriate multiplexers and multiplexer control logic.
Similarly, the rule getInputs, which calls the start method, can never fire together with the mkGCD
rules because the implicit condition of getInputs, i.e., (y == 0) is mutually exclusive with the
explicit condition (y != 0) in flip and sub. Nevertheless, since getInputs writes into the_gcd’s
registers via the start method, the compiler will insert the appropriate multiplexers and multiplexer
control logic.
In general, many rules may be enabled simultaneously, and subsets of rules that are simultaneously
enabled may both read and write common state. The BSV compiler will insert appropriate schedul-
ing, datapath multiplexing, and control to ensure that when rules fire in parallel, the net state change
is consistent with the atomic semantics of rules.
In order to generate code for a BSV design (for either Verilog or Bluesim), it is necessary to indicate
to the complier which module(s) are to be synthesized. A BSV module that is marked for code
generation is said to be a synthesized module.
In order to be synthesizable, a module must meet the following characteristics:
• The module must be of type Module and not of any other module type that can be defined
with ModuleCollect;
• Its interface must be fully specified; there can be no polymorphic types in the interface;
• Its interface is a type whose methods and subinterfaces are all convertible to wires (see Section
5.8.2).
• All other inputs to the module must be convertible to Bits (see Section 5.8.2).
3 The astute reader will recognize that in this small example, since the result method is initially ready, the test
bench will first output a result of 0 before initiating the first computation. Let us overlook this by imagining that
Euclid is clearing his throat before launching into his discourse.
1. A module can be annotated with the synthesize attribute (see section 13.1.1). The appro-
priate syntax is show below.
(* synthesize *)
module mkFoo (FooIfc);
...
endmodule
2. Alternatively, the -g compiler flag can be used on the bsc command line to indicate which
module is to be synthesized. In order to have the same effect as the attribute syntax shown
above, the flag would be used with the format -g mkFoo (the appropriate module name follows
the -g flag).
Note that multiple modules may be selected for code generation (by using multiple synthesize
attributes, multiple -g compiler flags, or a combination of the two).
Separate synthesis of a module can affect scheduling. This is because input wires to the module, such
as method arguments, now become a fixed resource that must be shared, whereas without separate
synthesis, module inlining allows them to be bypassed (effectively replicated). Consider a module
representing a register file containing 32 registers, with a method read(j) that reads the value of the
j’th register. Inside the module, this just indexes an array of registers. When separately synthesized,
the argument j becomes a 5-bit wide input port, which can only be driven with one value in any
given clock. Thus, two rules that invoke read(3) and read(11), for example, will conflict and then
they cannot fire in the same clock. If, however, the module is not separately synthesized, the module
and the read() method are inlined, and then each rule can directly read its target register, so the
rules can fire together in the same clock. Thus, in general, the addition of a synthesis boundary can
restrict behaviors.
As discussed in section 4.1, BSV supports polymorphic types, including interfaces (which are them-
selves types). Thus, a single BSV module definition, which provides a polymorphic interface, in effect
defines a family of different modules with different characteristics based on the specific parameter(s)
of the polymorphic interface. Consider the module definition presented in section 5.7.
Based on the specific type parameter given to the ArithIO interface, the code required to implement
mkGCD will differ. Since the Bluespec compiler does not create ”parameterized” Verilog, in order for
a module to be synthesizable, the associated interface must be fully specified (i.e not polymorphic).
If the mkGCD module is annotated for code generation as is
(* synthesize *)
module mkGCD (ArithIO#(Bit#(size_t)));
...
endmodule
and we then run the compiler, we get the following error message.
If however we instead re-write the definition of mkGCD such that all the references to the type
parameter size_t are replaced by a specific value, in other words if we write something like,
(* synthesize *)
module mkGCD32 (ArithIO#(Bit#(32)));
...
endmodule
then the compiler will complete successfully and provide code for a 32-bit version of the module
(called mkGCD32). Equivalently, we can leave the code for mkGCD unchanged and instantiate it inside
another synthesized module which fully specifies the provided interface.
(* synthesize *)
module mkGCD32(ArithIO#(Bit#(32)));
let ifc();
mkGCD _temp(ifc);
return (ifc);
endmodule
• An interface is convertible to wires if all methods and subinterfaces are convertible to wires.
• A Vector interface can be synthesized as long as the type inside the Vector is of type Clock,
Reset, Inout or a type which is convertible to bits.
What is a legal BSV source text, and what are its legal behaviors? These questions are addressed by
the static and dynamic semantics of BSV. The BSV compiler checks that the design is legal according
to the static semantics, and produces RTL hardware that exhibits legal behaviors according to the
dynamic semantics.
Conceptually, there are three phases in processing a BSV design, just like in Verilog and SystemVer-
ilog:
• Static checking: this includes syntactic correctness, type checking and proviso checking.
• Static elaboration: actual instantiation of the design and propagation of parameters, producing
the module instance hierarchy.
We refer to the first two as the static phase (i.e., pre-execution), and to the third as the dynamic
phase. Dynamic semantics are about the temporal behavior of the statically elaborated design,
that is, they describe the dynamic execution of rules and methods and their mapping into clocked
synchronous hardware.
A BSV program can also contain assertions; assertion checking can occur in all three phases, de-
pending on the kind of assertion.
The static semantics of BSV are about syntactic correctness, type checking, proviso checking, static
elaboration and static assertion checking. Syntactic correctness of a BSV design is checked by the
parser in the BSV compiler, according to the grammar described throughout this document.
BSV is statically typed, just like Verilog, SystemVerilog, C, C++, and Java. This means the usual
things: every variable and every expression has a type; variables must be assigned values that have
compatible types; actual and formal parameters/arguments must have compatible types, etc. All
this checking is done on the original source code, before any elaboration or execution.
BSV uses SystemVerilog’s new tagged union mechanism instead of the older ordinary unions, thereby
closing off a certain kind of type loophole. BSV also allows more type parameterization (polymor-
phism), without compromising full static type checking.
In BSV, overloading constraints and bit-width constraints are expressed using provisos (Sections 4.2
and 14.1.1). Overloading constraints provide an extensible mechanism for overloading.
BSV is stricter about bit-width constraints than Verilog and SystemVerilog in that it avoids implicit
zero-extension, sign-extension and truncation of bit-vectors. These operations must be performed
consciously by the designer, using library functions, thereby avoiding another source of potential
errors.
As in Verilog and SystemVerilog, static elaboration is the phase in which the design is instantiated,
starting with a top-level module instance, instantiating its immediate children, instantiating their
children, and so on to produce the complete instance hierarchy.
BSV has powerful generate-like facilities for succinctly expressing regular structures in designs. For
example, the structure of a linear pipeline may be expressed using a loop, and the structure of a
tree-structured reduction circuit may be expressed using a recursive function. All these are also
unfolded and instantiated during static elaboration. In fact, the BSV compiler unfolds all structural
loops and functions during static elaboration.
A fully elaborated BSV design consists of no more than the following components:
• A module instance hierarchy. There is a single top-level module instance, and each module
instance contains zero or more module instances as children.
• An interface instance. Each module instance presents an interface to its clients, and may itself
be a client of zero or more interfaces of other module instances.
• Method definitions. Each interface instance consists of zero or more method definitions.
A method’s body may contain zero or more invocations of methods in other interfaces.
Every method has an implicit condition, which can be regarded as a single output wire that
is asserted only when the method is ready to be invoked. The implicit condition may directly
test state internal to its module, and may indirectly test state of other modules by invoking
their interface methods.
• Rules. Each module instance contains zero or more rules, each of which contains a condition
and an action. The condition is a boolean expression. Both the condition and the action may
contain invocations of interface methods of other modules. Since those interface methods can
themselves contain invocations of other interface methods, the conditions and actions of a rule
may span many modules.
The dynamic semantics of BSV specify the temporal behavior of rules and methods and their map-
ping into clocked synchronous hardware.
Every rule has a syntactically explicit condition and action. Both of these may contain invocations
of interface methods, each of which has an implicit condition. A rule’s composite condition consists
of its syntactically explicit condition ANDed with the implicit conditions of all the methods invoked
in the rule. A rule is said to be enabled if its composite condition is true.
The simplest way to understand the dynamic semantics is through a reference semantics, which is
completely sequential. However, please do not equate this with slow execution; the execution steps
described below are not the same as clocks; we will see in the next section that many steps can be
mapped into each clock. The execution of any BSV program can be understood using the following
very simple procedure:
Repeat forever:
Step: Pick any one enabled rule, and perform its action.
(We say that the rule is fired or executed.)
Note that after each step, a different set of rules may be enabled, since the current rule’s action will
typically update some state elements in the system which, in turn, may change the value of rule
conditions and implicit conditions.
Also note that this sequential, reference semantics does not specify how to choose which rule to
execute at each step. Thus, it specifies a set of legal behaviors, not just a single unique behavior.
The principles that determine which rules in a BSV program will be chosen to fire (and, hence, more
precisely constrain its behavior) are described in section 6.2.3.
Nevertheless, this simple reference semantics makes it very easy for the designer to reason about
invariants (correctness conditions). Since only one rule is executed in each step, we only have to
look at the actions of each rule in isolation to check how it maintains or transforms invariants. In
particular, we do not have to consider interactions with other rules executing simultaneously.
Another way of saying this is: each rule execution can be viewed as an atomic state transition.4 Race
conditions, the bane of the hardware designer, can generally be explained as an atomicity violation;
BSV’s rules are a powerful way to avoid most races.
The reference semantics is based on Term Rewriting Systems (TRSs), a formalism supported by
decades of research in the computer science community [Ter03]. For this reason, we also refer to the
reference semantics as “the TRS semantics of BSV.”
A BSV design is mapped by the BSV compiler into efficient parallel clocked synchronous hardware.
In particular, the mapping permits multiple rules to be executed in each clock cycle. This is done
in a manner that is consistent with the reference TRS semantics, so that any correctness properties
ascertained using the TRS semantics continue to hold in the hardware.
Standard clocked synchronous hardware imposes the following restrictions:
• Persistent state is updated only once per clock cycle, at a clock edge. During a clock cycle,
values read from persistent state elements are the ones that were registered in the last cycle.
• Clock-speed requirements place a limit on the amount of combinational computation that can
be performed between state elements, because of propagation delay.
The composite condition of each rule is mapped into a combinational circuit whose inputs, possibly
many, sense the current state and whose 1-bit output specifies whether this rule is enabled or not.
The action of each rule is mapped into a combinational circuit that represents the state transition
function of the action. It can have multiple inputs and multiple outputs, the latter being the
computed next-state values.
Figure 1 illustrates a general scheme to compose rule components when mapping the design to clocked
synchronous hardware. The State box lumps together all the state elements in the BSV design (as
described earlier, state elements are explicitly specified in BSV). The BSV compiler produces a
rule-control circuit which conceptually takes all the enable (cond) signals and all the data (action)
outputs and controls which of the data outputs are actually captured at the next clock in the state
elements. The enable signals feed a scheduler circuit that decides which of the rules will actually
fire. The scheduler, in turn, controls data multiplexers that select which data outputs reach the
data inputs of state elements, and controls which state elements are enabled to capture the new
data values. Firing a rule simply means that the scheduler selects its data output and clocks it into
the next state.
At each clock, the scheduler selects a subset of rules to fire. Not all subsets are legal. A subset is
legal if and only if the rules in the subset can be ordered with the following properties:
4 We use the term atomic as it is used in concurrency theory (and in operating systems and databases), i.e., to
mean indivisible.
Figure 1: A general scheme for mapping an N-rule system into clocked synchronous hardware.
• A hypothetical sequential execution of the ordered subset of rules is legal at this point, ac-
cording to the TRS semantics. In particular, the first rule in the ordered subset is currently
enabled, and each subsequent rule would indeed be enabled when execution reaches it in the
hypothetical sequence.
A special case is where all rules in the subset are already currently enabled, and no rule would
be disabled by execution of prior rules in the order.
• The hardware execution produces the same net effect on the state as the hypothetical sequential
execution, even though the hardware execution performs reads and writes in a different order
from the hypothetical sequential execution.
The BSV compiler performs a very sophisticated analysis of the rules in a design and synthesizes an
efficient hardware scheduler that controls execution in this manner.
Note that the scheme in Figure 1 is for illustrative purposes only. First, it lumps together all the
state, shows a single rule-control box, etc., whereas in the real hardware generated by the BSV
compiler these are distributed, localized and modular. Second, it is not the only way to map the
design into clocked synchronous hardware. For example, any two enabled rules can also be executed
in a single clock by feeding the action outputs of the first rule into the action inputs of the second
rule, or by synthesizing hardware for a composite circuit that computes the same function as the
composition of the two actions, and so on. In general, these alternative schemes may be more
complex to analyze, or may increase total propagation delay, but the compiler may use them in
special circumstances.
In summary, the BSV compiler performs a detailed and sophisticated analysis of rules and their
interactions, and maps the design into very efficient, highly parallel, clocked synchronous hardware
including a dynamic scheduler that allows many rules to fire in parallel in each clock, but always
in a manner that is consistent with the reference TRS semantics. The designer can use the simple
reference semantics to reason about correctness properties and be confident that the synthesized
parallel hardware will preserve those properties. (See Section 13.3 for the “scheduling attributes”
mechanism using which the designer can guide the compiler in implementing the mapping.)
When coding in other HDLs, the designer must maintain atomicity manually. He must recognize
potential race conditions, and design the appropriate data paths, control and synchronization to
avoid them. Reasoning about race conditions can cross module boundaries, and can be introduced
late in the design cycle as the problem specification evolves. The BSV compiler automates all of this
and, further, is capable of producing RTL that is competitive with hand-coded RTL.
The previous section described how an efficient circuit can be built whose behavior will be consis-
tent with sequential TRS semantics of BSV. However, as noted previously, the sequential reference
semantics can be consistent with a range of different behaviors. There are two rule scheduling prin-
ciples that guide the BSV compiler in choosing which rules to schedule in a clock cycle (and help
a designer build circuits with predictable behavior). Except when overridden by an explicit user
command or annotation, the BSV compiler schedules rules according to the following two principles:
1. Every rule enabled during a clock cycle will either be fired as part of that clock cycle or a
warning will be issued during compilation.
2. A rule will fire at most one time during a particular clock cycle.
The first principle comes into play when two (or more) rules conflict - either because they are
competing for a limited resource or because the result of their simultaneous execution is not consistent
with any sequential rule execution. In the absence of a user annotation, the compiler will arbitrarily
choose 5 which rule to prioritize, but must also issue a warning. This guarantees the designer is
aware of the ambiguity in the design and can correct it. It might be corrected by changing the rules
themselves (rearranging their predicates so they are never simultaneously applicable, for example)
or by adding an urgency annotation which tells the compiler which rule to prefer (see section 13.3.3).
When there are no scheduling warnings, it is guaranteed that the compiler is making no arbitrary
choices about which rules to execute.
The second principle ensures that continuously enabled rules (like a counter increment rule) will
not executed an unpredictable number of time during a clock cycle. According to the first rule
scheduling principle, a rule that is always enabled will be executed at least once during a clock
cycle. However, since the rule remains enabled it theoretically could execute multiple times in a
clock cycle (since that behavior would be consistent with a sequential semantics). Since rules (even
simple things like a counter increment) consume limited resources (like register write ports) it is
pragmatically useful to restrict them to executing only once in a cycle (in the absence of specific
user instructions to the contrary). Executing a continuously enabled rule only once in a cycle is also
the more straightforward and intuitive behavior.
Together, these two principles allow a designer to completely determine the rules that will be chosen
to fire by the schedule (and, hence, the behavior of the resulting circuit).
Annotations on the methods of a module are used by the BSV compiler to model the hardware
behavior into TRS semantics. For example, all reads from a register must be scheduled before any
writes to the same resgister. That is to say, any rule which reads from a register must be scheduled
earlier than any other rule which writes to it. More generally, there exist scheduling constraints for
specific hardware modules which describe how methods interact within the schedule. The scheduling
annotations describe the constraints enforced by the BSV compiler.
C conflicts
CF conflict-free
5 The compiler’s choice, while arbitrary, is deterministic. Given the same source and compiler version, the same
schedule (and, hence, the same hardware) will be produced. However, because it is an arbitrary choice, it can be
sensitive to otherwise irrelevant details of the program and is not guaranteed to remain the same if the source or
compiler version changes.
SB sequence before
SBR sequence before restricted (cannot be in the same rule)
SA sequence after
SAR sequence after restricted (cannot be in the same rule)
Scheduling Annotations
Register
read write
read CF SB
write SA SBR
• Two read methods would be conflict-free (CF), that is, you could have multiple methods that
read from the same register in the same rule, sequenced in any order.
• A write is sequenced after (SA) a read.
• A read is sequenced before (SB) a write.
• And finally, if you have two write methods, one must be sequenced before the other, and they
cannot be in the same rule, as indicated by the annotation SBR.
The scheduling annotations are specific to the TRS model desired and a single hardware component
can have multiple TRS models. For example, a register may be implemented using a mkReg module
or a mkConfigReg module, which are identical except for their scheduling annotations.
Type synonyms are just for convenience and readability, allowing one to define shorter or more
meaningful names for existing types. The new type and the original type can be used interchangeably
anywhere.
The numeric is not required because the parameter to Int will always be numeric. numeric is only
required when the compiler can’t determine whether the parameter is a numeric or non-numeric
type. It will then default to assuming it is non-numeric. The user can override this default by
specifying numeric in the typedef statement.
A typedef statement can be used to define a synonym for an already defined synonym. Example:
7.2 Enumerations
Enumerations (enums) provide a way to define a set of unique symbolic constants, also called labels or
member names. Each enum definition creates a new type different from all other types. Enum labels
may be repeated in different enum definitions. Enumeration labels must begin with an uppercase
letter.
The optional derives clause is discussed in more detail in Sections 4.3 and 14.1. One common form is
deriving (Bits), which tells the compiler to generate a bit-representation for this enum. Another
common form of the clause is deriving (Eq), which tells the compiler to pick a default equality
operation for these labels, so they can also be tested for equality and inequality. A third common
form is deriving (Bounded), which tells the compiler to define constants minBound and maxBound
for this type, equal in value to the first and last labels in the enumeration. These specifications can
be combined, e.g., deriving (Bits, Eq, Bounded). All these default choices for representation,
equality and bounds can be overridden (see Section 14.1). The form deriving (Ord) is not currently
supported for enums.
The declaration may specify the encoding used by deriving(Bits) by assigning numbers to tags.
When an assignment is omitted, the tag receives an encoding of the previous tag incremented by one;
when the encoding for the initial tag is omitted, it defaults to zero. Specifying the same encoding
for more than one tag results in an error.
Multiple tags may be declared by using the index (Tag [ntags ]) or range (Tag [start :end ]) no-
tation. In the former case, ntags tags will be generated, from Tag0 to Tagn-1 ; in the latter case,
|end − start| + 1 tags, from Tagstart to Tagend .
Example. The boolean type can be defined in the language itself:
The compiler will pick a one-bit representation, with 1’b0 and 1’b1 as the representations for False
and True, respectively. It will define the == and != operators to also work on Bool values.
Example. Excerpts from the specification of a processor:
The first line defines an enum type with 32 register names. The second and third lines define type
synonyms for RegName that may be more informative in certain contexts (“destination” and “source”
registers). Because of the deriving clause, the compiler will pick a five-bit representation, with
values 5’h00 through 5’h1F for R0 through R31.
Example. Tag encoding when deriving(Bits) can be specified manually:
typedef enum {
Add = 5,
Sub = 0,
Not,
Xor = 3,
...
} OpCode deriving (Bits);
The Add tag will be encoded to five, Sub to zero, Not to one, and Xor to three.
Example. A range of tags may be declared in a single clause:
typedef enum {
Foo[2],
Bar[5:7],
Quux[3:2]
} Glurph;
typedef enum {
Foo0,
Foo1,
Bar5,
Bar6,
Bar7,
Quux3,
Quux2
} Glurph;
and so on. Struct member names must begin with a lowercase letter, whereas union member names
must begin with an uppercase letter.
In a tagged union, the member names are also called tags. Tags play a very important safety role.
Suppose we had the following:
The variable x not only contains the bits corresponding to one of its member types int or OneHot,
but also some extra bits (in this case just one bit) that remember the tag, 0 for Tagi and 1 for
Tagoh. When the tag is Tagi, it is impossible to read it as a OneHot member, and when the tag is
Tagoh it is impossible to read it as an int member, i.e., the syntax and type checking ensure this.
Thus, it is impossible accidentally to misread what is in a union value.
The optional derives clause is discussed in more detail in Section 14.1. One common form is deriving
(Bits), which tells the compiler to pick a default bit-representation for the struct or union. For
structs it is simply a concatenation of the representations of the members. For unions, the repre-
sentation consists of t + m bits, where t is the minimum number of bits to code for the tags in this
union and m is the number of bits for the largest member. Every union value has a code in the t-bit
field that identifies the tag, concatenated with the bits of the corresponding member, right-justified
in the m-bit field. If the member needs fewer than m bits, the remaining bits (between the tag and
the member bits) are undefined.
Struct and union typedefs can define new, polymorphic types, signalled by the presence of type
parameters in #(...). Polymorphic types are discussed in section 4.1.
Section 9.11 on struct and union expressions describes how to construct struct and union values and
to access and update members. Section 10 on pattern-matching describes a more high-level way to
access members from structs and unions and to test union tags.
Example. Ordinary, traditional record structures:
An instruction operand is either a 5-bit register specifier, a 22-bit literal value, or an indexed memory
specifier, consisting of two 5-bit register specifiers.
Example. Encoding instructions in a processor:
struct {
Op op; UInt26 target;
} Jump;
} Instruction
deriving (Bits);
An Instruction is either an Immediate or a Jump. In the former case, it contains a field, op,
containing a value of type Op; a field, rs, containing a value of type Reg; a field, rt, containing a
value of type CPUReg; and a field, imm, containing a value of type UInt16. In the latter case, it
contains a field, op, containing a value of type Op, and a field, target, containing a value of type
UInt26.
Example. Optional integers (an integer together with a valid bit):
A MaybeInt is either invalid, or it contains an integer (Valid tag). The representation of this type
will be 33 bits— one bit to represent Invalid or Valid tag, plus 32 bits for an int. When it carries
an invalid value, the remaining 32 bits are undefined. It will be impossible to read/interpret those
32 bits when the tag bit says it is Invalid.
This MaybeInt type is very useful, and not just for integers. We generalize it to a polymorphic type:
This Maybe type can be used with any type a. Consider a function that, given a key, looks up a
table and returns some value associated with that key. Such a function can return either an invalid
result (Invalid), if the table does not contain an entry for the given key, or a valid result Valid v
if v is associated with the key in the table. The type is polymorphic (type parameter a) because it
may be used with lookup functions for integer tables, string tables, IP address tables, etc. In other
words, we do not over-specify the type of the value v at which it may be used.
See Section 12.4 for an important, predefined set of struct types called Tuples for adhoc structs of
between two and seven members.
Example. Declare two array identifiers a and b containing int values at each index:
Example. The array values can be polymorphic, but they must defined during elaboration:
Integer wordSize;
wordSize = 16;
Multiple assignments to the same variable are just a shorthand for a cascaded computation. Example:
int x;
x = 23;
// Here, x represents the value 23
x = ifc.meth (34);
// Here, x represents the value returned by the method call
x = x + 1;
// Here, x represents the value returned by the method call, plus 1
Note that these assignments are ordinary, zero-time assignments, i.e., they never represent a dynamic
assignment of a value to a register. These assignments only represent the convenient naming of an
intermediate value in some zero-time computation. Dynamic assignments are always written using
the non-blocking assignment operator <=, and are described in Section 8.4.
In general, the left-hand side (lValue) in an assignment statement can be a series of index- and field-
selections from an identifier representing a nesting of arrays, structs and unions. The array-indexing
expressions must be computable during static elaboration.
For bit vectors, the left-hand side (lValue) may also be a range between two indices. The indices must
be computable during static elaboration, and, if the indices are not literal constants, the right-hand
side of the assignment should have a defined bit width. The size of the updated range (determined
by the two literal indices or by the size of the right-hand side) must be less than or equal to the size
of the target bit vector.
Example. Update an array variable b:
b[15] = foo.bar(x);
b[15:8] = foo.bar(x);
Example. Update a struct variable (using the processor example from Section 7.3):
cpu.pc = cpu.pc + 4;
i.e., it reassigns the struct variable to contain a new struct value in which all members other than
the updated member have their old values. The right-hand side is a struct expression; these are
described in Section 9.11.
Update of tagged union variables is done using normal assignment notation, i.e., one replaces the
current value in a tagged union variable by an entirely new tagged union value. In a struct it makes
sense to update a single member and leave the others unchanged, but in a union, one member
replaces another. Example (extending the previous processor example):
The right-hand sides of the assignments are tagged union expressions; these are described in Section
9.11.
The let statement is a shorthand way to declare and initialize a variable in a single statement. A
variable which has not been declared can be assigned an initial value and the compiler will infer the
type of the variable from the expression on the right hand side of the statement:
varDecl ::= let identifier = expression ;
Example:
let n = valueof(BuffSize);
The pseudo-function valueof returns an Integer value, which will be assigned to n at compile time.
Thus the variable n is assumed to have the type of Integer.
If the expression is the value returned by an actionvalue method, the notation will be:
varAssign ::= let identifier <- expression ;
Note the difference between this statement:
let m1 = m1displayfifo.first;
In the first example, m1displayfifo.first is a value method; m1 is assigned the value and type
returned by the value method. In the latter, rndm.get is an actionvalue method; z1 is assigned the
value and type returned by the actionvalue method.
The left-hand side must contain a writeable interface type, such as Reg#(t) (for some type t that
has a representation in bits). It is either an lValue or a parenthesized expression (e.g., the register
interface could be selected from an array of register interfaces or returned from a function). The
right-hand side must have the same type as the left-hand side would have if it were typechecked
as an expression (including read desugaring, as described below). BSV allows only the so-called
non-blocking assignments of Verilog, i.e., the statement specifies that the register gets the new value
at the end of the current cycle, and is only available in the next cycle.
Following BSV’s principle that all state elements (including registers) are module instances, and all
interaction with a module happens through its interface, a simple register assignment r<=e is just a
convenient alternative notation for a method call:
r. write (e)
r. read ()
The implicit addition of the . read method call to variables of type Reg#(t) is the simplest example
of read desugaring.
Example. Instantiating a register interface and a register, and using it:
List#(Reg#(int)) regs;
...
regs[3] <= regs[3] + 1; // increment the register at position 3
Note that when the square-bracket notation is used on the right-hand side, read desugaring is also
applied6 . This allows the expression regs[3] to be interpreted as a register read without unnecessary
clutter.
The indexed register assignment notation can also be used for partial register updates, when the
register contains an array of elements of some type t (in a particular case, this could be an array
of bits). This interpretation is just a shorthand for a whole register update where only the selected
element is updated. In other words,
6 To suppress read desugaring use asReg or asIfc
x[j] <= v;
where replace is a pure function that takes the whole value from register x and produces a whole
new value with the j’th element replaced by v. The statement then assigns this new value to the
register x.
It is important to understand the tool infers the appropriate meaning for an indexed register write
based on the types available and the context:
Reg#(Bit#(32)) x;
x[3] <= e;
List#(Reg#(a)) x;
y[3] <= e;
In the former case, x is a register containing an array of items (in this example a bit vector), so the
statement updates the third item in this array (a single bit) and stores the updated bit vector in
the register. In the latter case, y is an array of registers, so register at position 3 in the array is
updated. In the former case, multiple writes to different indices in a single rule with non-exclusive
conditions are forbidden (because they would be multiple conflicting writes to the same register)7 ,
writing the final result back to the register. In the latter case, multiple writes to different indices
will be allowed, because they are writes to different registers (though multiple writes to the same
index, under non-exclusive conditions would not be allowed, of course).
It also is possible to mix these notations, i.e., writing a single statement to perform a partial update
of a register in an array of registers.
Example: Mixing types of square-bracket notation in a register write
List#(Reg#(bit[3:0])) ys;
...
y[4][3] <= e; // Update bit 3 of the register at position 4
Just as there is a range notation for bit extraction and variable assignments, there is also a range
notation for register writes.
regWrite ::= lValue [ expression : expression ] <= expression
The index expressions in the range notation follow the same rules as the corresponding expressions
in variable assignment range updates (they must be static expressions and if they are not literal
constants the right-hand side should have a defined bit width). Just as the indexed, partial register
writes described in the previous subsection, multiple range-notation register writes cannot be mixed
in the same rule8 .
Example: A range-notation register write
Reg#(Bit#(32)) r;
Second, it can mean to select the named field out of a compile-time structure that contains a register
and write that register.
Example: Writing a register contained in a structure
In both cases, the same notation is used and the compiler infers which interpretation is appropriate.
As with square-bracket selection, struct member selection implies read desugaring, unless inhibited
by asReg or asIfc.
A begin-end statement is a block that allows one to collect multiple statements into a single state-
ment, which can then be used in any context where a statement is required.
<ctxt>BeginEndStmt ::= begin [ : identifier ]
{ <ctxt>Stmt }
end [ : identifier ]
The optional identifier labels are currently used for documentation purposes only; in the future they
may be used for hierarchical references. The statements contained in the block can contain local
variable declarations and all the other kinds of statements. Example:
case (e1)
e2, e3 : s2;
e4 : s4;
e5, e6, e7: s5;
default : s6;
endcase
is equivalent to:
The case expression (e1) is evaluated once, and tested for equality in sequence against the value
of each of the left-hand side expressions. If any test succeeds, then the corresponding right-hand
side statement is executed. If no test succeeds, and there is a default item, then the default item’s
right-hand side is executed. If no test succeeds, and there is no default item, then no right-hand side
is executed.
Example:
rule decode ;
case (sel)
2’b00: a <= 0;
2’b01: a <= 1;
2’b10: a <= 2;
2’b11: a <= 3;
endcase
endrule
rule finish ;
if (a == 3)
done <= True;
else
done <= False;
endrule
endmodule
While loops have the usual semantics. The predicate expression is evaluated and, if true, the loop
body statement is executed, and then the while loop is repeated. Note that if the predicate initially
evaluates false, the loop body is not executed at all.
Example. Sum the values in an array:
int a[32];
int x = 0;
int j = 0;
...
while (j < 32)
x = x + a[j];
A function definition is introduced by the function keyword. This is followed by the type of the
function return-value, the name of the function being defined, the formal arguments, and optional
provisos (provisos are discussed in more detail in Section 14.1). After this is the function body and,
finally, the endfunction keyword that is optionally labelled again with the function name. Each
formal argument declares an identifier and its type.
The function type is required for functions defined at the top level of a package and for recursive
functions (such as the factorial examples above). You may choose to leave out the types within a
function definition at lower levels for non-recursive functions,
If not at the top level of a package, Example 2 from the previous section could be rewritten as:
function f1(i);
return i.first();
endfunction
Note that currently incomplete type information will be ignored. If, in the above example, partial
type information were provided, it would be the same as no type information being provided. This
may cause a type-checking error to be reported by the compiler.
9 Expressions
Expressions occur on the right-hand sides of variable assignments, on the left-hand and right-hand
side of register assignments, as actual parameters and arguments in module instantiation, function
calls, method calls, array indexing, and so on.
There are many kinds of primary expressions. Complex expressions are built using the conditional
expressions and unary and binary operators.
expression ::= condExpr
| operatorExpr
| exprPrimary
exprPrimary ::= identifier
| intLiteral
| stringLiteral
| systemFunctionCall
| ( expression )
| · · · see other productions · · ·
Conditional expressions include the conditional operator and case expressions. The conditional
operator has the usual syntax:
Conditional expressions have the usual semantics. In an expression e1 :e2 :e3 , e1 can be a boolean
expression. If it evaluates to True, then the value of e2 is returned; otherwise the value of e3 is
returned. More generally, e1 can include pattern matching, and this is described in Section 10, on
pattern matching
Example.
// instantiating registers
Reg#(Bit#(4)) a <- mkReg(0);
Reg#(Bit#(4)) b <- mkReg(0);
rule dostuff;
a <= (b>4) ? 2 : 10;
endrule
endmodule
Binary operator expressions are built using the unop and binop operators listed in the following
table, which are a subset of the operators in SystemVerilog. The operators are listed here in order
of decreasing precedence.
Constructs that do not have any closing token, such as conditional statements and expressions, have
lowest precedence so that, for example,
e1 ? e2 : e3 + e4
is parsed as follows:
e1 ? e2 : (e3 + e4)
(e1 ? e2 : e3) + e4
In BSV programs one will sometimes encounter the Bit#(0) type. One common idiomatic example
is the type Maybe#(Bit#(0)) (see the Maybe#() type in Section 7.3). Here, the type Bit#(0) is just
used as a place holder, when all the information is being carried by the Maybe structure.
A begin-end expression is like an “inline” function, i.e., it allows one to express a computation using
local variables and multiple variable assignments and then finally to return a value. A begin-end
expression is analogous to a “let block” commonly found in functional programming languages. It
can be used in any context where an expression is required.
exprPrimary ::= beginEndExpr
beginEndExpr ::= begin [ : identifier ]
{ beginEndExprStmt }
expression
end [ : identifier ]
Optional identifier labels are currently used for documentation purposes only. The statements con-
tained in the block can contain local variable declarations and all the other kinds of statements.
beginEndExprStmt ::= varDecl | varAssign
| functionDef
| functionStmt
| systemTaskStmt
| ( expression )
Example:
int z;
z = (begin
int x2 = x * x; // x2 is local, x from surrounding scope
int y2 = y * y; // y2 is local, y from surrounding scope
(x2 + y2); // returned value (sum of squares)
end);
Any expression that is intended to act on the state of the circuit (at circuit execution time) is called
an action and has type Action. The type Action is special, and cannot be redefined.
Primitive actions are provided as methods in interfaces to predefined objects (such as registers or
arrays). For example, the predefined interface for registers includes a ._write() method of type
Action:
Section 8.4 describes special syntax for register reads and writes using non-blocking assignment so
that most of the time one never needs to mention these methods explicitly.
The programmer can create new actions only by building on these primitives, or by using Verilog
modules. Actions are combined by using action blocks:
exprPrimary ::= actionBlock
actionBlock ::= action [ : identifier ]
{ actionStmt }
endaction [ : identifier ]
actionStmt ::= <action>If | <action>Case
| <action>BeginEndStmt
| <action>For
| <action>While
| regWrite
| varDecl | varAssign
| varDo | varDeclDo
| functionStmt
| systemTaskStmt
| ( expression )
| actionBlock
The action block can be labelled with an identifier, and the endaction keyword can optionally be
labelled again with this identifier. Currently this is just for documentation purposes.
Example:
Action a;
a = (action
x <= x+1;
y <= z;
endaction);
The Standard Prelude package defines the trivial action that does nothing:
Action noAction;
action
endaction
The Action type is actually a special case of the more general type ActionValue, described in the
next section:
ActionValue#(a)
The type parameter a represents the type of the returned value. The type ActionValue is special,
and cannot be redefined.
Actionvalues are created using actionvalue blocks. The statements in the block contain the actions
to be performed, and a return statement specifies the value to be returned.
exprPrimary ::= actionValueBlock
actionValueBlock ::= actionvalue [ : identifier ]
{ actionValueStmt }
endactionvalue [ : identifier ]
actionValueStmt ::= <actionValue>If | <actionValue>Case
| <actionValue>BeginEndStmt
| <actionValue>For
| <actionValue>While
| regWrite
| varDecl | varAssign
| varDo | varDeclDo
| functionStmt
| systemTaskStmt
| ( expression )
| returnStmt
Given an actionvalue av, we use a special notation to perform the action and yield the value:
varDeclDo ::= type identifier <- expression ;
varDo ::= identifier <- expression ;
The first rule above declares the identifier, performs the actionvalue represented by the expression,
and assigns the returned value to the identifier. The second rule is similar and just assumes the
identifier has previously been declared.
Example. A stack:
interface IntStack;
method Action push (int x);
method ActionValue#(int) pop();
endinterface: IntStack
...
IntStack s1;
...
IntStack s2;
...
action
int x <- s1.pop; -- A
s2.push (x+1); -- B
endaction
In line A, we perform a pop action on stack s1, and the returned value is bound to x. If we wanted
to discard the returned value, we could have omitted the “x <-” part. In line B, we perform a push
action on s2.
Note the difference between this statement:
x <- s1.pop;
z = s1.pop;
In the former, x must be of type int; the statement performs the pop action and x is bound to
the returned value. In the latter, z must be of type Method#(ActionValue#(int)) and z is simply
bound to the method s1.pop. Later, we could say:
x <- z;
to perform the action and assign the returned value to x. Thus, the = notation simply assigns the
left-hand side to the right-hand side. The <- notation, which is only used with actionvalue right-hand
sides, performs the action and assigns the returned value to the left-hand side.
Example: Using an actionvalue block to define a pop in a FIFO.
import FIFO :: *;
// The next function makes a deq and first from a fifo and returns an actionvalue block
function ActionValue#(t) fifoPop(FIFO#(t) f) provisos(Bits#(t, st));
return(
actionvalue
f.deq;
return f.first;
endactionvalue
);
endfunction
// Module mkFifoWithPop
(* synthesize, always_ready = "clear" *)
module mkFifoWithPop(FifoWithPop#(Data));
// A fifo of depth 2
FIFO#(Data) fifo <- mkFIFO;
// methods
method enq = fifo.enq;
method clear = fifo.clear;
method pop = fifoPop(fifo);
endmodule
Function calls are expressed in the usual notation, i.e., a function applied to its arguments, listed in
parentheses. If a function does not have any arguments, the parentheses are optional.
exprPrimary ::= functionCall
functionCall ::= exprPrimary [ ( [ expression { , expression } ] ) ]
A function which has a result type of Action can be used as a statement when in the appropriate
context.
functionStmt ::= functionCall ;
Note that the function position is specified as exprPrimary, of which identifier is just one special
case. This is because in BSV functions are first-class objects, and so the function position can be
an expression that evaluates to a function value. Function values and higher-order functions are
described in Section 14.2.
Example:
Method calls are expressed by selecting a method from an interface using dot notation, and then
applying it to arguments, if any, listed in parentheses. If the method does not have any arguments
the parentheses are optional.
exprPrimary ::= methodCall
methodCall ::= exprPrimary . identifier [ ( [ expression { , expression } ] ) ]
The exprPrimary is any expression that represents an interface, of which identifier is just one special
case. This is because in BSV interfaces are first-class objects. The identifier must be a method in
the supplied interface. Example:
rule pushdata;
stack.push(counter); // calling an Action method
endrule
rule popdata;
let x <- stack.pop; // calling an ActionValue method
result <= x;
endrule
rule readValue;
let temp_val = stack.first; // calling a value method
endrule
rule inc_counter;
counter <= counter +1;
endrule
endmodule
We can assert that an expression must have a given type by using Verilog’s “type cast” notation:
exprPrimary ::= typeAssertion
typeAssertion ::= type ’ bitConcat
| type ’ ( expression )
bitConcat ::= { expression { , expression } }
In most cases type assertions are used optionally just for documentation purposes. Type assertions
are necessary in a few places where the compiler cannot work out the type of the expression (an
example is a bit-selection with run-time indexes).
In BSV although type assertions use Verilog’s type cast notation, they are never used to change an
expression’s type. They are used either to supply a type that the compiler is unable to determine by
itself, or for documentation (to make the type of an expression apparent to the reader of the source
code).
Section 7.3 describes how to define struct and union types. Section 8.1 describes how to declare
variables of such types. Section 8.2 describes how to update variables of such types.
To create a struct value, e.g., to assign it to a struct variable or to pass it an actual argument for a
struct formal argument, we use the following notation:
exprPrimary ::= structExpr
structExpr ::= Identifier { memberBind { , memberBind } }
memberBind ::= identifier : expression
The leading Identifier is the type name to which the struct type was typedefed. Each memberBind
specifies a member name (identifier ) and the value (expression) it should be bound to. The members
need not be listed in the same order as in the original typedef. If any member name is missing, that
member’s value is undefined.
Semantically, a structExpr creates a struct value, which can then be bound to a variable, passed as
an argument, stored in a register, etc.
Example (using the processor example from Section 7.3):
In this example, the mem field is undefined since it is omitted from the struct expression.
cpu.pc
Since the same member name can occur in multiple types, the compiler uses type information to
resolve which member name you mean when you do a member selection. Occasionally, you may
need to add a type assertion to help the compiler resolve this.
Update of struct variables is described in Section 8.2.
To create a tagged union value, e.g., to assign it to a tagged union variable or to pass it an actual
argument for a tagged union formal argument, we use the following notation:
exprPrimary ::= taggedUnionExpr
taggedUnionExpr ::= tagged Identifier { memberBind { , memberBind } }
| tagged Identifier exprPrimary
memberBind ::= identifier : expression
The leading Identifier is a member name of a union type, i.e., it specifies which variant of the union
is being constructed.
The first form of taggedUnionExpr can be used when the corresponding member type is a struct.
In this case, one directly lists the struct member bindings, enclosed in braces. Each memberBind
specifies a member name (identifier ) and the value (expression) it should be bound to. The members
do not need to be listed in the same order as in the original struct definition. If any member name
is missing, that member’s value is undefined.
Otherwise, one can use the second form of taggedUnionExpr , which is the more general notation,
where exprPrimary is directly an expression of the required member type.
Semantically, a taggedUnionExpr creates a tagged union value, which can then be bound to a variable,
passed as an argument, stored in a register, etc.
A tagged union member can be selected with the usual dot notation. If the tagged union value does
not have the tag corresponding to the member selection, the value is undefined. Example:
InstrOperand orand;
...
... orand.Indexed.regAddr ...
In this expression, if orand does not have the Indexed tag, the value is undefined. Otherwise, the
regAddr field of the contained struct is returned.
Selection of tagged union members is more often done with pattern matching, which is discussed in
Section 10.
The Maybe type is described in Section 7.3. Note that an interface expression looks similar to an
interface declaration (Section 5.2) except that it does not list type parameters and it contains method
definitions instead of method prototypes.
Interface values are first-class objects. For example, this makes it possible to write interface trans-
formers that convert one form of interface into another. Example:
Interfaces are similar to structs in the sense that both contain a set of named items—members in
structs, methods in interfaces. Both are first-class values—structs are created with struct expressions,
and interfaces are created with interface expressions. A named item is selected from both using the
same notation—struct.member or interface.method.
However, they are different in the following ways:
• Structs cannot contain methods; interfaces can contain nothing but methods (and subinter-
faces).
• Struct members can be selected; interface methods cannot be selected, they can only be invoked
(inside rules or other interface methods).
rules
Word instr = mem[pc];
rule instrExec;
case (instr) matches
tagged Add { .r1, .r2, .r3 }: begin
pc <= pc+1;
rf[r1] <= rf[r2] + rf[r3];
end;
tagged Jz {.r1, .r2} : if (r1 == 0)
begin
pc <= r2;
end;
endcase
endrule
endrules
// Definition of CounterType
typedef Bit#(16) CounterType;
reset_prefix = "reset_b",
clock_prefix = "counter_clk",
always_ready, always_enabled *)
module counter (IfcCounter#(CounterType));
endmodule
10 Pattern matching
Pattern matching provides a visual and succinct notation to compare a value against structs, tagged
unions and constants, and to access members of structs and tagged unions. Pattern matching can be
used in case statements, case expressions, if statements, conditional expressions, rule conditions,
and method conditions.
pattern ::= . identifier Pattern variable
| .* Wildcard
| constantPattern Constant
| taggedUnionPattern Tagged union
| structPattern Struct
| tuplePattern Tuple
constantPattern ::= intLiteral
| Identifier Enum label
taggedUnionPattern ::= tagged Identifier [ pattern ]
structPattern ::= tagged Identifier { identifier : pattern { , identifier : pattern } }
tuplePattern ::= { pattern { , pattern } }
A pattern is a nesting of tagged union and struct patterns with the leaves consisting of pattern
variables, constant expressions, and the wildcard pattern .*.
In a pattern .x, the variable x is declared at that point as a pattern variable, and is bound to the
corresponding component of the value being matched.
A constant pattern is an integer literal, or an enumeration label (such as True or False). Integer
literals can include the wildcard character ? (example: 4’b00??).
A tagged union pattern consists of the tagged keyword followed by an identifier which is a union
member name. If that union member is not a void member, it must be followed by a pattern for
that member.
In a struct pattern, the Identifier following the tagged keyword is the type name of the struct as
given in its typedef declaration. Within the braces are listed, recursively, the member name and a
pattern for each member of the struct. The members can be listed in any order, and members can
be omitted.
A tuple pattern is enclosed in braces and lists, recursively, a pattern for each member of the tuple
(tuples are described in Section 12.4).
A pattern always occurs in a context of known type because it is matched against an expression of
known type. Recursively, its nested patterns also have known type. Thus a pattern can always be
statically type-checked.
Each pattern introduces a new scope; the extent of this scope is described separately for each of
the contexts in which pattern matching may be used. Each pattern variable is implicitly declared
as a new variable within the pattern’s scope. Its type is uniquely determined by its position in the
pattern. Pattern variables must be unique in the pattern, i.e., the same pattern variable cannot be
used in more than one position in a single pattern.
In pattern matching, the value V of an expression is matched against a pattern. Note that static
type checking ensures that V and the pattern have the same type. The result of a pattern match is:
• A boolean value, True, if the pattern match succeeds, or False, if the pattern match fails.
• If the match succeeds, the pattern variables are bound to the corresponding members from V ,
using ordinary assignment.
• A pattern variable always succeeds (matches any value), and the variable is bound to that
value (using ordinary procedural assignment).
• A constant pattern succeeds if V is equal to the value of the constant. Integer literals can
include the wildcard character ?. An integer literal containing a wildcard will match any
constant obtained by replacing each wildcard character by a valid digit. For example, ’h12?4
will match any constant between ’h1204 and ’h12f4 inclusive.
• A tagged union pattern succeeds if the value has the same tag and, recursively, if the nested
pattern matches the member value of the tagged union.
• A struct or tuple pattern succeeds if, recursively, each of the nested member patterns matches
the corresponding member values in V . In struct patterns with named members, the textual
order of members does not matter, and members may be omitted. Omitted members are
ignored.
Conceptually, if the value V is seen as a flattened vector of bits, the pattern specifies the following:
which bits to match, what values they should be matched with and, if the match is successful, which
bits to extract and bind to the pattern identifiers.
Case statements can occur in various contexts, such as in modules, function bodies, action and
actionValue blocks, and so on. Ordinary case statements are described in Section 8.6. Here we
describe pattern-matching case statements.
<ctxt>Case ::= case ( expression ) matches
{ <ctxt>CasePatItem }
[ <ctxt>DefaultItem ]
endcase
<ctxt>CasePatItem ::= pattern { &&& expression } : <ctxt>Stmt
<ctxt>DefaultItem ::= default [ : ] <ctxt>Stmt
The keyword matches after the main expression (following the case keyword) signals that this is a
pattern-matching case statement instead of an ordinary case statement.
Each case item contains a left-hand side and a right-hand side, separated by a colon. The left-hand
side contains a pattern and an optional filter (&&& followed by a boolean expression). The right-hand
side is a statement. The pattern variables in a pattern may be used in the corresponding filter and
right-hand side. The case items may optionally be followed, finally, by a default item (the colon
after the default keyword is optional).
The value of the main expression (following the case keyword) is matched against each case item, in
the order given, until an item is selected. A case item is selected if and only if the value matches the
pattern and the filter (if present) evaluates to True. Note that there is a left-to-right sequentiality
in each item— the filter is evaluated only if the pattern match succeeds. This is because the filter
expression may use pattern variables that are meaningful only if the pattern match succeeds. If none
of the case items matches, and a default item is present, then the default item is selected.
If a case item (or the default item) is selected, the right-hand side statement is executed. Note that
the right-hand side statement may use pattern variables bound on the left hand side. If none of the
case items succeed, and there is no default item, no statement is executed.
Example (uses the Maybe type definition of Section 7.3):
First, the expression f(a) is evaluated. In the first arm, the value is checked to see if it has the form
tagged Valid .x, in which case the pattern variable x is assigned the component value. If so, then
the case arm succeeds and we execute return x. Otherwise, we fall through to the second case arm,
which must match since it is the only other possibility, and we return 0.
Example:
InstrOperand orand;
...
case (orand) matches
tagged Register .r : x = rf [r];
tagged Literal .n : x = n;
tagged Indexed {regAddr: .ra, regIndex: .ri} : x = mem[ra+ri];
endcase
Example:
package PipelineFIFO;
import FIFO::*;
// STATE ----------------
If statements are described in Section 8.6. As the grammar shows, the predicate (condPredicate)
can be a series of pattern matches and expressions, separated by &&&. Example:
Here, the value of e1 is matched against the pattern p1 ; if it succeeds, the expression e2 is evaluated;
if it is true, the value of e3 is matched against the pattern p3 ; if it succeeds, stmt1 is executed,
otherwise stmt2 is executed. The sequential order is important, because e2 and e3 may use pattern
variables bound in p1 , and stmt1 may use pattern variables bound in p1 and p3 , and pattern variables
are only meaningful if the pattern matches. Of course, stmt2 cannot use any of the pattern variables,
because none of them may be meaningful when it is executed.
In general the condPredicate can be a series of terms, where each term is either a pattern match
or a filter expression (they do not have to alternate). These are executed sequentially from left to
right, and the condPredicate succeeds only if all of them do. In each pattern match e matches p, the
value of the expression e is matched against the pattern p and, if successful, the pattern variables are
bound appropriately and are available for the remaining terms. Filter expressions must be boolean
expressions, and succeed if they evaluate to True. If the whole condPredicate succeeds, the bound
pattern variables are available in the corresponding “consequent” arm of the construct.
The following contexts also permit a condPredicate cp with pattern matching:
Example. Continuing the Pipeline FIFO example from the previous section (10.2).
// INTERFACE ----------------
endmodule: mkPipelineFIFO
endpackage: PipelineFIFO
Pattern matching can be used in variable assignments for convenient access to the components of a
tuple or struct value.
varAssign ::= match pattern = expression ;
The pattern variables in the left-hand side pattern are declared at this point and their scope extends
to subsequent statements in the same statement sequence. The types of the pattern variables are
determined by their position in the pattern.
The left-hand side pattern is matched against the value of the right-hand side expression. On a
successful match, the pattern variables are assigned the corresponding components in the value.
Example:
rule r1;
match {.in, .start} = data;
//using "in" as a local variable
a <= in;
endrule
BSV contains a powerful and convenient notation for expressing finite state machines (FSMs). FSMs
are essentially well-structured processes involving sequencing, parallelism, conditions and loops, with
a precise compositional model of time. In principle, FSMs can be coded with rules, which are strictly
more powerful, but the FSM sublanguage herein provides a succinct notation for FSM structures
and automates all the generation and management of the actual FSM state. In fact, the BSV
compiler translates all the constructs described here internally into rules. In particular, the primitive
statements in these FSMs are standard actions (Section 9.6), obeying all the scheduling semantics
of actions (Section 6.2).
First, one uses the Stmt sublanguage, described in Section C.5.1 to compose the actions of an
FSM using sequential, parallel, conditional and looping structures. This sublanguage is within the
expression syntactic category, i.e., a term in the sublanguage is an expression whose value is of type
Stmt. This value can be bound to identifiers, passed as arguments and results of functions, held in
static data structures, etc., like any other value. Finally, the FSM can be instantiated into hardware,
multiple times if desired, by passing the Stmt value to the module constructor mkFSM. The resulting
module interface has type FSM, which has methods to start the FSM and to wait until it completes.
In order to use this sublanguage, it is necessary to import the StmtFSM package, which is described
in more detail in Section C.5.1.
12 Important primitives
These primitives are available via the Standard Prelude package and other standard libraries. See
also Appendix C more useful libraries.
The type bit[m:0] and its synonym Bit#(Mplus1) represents bit-vectors of width m + 1, provided
the type Mplus1 has been suitably defined. The lower (lsb) index must be zero. Example:
It takes a bit-vector of size mn and returns a 2-tuple (a pair, see Section 12.4) of bit-vectors of size
m and n, respectively. The proviso expresses the size constraints using the built-in Add type class.
The function split is polymorphic, i.e, m and n may be different in different applications of the func-
tion, but each use is fully type-checked statically, i.e., the compiler verifies the proviso, performing
any calculations necessary to do so.
BSV is currently very strict about bit-width compatibility compared to Verilog and SystemVerilog,
in order to reduce the possibility of unintentional errors. In BSV, the types bit[m:0] and bit[n:0]
are compatible only if m = n. For example, an attempt to assign from one type to the other, when
m6=n, will be reported by the compiler as a type-checking error—there is no automatic padding or
truncation. The Standard Prelude package (see Section B) contains functions such as extend() and
truncate(), which may be used explicitly to extend or truncate to a required bit-width. These
functions, being overloaded over all bit widths, are convenient to use, i.e., you do not have to
constantly calculate the amount by which to extend or truncate; the type checker will do it for you.
12.3 String
The type String is defined in the Standard Prelude package (B.2.7). Strings are mostly used in
system tasks (such as $display). Strings can be concatenated using the strConcat function, and
they can be tested for equality and inequality using the == and != operators. String literals, written
in double-quotes, are described in Section 2.5.
12.4 Tuples
It is frequently necessary to group a small number of values together, e.g., when returning multiple
results from a function. Of course, one could define a special struct type for this purpose, but BSV
predefines a number of structs called tuples that are convenient:
Values of these types can be created by applying a predefined family of constructor functions:
tpl_1 (e)
tpl_2 (e)
...
tpl_7 (e)
where the expression e evaluates to tuple value. Of course, only the first two are applicable to
Tuple2 types, only the first three are applicable to Tuple3 types, and so on.
In using a tuple component selector, it is sometimes necessary to use a static type assertion to help
the compiler work out the type of the result. Example:
UInt#(6)’(tpl_2 (e))
Tuple components are more conveniently selected using pattern matching. Example:
12.5 Registers
The most elementary module available in BSV is the register (B.4), which has a Reg interface.
Registers are instantiated using the mkReg module, whose single parameter is the initial value of the
register. Registers can also be instantiated using the mkRegU module, which takes no parameters
(don’t-care initial value). The Reg interface type and the module types are shown below.
Registers are polymorphic, i.e., in principle they can hold a value of any type but, of course, ulti-
mately registers store bits. Thus, the provisos on the modules indicate that the type must be in the
Bits type class (overloading group), i.e., the operations pack() and unpack() must be defined on
this type to convert into to bits and back.
Section 8.4 describes special notation whereby one rarely uses the _write() and _read methods
explicitly. Instead, one more commonly uses the traditional non-blocking assignment notation for
writes and, for reads, one just mentions the register interface in an expression.
Since mentioning the register interface in an expression is shorthand for applying the _read method,
BSV also provides a notation for overriding this implicit read, producing an expression representing
the register interface itself:
asReg (r)
Since it is also occasionally desired to have automatically read interfaces that are not registers, BSV
also provides a notation for more general suppression of read desugaring, producing an expression
that always represents an interface itself:
asIfc(ifc)
12.6 FIFOs
Package FIFO (C.1.2) defines several useful interfaces and modules for FIFOs:
The FIFO interface type is polymorphic, i.e., the FIFO contents can be of any type a. However,
since FIFOs ultimately store bits, the content type a must be in the Bits type class (overloading
group); this is specified in the provisos for the modules.
The module mkFIFO leaves the capacity of the FIFO unspecified (the number of entries in the FIFO
before it becomes full).
The module mkSizedFIFO takes the desired capacity of the FIFO explicitly as a parameter.
Of course, when compiled, mkFIFO will pick a particular capacity, but for formal verification purposes
it is useful to leave this undetermined. It is often useful to be able to prove the correctness of a design
without relying on the capacity of the FIFO. Then the choice of FIFO depth can only affect circuit
performance (speed, area) and cannot affect functional correctness, so it enables one to separate the
questions of correctness and “performance tuning.” Thus, it is good design practice initially to use
mkFIFO and address all functional correctness questions. Then, if performance tuning is necessary,
it can be replaced with mkSizedFIFO.
12.7 FIFOFs
Package FIFOF (C.1.2) defines several useful interfaces and modules for FIFOs. The FIFOF interface
is like FIFO, but it also has methods to test whether the FIFO is full or empty:
The module mkFIFOF leaves the capacity of the FIFO unspecified (the number of entries in the FIFO
before it becomes full). The module mkSizedFIFOF takes the desired capacity of the FIFO as an
argument.
BSV supports a number of Verilog’s system tasks and functions. There are two types of system tasks;
statements which are conceptually equivalent to Action functions, and calls which are conceptually
equivalent to ActionValue and Value functions. Calls can be used within statements.
systemTaskStmt ::= systemTaskCall ;
The values output are sized automatically to the largest possible value, with leading zeros, or in the
case of decimal values, leading spaces. The automatic sizing of displayed data can be overridden
by inserting a value n indicating the size of the displayed data. If n=0 the output will be sized to
minimum needed to display the data without leading zeros or spaces.
ActionValues (see Section 9.7) whose returned type is displayable can also be directly displayed.
This is done by performing the associated action (as part of the action invoking $display) and
displaying the returned value.
Example:
$display ("%t", $time);
For display statements in different rules, the outputs will appear in the usual logical scheduling order
of the rules. For multiple display statements within a single rule, technically there is no defined
ordering in which the outputs should appear, since all the display statements are Actions within
the rule and technically all Actions happen simultaneously in the atomic transaction. However, as
a convenience to the programmer, the compiler will arrange for the display outputs to appear in
the normal textual order of the source text, taking into accout the usual flow around if-then-elses,
statically elaborated loops, and so on. However, for a rule that comprises separately compiled parts
(for example, a rule that invokes a method in a separately compiled module), the system cannot
guarantee the ordering of display statements across compilation boundaries. Within each separately
compiled part, the display outputs will appear in source text order, but these groups may appear in
any order. In particular, verification engineers should be careful about these benign (semantically
equivalent) reorderings when checking the outputs for correctness.
12.8.2 $format
Argument Description
"r" or "rb" open for reading
"w" or "wb" truncate to zero length or create for writing
"a" or "ab" append; open for writing at end of file, or create for writing
"r+", or "r+b", or "rb+" open for update (reading and writing)
"w+", or "w+b", or "wb+" truncate or create for update
"a+", or "a+b", or "ab+" append; open or create for update at end of file
The $fclose system call is of type Action and can be used in any context where an action is
expected.
de-sugars to
let x <- $swriteAV("The value is %d", count);
foo <= x;
An ActionValue value version is available for each of these tasks. The associated syntax is given
below.
systemTaskCall ::= stringAVTaskName ( [ expression [ , expression ] ] )
stringAVTaskName ::= $swriteAV | $swritebAV | $swriteoAV | $swritehAV | $sformatAV
The ActionValue versions of these tasks can also be called directly by the user.
Use of the system tasks described in this section allows a designer to populate state elements with
dynamically generated debugging strings. These values can then be viewed using other display tasks
(using the %s format specifier) or output to a VCD file for examination in a waveform viewer.
$fclose ( lfh ) ;
$finish(0);
endrule
The $stime function returns a 32-bit integer (specifically, of type Bit#(32)) representing time,
scaled to the timescale unit of the module that invoked it. If the actual simulation time does not fit
in 32 bits, the lower-order 32 bits are returned.
There are two system tasks defined for the Real data type (section B.2.6), used to convert between
Real and IEEE standard 64-bit vector representation, $realtobits and $bitstoreal. They are
identical to the Verilog functions.
systemTaskCall ::= $realtobits ( expression )
systemTaskCall ::= $bitstoreal ( expression )
Information for use in simulation can be provided on the command line. This information is spec-
ified via optional arguments in the command used to invoke the simulator. These arguments are
distinguished from other simulator arguments by starting with a plus (+) character and are therefore
known as plusargs. Following the plus is a string which can be examined during simulation via
system functions.
systemTaskCall ::= $test$plusargs ( expression )
The $test$plusargs system function call is conceptually of ActionValue type (see Section 9.7),
and can be used anywhere an ActionValue is expected. An argument of type String is expected
and a boolean value is returned indicating whether the provided string matches the beginning of any
plusarg from the command line.
This section describes how to guide the compiler in some of its decisions using BSV’s attribute
syntax.
attributeInstances ::= attributeInstance
{ attributeInstance }
attributeInstance ::= (* attrSpec { , attrSpec } *)
attrSpec ::= attrName [ = expression ]
attrName ::= identifier |Identifier
Multiple attributes can be written together on a single line
(* synthesize, always_ready = "read, subifc.enq" *)
Attributes can be associated with a number of different language constructs such as module, interface,
and function definitions. A given attribute declaration is applied to the first attribute construct that
follows the declaration.
In addition to compiler flags on the command line, it is possible to guide the compiler with attributes
that are included in the BSV source code.
The attributes synthesize and noinline control code generation for top-level modules and func-
tions, respectively.
13.1.1 synthesize
When the compiler is directed to generate Verilog or Bluesim code for a BSV module, by default it
tries to integrate all definitions into one big module. The synthesize attribute marks a module for
code generation and ensures that, when generated, instantiations of the module are not flattened but
instead remain as references to a separate module definition. Modules that are annotated with the
synthesize attribute are said to be synthesized modules. The BSV hierarchy boundaries associated
with synthesized modules are maintained during code generation. Not all BSV modules can be
synthesized modules (i.e.,can maintain a module boundary during code generation). Section 5.8
describes in more detail which modules are synthesizable.
13.1.2 noinline
The noinline attribute is applied to functions, instructing the compiler to generate a separate
module for the function. This module is instantiated as many times as required by its callers. When
used in complicated calling situations, the use of the noinline attribute can simplify and speed up
compilation. The noinline attribute can only be applied to functions that are defined at the top
level and the inputs and outputs of the function must be in the typeclass Bits.
Example:
(* noinline *)
function Bit#(LogK) popCK(Bit#(K) x);
return (popCountTable(x));
endfunction: popCK
Interface attributes express protocol and naming requirements for generated Verilog interfaces. They
are considered during generation of the Verilog module which uses the interface. These attributes
can be applied to synthesized modules, methods, interfaces, and subinterfaces at the top level only.
If the module is not synthesized, the attribute is ignored. The following table shows which attributes
can be applied to which elements. These attributes cannot be applied to Clock, Reset, or Inout
subinterface declarations.
There is a direct correlation between interfaces in Bluespec and ports in the generated Verilog. These
attributes can be applied to interfaces to control the naming and the protocols of the generated
Verilog ports.
Bluespec uses a simple Ready-Enable micro-protocol for each method within the module’s interface.
Each method contains both a output Ready (RDY) signal and an input Enable (EN) signal in
addition to any needed directional data lines. When a method can be safely called it asserts its
RDY signal. When an external caller sees the RDY signal it may then call (in the same cycle) the
method by asserting the method’s EN signal and providing any required data.
Generated Verilog ports names are based the method name and argument names, with some standard
prefixes. In the ActionValue method method1 shown below
Interface attributes allow control over the naming and protocols of individual methods or entire
interfaces.
ready= and enable= Ready and enable ports use RDY_ and EN_ as the default prefix to the
method names. The attributes ready= ”string” and enable= ”string” replace the prefix annotation
and method name with the specified string as the name instead. These attributes may be associated
with method declarations (methodProto) only (Section 5.2).
In the above example, the following attribute would replace the RDY_method1 with avMethodIsReady
and EN_method1 with GO.
Note that the ready= attribute is ignored if the method or module is annotated as always_ready
or always_enabled, while the enable= attribute is ignored for value methods as those are annotated
as always_enabled.
result= By default the output port for value methods and ActionValue methods use the method
name. The attribute result = ”string” causes the output to be renamed to the specified string. This
is useful when the desired port names must begin with an upper case letter, which is not valid for
a method name. These attributes may be associated with method declarations (methodProto) only
(Section 5.2).
In the above example, the following attribute would replace the method1 port with OUT.
(* result = "OUT" *)
Note that the result= attribute is ignored if the method is an Action method which does not
return a result.
prefix= and port= By default, the input ports for methods are named by using the name of the
method as the prefix and the name of the method argument as the suffix, into method_argument.
The prefix and/or suffix name can be replaced by the attributes prefix= ”string” and port= ”string”.
By combining these attributes any desired string can be generated. The prefix= attribute replaces
the method name and the port= attribute replaces the argument name in the generated Verilog port
name. The prefix string may be empty, in which case the joining underscore is not added.
The prefix= attribute may be associated with method declarations (methodProto) or sub-interface
declarations (subinterfaceDecl). The port= attribute may be associated with each method prototype
argument in the interface declaration (methodProtoFormal ) (Section 5.2).
In the above example, the following attribute would replace the method1_data_in port with IN_DATA.
(* prefix = "" *)
method ActionValue#( type_out )
method1( (* port="IN_DATA" *) type_in data_in ) ;
Note that the prefix= attribute is ignored if the method does not have any arguments.
The prefix= attribute may also be used on sub-interface declarations to aid the renaming of interface
hierarchies. By default, interface hierarchies are named by prefixing the sub-interface name to names
of the methods within that interface (Section 5.2.1.) Using the prefix attribute on the subinterface
is a way of replacing the sub-interface name. This is demonstrated in the example in Section 13.2.3.
The port protocol attributes always_enabled and always_ready remove unnecessary ports. In all
cases the compiler verifies that the attributes are correctly applied.
The attribute always_enabled specifies that no enable signal will be generated for the associated
interface methods. The methods will be executed on every clock cycle and the compiler verifies that
the caller does this.
The attribute always_ready specifies that no ready signals will be generated. The compiler verifies
that the associated interface methods are permanently ready. always_enabled implies always_ready.
The always_ready and always_enabled attributes can be associated with the method declara-
tions (methodProto), the sub-interface declarations (subinterfaceDecl), or the interface declaration
(interfaceDecl) itself. In these cases, the attribute does not take any arguments. Example:
interface Test;
(* always_enabled *)
method ActionValue#(Bool) check;
endinterface: Test
The attributes can also be associated with a module, in which case the attribute can have as an
argument the list of methods to which the attribute is applied. When associated with a module, the
attributes are applied when the interface is implemented by a module, not at the declaration of the
interface. Example:
In this example, note that only the enq method of the subifc interface is always_ready. Other
methods of the interface, such as deq, are not always_ready.
If every method of the interface is always_ready or always_enabled, individual methods don’t have
to be specified when applying the attribute to a module. Example:
(* always_enabled *)
module mkServer (ILookup);
endinterface: ILookup
Scheduling attributes are used to express certain performance requirements. When the compiler
maps rules into clocks, as described in Section 6.2.2, scheduling attributes guide or constrain its
choices, in order to produce a schedule that will meet performance goals.
Scheduling attributes are most often attached to rules or to rule expressions, but some can also be
added to module definitions.
The scheduling attributes are are only applied when the module is synthesized.
13.3.1 fire_when_enabled
The fire_when_enabled scheduling attribute immediately precedes a rule (just before the rule
keyword) and governs the rule.
It asserts that this rule must fire whenever its predicate and its implicit conditions are true, i.e.,
when the rule conditions are true, the attribute checks that there are no scheduling conflicts with
other rules that will prevent it from firing. This is statically verified by the compiler. If the rule
won’t fire, the compiler will report an error.
Example. Using fire_when_enabled to ensure the counter is reset:
// Definition of CounterType
typedef Bit#(16) CounterType;
(* synthesize,
reset_prefix = "reset_b",
clock_prefix = "counter_clk",
always_ready = "readCounter",
always_enabled= "readCounter" *)
// The attribute fire_when_enabled will check that this rule will fire
// if counter == ’1
(* fire_when_enabled *)
rule resetCounter (counter == ’1);
counter <= 1;
endrule
Rule resetCounter conflicts with rule updateCounter because both try to modify the counter
register when it contains all its bits set to one. If the rule updateCounter is more urgent, only the
rule updateCounter will fire. In this case, the assertion fire_when_enabled will be violated and
the compiler will produce an error message. Note that without the assertion fire_when_enabled
the compilation process will be correct.
13.3.2 no_implicit_conditions
The no_implicit_conditions scheduling attribute immediately precedes a rule (just before the
rule keyword) and governs the rule.
It asserts that the implicit conditions of all interface methods called within the rule must always
be true; only the explicit rule predicate controls whether the rule can fire or not. This is statically
verified by the compiler, and it will report an error if necessary.
Example:
// Definition of CounterType
typedef Bit#(16) CounterType;
/* Next rule increases the counter with each counter_clk rising edge
if the maximum has not been reached */
(* no_implicit_conditions *)
rule updateCounter;
if (counter != ’1)
counter <= counter + 1;
endrule
The assertion no_implicit_conditions is incorrect for the rule resetCounter, resulting in a com-
pilation error. This rule has the implicit condition in the FIFO module due to the fact that the deq
method cannot be invoked if the fifo valueFifo is empty. Note that without the assertion no error
will be produced and that the condition if (counter != ’1) is not considered an implicit one.
13.3.3 descending_urgency
The compiler maps rules into clocks, as described in Section 6.2.2. In each clock, amongst all the
rules that can fire in that clock, the system picks a subset of rules that do not conflict with each
other, so that their parallel execution is consistent with the reference TRS semantics. The order in
which rules are considered for selection can affect the subset chosen. For example, suppose rules r1
and r2 conflict, and both their conditions are true so both can execute. If r1 is considered first and
selected, it may disqualify r2 from consideration, and vice versa. Note that the urgency ordering is
independent of the TRS ordering of the rules, i.e., the TRS ordering may be r1 before r2, but either
one could be considered first by the compiler.
The designer can specify that one rule is more urgent than another, so that it is always considered
for scheduling before the other. The relationship is transitive, i.e., if rule r1 is more urgent than
rule r2, and rule r2 is more urgent than rule r3, then r1 is considered more urgent than r3.
Urgency is specified with the descending_urgency attribute. Its argument is a string containing a
comma-separated list of rule names (see Section 5.6 for rule syntax, including rule names). Example:
This example specifies that r1 is more urgent than r2 which, in turn, is more urgent than r3.
If urgency attributes are contradictory, i.e., they specify both that one rule is more urgent than
another and its converse, the compiler will report an error. Note that such a contradiction may
be a consequence of a collection of urgency attributes, because of transitivity. One attribute may
specify r1 more urgent than r2, another attribute may specify r2 more urgent than r3, and another
attribute may specify r3 more urgent than r1, leading to a cycle, which is a contradiction.
The descending_urgency attribute can be placed in one of three syntactic positions:
• It can be placed just before the module keyword in a module definitions (Section 5.3), in which
case it can refer directly to any of the rules inside the module.
• It can be placed just before the rule keyword in a rule definition, (Section 5.6) in which case
it can refer directly to the rule or any other rules at the same level.
• It can be placed just before the rules keyword in a rules expression (Section 9.13), in which
case it can refer directly to any of the rules in the expression.
In addition, an urgency attribute can refer to any rule in the module hierarchy at or below the
current module, using a hierarchical name. For example, suppose we have:
endmodule: mkFoo
The hierarchical name the_bar.r2 refers to a rule named r2 inside the module instance the_bar.
This can be several levels deep, i.e., the scheduling attribute can refer to a rule deep in the module
hierarchy, not just the sub-module immediately below. In general a hierarchical rule name is a
sequence of module instance names and finally a rule name, separated by periods.
A reference to a rule in a sub-module cannot cross synthesis boundaries. This is because synthesis
boundaries are also scheduler boundaries. Each separately synthesized part of the module hierarchy
contains its own scheduler, and cannot directly affect other schedulers. Urgency can only apply to
rules considered within the same scheduler.
If rule urgency is not specified, and it impacts the choice of schedule, the compiler will print a
warning to this effect during compilation.
Example. Using descending_urgency to control the scheduling of conflicting rules:
// Definition of CounterType
endmodule
Rule resetCounter conflicts with rule updateCounter because both try to modify the counter
register when it contains all its bits set to one. Without any descending_urgency attribute, the
updateCounter rule may obtain more urgency, meaning that if the predicate of resetCounter is
met, only the rule updateCounter will fire. By setting the descending_urgency attribute the
designer can control the scheduling in the case of conflicting rules.
13.3.4 execution_order
With the execution_order attribute, the designer can specify that, when two rules fire in the same
cycle, one rule should sequence before the other. This attribute is similar to the descending_urgency
attribute (section 13.3.3) except that it specifies the execution order instead of the urgency order.
The execution_order attribute may occur in the same syntactic positions as the descending_urgency
attribute (Section 13.3.3) and takes a similar argument, a string containing a comma-separated list
of rule names. Example:
This example specifies that r1 should execute before r2 which, in turn, should execute before r3.
If two rules cannot execute in the order specified, because of method calls which must sequence in
the opposite order, for example, then the two rules are forced to conflict.
13.3.5 mutually_exclusive
The scheduler always attempts to deduce when two rules are mutually exclusive (based on their
predicates). However, this deduction can fail even when two rules are actually exclusive, either
because the scheduler effort limit is exceeded or because the mutual exclusion depends on a higher-
level invariant that the scheduler does not know about. The mutually_exclusive attribute allows
the designer to overrule the scheduler’s deduction and forces the generated schedule to treat the
annotated rules as exclusive. The mutually_exclusive attribute may occur in the same syntactic
positions as the descending_urgency attribute (Section 13.3.3) and takes a similar argument, a
string containing a comma-separated list of rule names. Example:
This example specifies that every pair of rules that are in the annotation (i.e (r1, r2), (r1, r3), and
(r2, r3)) is a mutually-exclusive rule pair.
Since an asserted mutual exclusion does not come with a proof of this exclusion, the compiler will
insert code that will check and generate a runtime error if two rules ever execute during the same clock
cycle during simulation. This allows a designer to find out when their use of the mutually_exclusive
attribute is incorrect.
13.3.6 conflict_free
Like the mutually_exclusive rule attribute (section 13.3.5), the conflict_free rule attribute is a
way to overrule the scheduler’s deduction about the relationship between two rules. However, unlike
rules that are annotated mutually_exclusive, rules that are conflict_free may fire in the same
clock cycle. Instead, the conflict_free attribute asserts that the annotated rules will not make
method calls that are inconsistent with the generated schedule when they execute.
The conflict_free attribute may occur in the same syntactic positions as the descending_urgency
attribute (Section 13.3.3) and takes a similar argument, a string containing a comma-separated list
of rule names. Example:
This example specifies that every pair of rules that are in the annotation (i.e (r1, r2), (r1, r3), and
(r2, r3)) is a conflict-free rule pair.
For example, two rules may both conditionally enqueue data into a FIFO with a single enqueue
port. Ordinarily, the scheduler would conclude that the two rules conflict since they are competing
for a single method. However, if they are annotated as conflict_free the designer is asserting that
when one rule is enqueuing into the FIFO, the other will not be, so the conflict is apparent, not real.
With the annotation, the schedule will be generated as if any conflicts do not exist and code will be
inserted into the resulting model to check if conflicting methods are actually called by the conflict
free rules during simulation.
It is important to know the conflict_free attribute’s capabilities and limitations. The attribute
works with more than method calls that totally conflict (like the single enqueue port). During simu-
lation, it will check and report any method calls amongst conflict_free rules that are inconsistent
with the generated schedule (including registers being read after they have been written and wires
being written after they are read). On the other hand, the conflict_free attribute does not over-
rule the scheduler’s deductions with respect to resource usage (like uses of a multi-ported register
file).
13.3.7 preempts
The designer can also prevent a rule from firing whenever another rule (or set of rules) fires. The
preempts attribute accepts two elements as arguments. Each element may be either a rule name or
a list of rule names. A list of rule names must be separated by commas and enclosed in parentheses.
In each cycle, if any of the rule names specified in the first list can be executed and are scheduled
to fire, then none of the rules specified in the second list will be allowed to fire.
The preempts attribute is similar to the descending_urgency attribute (section 13.3.3), and may
occur in the same syntactic positions. The preempts attribute is equivalent to forcing a conflict
and adding descending_urgency. With descending_urgency, if two rules do not conflict, then
both would be allowed to fire even if an urgency order had been specified; with preempts, if one
rule preempts the other, they can never fire together. If r1 preempts r2, then the compiler forces a
conflict and gives r1 priority. If r1 is able to fire, but is not scheduled to, then r2 can still fire.
Examples:
If the rule r1 in the submodule the_bar will fire, then neither r2 nor r3 will fire.
The split/nosplit attributes are applied to Action and ActionValue statements, but cannot
preceed certain expressions inside an action/endaction including return, variable declarations,
instantiations, and function statements.
When a rule contains an if (or case) statement, the compiler has the option either of splitting the
rule into two mutually exclusive rules, or leaving it as one rule for scheduling but using MUXes in the
production of the action. Rule splitting can sometimes be desirable because the two split rules are
scheduled independently, so non-conflicting branches of otherwise conflicting rules can be scheduled
concurrently. Splitting also allows the split fragments to appear in different positions in the logical
execution order, providing the effect of condition dependent scheduling.
Splitting is turned off by default for two reasons:
• When a rule contains many if statements, it can lead to an exponential explosion in the
number of rules. A rule with 15 if statements might split into 215 rules, depending on how
independent the statements and their branch conditions are. An explosion in the number of
rules can dramatically slow down the compiler and cause other problems for later compiler
phases, particularly scheduling.
• Splitting propagates the branch condition of each if to the predicates of the split rules. Re-
sources required to compute rule predicates are reserved on every cycle. If a branch condition
requires a scarce resource, this can starve other parts of the design that want to use that
resource.
The split and nosplit attributes override any compiler flags, either the default or a flag entered
on the command line (-split-if).
The split attribute splits all branches in the statement immediately following the attribute state-
ment, which must be an Action statement. A split immediately preceeding a binding (e.g. let)
statement is not valid. If there are nested if or case statements within the split statement, it will
continue splitting recursively through the branches of the statement. The nosplit attribute can be
used to disable rule splitting within nested if statements.
Example:
rule finish ;
(*split*)
if (a == 3)
begin
done <= True;
end
else
(*nosplit*)
if (a == 0)
begin
done <= False;
a <= 1;
end
else
begin
done <= False;
end
endrule
endmodule
To enable rule splitting for an entire design, use the compiler flag -split-if at compile time. See the
user guide for more information on compiler flags. You can enable rule splitting for an entire design
with the -split-if flag and then disable the effect for specific rules, by specifying the nosplit
attribute before the rules you do not want to split.
The following attributes control the definition and naming of clock oscillator, clock gate, and reset
ports. The attributes can only be applied to top-level module definitions.
The generated port renaming attributes clock_prefix=, gate_prefix=, and reset_prefix= re-
name the ports for the clock oscillators, clock gates, and resets in a module by specifying a prefix
string to be added to each port name. The prefix is used only when a name is not provided for the
port, (as described in Sections 13.5.3 and 13.6.1), requiring that the port name be created from the
prefix and argument name. The attributes are associated with a module and are only applied when
the module is synthesized.
clock_prefix= CLK Provides the prefix string to be added to port names for
all the clock oscillators in a module.
gate_prefix= CLK GATE Provides the prefix string to be added to port names for
all the clock gates in a module.
reset_prefix= RST N Provides the prefix string to be added to port names for
all the resets in a module.
If a prefix is specified as the empty string, then no prefix will be used when creating the port names;
that is the argument name alone will be used as the name.
Example:
Where CK is the default clock (using the user-supplied prefix), RST_N is the default reset (using the
default prefix), and CK_clk2 is the oscillator for the input clk2 (using the user-supplied prefix).
When a module is synthesized, one port, for the oscillator, is created for each clock input (including
the default clock). The gate for the clock is defaulted to a logical 1. The attributes gate_all_clocks
and gate_input_clocks= specify that a second port be generated for the gate.
The attribute gate_all_clocks will add a gate port to the default clock and to all input clocks.
The attribute gate_input_clocks= is used to individually specify each input clock which should
have a gate supplied by the parent module.
If an input clock is part of a vector of clocks, the gate port will be added to all clocks in the vector.
Example:
In this example, a gate port will be added to both the clocks in the vector clks and the clock c2.
A gate port cannot be added to just one of the clocks in the vector clks.
The gate_input_clocks= attribute can be used to add a gate port to the default clock. Example:
( * gate_input_clocks = "default_clock" * )
Note that by having a gate port, the compiler can no longer assume the gate is always logical 1. This
can cause an error if the clock is connected to a submodule which requires the gate to be logical 1.
The gate synthesis attributes are associated with a module and are only applied when the module
is synthesized.
The default clock and reset naming attributes are associated with a module and are only applied
when the module is synthesized.
The attributes default_clock_osc=, default_clock_gate=, and default_reset= provide the
names for the default clock oscillator, default gate, and default reset ports for a module. When
a name for the default clock or reset is provided, any prefix attribute for that port is ignored.
The attributes default_gate_inhigh and default_gate_unused indicate that a gate port should
not be generated for the default clock and whether the gate is always logical 1 or unused. The default
is default_gate_inhigh. This is only necessary when the attribute gate_all_clocks (section
13.5.2) has been used.
The attributes no_default_clock and no_default_reset are used to remove the ports for the
default clock and the default reset.
The clock_family and clock_ancestors attributes indicate to the compiler that clocks are in the
same domain in situations where the compiler may not recognize the relationship. For example, when
clocks split in synthesized modules and are then recombined in a subsequent module, the compiler
may not recognize that they have a common ancestor. The clock_ancestors and clock_family
attributes allow the designer to explicitly specify the family relationship between the clocks. These
attributes are applied to modules only.
The clock_ancestors attribute specifies an ancestry relationship between clocks. A clock is a gated
version of its ancestors. In other words, if clk1 is an ancestor of clk2 then clk2 is a gated version
of clk1, as specified in the following statement:
Multiple ancestors as well as multiple independent groups can be listed in a single attribute state-
ment. For example:
(* clock_ancestors = "clk1 AOF clk2 AOF clk3, clk1 AOF clk4, clka AOF clkb" *)
The above statement specifies that clk1 is an ancestor of clk2, which is itself an ancestor of clk3;
that clk1 is also an ancestor of clk4; and that clka is an ancestor of clkb. You can also repeat
the attribute statement instead of including all clock ancestors in a single statement. Example:
For clocks which do not have an ancestor relationship, but do share a common ancestor, you can
use the clock_family attribute. Clocks which are in the same family have the same oscillator with
a different gate. To be in the same family, one does not have to be a gated version of the other,
instead they may be gated versions of a common ancestor.
The non-default clock and reset inputs to a module will have a port name created using the argument
name and any associated prefix for that port type. This name can be overridden on a per-argument
basis by supplying argument-level attributes that specify the names for the ports.
These attributes are applied to the clock module arguments, except for reset= which is applied to
the reset module arguments.
osc= Clock or vector of clocks Provides the full name of the oscillator port.
module arguments
gate= Clock or vector of clocks Provides the full name of the gate port.
module arguments
gate_inhigh Clock or vector of clocks Indicates that the gate port should be omitted and
module arguments the gate is assumed to be high.
gate_unused Clock or vector of clocks Indicates that the gate port should be omitted and is
module arguments never used within the module.
reset= Reset or vector of resets Provides the full name of the reset port.
module arguments
Example:
(* synthesize *)
module mkMod((* osc="ACLK", gate="AGATE" *) Clock clk,
(* reset="RESET" *) Reset rst,
ModIfc ifc);
The attributes can be applied to the base name generated for a vector of clocks, gates or resets.
Example:
(* synthesize *)
module mkMod((* osc="ACLK", gate="AGATE" *) Vector#(2, Clock) clks,
(* reset="ARST" *) Vector#(2, Reset) rsts,
ModIfc ifc);
13.6.2 clocked_by=
The attribute clocked_by= allows the user to assert which clock a reset, inout, or value module
argument is associated with, to specify that the argument has no_clock, or to associate the argument
with the default_clock. If the clocked_by= attribute is not provided, the default clock will be
used for inout and value arguments; the clock associated with a reset argument is dervied from where
the reset is connected.
Examples:
To specify that an argument is not associated with any clock domain, the clock no_clock is used.
Example:
13.6.3 reset_by=
The attribute reset_by= allows the user to assert which reset an inout or value module argument is
associated with, to specify that the argument has no_reset, or to associate the argument with the
default_reset. If the reset_by= attribute is not provided, the default reset will be used.
Examples:
To specify that the port is not associated with any reset, no_reset is used. Example:
13.6.4 port=
The attribute port= allows renaming of value module arguments. These are port-like arguments
that are not clocks, resets or parameters. It provides the full name of the port generated for the
argument. This is the same attribute as the port= attribute in Section 13.2.1, as applied to module
arguments instead of interface methods.
A BSV design can specify comments to be included in the generated Verilog by use of the doc
attribute.
Top-level
Attribute name Section module Submodule rule rules
definitions instantiations definitions expressions
√ √ √ √
doc= 13.7
Example:
Or:
Multiple doc attributes will appear together in the order that they are given. doc attributes can be
added to modules, module instantiations, and rules, as described in the following sections.
13.7.1 Modules
The Verilog file that is generated for a synthesized BSV module contains a header comment prior
to the Verilog module definition. A designer can include additional comments between this header
and the module by attaching a doc attribute to the module being synthesized. If the module is not
synthesized, the doc attributes are ignored.
Example:
(* synthesize *)
(* doc = "This is important information about the following module" *)
module mkMod (IFC);
...
endmodule
Client c;
...
(* doc = "This submodule does a third thing" *)
c <- mkClient;
The syntax also works if the type of the module interface is given with let, a variable, or the current
module type. Example:
If the submodule being instantiated is a separately synthesized module or primitive, then its corre-
sponding Verilog instantiation will be preceded by the comments. Example:
// submodule the_f
// This submodule does something
wire the_f$CLR, the_f$DEQ, the_f$ENQ;
FIFO2 #(.width(1)) the_f(...);
If the submodule is not separately synthesized, then there is no place in the Verilog module to attach
the comment. Instead, the comment is included in the header at the beginning of the module.
For example, assume that the module the_sub was instantiated inside mkTop with a user-provided
comment but was not separately synthesized. The generated Verilog would include these lines:
// ...
// Comments on the inlined module ‘the_sub’:
// This is the submodule
//
module mkTop(...);
The doc attribute can be attached to submodule instantiations inside functions and for-loops.
If several submodules are inlined and their comments carry to the top-module’s header comment, all
of their comments are printed. To save space, if the comments on several modules are the same, the
comment is only displayed once. This can occur, for instance, with doc attributes on instantiations
inside for-loops. For example:
If the doc attribute is attached to a register instantiation and the register is inlined (as is the default),
the Verilog comment is included with the declaration of the register signals. Example:
// register the_r
// This is a register
reg the_r;
wire the_r$D_IN, the_r$EN;
If the doc attribute is attached to an RWire instantiation, and the wire instantiation is inlined (as
is the default), then the comment is carried to the top-module’s header comment.
If the doc attribute is attached to a probe instantiation, the comment appears in the Verilog above
the declaration of the probe signals. Since the probe signals are declared as a group, the comments
are listed at the start of the group. Example:
// probes
//
// Comments for probe ‘the_r’:
// This is a probe
//
wire the_s$PROBE;
wire the_r$PROBE;
...
13.7.3 Rules
In generated Verilog, a designer might want to include a comment on rule scheduling signals (such as
CAN_FIRE_ and WILL_FIRE_ signals), to say something about the actions that are performed when
that rule is executed. This can be achieved with a doc attribute attached to a BSV rule declaration
or rules expression.
The doc attribute can be attached to any rule..endrule or rules...endrules statement. Exam-
ple:
If any scheduling signals for the rule are explicit in the Verilog output, their definition will be
preceeded by the comment. Example:
// rule RL_do_something
// This rule is important
assign CAN_FIRE_RL_do_something = b ;
assign WILL_FIRE_RL_do_something = CAN_FIRE_RL_do_something ;
If the signals have been inlined or otherwise optimized away and thus do not appear in the Verilog,
then there is no place to attach the comments. In that case, the comments are carried to the top
module’s header. Example:
// ...
// Comments on the inlined rule ‘RL_do_something’:
// This rule is important
//
module mkTop(...);
The designer can ensure that the signals will exist in the Verilog by using an appropriate compiler
flag, the -keep-fires flag which is documented in the Bluespec SystemVerilog User Guide.
The doc attribute can be attached to any rule..endrule expression, such as inside a function or
inside a for-loop.
As with comments on submodules, if the comments on several rules are the same, and those comments
are carried to the top-level module header, the comment is only displayed once.
// ...
// Comments on the inlined rules ‘RL_do_something_2’, ‘RL_do_something_1’,
// ‘RL_do_something’:
// This rule is important
//
module mkTop(...);
14 Advanced topics
This section can be skipped on first reading.
Note that for most BSV programming, one just needs to know about a few predefined type classes
such as Bits and Eq, about provisos, and about the automatic mechanism for defining the overloaded
functions in those type classes using a deriving clause. The brief introduction in Sections 4.2 and
4.3 should suffice.
This section is intended for the advanced programmer who may wish to define new type classes
(using a typeclass declaration), or explicitly to define overloaded functions using an instance
declaration.
In programming languages, the term overloading refers to the use of a common function name or
operator symbol to represent some number (usually finite) of functions with distinct types. For
example, it is common to overload the operator symbol + to represent integer addition, floating
point addition, complex number addition, matrix addition, and so on.
Note that overloading is distinct from polymorphism, which is used to describe a single function
or operator that can operate at an infinity of types. For example, in many languages, a single
polymorphic function arraySize() may be used to determine the number of elements in any array,
no matter what the type of the contents of the array.
A type class (or overloading group) further recognizes that overloading is often performed with
related groups of function names or operators, giving the group of related functions and operators a
name. For example, the type class Ord contains the overloaded operators for order-comparison: <,
<=, > and >=.
If we specify the functions represented by these operator symbols for the types int, Bool, bit[m:0]
and so on, we say that those types are instances of the Ord type class.
A proviso is a (static) condition attached to some constructs. A proviso requires that certain types
involved in the construct must be instances of certain type classes. For example, a generic sort
function for sorting lists of type List#(t) will have a proviso (condition) that t must be an instance
of the Ord type class, because the generic function uses an overloaded comparison operator from
that type class, such as the operator < or >.
Type classes are created explicitly using a typeclass declaration (Section 14.1.2). Further, a type
class is explicitly populated with a new instance type t, using an instance declaration (Section
14.1.3), in which the programmer provides the specifications for the overloaded functions for the
type t.
14.1.1 Provisos
This prototype expresses the idea that the sorting function takes an input list xs of items of type
t (presumably unsorted), and produces an output list of type t (presumably sorted). In order to
perform its function it needs to compare elements of the list against each other using an overloaded
comparison operator such as <. This, in turn, requires that the overloaded operator be defined on
objects of type t. This is exactly what is expressed in the proviso, i.e., that t must be an instance
of the type class (overloading group) Ord, which contains the overloaded operator <.
Thus, it is permissible to apply sort to lists of Integers or lists of Bools, because those types are
instances of Ord, but it is not permissible to apply sort to a list of, say, some interface type Ifc
(assuming Ifc is not an instance of the Ord type class).
can be read literally as saying that the types macAddress and 48 are in the Bits type class, or
can be read more generally as saying that values of type macAddress can be converted to and from
values of the type bit[47:0] using the pack and unpack overloaded functions of type class Bits.
We sometimes also refer to provisos as contexts, meaning that they constrain the types that may be
used within the construct to which the provisos are attached.
Occasionally, if the context is too weak, the compiler may be unable to figure out how to resolve an
overloading. Usually the compiler’s error message will be a strong hint about what information is
missing. In these situations it may be necessary for the programmer to guide the compiler by adding
more type information to the program, in either or both of the following ways:
• Add a static type assertion (Section 9.10) to some expression that narrows down its type.
• Add a proviso to the surrounding construct.
This defines the type class Literal. Any type a that is an instance of Literal must have an
overloaded function called fromInteger that converts an Integer value into the type a. In fact,
this is the mechanism that BSV uses to interpret integer literal constants, e.g., to resolve whether a
literal like 6847 is to be interpreted as a signed integer, an unsigned integer, a floating point number,
a bit value of 10 bits, a bit value of 8 bits, etc. (See Section 2.3.1 for a more detailed description.).
The typeclass also provides a function inLiteralRange that takes an argument of type a and an
Integer and returns a Bool. In the standard Literal typeclass this boolean indicates whether or
not the supplied Integer is in the range of legal values for the type a.
Example (from a predefined type class in BSV):
This defines the type class Bounded. Any type a that is an instance of Bounded will have two values
called minBound and maxBound that, respectively, represent the minimum and maximum of all values
of this type.
Example (from a predefined type class in BSV):10
This defines the type class Arith with super type class Literal, i.e., the proviso states that in order
for a type data_t to be an instance of Arith it must also be an instance of the type class Literal.
Further, it has six overloaded functions with the given names and types. Said another way, a type
that is an instance of the Arith type class must have a way to convert integer literals into that type,
and it must have addition, subtraction, negation, multiplication, and division defined on it.
The semantics of a dependency say that once the types on the left of the determines keyword are
fixed, the types on the right are uniquely determined. The types on either side of the list can be a
single type or a list of types, in which case they are enclosed in parentheses.
Example of a typeclass definition specifying type dependencies:
notation allows an identifier to be constructed from arbitrary characters beginning with a backslash and ending with
a whitespace (the backslash and whitespace are not part of the identifier.)
For any type t we know that Get#(t) and Put#(t) are connectable because of the following decla-
ration in the GetPut package:
In the Connectable dependency above, it states that a determines b. Therefore, you know that if a
is Get#(t), the only possibility for b is Put#(t).
Example of a typeclass definition with lists of types in the dependencies:
In the above example, if a were UInt#(16) the dependency would require that b had to be 16; but
the fact that something occupies 16 bits by no means implies that it has to be a UInt.
A type can be declared to be an instance of a class in two ways, with a general mechanism or with
a convenient shorthand. The general mechanism of instance declarations is the following:
typeclassInstanceDef ::= instance typeclassIde # ( type { , type } ) [ provisos ] ;
{ varAssign ; | functionDef | moduleDef }
endinstance [ : typeclassIde ]
This says that the types are an instance of type class typeclassIde with the given provisos. The
varAssigns, functionDef s and moduleDef s specify the implementation of the overloaded identifiers
of the type class.
Example, declaring a type as an instance of the Eq typeclass:
instance Eq#(Color);
function Bool \== (Color x, Color y); //must use \== with a trailing
return True; //space to define custom instances
endfunction //of the Eq typeclass
endinstance
The shorthand mechanism is to attach a deriving clause to a typedef of an enum, struct or tagged
union and let the compiler do the work. In this case the compiler chooses the “obvious” implementa-
tion of the overloaded functions (details in the following sections). The only type classes for which
deriving can be used for general types are Bits, Eq and Bounded. Furthermore, deriving can be
used for any class if the type is a data type that is isomorphic to a type that has an instance for the
derived class.
derives ::= deriving ( typeclassIde { , typeclassIde } )
Example:
The type class Bits contains the types that are convertible to bit strings of a certain size. Many
constructs have membership in the Bits class as a proviso, such as putting a value into a register,
array, or FIFO.
Example: The Bits type class definition (which is actually predefined in BSV) looks something like
this:
Here, a represents the type that can be converted to/from bits, and n is always instantiated by a
size type (Section 4) representing the number of bits needed to represent it. Implementations of
modules such as registers and FIFOs use these functions to convert between values of other types
and the bit representations that are really stored in those elements.
Example: The most trivial instance declaration states that a bit-vector can be converted to a bit
vector, by defining both the pack and unpack functions to be identity functions:
Example:
Note that the deriving (Eq) phrase permits us to use the equality operator == on Color types
in the pack function. Red, Green and Blue are coded as 3, 2 and 1, respectively. If we had used
the deriving(Bits) shorthand in the Color typedef, they would have been coded as 0, 1 and 2,
respectively (Section 14.1.6).
The pseudo-function SizeOf#(t) can be applied to a type t to get the numeric type representing its
bit size. The type t must be in the Bits class, i.e., it must already be an instance of Bits#(t,n),
either through a deriving clause or through an explicit instance declaration. The SizeOf function
then returns the corresponding bit size n. Note that SizeOf returns a numeric type, not a numeric
value, i.e., the output of SizeOf can be used in a type expression, and not in a value expression.
SizeOf, which converts a type to a (numeric) type, should not be confused with the pseudo-function
valueof, described in Section 4.2.1, which converts a numeric type to a numeric value.
Example:
When attaching a deriving(Bits) clause to a user-defined type, the instance derived for the Bits
type class can be described as follows:
• For an enum type it is simply an integer code, starting with zero for the first enum constant
and incrementing by one for each subsequent enum constant. The number of bits used is the
minimum number of bits needed to represent distinct codes for all the enum constants.
• For a struct type it is simply the concatenation of the bits for all the members. The first
member is in the leftmost bits (most significant) and the last member is in the rightmost bits
(least significant).
• For a tagged union type, all values of the type occupy the same number of bits, regardless of
which member it belongs to. The bit representation consists of two parts—a tag on the left
(most significant) and a member value on the right (least significant).
The tag part uses the minimum number of bits needed to code for all the member names. The
first member name is given code zero, the next member name is given code one, and so on.
The size of the member value part is always the size of the largest member. The member value
is stored in this field, right-justified (i.e., flush with the least-significant end). If the member
value requires fewer bits than the size of the field, the intermediate bits are don’t-care bits.
This is the same type as in Section 14.1.4 except that Red, Green and Blue are now coded as 0, 1
and 2, instead of 3, 2, and 1, respectively, because the canonical choice made by the compiler is to
code consecutive labels incrementing from 0.
Example. The boolean type can be defined in the language itself:
The type Bool is represented with one bit. False is represented by 0 and True by 1.
Example. A struct type:
The type Glurph is represented in 24 bits, with foo in the upper 8 bits and bar in the lower 16 bits.
Example. Another struct type:
The type Coord is represented in 64 bits, with x in the upper 32 bits and y in the lower 32 bits.
Example. The Maybe type from Section 7.3:
is represented in 1 + n bits, where n bits are needed to represent values of type a. If the leftmost
bit is 0 (for Invalid) the remaining n bits are unspecified (don’t-care). If the leftmost bit is 1 (for
Valid) then the remaining n bits will contain a value of type a.
14.1.7 Deriving Eq
The Eq type class contains the overloaded operators == (logical equality) and != (logical inequality):
When deriving(Eq) is present on a a user-defined type definition t, the compiler defines these
equality/inequality operators for values of type t. It is the natural recursive definition of these
operators, i.e.,
• If t is an enum type, two values of type t are equal if they represent the same enum constant.
• If t is a struct type, two values of type t are equal if the corresponding members are pairwise
equal.
• If t is a tagged union type, two values of type t are equal if they have the same tag (member
name) and the two corresponding member values are equal.
The predefined type class Bounded contains two overloaded identifiers minBound and maxBound rep-
resenting the minimum and maximum values of a type a:
The clause deriving(Bounded) can be attached to any user-defined enum definition t, and the
compiler will define the values minBound and maxBound for values of type t as the first and last enum
constants, respectively.
The clause deriving(Bounded) can be attached to any user-defined struct definition t with the
proviso that the type of each member is also an instance of Bounded. The compiler-defined minBound
(or maxBound) will be the struct with each member having its respective minBound (respectively,
maxBound).
Generally speaking, the deriving(...) clause can only be used for the predefined type classes Bits,
Eq and Bounded. However there is a special case where it can be used for any type class. When a
user-defined type t is isomorphic to an existing type t0 , then all the functions on t0 automatically
work on t, and so the compiler can trivially derive a function for t by just using the corresponding
function for t0 .
There are two situations where a newly defined type is isomorphic to an old type: a struct or tagged
union with precisely one member. For example:
One sometimes defines such a type precisely for type-safety reasons because the new type is distinct
from the old type although isomorphic to it, so that it is impossible to accidentally use a t value in
a t0 context and vice versa. Example:
The typedef could also have been written with a singleton tagged union instead of a singleton struct:
In BSV it is possible to write an expression whose value is a function value. These function values
can be passed as arguments to other functions, returned as results from functions, and even carried
in data structures.
Example - the function map, as defined in the package Vector (C.2.8):
return yvect;
endfunction: map
The function map is polymorphic, i.e., is defined for any size type vsize and value types a_type and
b_type. It takes two arguments:
• A function func with input of type a_type and output of type b_type.
• A vector xvect of size vsize containing values of type a_type.
Its result is a new vector yvect that is also of size vsize and containing values of type b_type,
such that yvect[j]=func(xvect[j]). In the last line of the example, we call map passing it the sqr
function and the vector avect to produce a vector bvect that contains the squared versions of all
the elements of vector avect.
Observe that in the last line, the expression sqr is a function-valued expression, representing the
squaring function. It is not an invocation of the sqr function. Similarly, inside map, the identifier
func is a function-valued identifier, and the expression func (xsize [j]) invokes the function.
The function map could be called with a variety of arguments:
or
In other words, map captures, in one definition, the generic idea of applying some function to all
elements of a vector and returning all the results in another vector. This is a very powerful idea
enabled by treating functions as first-class values. Here is another example, which may be useful in
many hardware designs:
endinterface: SearchableFIFO
The SearchableFIFO interface is like a normal FIFO interface (contains usual enq() and deq()
methods), but it has an additional bit of functionality. It has a search() method to which you
can pass a search key key, and it searches the FIFO using that key, returning True if the search
succeeds.
Inside the mkSearchableFIFO module, the method applies some element test predicate test_func to
each element of the FIFO and ORs all the results. The particular element-test function test_func to
be used is passed in as a parameter to mkSearchableFIFO. In one instantiation of mkSearchableFIFO
we might pass in the equality function for this parameter (“search this FIFO for this particular ele-
ment”). In another instantiation of mkSearchableFIFO we might pass in the “greater-than” function
(“search this FIFO for any element greater than the search key”). Thus, a single FIFO definition cap-
tures the general idea of being able to search a FIFO, and can be customized for different applications
by passing in different search functions to the module constructor.
A final important point is that all this is perfectly synthesizable in BSV, i.e., the compiler can
produce RTL hardware for such descriptions. Since polymporphic modules cannot be synthesized,
for synthesis a non-polymorphic version of the module would have to be instantiated.
| inputResetBVIStmt
| defaultResetBVIStmt
| noResetBVIStmt
| ouputResetBVIStmt
| ancestorBVIStmt
| sameFamilyBVIStmt
| scheduleBVIStmt
| pathBVIStmt
| inoutBVIStmt
The optional identifier immediately following the "BVI" is the name of the Verilog module to be
imported. This will usually be found in a Verilog file of the same name (identifier.v ). If this identifier
is excluded, it is assumed that the Verilog module name is the same as the BSV name of the module.
The moduleProto is the first line in the module definition as described in Section 5.3.
The BSV wrapper returns an interface. All arguments and return values must be in the Bits class
or be of type Clock, Reset, or a subinterface which meets these requirements. Note that the BSV
module’s parameters have no inherent relationship to the Verilog module’s parameters. The BSV
wrapper is used to connect the Verilog ports to the BSV parameters, performing any data conversion,
such as packs or unpacks, as necessary.
Example of the header of a BVI import statement:
Since the Verilog module’s name matches the BSV name, the header could be also written as:
import "BVI"
module RWire (VRWire#(a))
provisos (Bits#(a,sa));
...
endmodule: vMkRWire
The module body may contain both moduleStmts and importBVIStmts. Typically when including a
Verilog module, the only module statements would be a few local definitions. However, all module
statements, except for method definitions, sub-interface definitions, and return statements, are valid,
though most are rarely used in this instance. Only the statements specific to importBVIStmt bodies
are described in this section.
The importBVIStmts must occur at the end of the body, after the moduleStmts. They may be
written in any order.
The following is an example of embedding a Verilog SRAM model in BSV. The Verilog file is shown
after the BSV wrapper.
This is the Verilog module being wrapped in the above BVI import statement.
input clk;
input [ADDRESS_WIDTH-1:0] v_in_address;
input [DATA_WIDTH-1:0] v_in_data;
input v_in_write_not_read;
input v_in_enable;
15.2 Method
The method statement is used to connect methods in a Bluespec interface to the appropriate Verilog
wires. The syntax imitates a function prototype in that it doesn’t define, but only declares. In the
The BSV types of all the method’s arguments and its result (if any) must all be in the Bits typeclass.
Any of the port names may have an attribute attached to them. The allowable attributes are reg,
const, unused, and inhigh. The attributes are translated into port descriptions. Not all port
attributes are allowed on all ports.
For the output ports, the ready port and the method return value, the properties reg and const
are allowed. The reg attribute specifies that the value is coming directly from a register with no
intermediate logic. The const attribute indicates that the value is hardwired to a constant value.
For the input ports, the input arguments and the enable port, reg and unused are allowed. In this
context reg specifies that the value is immediately written to a register without intermediate logic.
The attribute unused indicates that the port is not used inside the module; its value is ignored.
Additionally, for the method enable, there is the inhigh property, which indicates that the method
is always_enabled, as described in Section 13.2.2. Inside the module, the value of the enable is
assumed to be 1 and, as a result, the port doesn’t exist. The user still gives a name for the port as
a placeholder. Note that only Action or ActionValue methods can have an enable signal.
The following code fragment shows an attribute on a method enable:
The output ports may be shared across methods (and ready signals).
The port statement declares an input port, which is not part of a method, along with the value to
be passed to the port. While parameters must be compile-time constants, ports can be dynamic.
The port statements are analogous to arguments to a BSV module, but are rarely needed, since
BSV style is to interact and pass arguments through methods.
portBVIStmt ::= port identifier [ clocked_by ( clockId ) ]
[ reset_by ( resetId ) ] = expression ;
The defining operator <- or = may be used.
The value of expression is supplied to the Verilog port named identifier. The type of expression
must be in the Bits typeclass. The expression may be dynamic (e.g. the _read method of a register
instantiated elsewhere in the module body), which differentiates it from a parameter statement. The
Bluespec compiler cannot check that the import has specified the same size as declared in the Verilog
module. If the width of the value is not the same as that expected by the Verilog module, Verilog
will truncate or zero-extend the value to fit.
Example - Setting port widths to a specific width:
The clocked_by clause is used to specify the clock domain that the port is associated with, named
by clockId. Any clock in the domain may be used. The values no_clock and default_clock, as
described in Section 15.5, may be used. If the clause is omitted, the associated clock is the default
clock.
Example - BVI import statement including port statements
The reset_by clause is used to specify the reset the port is associated with, named by resetId. Any
reset in the domain may be used. The values no_reset and default_reset, as described in Section
15.8 may be used. If the clause is omitted, the associated reset is the default reset.
The input_clock statement specifies how an incoming clock to a module is connected. Typically,
there are two ports, the oscillator and the gate, though the connection may use fewer ports.
inputClockBVIStmt ::= input_clock [ identifier ] ( [ portsDef ] ) = expression ;
portsDef ::= portId [ , [ attributeInstances ] portId ]
portId ::= identifier
The defining operator = or <- may be used.
The identifier is the clock name which may be used elsewhere in the import to associate the clock with
resets and methods via a clocked_by clause, as described in Sections 15.7 and 15.2. The portsDef
statement describes the ports that define the clock. The clock value which is being connected is
given by expression.
If the expression is an identifier being assigned with =, and the user wishes this to be the name of
the clock, then the identifier of the clock can be omitted and the expression will be assumed to be
the name. The clock name can be omitted in other circumstances, but then no name is associated
with the clock. An unamed clock cannot be referred to elsewhere, such as in a method or reset or
other statement. Example:
is equivalent to:
The user may leave off the gate (one port) or the gate and the oscillator (no ports). It is the
designer’s responsibility to ensure that not connecting ports does not lead to incorrect behavior. For
example, if the Verilog module is purely combinational, there is no requirement to connect a clock,
though there may still be a need to associate its methods with a clock to ensure that they are in
the correct clock domain. In this case, the portsDef would be omitted. Example of an input clock
without any connection to the Verilog ports:
If the clock port is specified and the gate port is to be unconnected, an attribute, either unused
or inhigh, describing the gate port should be specified. The attribute unused indicates that the
submodule doesn’t care what the unconnected gate is, while inhigh specifies the gate is assumed in
the module to be logical 1. It is an error if a clock with a gate that is not logical 1 is connected to
an input clock with an inhigh attribute. The default when a gate port is not specified is inhigh,
though it is recommended style that the designer specify the attribute explicitly.
To add an attribute, the usual attribute syntax, (* attribute_name *) immediately preceeding
the object of the attribute, is used. For example, if a Verilog module has no internal transitions and
responds only to method calls, it might be unnecessary to connect the gating signal, as the implicit
condition mechanism will ensure that no method is invoked if its clock is off. So the second portId,
for the gate port, would be marked unused.
The options for specifying the clock ports in the portsDef clause are:
In an input_clock statement, it is an error if both the port names and the input clock name are
omitted, as the clock is then unusable.
In BSV, each module has an implicit clock (the current clock ) which is used to clock all instantiated
submodules unless otherwise specified with a clocked_by clause. Other clocks to submodules must
be explicitly passed as input arguments.
Every BVI import module must declare which input clock (if any) is the default clock. This default
clock is the implicit clock provided by the parent module, or explicitly given via a clocked_by
clause. The default clock is also the clock associated with methods and resets in the BVI import
when no clocked_by clause is specified.
The simplest definition for the default clock is:
default_clock no_clock;
This statement indicates the implicit clock from the parent module is ignored (and not connected).
Consequently, the default clock for methods and resets becomes no_clock, meaning there is no
associated clock.
To save typing, you can merge the default_clock and input_clock statements into a single line:
defaultClockBVIStmt ::= default_clock [ identifier ] [ ( portsDef ) ] [ = expression ] ;
The defining operator = or <- may be used.
This is precisely equivalent to defining an input clock and then declaring that clock to be the default
clock. Example:
is equivalent to:
is equivalent to:
If the portnames are excluded, the names default to CLK, CLK_GATE. Example:
is equivalent to:
Alternately, if the expression is an identifier being assigned with =, and the user wishes this to be
the name of the default clock, then he can leave off the name of the default clock and expression
will be assumed to be the name. Example:
is equivalent to:
If an expression is provided, both the ports and the name cannot be omitted.
However, omitting the entire statement is equivalent to:
specifying that the current clock is to be associated with all methods which do not specify otherwise.
The output_clock statement gives the port connections for a clock provided in the module’s inter-
face.
outputClockBVIStmt ::= output_clock identifier [ ( portsDef ) ];
The identifier defines the name of the output clock, which must match a clock declared in the
module’s interface. Example:
interface ClockGenIfc;
interface Clock gen_clk;
endinterface
It is an error for the same identifier to be declared by more than one output_clock statement.
The input_reset statement defines how an incoming reset to the module is connected. Typically
there is one port. BSV assumes that the reset is inverted (the reset is asserted with the value 0).
inputResetBVIStmt ::= input_reset [ identifier ] [ ( portId ) ] [ clocked_by ( clockId ) ]
= expression ;
portId ::= identifier
clockId ::= identifier
where the = may be replaced by <-.
The reset given by expression is to be connected to the Verilog port specified by portId. The identifier
is the name of the reset and may be used elsewhere in the import to associate the reset with methods
via a reset_by clause.
The clocked_by clause is used to specify the clock domain that the reset is associated with, named
by clockId. Any clock in the domain may be used. If the clause is omitted, the associated clock is
the default clock. Example:
is equivalent to:
If the expression is an identifier being assigned with =, and the user wishes this to be the name of the
reset, then he can leave off the identifier of the reset and the expression will be assumed to be the
name. The reset name can be left off in other circumstances, but then no name is associated with
the reset. An unamed reset cannot be referred to elsewhere, such as in a method or other statement.
In the cases where a parent module needs to associate a reset with methods, but the reset is not
used internally, the statement may contain a name, but not specify a port. In this case, there is no
port expected in the Verilog module. Example:
default_reset no_reset;
The keyword default_reset may be omitted when declaring an unused reset. The above statement
can thus be written as:
This statement declares that the implicit reset from the parent module is ignored (and not con-
nected). In this case, the default reset for methods becomes no_reset, meaning there is no associated
reset.
To save typing, you can merge the default_reset and input_reset statements into a single line:
defaultResetBVIStmt ::= default_reset [ identifier ] [ ( portId ) ] [ clocked_by ( clockId ) ]
[ = expression ] ;
The defining operator = or <- may be used.
This is precisely equivalent to defining an input reset and then declaring that reset to be the default.
Example:
is equivalent to:
is equivalent to
The clocked_by clause is optional; if omitted, the reset is clocked by the default clock. Example:
is equivalent to
If the portId is excluded, the reset port name defaults to RST_N. Example:
is equivalent to:
Alternatively, if the expression is an identifier being assigned with =, and the user wishes this to be
the name of the default reset, then he can leave off the name of the default reset and expression will
be assumed to be the name. Example:
is equivalent to:
specifying that the current reset is to be associated with all methods which do not specify otherwise.
The output_reset statement gives the port connections for a reset provided in the module’s inter-
face.
outputResetBVIStmt ::= output_reset identifier [ ( portId ) ] [ clocked_by ( clockId ) ];
The identifier defines the name of the output reset, which must match a reset declared in the
module’s interface. Example:
interface ResetGenIfc;
interface Reset gen_rst;
endinterface
It is an error for the same identifier to be declared by more than one output_reset statement.
There are two statements for specifying the relationship between clocks: ancestor and same_family.
ancestorBVIStmt ::= ancestor ( clockId , clockId ) ;
This statement indicates that the second named clock is an ancestor of the first named clock. To
say that clock1 is an ancestor of clock2, means that clock2 is a gated version of clock1. This
is written as:
For clocks which do not have an ancestor relationship, but do share a common ancestor, we have:
sameFamilyBVIStmt ::= same_family ( clockId , clockId ) ;
This statement indicates that the clocks specified by the clockIds are in the same family (same clock
domain). When two clocks are in the same family, they have the same oscillator with a different
gate. To be in the same family, one does not have to be a gated version of the other, instead they
may be gated versions of a common ancestor. Note that ancestor implies same_family, which then
need not be explicitly stated. For example, a module which gates an input clock:
15.11 Schedule
| SBR
| C
The schedule statement specifies the scheduling constraints between methods in an imported mod-
ule. The operators relate two sets of methods; the specified relation is understood to hold for each
pair of an element of the first set and an element of the second set. The order of the methods in the
lists is unimportant and the parentheses may be omitted if there is only one name in the set.
CF conflict-free
SB sequences before
SBR sequences before, with range conflict (that is, not composable in parallel)
C conflicts
It is an error to specify two relationships for the same pair of methods. It is an error to specify a
scheduling annotation other than CF for methods clocked by unrelated clocks. For such methods,
CF is the default; for methods clocked by related clocks the default is C. The compiler generates a
warning if an annotation between a method pair is missing. Example:
15.12 Path
The path statement indicates that there is a combinational path from the first port to the second
port.
pathBVIStmt ::= path ( portId , portId ) ;
It is an error to specify a path between ports that are connected to methods clocked by unrelated
clocks. This would be, by definition, an unsafe clock domain crossing. Note that the compiler
assumes that there will be a path from a value or ActionValue method’s input parameters to its
result, so this need not be specified explicitly.
The paths defined by the path statement are used in scheduling. A path may impact rule urgency
by implying an order in how the methods are scheduled. The path is also used in checking for
combinational cycles in a design. The compiler will report an error if it detects a cycle in a design.
In the following example, there is a path declared between WSET and WHAS, as shown in figure 2.
Figure 2: Path in the RWire0 Verilog module between WSET and WHAS ports
15.13 Inout
The following statements describe how to pass an inout port from a wrapped Verilog module through
a BSV module. These ports are represented in BSV by the type Inout. There are two ways that
an Inout can appear in BSV modules: as an argument to the module or as a subinterface of the
interface provided by the module. There are, therefore, two ways to declare an Inout port in a
BVI import: the statement inout declares an argument of the current module; and the statement
ifc_inout declares a subinterface of the provided interface.
inoutBVIStmt ::= inout portId [ clocked_by ( clockId ) ]
[ reset_by ( resetId ) ] = expression ;
The value of portId is the Verilog name of the inout port and expression is the name of an argument
from the module.
inoutBVIStmt ::= ifc_inout identifier (inoutId ) [ clocked_by ( clockId ) ]
[ reset_by ( resetId ) ] ;
Here, the identifier is the name of the subinterface of the provided interface and portId is, again,
the Verilog name of the inout port.
The clock and reset associated with the Inout are assumed to be the default clock and default reset
unless explicitly specified.
Example:
interface Q;
interface Inout#(Bit#(13)) q_inout;
interface Clock c_clock;
endinterface
inout iport = x;
ifc_inout q_inout(qport);
output_clock c_clock(clockport);
endmodule
This defines a function identifier in the BSV source code which is implemented by a C function of
the same name. A different link name (C name) can be specified immediately after the "BDPI",
using an optional [identifier = ]. The link name is not bound by BSV case-restrictions on identifiers
and may start with a capital letter.
Example of an import statement where the C name matches the BSV name:
// the C function and the BSV function are both named checksum
import "BDPI" function Bit#(32) checksum (Bit#(n), Bit#(32));
Example of an import statement where the C name does not match the BSV name:
The first type specifies the return type of the function. The optional CFuncArgs specify the argu-
ments of the function, along with an optional identifier to name the arguments.
For instance, in the above checksum example, you might want to name the arguments to indicate
that the first argument is the input value and the second argument is the size of the input value.
The importBDPI syntax provides the ability to import simple C functions that the user may already
have. A C function with an argument of type char or unsigned char should be imported as a BSV
function with an argument of type Bit#(8). For int or unsigned int, use Bit#(32). For long
long or unsigned long long, use Bit#(64). While BSV creates unsigned values, they can be
passed to a C function which will treat the value as signed. This can be reflected in BSV with
Int#(8), Int#(32), Int#(64), etc.
The user may also import new C functions written to match a given BSV function type. For instance,
a function on bit-vectors of size 17 (that is, Bit#(17)) would expect to pass this value as the C type
unsigned int and the C function should be aware that only the first 17 bits of the value are valid
data.
Wide data Bit vectors of size 65 or greater are passed by reference, as type unsigned int*. This
is a pointer to an array of 32-bit words, where bit 0 of the BSV vector is bit 0 of the first word in
the array, and bit 32 of the BSV vector is bit 0 of the second word, etc. Note that we only pass the
pointer; no size value is passed to the C function. This is because the size is fixed and the C function
could have the size hardcoded in it. If the function needs the size as an additional parameter, then
either a C or BSV wrapper is needed. See the examples below.
Polymorphic data As the above table shows, bit vectors of variable size are passed by reference,
as type unsigned int*. As with wide data, this is a pointer to an array of 32-bit words, where bit
0 of the BSV vector is bit 0 of the first word in the array, and bit 32 of the BSV vector is bit 0 of
the second word, etc. No size value is passed to the C function, because the import takes no stance
on how the size should be communicated. The user will need to handle the communication of the
size, typically by adding an additional argument to the import function and using a BSV wrapper
to pass the size via that argument, as follows:
Imported functions can be value functions, Action functions, or ActionValue functions. The ac-
ceptable return types are the same as the acceptable argument types, except that String is not
permitted as a return type.
Imported functions with return values correlate to C functions with return values, except in the
cases of wide and polymorphic data. In those cases, where the BSV type correlates to unsigned
int*, the simulator will allocate space for the return result and pass a pointer to this memory to
the C function. The C function will not be responsible for allocating memory. When the C function
finishes execution, the simulator copies the result in that memory to the simulator state and frees
the memory. By convention, this special argument is the first argument to the C function.
For example, the following BSV import:
So far we have only mentioned Bit and String types for arguments and return values. Other types
are allowed as arguments and return values, as long as they can be packed into a bit-vector. These
types include Int, UInt, Bool, and Maybe, all of which have an instance in the Bits class.
For example, this is a valid import:
Since a Bool packs to a Bit#(1), it would connect to a C function such as the following:
unsigned char
my_and (unsigned char x, unsigned char y);
In this next example, we have two C functions, signedGT and unsignedGT, both of which implement
a greater-than function, returning a Bool indicating whether x is greater than y.
Because the function signedGT assumes that the MSB is a sign bit, we use the type-system to make
sure that we only call that function on signed values by specifying that the function only works on
Int#(32). Similarly, we can enforce that unsignedGT is only called on unsigned values, by requiring
its arguments to be of type UInt#(32).
The C functions would be:
In both cases, the packed value is of type Bit#(32), and so the C function is expected to take the its
arguments as unsigned int. The difference is that the signedGT function will then treat the values
as signed values while the unsignedGT function will treat them as unsigned values. Both functions
return a Bool, which means the C return type is unsigned char.
Argument and return types to imported functions can also be structs, enums, and tagged unions.
The C function will receive the data in bit form and must return values in bit form.
Shared resources In some situations, several imported functions may share access to a resource,
such as memory or the file system. If these functions wish to share file handles, pointers, or other
cookies between each other, they will have to pass the data as a bit-vector, such as unsigned
int/Bit#(32).
When to use Action components If an imported function has a side effect or if it matters
how many times or in what order the function is called (relative to other calls), then the imported
function should have an Action component in its BSV type. That is, the functions should have a
return type of Action or ActionValue.
Removing indirection for polymorphism within a range A polymorphic type will always
become unsigned int* in the C, even if there is a numeric proviso which restricts the size. Consider
the following import:
This is a polymorphic vector, so the conversion rules indicate that it should appear as unsigned
int* in the C. However, the proviso indicates that the value of n can never be greater than 32. To
make the import be a specific size and not a pointer, you could use a wrapper, as in the example
below.
References
[Acc04] Accellera. SystemVerilog 3.1a Language Reference Manual: Accellera’s Extensions to Ver-
ilog (R), 2004. See: www.accelera.org, www.systemverilog.org.
[IEE01] IEEE. IEEE Standard Verilog (R) Hardware Description Language, March 2001. IEEE Std
1364-2001.
[IEE02] IEEE. IEEE Standard VHDL Language Reference Manual, IEEE Std 1076-1993, 2002.
[Ter03] Terese. Term Rewriting Systems. Cambridge University Press, 2003.
A Keywords
In general, keywords do not use uppercase letters (the only exception is the keyword valueOf). The
following are the keywords in BSV (and so they cannot be used as identifiers).
Action
ActionValue
BVI
C
CF
SB
SBR
action endaction
actionvalue endactionvalue
ancestor
begin
bit
case endcase
clocked_by
default
default_clock
default_reset
dependencies
deriving
determines
else
enable
end
enum
export
for
function endfunction
if
ifc_inout
import
inout
input_clock
input_reset
instance endinstance
interface endinterface
let
match
matches
method endmethod
module endmodule
numeric
output_clock
output_reset
package endpackage
parameter
path
port
provisos
reset_by
return
rule endrule
rules endrules
same_family
schedule
struct
tagged
type
typeclass endtypeclass
typedef
union
valueOf
valueof
void
while
The following are keywords in SystemVerilog (which includes all the keywords in Verilog). Although
most of them are not used in BSV, for compatibility reasons they are not allowed as identifiers in
BSV either.
B.1.1 Bits
Bits defines the class of types that can be converted to bit vectors and back. Membership in this
class is required for a data type to be stored in a state, such as a Register or a FIFO, or to be used
at a synthesized module boundary. Often instance of this class can be automatically derived using
the deriving statement.
typeclass Bits #(type a, numeric type n)
function Bit#(n) pack(a x);
function a unpack(Bit#(n) x);
endtypeclass
Bits Functions
pack Converts element a of datatype data_t to a element of datatype
Bit#() of size_a.
B.1.2 Eq
Eq defines the class of types whose values can be compared for equality. Instances of the Eq class
are often automatically derived using the deriving statement.
The equality functions == and != are Boolean functions which return a value of True if the equality
condition is met. When defining an instance of an Eq typeclass, the \== and \/= notations must be
used. If using or referring to the functions, the standard Verilog operators == and != may be used.
Eq Functions
== Returns True if x is equal to y.
function Bool \== (data_t x, data_t y,);
B.1.3 Literal
Literal defines the class of types which can be created from integer literals.
The fromInteger function converts an Integer into an element of datatype data_t. Whenever you
write an integer literal in BSV(such as “0” or “1”), there is an implied fromInteger applied to it,
which turns the literal into the type you are using it as (such as Int, UInt, Bit, etc.). By defining
an instance of Literal for your own datatypes, you can create values from literals just as for these
predefined types.
The typeclass also provides a function inLiteralRange that takes an argument of the target type
and an Integer and returns a Bool that indicates whether the Integer argument is in the legal
range of the target type. For example, assuming x has type Bit#(4), inLiteralRange(x, 15)
would return True, but inLiteralRange(x,22) would return False.
Literal Functions
fromInteger Converts an element x of datatype Integer into an element of data
type data_t
function data_t fromInteger(Integer x);
B.1.4 RealLiteral
RealLiteral defines the class of types which can be created from real literals.
typeclass RealLiteral #(type data_t);
function data_t fromReal(Real x);
endtypeclass
The fromReal function converts a Real into an element of datatype data_t. Whenever you write
a real literal in BSV(such as “3.14”), there is an implied fromReal applied to it, which turns the
real into the specified type. By defining an instance of RealLiteral for a datatype, you can create
values from reals for any type.
RealLiteral Functions
fromReal Converts an element x of datatype Real into an element of data
type data_t
function data_t fromReal(Real x);
B.1.5 Arith
Arith defines the class of types on which arithmetic operations are defined.
typeclass Arith #(type data_t)
provisos (Literal#(data_t));
function data_t \+ (data_t x, data_t y);
function data_t \- (data_t x, data_t y);
function data_t negate (data_t x);
function data_t \* (data_t x, data_t y);
function data_t \/ (data_t x, data_t y);
function data_t \% (data_t x, data_t y);
function data_t abs (data_t x);
function data_t signum (data_t x);
function data_t \** (data_t x, data_t y);
function data_t exp_e (data_t x);
function data_t log (data_t x);
function data_t logb (data_t b, data_t x);
function data_t log2 (data_t x);
function data_t log10 (data_t x);
endtypeclass
The Arith functions provide arithmetic operations. For the arithmetic symbols, when defining an
instance of the Arith typeclasss, the escaped operator names must be used as shown in the tables
below. The negate name may be used instead of the operator for negation. If using or referring to
these functions, the standard (non-escaped) Verilog operators can be used.
Arith Functions
+ Element x is added to element y.
function data_t \+ (data_t x, data_t y);
negate Change the sign of the number. When using the function the Ver-
ilog negate operator, -, may be used.
-
function data_t negate (data_t x);
* Element x is multiplied by y.
function data_t \* (data_t x, data_t y);
Note: Division by 0 is undefined. Both x/0 and x%0 will generate errors at compile-time and
run-time for most instances.
signum Returns a unit value with the same sign as x, such that
abs(x)*signum(x) = 1. signum(12) returns 1 and signum(-12)
returns -1.
function data_t signum (data_t x);
B.1.6 Ord
Ord defines the class of types for which an order is defined, which allows comparison operations.
The Ord functions are Boolean functions which return a value of True if the comparison condition
is met.
Ord Functions
< Returns True if x is less than y.
function Bool \< (data_t x, data_t y);
B.1.7 Bounded
Bounded defines the class of types with a finite range and provides functions to define the range.
The Bounded functions minBound and maxBound define the minimum and maximum values for the
type data_t.
Bounded Functions
minBound The minimum value the type data_t can have.
data_t minBound;
B.1.8 Bitwise
Bitwise defines the class of types on which bitwise operations are defined.
The Bitwise functions compare two operands bit by bit to calculate a result. That is, the bit in the
first operand is compared to its equivalent bit in the second operand to calculate a single bit for the
result.
Bitwise Functions
& Performs an and operation on each bit in x1 and x2 to calculate
the result.
function data_t \& (data_t x1, data_t x2);
B.1.9 BitReduction
BitReduction defines the class of types on which the Verilog bit reduction operations are defined.
typeclass BitReduction #(type x, numeric type n)
function x#(1) reduceAnd (x#(n) d);
function x#(1) reduceOr (x#(n) d);
function x#(1) reduceXor (x#(n) d);
function x#(1) reduceNand (x#(n) d);
function x#(1) reduceNor (x#(n) d);
function x#(1) reduceXnor (x#(n) d);
endtypeclass
BitReduction Functions
reduceAnd Performs an and bit reduction operation between the elements of
d to calculate the result.
&
function x#(1) reduceAnd (x#(n) d);
B.1.10 BitExtend
The BitExtend operations take as input of one size and changes it to an input of another size, as
described in the tables below. It is recommended that extend be used in place of zeroExtend or
signExtend, as it will automatically perform the correct operation based on the data type of the
argument.
BitExtend Functions
extend Performs either a zeroExtend or a signExtend as appropriate, de-
pending on the data type of the argument (zeroExtend for an un-
signed argument, signExtend for a signed argument).
function x#(n) extend (x#(m) d)
provisos (Add#(k, m, n));
truncate Removes bits from the MSB of argument d of size n to make the
datatype size m.
function x#(m) truncate (x#(n) d)
provisos (Add#(k, n, m));
B.2.1 Bit
Nat The data type Nat is defined as a 32 bit wide bit-vector. This is a
special case of Bit.
typedef Bit#(32) Nat;
The Bit data type provides functions to concatenate and split bit-vectors.
Bit Functions
{x,y} Concatenate two bit vectors, x of size n and y of size m returning a
bit vector of size k. The Verilog operator { } is used.
function Bit#(k) bitconcat(Bit#(n) x, Bit#(m) y)
provisos (Add#(n, m, k));
split Split a bit vector into two bit vectors (higher-order bits (n), lower-
order bits (m)).
function Tuple2 #(Bit#(n), Bit#(m)) split(Bit#(k) x)
provisos (Add#(n, m, k));
B.2.2 UInt
B.2.3 Int
B.2.4 Integer
The Integer type is a data type used for integer values and functions. Because Integer is not
part of the Bits typeclass, the Integer type is used for static elaboration only; all values must be
resolved at compile time.
Integer Functions
div Element x is divided by element y and the result is rounded toward
negative infinity. Division by 0 is undefined.
function Integer div(Integer x, Integer y);
mod Element x is divided by element y using the div function and the
remainder is returned as an Integer value. div and mod satisfy the
identity (div(x, y)∗y)+mod(x, y) == x. Division by 0 is undefined.
function Integer mod(Integer x, Integer y);
rem Element x is divided by element y using the quot function and the
remainder is returned as an Integer value. quot and rem satisfy
the identity (quot(x, y) ∗ y) + rem(x, y) == x. Division by 0 is
undefined.
function Integer rem(Integer x, Integer y);
B.2.5 Bool
The Bool type is defined to have two values, True and False.
typedef enum {False, True} Bool;
Bool Functions
not Returns True if x is false, returns False if x is true.
!
function Bool not (Bool x);
B.2.6 Real
The Real type is a data type used for real values and functions.
Real numbers are of the form:
real number ::= [ sign ]unsign num[ .unsign num ] exp [ sign ]unsign num
| [ sign ]unsign num.unsign num
sign ::= + | -
exp ::= e | E
unsign num ::= decimal digit { [ ]decimal digit }
decimal digit ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
If there is a decimal point, there must be digits following the decimal point. An exponent can start
with either an E or an e, followed by an optional sign (+ or -), followed by digits. There cannot be an
exponent or a sign without any digits. Any of the numeric components may include an underscore,
but an underscore cannot be the first digit of the number.
Unlike Integer, Real numbers are of limited precision. They are represented as IEEE floating point
numbers of 64 bit length, as defined by the IEEE standard.
Because Real is not part of the Bits typeclass, the Real type is used for static elaboration only; all
values must be resolved at compile time.
There are many functions defined for Real types, provided in the Real package (Section C.4.1). To
use these functions, the Real package must be imported.
There are two system tasks defined for the Real data type, used to convert between Real and IEEE
standard 64-bit vector representation (Bit#(64)).
B.2.7 String
Strings are mostly used in system tasks (such as $display). The String type belongs to the Eq type
class; strings can be tested for equality and inequality using the == and != operators. The String
type is also part of the Arith class, but only the addition (+) operator is defined. All other Arith
operators will produce an error message.
String Functions
strConcat Concatenates two elements of type String, x and y.
+
function String strConcat(String x, String y);
B.2.8 Fmt
The Fmt primitive type provides a representation of arguments to the $display family of system
tasks (Section 12.8.1) that can be manipulated in BSV code. Fmt representations of data objects
can be written hierarchically and applied to polymorphic types.
Objects of type Fmt can be supplied directly as arguments to system tasks in the $display family.
An object of type Fmt is returned by the $format (Section 12.8.2) system task.
The Fmt type is part of the Arith class, but only the addition (+) operator is defined. All other
Arith operators will produce an error message.
B.2.9 Maybe
The Maybe type is used for tagging values as either Valid or Invalid. If the value is Valid, the value
contains a datatype data_t.
typedef union tagged {
void Invalid;
data_t Valid;
} Maybe #(type data_t) deriving (Eq, Bits);
The Maybe data type provides functions to check if the value is Valid and to extract the valid value.
Maybe Functions
fromMaybe Extracts the Valid value out of a Maybe argument. If the tag is
Invalid the default value, defaultval, is returned.
function data_t fromMaybe( data_t defaultval,
Maybe#(data_t) val ) ;
B.2.10 Tuples
Tuples are predefined structures which group a small number of values together. The following
pseudocode explains the structure of the tuples. You cannot define your own tuples, but must use
the six predefined tuples, Tuple2 through Tuple7. As shown, Tuple2 groups two items together,
Tuple3 groups three items together, up through Tuple7 which groups seven items together.
typedef struct{
a tpl_1;
b tpl_2;
} Tuple2 #(type a, type b) deriving (Bits, Eq, Bounded);
typedef struct{
a tpl_1;
b tpl_2;
c tpl_3;
} Tuple3 #(type a, type b, type c) deriving (Bits, Eq, Bounded);
typedef struct{
a tpl_1;
b tpl_2;
c tpl_3;
d tpl_4;
} Tuple4 #(type a, type b, type c, type d) deriving (Bits, Eq, Bounded);
typedef struct{
a tpl_1;
b tpl_2;
c tpl_3;
d tpl_4;
e tpl_5;
} Tuple5 #(type a, type b, type c, type d, type e)
deriving (Bits, Eq, Bounded);
typedef struct{
a tpl_1;
b tpl_2;
c tpl_3;
d tpl_4;
e tpl_5;
f tpl_6;
} Tuple6 #(type a, type b, type c, type d, type e, type f)
deriving (Bits, Eq, Bounded);
typedef struct{
a tpl_1;
b tpl_2;
c tpl_3;
d tpl_4;
e tpl_5;
f tpl_6;
g tpl_7;
} Tuple7 #(type a, type b, type c, type d, type e, type f, type g)
deriving (Bits, Eq, Bounded);
Tuples cannot be manipulated like normal structures; you cannot create values of and select fields
from tuples as you would a normal structure. Values of these types can be created only by applying
a predefined family of constructor functions.
Fields of these types can be extracted only by applying a predefined family of selector functions.
B.2.11 Clock
Clock is an abstract type of two components: a single Bit oscillator and a Bool gate.
typedef ... Clock ;
Clock is in the Eq type class, meaning two values can be compared for equality.
B.2.12 Reset
Reset is in the Eq type class, meaning two fields can be compared for equality.
B.2.13 Inout
An Inout type is a first class type that is used to pass Verilog inouts through a BSV module.
Inout#(a);
Inout#(int) foo;
An Inout type is a valid subinterface type (like Clock and Reset). A value of an Inout type is
clocked_by and reset_by a particular Clock and Reset.
Inouts are connectable via the Connectable typeclass. The use of mkConnection instantiates a
Verilog module InoutConnect. The Inouts must be on the same clock and the same reset. The
clock and reset of the Inouts may be different than the clock and reset of the parent module of the
mkConnection.
instance Connectable#(Inout#(a, x1), Inout#(a, x2))
provisos (Bit#(a,sa));
B.2.14 Action/ActionValue
Any expression that is intended to act on the state of the circuit (at circuit execution time) is called
an action and has type Action or ActionValue#(a). The type parameter a represents the type of
the returned value.
The types Action and ActionValue are special keywords, and therefore cannot be redefined.
typedef · · · abstract · · · struct ActionValue#(type a);
Action Functions
noAction An empty Action, this is an Action that does nothing.
function Action noAction();
B.2.15 Rules
A rule expression has type Rules and consists of a collection of individual rule constructs. Rules
are first class objects, hence variables of type Rules may be created and manipulated. Rules values
must eventually be added to a module in order to appear in synthesized hardware.
The Rules data type provides functions to create, manipulate, and combine values of the type Rules.
Rules Functions
emptyRules An empty rules variable.
function Rules emptyRules();
addRules Takes rules r and adds them into a module. This function may only
be called from within a module. The return type void indicates
that the instantiation does not return anything.
function module addRules#(Rules r) (void);
rJoinPreempts Union of two sets of rules, with rules on the left getting scheduling
precedence and blocking the rules on the right.That is, if a rule in
set x fires, then all rules in set y are prevented from firing. This is
the same as specifying descending_urgency plus a forced conflict.
function Rules rJoinPreempts(Rules x, Rules y);
rJoinDescendingUrgency
Union of two sets of rule, with rules in the left having higher urgency.That
is, if some rules compete for resources, then scheduling will select rules in
set x set before set y. If the rules do not conflict, no conflict is added; the
rules can fire together.
function Rules rJoinDescendingUrgency(Rules x, Rules y);
rJoinMutuallyExclusive
Union of two sets of rule, with rules in the all rules in the left set anno-
tated as mutually exclusive with all rules in the right set.No relationship
between the rules in the left set or between the rules in the right set
is assumed. This annotation is used in scheduling and checked during
simulation.
function Rules rJoinMutuallyExclusive(Rules x, Rules y);
rJoinExecutionOrder
Union of two sets of rule, with the rules in the left set executing before the
rules in the right set.No relationship between the rules in the left set or
between the rules in the right set is assumed. If any pair of rules cannot
execute in the specified order in the same clock cycle, that pair of rules
will conflict.
function Rules rJoinExecutionOrder(Rules x, Rules y);
rJoinConflictFree
Union of two sets of rule, with the rules in the left set annotated as
conflict-free with the rules in the right set. This assumption is used during
scheduling and checked during simulation. No relationship between the
rules in the left set or between the rules in the right set is assumed.
function Rules rJoinConflictFree(Rules x, Rules y);
These classes are used in provisos to express constraints between the sizes of types.
function Vector#(mvsize,element_type)
concat(Vector#(m,Vector#(n,element_type)) xss)
provisos (Mul#(m,n,mvsize)); // m * n = mvsize
These type functions are used when “defining” size relationships between data types, where the
defined value need not (or cannot) be named in a proviso. They may be used in datatype definition
statements when the size of the datatype may be calculated from other parameters.
Prelude provides these pseudo-functions to convert between types and numeric values. The pseudo-
function valueof (or valueOf) is used to convert a numeric type into the corresponding Integer
value. The pseudo-function SizeOf is used to convert a type t into the numeric type representing
its bit size.
Example:
module mkFoo (Foo#(n));
UInt#(n) x;
Integer y = valueOf(n);
endmodule
SizeOf Converts a type into a numeric type representing its bit size.
function t SizeOf#(any_type)
provisos (Bits#(any_type, sa)) ;
Example:
any_type x = structIn;
Bit#(SizeOf#(any_type)) = pack(structIn);
B.4.1 Reg
The most elementary module available in BSV is the register, which has a Reg interface. Registers
are polymorphic, i.e., in principle they can hold a value of any type but, of course, ultimately registers
store bits. Thus, the provisos on register modules indicate that the type of the value stored in the
register must be in the Bits type class, i.e., the operations pack and unpack are defined on the type
to convert into bits and back.
Note that all Bluespec registers are considered atomic units, which means that even if one bit is
updated (written), then all the bits are considered updated. This prevents multiple rules from
updating register fields in an inconsistent manner.
Interfaces and Methods
The Reg interface contains two methods, _write and _read.
interface Reg #(type a_type);
method Action _write(a_type x1);
method a_type _read();
endinterface: Reg
The _write and _read methods are rarely used. Instead, for writes, one uses the non-blocking
assignment notation and, for reads, one just mentions the register interface in an expression.
Reg Interface
Method Arguments
Name Type Description Name Description
_write Action writes a value x1 x1 data to be written
_read a_type returns the value of the
register
Modules
Prelude provides three modules to create a register: mkReg creates a register with a given reset value,
mkRegU creates a register without any reset, and mkRegA creates a register with a given reset value
and with asynchronous reset logic.
mkReg Make a register with a given reset value. Reset logic is synchronous.
module mkReg#(a_type resetval)(Reg#(a_type))
provisos (Bits#(a_type, sizea));
mkRegU Make a register without any reset; initial simulation value is alternating
01 bits.
module mkRegU(Reg#(a_type))
provisos (Bits#(a_type, sizea));
mkRegA Make a register with a given reset value. Reset logic is asynchronous.
module mkRegA#(a_type resetval)(Reg#(a_type))
provisos (Bits#(a_type, sizea));
Scheduling Annotations
mkReg, mkRegU, mkRegA
read write
read CF SB
write SA SBR
Functions
Three functions are provided for using registers: asReg returns the register interface instead of the
value of the register; readReg reads the value of a register, useful when managing arrays or lists of
registers; and writeReg to write a value into a register, also useful when managing arrays or lists of
registers.
asReg Treat a register as a register, i.e., suppress the normal behavior where the
interface name implicitly represents the value that the register contains
(the _read value). This function returns the register interface, not the
value of the register.
function Reg#(a_type) asReg(Reg#(a_type) regIfc);
readReg Read the value out of a register. Useful for giving as the argument to
higher-order array and list functions.
function a_type readReg(Reg#(a_type) regIfc);
writeReg Write a value into a register. Useful for giving as the argument to higher-
order array and list functions.
function Action writeReg(Reg#(a_atype) regIfc, a_type din);
B.4.2 RWire
An RWire is a primitive stateless module whose purpose is to allow data transfer between methods
and rules without the cycle latency of a register. That is, a RWire may be written in a cycle and
that value can be read out in the same cycle; values are not stored across clock cycles.
Interfaces and Methods
The RWire interface is conceptually similar to a register’s interface, but the output value is wrapped
in a Maybe type. The wset method places a value on the wire and sets the valid signal. The read-like
method, wget, returns the value and a valid signal in a Maybe type. The output is only Valid if a
write has a occurred in the same clock cycle, otherwise the output is Invalid.
RWire Interface
Method Arguments
Name Type Description Name Description
wset Action writes a value and sets the valid datain data to be sent
signal on the wire
wget Maybe returns the value and the valid
signal
Scheduling Annotations
mkRWire
wget wset
wget CF SA
wset SB C
B.4.3 Wire
The Wire interface and module are simular to RWire, but the valid bit is hidden from the user and
the validity of the read is considered an implicit condition. The Wire interface works like the Reg
interface, so mentioning the name of the wire gets (reads) its contents whenever they’re valid, and
using <= writes the wire. Wire is an RWire that is designed to be interchangeable with Reg. You
can replace a Reg with an Wire without changing the syntax.
Modules
The mkWire module is provided to create a Wire.
Scheduling Annotations
mkWire
read write
read CF SA
write SB C
B.4.4 BypassWire
BypassWire is an implementation of the Wire interface where the _write method is an always_enabled
method. The compiler will issue a warning if the method does not appear to be called every clock
cycle. The advantage of this tradeoff is that the _read method of this interface does not carry
any implicit condition (so it can satisfy a no_implicit_conditions assertion or an always_ready
method).
Scheduling Annotations
mkBypassWire
read write
read CF SA
write SB C
B.4.5 DWire
DWire is an implementation of the Wire interface where the _read method is an always_ready
method and thus has no implicit conditions. Unlike the BypassWire however, the _write method
need not be always enabled. On cycles when a DWire is written to, the _read method returns that
value. On cycles when no value is written, the _read method instead returns a default value that is
specified as an argument during instantiation.
Scheduling Annotations
mkDWire
read write
read CF SA
write SB C
B.4.6 PulseWire
PulseWire Interface
Name Type Description
send Action sends a signal down the wire
_read Bool returns the valid signal
interface PulseWire;
method Action send();
method Bool _read();
endinterface
Modules
The mkPulseWire and mkPulseWireOR modules are provided to create a PulseWire. The mkPulseWireOR
is nearly identical to the mkPulseWire module except that the send method in the mkPulseWireOR
does not conflict with itself. Calling the send method for a mkPulseWire from 2 rules causes the two
rules to conflict while in the mkPulseWireOR there is no conflict. In other words, the mkPulseWireOR
acts a logical ”OR”.
mkPulseWire The writing to this type of wire is used in rules and action methods
to send a single bit to signal other methods or rules in the same
clock cycle.
module mkPulseWire(PulseWire);
mkPulseWireOR Returns a PulseWire which acts like a logical ”Or”. The send
method of the same wire can be used in two different rules without
conflict.
module mkPulseWireOR(PulseWire);
Scheduling Annotations
mkPulseWire
read send
read CF SA
send SB C
Scheduling Annotations
mkPulseWireOR
read send
read CF SA
send SB SBR
module mkCounter(Counter#(size_t));
Reg#(Bit#(size_t)) value <- mkReg(0); // define a Reg
B.4.7 ReadOnly
ReadOnly is an interface which provides a value. The _read shorthand can be used to read the
value.
Interfaces and Methods
ReadOnly Interface
Method Arguments
Name Type Description Name Description
_read a_type Reads the data a_type Data to be read, of
datatype type.
Example - using function constFn to set the initial values of the registers in a list:
parity Returns the parity of the bit argument v. Example: parity( 5’b1)
= 1, parity( 5’b3) = 0;
function Bit#(1) parity(Bit#(n) v);
countOnes Returns the count of the number of 1’s in the bit vector bin.
function UInt#(lgn1) countOnes ( Bit#(n) bin )
provisos (Add#(1, n, n1), Log#(n1, lgn1),
Add#(1, xx, lgn1) );
countZerosMSB For the bit vector bin, count the number of 0s until the first 1,
starting from the most significant bit (MSB).
function UInt#(lgn1) countZerosMSB ( Bit#(n) bin )
provisos (Add#(1, n, n1), Log#(n1, lgn1) );
countZerosLSB For the bit vector bin, count the number of 0s until the first 1,
starting from the least significant bit (LSB).
function UInt#(lgn1) countZerosLSB ( Bit#(n) bin )
provisos (Add#(1, n, n1), Log#(n1, lgn1) );
The Environment section of the Prelude contains some value definitions that remain static within a
compilation, but may vary between compilations.
Test whether the compiler is generating C.
compilerVersion Returns a String containing the compiler version. This si the same
string used with the -v flag.
String compilerVersion;
Example:
the statement:
$display("compilerversion = %d", compilerVersion);
produces this output:
Bluespec Compiler, version 3.8.56 (build 7084, 2005-07-22)
Example:
the statement:
$display("date = %s", date);
produces this output:
"Mon Feb 6 08:39:59 EST 2006"
C Foundation Libraries
Section B defined the Standard Prelude package, which is automatically imported into every package.
This section describes BSV’s large and continuously growing collection of AzureIPTM Foundation
libraries. To use any of these libraries in a package you must explicitly import the package using an
import clause.
Bluespec’s AzureIPTM intellectual property (IP) accelerates hardware design and modeling. There
are two AzureIP library families, Foundation and Premium:
• Foundation is an extensive family of components, types and functions that are included with
the Bluespec toolsets for use in your models and designs – they serve as a foundational base
for your modeling and implementation work.
• Premium is the designation for Bluespec’s fee-based AzureIP.
Package Name
import RegFile :: * ;
Description
This package provides 5-read-port 1-write-port register array modules.
Note: In a design that uses RegFiles, some of the read ports may remain unused. This may generate
a warning in various downstream tool. Downstream tools should be instructed to optimize away the
unused ports.
Interfaces and Methods
The RegFile package defines one interface, RegFile. The RegFile interface provides two methods,
upd and sub. The upd method is an Action method used to modify (or update) the value of an
element in the register file. The sub method (from ”sub”script) is a Value method which reads and
returns the value of an element in the register file. The value returned is of a datatype data_t.
Method Arguments
Name Type Description Name Description
upd Action Change or update an el- addr index of the element to be
ement within the register changed, with a datatype of
file. index_t
d new value to be stored, with a
datatype of data_t
sub data t Read an element from addr index of the element, with a
the register file and re- datatype of index_t
turn it.
Modules
The RegFile package provides three modules: mkRegFile creates a RegFile with registers allocated
from the lo_index to the hi_index; mkRegFileFull creates a RegFile from the minimum index to
the maximum index; and mkRegFileWCF creates a RegFile from lo_index to hi_index for which
the reads and the write are scheduled conflict-free. There is a second set of these modules, the
RegFileLoad variants, which take as an argument a file containing the initial contents of the array.
mkRegFileFull Create a RegFile from min to max index where the index is of a datatype
index_t and the elements are of datatype data_t. The min and max are
specified by the Bounded typeclass instance (0 to N-1 for N-bit numbers).
module mkRegFileFull#( RegFile#(index_t, data_t) )
provisos (Bits#(index_t, size_index),
Bits#(data_t, size_data)
Bounded#(index_t) );
mkRegFileWCF Create a RegFile from lo_index to hi_index for which the reads and the
write are scheduled conflict-free. For the implications of this scheduling,
see the documentation for ConfigReg (Section C.1.5).
module mkRegFileWCF#( index_t lo_index, index_t hi_index )
( RegFile#(index_t, data_t) )
provisos (Bits#(index_t, size_index),
Bits#(data_t, size_data));
The RegFileLoad variants provide the same functionality as RegFile, but each constructor function
takes an additional file name argument. The file contains the initial contents of the array using the
Verilog hex memory file syntax, which allows white spaces (including new lines, tabs, underscores,
and form-feeds), comments, binary and hexadecimal numbers. Length and base format must not be
specified for the numbers.
mkRegFileLoad Create a RegFile using the file to provide the initial contents of the array.
module mkRegFileLoad#
( String file, index_t lo_index, index_t hi_index)
( RegFile#(index_t, data_t) )
provisos (Bits#(index_t, size_index),
Bits#(data_t, size_data));
mkRegFileFullLoad Create a RegFile from min to max index using the file to provide the initial
contents of the array. The min and max are specified by the Bounded
typeclass instance (0 to N-1 for N-bit numbers).
module mkRegFileFullLoad#( String file)
( RegFile#(index_t, data_t))
provisos (Bits#(index_t, size_index),
Bits#(data_t, size_data),
Bounded#(index_t) );
mkRegFileWCFLoad Create a RegFile from lo_index to hi_index for which the reads and
the write are scheduled conflict-free (see Section C.1.5), using the file to
provide the initial contents of the array.
module mkRegFileWCFLoad#
( String file, index_t lo_index, index_t hi_index)
( RegFile#(index_t, data_t) )
provisos (Bits#(index_t, size_index),
Bits#(data_t, size_data));
Examples
Use mkRegFileLoad to create Register files and then read the values.
Verilog Modules
RegFile modules correspond to the following Verilog modules, which are found in the Bluespec
Verilog library, $BLUESPECDIR/Verilog/.
There are three FIFO packages, FIFO, FIFOF, and FIFOLevel. The following table shows when to use
each FIFO, and which methods are in implemented in each FIFO. All FIFOs include the methods
enq, deq, first, clear. These are referred to as the common methods in the table.
Common Methods
The following four methods are provided in all FIFO packages.
Method Argument
Name Type Description Name Description
enq Action adds an entry to the FIFO x1 variable to be added to the FIFO
must be of type element type
deq Action removes first entry from
the FIFO
first element type returns first entry the entry returned is of
element type
clear Action clears all entries from the
FIFO
Package Name
import FIFO :: * ;
import FIFOF :: * ;
Description
The FIFO package defines the FIFO interface and four module constructors. The FIFO package is
for FIFOs with implicit full and empty signals.
The FIFOF package defines FIFOs with explicit full and empty signals.
The standard version of FIFOF has FIFOs with the enq, deq and first methods guarded by the
appropriate (notFull or notEmpty) implicit condition for safety and improved scheduling.
Unguarded (UG) versions of FIFOF are available for the rare cases when implicit conditions are not
desired.
Guarded (G) versions of FIFOF are available which allow more control over implicit conditions. With
the guarded versions the user can specify whether the enqueue or dequeue side is guarded.
Interfaces and methods
Method Argument
Name Type Description Name Description
enq Action adds an entry to the FIFO x1 variable to be added to the FIFO
must be of type element type
deq Action removes first entry from
the FIFO
first element type returns first entry the entry returned is of ele-
ment type
clear Action clears all entries from the
FIFO
The FIFO and FIFOF interfaces belong to the toGet and toPut typeclasses. You can use the toGet
and toPut functions to convert FIFO and FIFOF interfaces to Get and Put interfaces (Section C.6.1).
Modules
The FIFO and FIFOF interface types are provided by the module constructors: mkFIFO, mkFIFO1,
mkSizedFIFO, mkDepthParamFIFO, and mkLFIFO. Each FIFO is safe with implicit conditions; it does
not allow an enq when the FIFO is full and does not allow a deq or first when the FIFO is empty.
Most FIFOs do not allow simultaneous enqueue and dequeue operations when the FIFO is full.
The exception is the pipeline FIFO (mkLFIFO), which does allow simultaneous enqueue and dequeue
operations in the same clock cycle when full, as shown in the following table.
For creating a FIFOF interface use the "F" version of the module, such as mkFIFOF.
Unguarded (UG) versions of FIFOF are available for the rare cases when implicit conditions are not
desired. During rule and method processing the implicit conditions for correct FIFO operations are
NOT considered. That is, with an unguarded FIFO, it is possible to enqueue when full, and to
dequeue when empty. The Unguarded versions of the FIFOF modules provide the FIFOF interface.
There is also available a guarded (G) version of each of the FIFOFs. The guarded FIFOF takes two
Boolean parameters; ugenq and ugdeq. Setting either parameter TRUE indicates the method (enq for
ugenq, deq for ugdeq) is unguarded. If both are TRUE the FIFOF behaves the same as an unguarded
FIFOF. If both are FALSE the behavior is the same as a regular FIFOF.
FIFO of depth 1
mkFIFO1 module mkFIFO1(FIFO#(element_type))
mkFIFOF1 provisos (Bits#(element_type, width_any));
mkUGFIFOF1
Pipeline FIFO of depth 1. deq and enq can be simultaneously applied in the same clock
cycle when the FIFO is full.
mkLFIFO module mkLFIFO (FIFO#(element_type))
mkLFIFOF provisos (Bits#(element_type, width_any));
mkUGLFIFOF
Guarded pipeline FIFOF of depth 1. deq and enq can be simultaneously applied in the same
clock cycle when the FIFOF is full.
mkGLFIFOF module mkGLFIFOF (Bool ugenq, Bool ugdeq)(FIFOF#(element_type))
provisos (Bits#(element_type, width_any));
Functions
The FIFO package provides a function fifofToFifo to convert an interface of type FIFOF to an
interface of type FIFO.
Scheduling Annotations
Scheduling constraints describe how methods interact within the schedule. For example, a clear to
a given FIFO must be sequenced after (SA) an enq to the same FIFO. That is, when both enq and
clear execute in the same cycle, the resulting FIFO state is empty. For correct rule behavior the
rule executing enq must be scheduled before the rule calling clear.
The table below lists the scheduling annotations for the FIFO modules mkFIFO, mkSizedFIFO, and
mkFIFO1.
Scheduling Annotations
mkFIFO, mkSizedFIFO, mkFIFO1
enq first deq clear
enq C CF CF SB
first CF CF SB SB
deq CF SA C SB
clear SA SA SA SBR
The table below lists the scheduling annotations for the pipeline FIFO module, mkLFIFO. The pipeline
FIFO has a few more restrictions since there is a combinational path between the deq side and the
enq side, thus restricting deq calls before enq.
Scheduling Annotations
mkLFIFO
enq first deq clear
enq C SA SAR SB
first SB CF SB SB
deq SBR SA C SB
clear SA SA SA SBR
The FIFOF modules add the notFull and notEmpty methods. These methods have SB annotations
with the Action methods that change FIFO state. These SB annotations model the atomic behavior
of a FIFO, that is when enq, deq, or clear are called the state of notFull and notEmpty are
changed. This is no different than the annotations on mkReg (which is read SB write), where
actions are atomic and the execution module is one rule fires at a time. This does differ from a pure
hardware module of a FIFO or register where the state does not change until the clock edge.
Scheduling Annotations
mkFIFOF, mkSizedFIFOF, mkFIFOF1
enq notFull first deq notEmpty clear
enq C SA CF CF SA SB
notFull SB CF CF SB CF SB
first CF CF CF SB CF SB
deq CF SA SA C SA SB
notEmpty SB CF CF SB CF SB
clear SA SA SA SA SA SBR
Verilog Modules
FIFO and FIFOF modules correspond to the following Verilog modules, which are found in the Blue-
spec Verilog library, $BLUESPECDIR/Verilog/.
FIFO2.v FIFO20.v
mkFIFO
mkFIFOF
mkUGFIFOF
mkGFIFOF
FIFO1.v FIFO10.v
mkFIFO1
mkFIFOF1
mkUGFIFOF1
mkGFIFOF1
SizedFIFO.v SizedFIFO0.v
mkDepthParamFIFOF
mkUGDepthParamFIFOF
mkGDepthParamFIFOF
FIFOL1.v FIFOL10.v
mkLFIFO
mkLFIFOF
mkUGLFIFOF
mkGLFIFOF
C.1.4 FIFOLevel
Package Name
import FIFOLevel :: * ;
Description
The BSV FIFOLevel library provides enhanced FIFO interfaces and modules which include methods
to indicate the level or the current number of items stored in the FIFO. Both single clock and dual
clock (separate clocks for the enqueue and dequeue sides) versions are included in this package.
Interfaces and methods
The FIFOLevelIfc interface defines methods to compare the current level to Integer constants for
a single clock. The SyncFIFOLevelIfc defines the same methods for dual clocks; thus it provides
methods for both the source (enqueue) and destination (dequeue) clock domains. Instead of methods
to compare the levels, the FIFOCountIfc and SyncFIFOCountIfc define methods to return counts
of the FIFO contents, for single clocks and dual clocks respectively.
FIFOLevelIfc
Method Argument
Name Type Description Name Description
isLessThan Bool Returns True if the depth c1 an Integer compile-
of the FIFO is less than the time constant
Integer constant, c1.
isGreaterThan Bool Returns True if the depth of c1 an Integer compile-
the FIFO is greater than the time constant
Integer constant, c1.
In addition to common FIFO methods, the FIFOCountIfc interface defines a method to return the
current number of elements as an bit-vector. See Section C.1.2 for details on enq, deq, first,
clear, notFull, and notEmpty. Note that the FIFOCountIfc interface has a type parameter for the
fifoDepth. This numeric type parameter is needed, since the width of the counter is dependent on
the FIFO depth. The fifoDepth parameter must be > 2.
FIFOCountIfc
Method
Name Type Description
count UInt#(TLog#(TAdd#(fifoDepth,1))) Returns the number of items in the FIFO.
The interfaces SyncFIFOLevelIfc and SyncFIFOCountIfc are dual clock versions of the FIFOLevelIfc
and FIFOCountIfc. Methods are provided for both source and destination clock domains. The fol-
lowing table describes the dual clock notFull and notEmpty methods, as well as the dual clock
clear methods, which are common to both interfaces. Note that the SyncFIFOLevelIfc and
SyncFIFOCountIfc interfaces each have a type parameter for fifoDepth. This numeric type pa-
rameter is needed, since the width of the counter is dependent on the FIFO depth. The fifoDepth
parameter must be a power of 2 and >= 2.
In addition to common FIFO methods (Section C.1.2) and the common dual clock methods above,
the SyncFIFOLevelIfc interface defines methods to compare the current level to Integer constants.
Methods are provided for both the source (enqueue side) and destination (dequeue side) clock do-
mains.
SyncFIFOLevelIfc Methods
Method Argument
Name Type Description Name Description
sIsLessThan Bool Returns True if the depth of c1 an Integer compile-
the FIFO, as appears on the time constant
source side clock, is less than the
Integer constant, c1.
sIsGreaterThan Bool Returns True if the depth of the c1 an Integer compile-
FIFO, as it appears on the source time constant.
side clock, is greater than the
Integer constant, c1.
dIsLessThan Bool Returns True if the depth of the c1 an Integer compile-
FIFO, as it appears on the desti- time constant
nation side clock, is less than the
Integer constant, c1.
dIsGreaterThan Bool Returns True if the depth of the c1 an Integer compile-
FIFO, as appears on the destina- time constant.
tion side clock, is greater than the
Integer constant, c1.
In addition to common FIFO methods (Section C.1.2) and the common dual clock methods above,
the SyncFIFOCountIfc interface defines methods to return the current number of elements. Methods
are provided for both the source (enqueue side) and destination (dequeue side) clock domains.
SyncFIFOCountIfc
Method
Name Type Description
sCount UInt#(TLog#(TAdd#(fifoDepth,1))) Returns the number of items in the FIFO
from the source side.
dCount UInt#(TLog#(TAdd#(fifoDepth,1))) Returns the number of items in the FIFO
from the destination side.
The module mkFIFOCount provides the interface FIFOCountIfc. There is also available a guarded (G)
version of FIFOCount which takes three Boolean parameters; ugenq, ugdeq, and ugcount. Setting
any of the parameters to TRUE indicates the method (enq for ugenq, deq for ugdeq, and count for
ugcount) is unguarded. If all three are FALSE the behavior is the same as a regular FIFOCount.
The modules mkSyncFIFOLevel and mkSyncFIFOCount are dual clock FIFOs, where enqueue and
dequeue methods are in separate clocks domains, sClkIn and dClkIn respectively. Because of the
synchronization latency, the flag indicators will not necessarily be identical between the source and
the destination clocks. Note however, that the sNotFull and dNotEmpty flags always give proper
(pessimistic) indications for the safe use of enq and deq methods; these are automatically included
as implicit condition in the enq and deq (and first) methods.
The module mkSyncFIFOLevel provides the SyncFIFOLevelIfc interface.
Figure 3: SyncFIFOCount
The module provides sClear and dClear methods, both of which cause the contents of the FIFO
to be removed. Since the clears must be synchronized and acknowledged from one domain to the
other, there is a non-trivial delay before the FIFO recovers from the clear and can accept additional
enqueues or dequeues (depending on which side is cleared). The calling of either method immediately
disables other activity in the calling domain. That is, calling sClear in cycle n causes the enqueue
to become unready in the next cycle, n+1. Likewise, calling dClear in cycle n causes the dequeue to
become unready in the next cycle, n+1.
After the sClear method is called, the FIFO appears empty on the dequeue side after three dClock
edges. Three sClock edges later, the FIFO returns to a state where new items can be enqueued. The
latency is due to the full handshaking synchronization required to send the clear signal to dClock
and receive the acknowledgement back.
For the dClear method call, the enqueue side is cleared in three sClkIn edges and items can be
enqueued at the fourth edge. All items enqueued at or before the clear are removed from the FIFO.
Note that there is a ready signal associated with both sClear and dClear methods to ensure that
the clear is properly sent between the clock domains. Also, sRstIn must be synchronized with the
sClkIn.
Example
The following example shows the use of SyncFIFOLevel as a way to collect data into a FIFO, and
then send it out in a burst mode. The portion of the design shown, waits until the FIFO is almost
full, and then sets a register, burstOut which indicates that the FIFO should dequeue. When the
FIFO is almost empty, the flag is cleared, and FIFO fills again.
. . .
// Define a fifo of Int(#23) with 128 entries
SyncFIFOLevelIfc#(Int#(23),128) fifo <- mkSyncFIFOLevel(sclk, rst, dclk ) ;
. . .
// Set and clear the burst mode depending on fifo status
rule timeToDeque( dFifoAlmostFull && ! burstOut ) ;
burstOut <= True ;
endrule
endrule
Verilog Modules
The modules described in this section correspond to the following Verilog modules, which are found
in the Bluespec Verilog library, $BLUESPECDIR/Verilog/.
SizedFIFO.v SizedFIFO0.v
mkFIFOLevel
mkFIFOCount
SyncFIFOLevel.v
mkSyncFIFOLevel
mkSyncFIFOCount
C.1.5 ConfigReg
Package Name
import ConfigReg :: * ;
Description
The ConfigReg package provides a way to create registers where each update clobbers the current
value, but the precise timing of updates is not important. These registers are identical to the mkReg
registers except that their scheduling annotations allows reads and writes to occur in either order
during rule execution.
Rules which fire during the clock cycle where the register is written read a stale value (that is the
value from the beginning of the clock cycle) regardless of firing order and writes which have occurred
during the clock cycle. Thus if rule r1 writes to a ConfigReg cr and rule r2 reads cr later in the
same cycle, the old or stale value of cr is read, not the value written in r1. If a standard register
is used instead, rule r2’s execution will be blocked by r1’s execution or the scheduler may create a
different rule execution order.
The hardware implementation is identical for the more common registers (mkReg, mkRegU and
mkRegA), and the module constructors parallel these as well.
Interfaces
The ConfigReg interface is an alias of the Reg interface (section B.4.1).
typedef Reg#(a_type) ConfigReg #(type a_type);
Modules
The ConfigReg package provides three modules; mkConfigReg creates a register with a given re-
set value and synchronous reset logic, mkConfigRegU creates a register without any reset, and
mkConfigRegA creates a register with a given reset value and asynchronous reset logic.
mkConfigReg Make a register with a given reset value. Reset logic is synchronous
module mkConfigReg#(a_type resetval)(Reg#(a_type))
provisos (Bits#(a_type, sizea));
mkConfigRegU Make a register without any reset; initial simulation value is alternating
01 bits.
module mkConfigRegU(Reg#(a_type))
provisos (Bits#(a_type, sizea));
mkConfigRegA Make a register with a given reset value. Reset logic is asynchronous.
module mkConfigRegA#(a_type, resetval)(Reg#(a_type))
provisos (Bits#(a_type, sizea));
Scheduling Annotations
mkConfigReg, mkConfigRegU, mkConfigRegA
read write
read CF CF
write CF SBR
C.1.6 DReg
Package Name
import DReg :: * ;
Description
The DReg package allows a designer to create registers which store a written value for only a single
clock cycle. The value written to a DReg is available to read one cycle after the write. If more than
one cycle has passed since the register has been written however, the value provided by the register
is instead a default value (that is specified during module instantiation). These registers are useful
when wanting to send pulse values that are only asserted for a single clock cycle. The DReg is the
register equivalent of a DWire B.4.5.
Modules
The DReg package provides three modules; mkDReg creates a register with a given reset/default value
and synchronous reset logic, mkDRegU creates a register without any reset (but which still takes a
default value as an argument), and mkDRegA creates a register with a given reset/default value and
asynchronous reset logic.
mkDReg Make a register with a given reset/default value. Reset logic is syn-
chronous
module mkDReg#(a_type dflt_rst_val)(Reg#(a_type))
provisos (Bits#(a_type, sizea));
mkDRegU Make a register without any reset but with a specified default; initial
simulation value is alternating 01 bits.
module mkDRegU#(a_type dflt_val)(Reg#(a_type))
provisos (Bits#(a_type, sizea));
mkDRegA Make a register with a given reset/default value. Reset logic is asyn-
chronous.
module mkDRegA#(a_type, dflt_rst_val)(Reg#(a_type))
provisos (Bits#(a_type, sizea));
Scheduling Annotations
mkDReg, mkDRegU, mkDRegA
read write
read CF SB
write SA SBR
C.1.7 RevertingVirtualReg
Package Name
import RevertingVirtualReg :: * ;
Description
The RevertingVirtualReg package allows a designer to force a schedule when scheduling attributes
cannot be used. Since scheduling attributes cannot be put on methods, this allows a designer to
control the schedule between two methods, or between a method and a rule by adding a virtual
register between the two. The module RevertingVirtualReg creates a virtual register; no actual
state elements are generated.
Modules
The RevertingVirtualReg package provides the module mkRevertingVirtualReg. The properties
of the module are:
These imply that all allowed reads will return the reset value (since they precede any writes in the
cycle); thus the module neither needs nor instantiates any actual state element.
mkRevertingVirtualReg Creates a virtual register reverting to the reset value at the end of
each clock cycle.
module mkRevertingVirtualReg#(a_type rst)(Reg#(a_type))
provisos (Bits#(a_type, sizea));
Scheduling Annotations
mkRevertingVirtualReg
read write
read CF SB
write SA SBR
Example Use mkRevertingVirtualReg to create the execution order of the rule followed by the method
In a given cycle, reads always precede writes for a register. Therefore the reading of virtualReg
by the_rule will precede the writing of virtualReg in the_method. The execution order will be
the_rule followed by the_method.
C.1.8 BRAM
Package Name
import BRAM :: * ;
Description
This package provides Block RAMS for use in Xilinx FPGAs. The ClientServer package must also
be imported when using the BRAM package.
Types and type classes
The BRAM package defines a structure, BRAMRequest, along with types BRAMServer and BRAMClient.
Modules
The BRAM package provides the following modules: mkSyncBRAM, mkBRAM, and mkSyncBRAMLoadEither.
These modules correspond to the Xilinx Dual-Port Block RAM with two write ports.
mkSyncBRAM Creates a 2-port BRAM with two clocks; clkA is shared by the primary
read and write port, clkB is shared by the secondary read and write
port. Resets must be synched to the clock domains. There is no default
clock or reset domain.
module mkSyncBRAM#
(Clock clkA, Reset rstNA, Clock clkB, Reset rstNB)
(BRAM#(addr_t, data_t))
provisos (Bits(addr_t, addr_sz), Bits#(data_t, data_sz),
Add#(z, 1, addr_sz), Add#(x, 1, data_sz),
Bounded#(addr_t));
mkSyncBRAMLoadEither Creates a dual-clock, two port BRAM and loads the initial values from
a file containing either hex or binary values. Resets must be synched
to the clock domains. There is no default clock or reset domain.
module mkSyncBRAMLoadEither#
(Clock clkA,Reset rstNA,Clock clkB,
Reset rstNB, String file, Integer binary)
(BRAM#(addr_t, data_t))
provisos(Bits#(addr_t, addr_sz),Bits#(data_t, data_sz),
Add#(z, 1, addr_sz), Add#(x, 1, data_sz),
Bounded#(addr_t));
Verilog Modules
BRAM modules correspond to the following Verilog modules, which are found in the Bluespec Verilog
library, $BLUESPECDIR/Verilog/.
mkSyncBRAM BRAM.v
mkBRAM
mkSyncBRAMLoadEither BRAMLoad.v
C.1.9 BRAMFIFO
Description
The BRAMFIFOs are FIFOs which utilize the Xilinx Block RAMs, as implemented in the BRAM
package, described in Section C.1.8.
This package is provided as both a compiled library package and as BSV source code to facilitate
customization. The source code file can be found in the $BLUESPECDIR/BSVSource directory. To
customize a package, copy the file into a local directory and then include the local directory in the
path when compiling. This is done by specifying the path with the -p option as described in the
BSV Users Guide.
Packages
To include a package in your design, use the import syntax.
import BRAMFIFO :: * ;
Interfaces
The BRAMFIFO package uses FIFOF, FIFO, and SyncFIFOIfc interfaces, as defined in the FIFOF, FIFO,
(both in Section C.1.3) and Clocks (Section C.8.7) packages.
Modules
mkSyncBRAMFIFO Provides a Xilinx BRAM based FIFO for sending data across clock
domains. The enq method is in the source sClkIn domain, while the
deq and first methods are in the destination dClkIn domain. The
input and output clocks, along with the input and output resets, are
explicitly provided. The default clock and reset are ignored.
module mkSyncBRAMFIFO#(Integer depth,
Clock sClkIn, Reset sRstIn,
Clock dClkIn, Reset dRstIn)
(SyncFIFOIfc#(element_type))
provisos(Bits#(element_type, width_element),
Add#(1, z, width_element));
mkSyncBRAMFIFOToCC Provides a Xilinx BRAM based FIFO to send data from a second clock
domain into the current clock domain. The output clock and reset are
the current clock and reset.
module mkSyncBRAMFIFOToCC#(Integer depth,
Clock sClkIn, Reset sRstIn)
(SyncFIFOIfc#(element_type))
provisos(Bits#(element_type, width_element),
Add#(1, z, width_element));
mkSyncBRAMFIFOFromCC Provides a Xilinx BRAM based FIFO to send data from the current
clock domain into a second clock domain. The input clock and reset
are the current clock and reset.
module mkSyncBRAMFIFOFromCC#(Integer depth,
Clock dClkIn, Reset dRstIn)
(SyncFIFOIfc#(element_type))
provisos(Bits#(element_type, width_element),
Add#(1, z, width_element));
Here, the type variable element_type represents the type of the contents of the elements while the
numeric type variable vsize represents the length of the vector.
If the elements are in the Bits class, then the vector is as well. Thus a vector of these elements can
be stored into Registers or FIFOs; for example a Register holding a vector of type int. Note that a
vector can also store abstract types, such as a vector of Rules or a vector of Reg interfaces. These
are useful during static elaboration although they have no hardware implementation.
Typeclasses
A vector can be turned into bits if the individual elements can be turned into bits. When packed
and unpacked, the zeroth element of the vector is stored in the least significant bits. The size of the
resulting bits is given by tsize = vsize ∗ SizeOf#(element type) which is specified in the provisos.
instance Bits #( Vector#(vsize, element_type), tsize)
provisos (Bits#(element_type, sizea),
Mul#(vsize, sizea, tsize));
Vectors are zero-indexed; the first element of a vector v, is v[0]. When vectors are packed, they are
packed in order from the LSB to the MSB.
Example. Vector#(5, Bit#(7)) v1;
From the type, you can see that this will back into a 35-bit vector (5 elements, each with 7 bits).
34 bit positions 0
MSB LSB
v1[4] v1[3] v1[2] v1[1] v1[0]
Example. A vector with a structure:
typedef struct { Bool a, UInt#(5) b} Newstruct deriving (Bits);
Vector#(3, NewStruct) v2;
The structure, Newstruct packs into 6 bits. Therefore v2 will pack into an 18-bit vector. And its
structure would look as follows:
17 16 - 12 11 10 - 6 5 0
MSB v2[2].a v2[2].b v2[1].a v2[1].b v2[0].a v2[0].b LSB
v2[2] v2[1] v2[0]
Vectors can be compared for equality if the elements can. That is, the operators == and != are
defined.
Vectors are bounded if the elements are.
The following functions are used to create new vectors, with and without defined elements. There
are no Bluespec SystemVerilog constructors available for this abstract type (and hence no pattern-
matching is available for this type) but the following functions may be used to construct values of
the Vector type.
newVector Generate a vector with undefined elements, typically used when vectors are de-
clared.
genVector Generate a vector containing integers 0 through n-1, vector[0] will have value 0.
genWith Generate a vector of elements by applying the given function to 0 through n-1.
The argument to the function is another function which has one argument of type
Integer and returns an element_type.
cons Adds an element to a vector creating a vector one element larger. The new element
will be at the 0th position. This function can lead to large compile times, so it
can be an inefficient way to create and populate a vector. Instead, the designer
should build a vector, then set each element to a value.
append Append two vectors containing elements of the same type, returning the combined
vector. The resulting vector will contain all the elements of vecta followed by all
the elements of vectb.
concat Append (concatenate) many vectors, that is a vector of vectors into one vector.
function Vector#(mvsize,element_type)
concat(Vector#(m,Vector#(n,element_type)) xss)
provisos (Mul#(m,n,mvsize));
Create a new vector, my_vector, of 5 elements of datatytpe Integer with elements 0, 1, 2, 3 and 4.
Create a vector, my_vector, by applying the given function add2 to 0 through n-1.
function Integer add2 (Integer a);
Integer c = a + 2;
return(c);
endfunction
// matrix[0] = {0, 1, 2}
// matrix[1] = {3, 4, 5}
// matrix[2] = {6, 7, 8}
These functions are used to select elements or vectors from existing vectors, while retaining the input
vector.
anyVector[i];
anyVector[i] = newValue;
select The select function is another form of the subscript notation ([i]), mainly provided
for backwards-compatibility. The select function is also useful as an argument to
higher-order functions. The subscript notation is generally recommended because
it will report a more useful position for any selection errors.
function element_type
select(Vector#(vsize,element_type) vect, idx_type index);
update Update an element in a vector returning a new vector with one element
changed/updated. This function does not change the given vector. This is an-
other form of the subscript notation (see above), mainly provided for backwards
compatibility. The update function may also be useful as an argument to a higher-
order function. The subscript notation is generally recommended because it will
report a more useful position for any update errors.
head Extract the zeroth (head) element of a vector. The vector must have at least one
element.
function element_type
head (Vector#(vsize, element_type) vect)
provisos(Add#(1,xxx,vxize)); // vsize >= 1
last Extract the highest (tail) element of a vector. The vector must have at least one
element.
function element_type
last (Vector#(vsize, element_type) vect)
provisos(Add#(1,xxx,vxize)); // vsize >= 1
tail Remove the head element of a vector leaving its tail in a smaller vector.
function Vector#(vsize,element_type)
tail (Vector#(vsize1, element_type) xs)
provisos (Add#(1, vsize, vsize1));
init Remove the last element of a vector leaving its initial part in a smaller vector.
function Vector#(vsize,element_type)
init (Vector#(vsize1, element_type) xs)
provisos (Add#(1, vsize, vsize1));
take Take a number of elements from a vector starting from index 0. The number of
elements to take is indicated by the type of the context where this is called, and
is not specified as an argument to the function.
function Vector#(vxize2,element_type)
take (Vector#(vsize,element_type) vect)
provisos (Add#(vsize2,xxx,vsize)); // vsize2 <= vsize.
drop Drop a number of elements from the vector starting at the 0th position. The
takeTail elements in the result vector will be in the same order as the input vector.
function Vector#(vxize2,element_type)
drop (Vector#(vsize,element_type) vect)
provisos (Add#(vsize2,xxx,vsize)); // vsize2 <= vsize.
function Vector#(vxize2,element_type)
takeTail (Vector#(vsize,element_type) vect)
provisos (Add#(vsize2,xxx,vsize)); // vsize2 <= vsize.
function Vector#(vsize2,element_type)
takeAt (Integer startPos, Vector#(vsize,element_type) vect)
provisos (Add#(vsize2,xxx,vsize)); // vsize2 <= vsize
newvalue = head(my_vector);
// newvalue = 6
newvalue = last(my_vector);
// newvalue = 11
Create a vector, my_vector2, of size 4 by removing the head (zeroth) element of the vector my_vector1.
Create a vector, my_vector2, of size 4 by removing the tail (last) element of the vector my_vector1.
Create a 2 element vector, my_vector2, by taking the first two elements of the vector my_vector1.
// my_vector1 is vector with 5 elements {0,1,2,3,4}
Create a 3 element vector, my_vector2, by taking the last 3 elements of vector, my_vector1. using
takeTail
// my_vector1 is Vector with 5 elements {0,1,2,3,4}
Create a 3 element vector, my_vector2, by taking the 1st - 3rd elements of vector, my_vector1.
using takeAt
// my_vector1 is Vector with 5 elements {0,1,2,3,4}
The following functions generate a new vector by changing the position of elements within the vector.
rotate Move the zeroth element to the highest and shift each element lower by one. For
example, the element at index n moves to index n-1.
function Vector#(vsize,element_type)
rotate (Vector#(vsize,element_type) vect);
rotateR Move last element to the zeroth element and shift each element up by one. For
example, the element at index n moves to index n+1.
function Vector#(vsize,element_type)
rotateR (Vector#(vsize,element_type) vect);
rotateBy Shift each element n places. The last n elements are moved to the begining, the
element at index 0 moves to index n, index 1 to index n+1, etc.
shiftInAt0 Shift a new element into the vector at index 0, bumping the index of all other
element up by one. The highest element is dropped.
function Vector#(vsize,element_type)
shiftInAt0 (Vector#(vsize,element_type) vect,
element_type newElement);
shiftInAtN Shift a new element into the vector at index n, bumping the index of all other
elements down by one. The 0th element is dropped.
function Vector#(vsize,element_type)
shiftInAtN (Vector#(vsize,element_type) vect,
element_type newElement);
function Vector#(vsize,element_type)
reverse(Vector#(vsize,element_type) vect);
function Vector#(m,Vector#(n,element_type))
transpose ( Vector#(n,Vector#(m,element_type)) matrix );
my_vector2 = transpose(my_vector1);
// my_vector2 has the values:
// {{0,5,10},{1,6,11},{2,7,12},{3,8,13},{4,9,14}}
The following functions are used to test vectors. The first three functions are Boolean functions, i.e.
they return True or False values.
The following two functions return the number of elements in the vector which match a condition.
countElem Returns the number of elements in the vector which are equal to a given value.
The return value is in the range of 0 to vsize.
countIf Returns the number of elements in the vector which satisfy a given predicate
function. The return value is in the range of 0 to vsize.
findElem Returns the index of the first element in the vector which equals a given value.
Returns an Invalid if not found or Valid with a value of 0 to vsize-1 if found.
findIndex Returns the index of the first element in the vector which satisfies a given predicate.
Returns an Invalid if not found or Valid with a value of 0 to vsize-1 if found.
if (all(isPositive, my_vector1))
$display ("Vector contains all negative values");
if (any(isPositive, my_vector1))
$display ("Vector contains some negative values");
rotateBitsBy Shift each bit to a higher index by n places. The last n bits are moved to the
begininng and the bit at index (0) moves to index (n).
writeVReg Returns an Action which is the write of all registers in vr with the data from
vdin.
The family of zip functions takes two or more vectors and combines them into one vector of Tuples.
Several variations are provided for different resulting Tuples, as well as support for mis-matched
vector sizes.
zipAny Combine two vectors into one vector of pairs (2-tuples); result is as long as the
smaller vector.
unzip Separate a vector of pairs (i.e. a Tuple2#(a,b)) into a pair of two vectors.
A function can be applied to all elements of a vector, using high-order functions such as map. These
functions take as an argument a function, which is applied to the elements of the vector.
function Vector#(vsize,b_type)
map (function b_type func(a_type x),
Vector#(vsize, a_type) vect);
Consider the following code example which applies the extend function to each element of avector
into a new vector, resultvector.
Vector#(13,Bit#(5)) avector;
Vector#(13,Bit#(10)) resultvector;
...
resultvector = map( extend, avector ) ;
The zipWith functions combine two or more vectors with a function and generate a new vector.
These functions combine features of map and zip functions.
function Vector#(vsize,c_type)
zipWith (function c_type func(a_type x, b_type y),
Vector#(vsize,a_type) vecta,
Vector#(vsize,b_type) vectb );
zipWithAny Combine two vectors with a function; result is as long as the smaller vector.
function Vector#(vsize,c_type)
zipWithAny (function c_type func(a_type x, b_type y),
Vector#(m,a_type) vecta,
Vector#(n,b_type) vectb )
provisos (Max#(n, vsize, n), Max#(m, vsize, m));
function Vector#(vsize,d_type)
zipWith3(function d_type func(a_type x, b_type y, c_type z),
Vector#(vsize,a_type) vecta,
Vector#(vsize,b_type) vectb,
Vector#(vsize,c_type) vectc );
zipWithAny3 Combine three vectors with a function; result is as long as the smallest vector.
function Vector#(vsize,c_type)
zipWithAny3(function d_type func(a_type x, b_type y, c_type z),
Vector#(m,a_type) vecta,
Vector#(n,b_type) vectb,
Vector#(o,c_type) vectc )
provisos (Max#(n, vsize, n), Max#(m, vsize, m), Max#(o, vsize, o));
Examples - ZipWith
Create a vector by applying a function over the elements of 3 vectors.
// the function add3 adds 3 values
function Int#(n) add3 (Int #(n) a,Int #(n) b,Int #(n) c);
Int#(n) d = a + b +c ;
return d;
endfunction
// my_vector1 = {0,1,2,3,4}
// my_vector2 = {5,6,7,8,9}
// my_vector3 = {10,11,12,13,14}
The fold family of functions reduces a vector to a single result by applying a function over all its
elements. That is, given a vector of element_type, V0 , V1 , V2 , ..., Vn−1 , a seed of type b_type, and
a function func, the reduction for foldr is given by
Note that foldr start processing from the highest index position to the lowest, while foldl starts
from the lowest index (zero), i.e. foldl is:
foldr Reduce a vector by applying a function over all its elements. Start processing
from the highest index to the lowest.
foldl Reduce a vector by applying a function over all its elements. Start processing
from the lowest index (zero).
The functions foldr1 and foldl1 use the first element as the seed. This means they only work on
vectors of at least one element. Since the result type will be the same as the element type, there is
no b_type as there is in the foldr and foldl functions.
foldr1 foldr function for a non-zero sized vector, using element Vn−1 as a seed. Vector
must have at least 1 element. If there is only one element, it is returned.
foldl1 foldl function for a non-zero sized vector, using element V0 as a seed. Vector must
have at least 1 element. If there is only one element, it is returned.
The fold function also operates over a non-empty vector, but processing is accomplished in a binary
tree-like structure. Hence the depth or delay through the resulting function will be O(log2 (vsize)
rather than O(vsize).
fold Reduce a vector by applying a function over all its elements, using a binary tree-
like structure. The function returns the same type as the arguments.
mapPairs Map a function over a vector consuming two elements at a time. Any straggling
element is processed by the second function.
function Vector#(vsize2,b_type)
mapPairs (
function b_type func1(a_type x, a_type y),
function b_type func2(a_type x),
Vector#(vsize,a_type) vect )
provisos (Div#(vsize, 2, vsize2));
joinActions Join a number of actions together. joinActions is used for static elaboration
only, no hardware is generated.
joinRules Join a number of rules together.joinRules is used for static elaboration only,
no hardware is generated.
Example - Folds
Use fold to find the sum of the elements in a vector.
// my_vector1 is a vector of five integers {1,2,3,4,5}
// \+ is a function which returns the sum of the elements
// make sure you leave a space after the \+ and before the ,
Create a new vector using mapPairs. The function sum is applied to each pair of elements (the first
and second, the third and fourth, etc.). If there is an uneven number of elements, the function pass
is applied to the remaining element.
// pass is defined as a
function Int#(4) pass (Int #(4) a);
return(a);
endfunction
my_vector2 = mapPairs(sum,pass,my_vector1);
// my_vector2 has the elements {1,5,4}
// my_vector2[0] = 0 + 1
// my_vector2[1] = 2 + 3
// my_vector2[3] = 4
The scan family of functions applies a function over a vector, creating a new vector result. The
scan function is similar to fold, but the intermediate results are saved and returned in a vector,
instead of returning just the last result. The result of a scan function is a vector. That is, given a
vector of element_type, V0 , V1 , ..., Vn−1 , an initial value initb of type b_type, and a function func,
application of the scanr functions creates a new vector W , where
Wn = init;
Wn−1 = f unc(Vn−1 , Wn );
Wn−2 = f unc(Vn−2 , Wn−1 );
...
W1 = f unc(V1 , W2 );
W0 = f unc(V0 , W1 );
scanr Apply a function over a vector, creating a new vector result. Processes elements
from the highest index position to the lowest, and fill the resulting vector in the
same way. The result vector is 1 element longer than the input vector.
function Vector#(vsize1,b_type)
scanr(function b_type func(a_type x1, b_type x2),
b_type initb,
Vector#(vsize,a_type) vect)
provisos (Add#(1, vsize, vsize1));
sscanr Apply a function over a vector, creating a new vector result. The elements are pro-
cessed from the highest index position to the lowest. The Wn element is dropped
from the result. Input and output vectors are the same size.
function Vector#(vsize,b_type)
sscanr(function b_type func(a_type x1, b_type x2),
b_type initb,
Vector#(vsize,a_type) vect );
The scanl function creates the resulting vector in a similar way as scanr except that the processing
happens from the zeroth element up to the nth element.
W0 = init;
W1 = f unc(W0 , V0 );
W2 = f unc(W1 , V1 );
...
Wn−1 = f unc(Wn−2 , Vn−2 );
Wn = f unc(Wn−1 , Vn−1 );
The sscanl function drops the first result, init, shifting the result index by one.
scanl Apply a function over a vector, creating a new vector result. Processes elements
from the zeroth element up to the nth element. The result vector is 1 element
longer than the input vector.
function Vector#(vsize1,a_type)
scanl(function a_type func(a_type x1, b_type x2),
a_type q,
Vector#(vsize, b_type) vect)
provisos (Add#(1, vsize, vsize1));
sscanl Apply a function over a vector, creating a new vector result. Processes elements
from the zeroth element up to the nth element. The first result, init, is dropped,
shifting the result index up by one. Input and output vectors are the same size.
function Vector#(vsize,a_type)
sscanl(function a_type func(a_type x1, b_type x2),
a_type q,
Vector#(vsize, b_type) vect );
Examples - Scan
Create a vector of factorials.
// \* is a function which returns the result of a multiplied by b
function Bit #(16) \* (Bit #(16) b, Bit #(8) a);
return (extend (a) * b);
endfunction
// my_vector1 = {1,2,3,4,5,6,7}
Vector#(8,Bit #(16)) my_vector2 = scanl (\*, 16’d1, my_vector1);
// 7 multipliers are generated
// my_vector2 = {1,1,2,6,24,120,720,5040}
// foldr with the same arguments would return just 5040.
Within Bluespec, there are some functions which can only be invoked in certain contexts. Two
common examples are: ActionValue, and module instantiation. ActionValues can only be invoked
within an Action context, such as a rule block or an Action method, and can be considered as two
parts - the action and the value. Module instantiation can similarly be considered, modules can only
be instantiated in the module context, while the two parts are the module instantiation (the action
performed) and the interface (the result returned). These situations are considered monadic.
When a monadic function is to be applied over a vector using map-like functions such as map,
zipWith, or replicate, the monadic versions of these functions must be used. Moreover, the context
requirements of the applied function must hold. The common application for these functions is in
the generation (or instantiation) of vectors of hardware components.
mapM Takes a monadic function and a vector, and applies the function to all vector
elements returning the vector of corresponding results.
mapM_ Takes a monadic function and a vector, applies the function to all vector elements,
and throws away the resulting vector leaving the action in its context.
zipWithM Take a monadic function (which takes two arguments) and two vectors; the func-
tion applied to the corresponding element from each vector would return an action
and result. Perform all those actions and return the vector of corresponding re-
sults.
zipWithM_ Take a monadic function (which takes two arguments) and two vectors; the func-
tion is applied to the corresponding element from each vector. This is the same as
zipWithM but the resulting vector is thrown away leaving the action in its context.
function m#(void)
zipWithM_(function m#(c_type) func(a_type x, b_type y),
Vector#(vsize, a_type) vecta,
Vector#(vsize, b_type) vectb )
provisos (Monad#(m));
zipWith3M Same as zipWithM but combines three vectors with a function. The function is
applied to the corresponding element from each vector and returns an action and
the vector of corresponding results.
genWithM Generate a vector of elements by applying the given monadic function to 0 through
n-1.
replicateM Generate a vector of elements by using the given monadic value repeatedly.
// ...
There are functions which convert to and from List and Vector.
function List#(element_type)
toList (Vector#(vsize, element_type) vect);
There are functions which convert to and from array and Vector.
function element_type[ ]
vectorToArray (Vector#(vsize, element_type) vect);
C.2.14 ListN
Package name
import ListN :: * ;
Description
ListN is an alternative implementation of Vector which is preferred for list processing functions, such
as head, tail, map, fold, etc. All Vector functions are available, by substituting ListN for Vector. See
the Vector documentation (C.2) for details. If the implementation requires random access to items
in the list, the Vector construct is recommended. Using ListN where Vectors is recommended (and
visa-versa) can lead to very long static elaboration times.
The ListN package defines an abstract data type which is a ListN of a specific length. Functions
which create and operate on this type are also defined within this package. Because it is abstract,
there are no constructors available for this type (like Cons and Nil for the List type).
struct ListN#(vsize,a_type)
· · · abstract · · ·
Here, the type variable “a_type” represents the type of the contents of the listN while type variable
“vsize” represents the length of the ListN.
A list is tagged Nil if it has no elements, otherwise it is tagged Cons. Cons is a structure of a single
element and the rest of the list.
Lists are most often used during static elaboration (compile-time) to manipulate collections of ob-
jects. Since List#(element_type) is not in the Bits typeclass, lists cannot be stored in registers
or other dynamic elements. However, one can have a list of registers or variables corresponding to
hardware functions.
cons Adds an element to a list. The new element will be at the 0th position.
function List#(element_type)
cons (element_type x, List#(element_type) xs);
function List#(element_type)
replicate(Integer n, element_type elem);
append Append two lists, returning the combined list. The elements of both lists must be
the same datatype, element_type. The combined list will contain all the elements
of xs followed in order by all the elements of ys.
function List#(element_type)
append(List#(element_type) xs, List#(element_type) ys);
concat Append (concatenate) many lists, that is a list of lists, into one list.
//my_list = {1,1,1,1,1}
//my_list2 = {1,2,3,4,5}
[i] The square-bracket notation is available to extract an element from a list or update
an element within it. Extracts or updates the ith element, where the first element
is [0]. Index i must be of an indexable type; (e.g. Integer, Bit#(n), Int#(n) or
UInt#(n).). The square-bracket notation for lists can also be used with register
writes.
anyList[i];
anyList[i] = newValue;
select The select function is another form of the subscript notation ([i]), mainly provided
for backwards-compatibility. The select function is also useful as an argument to
higher-order functions. The subscript notation is generally recommended because
it will report a more useful position for any selection errors.
function element_type
select(List#(element_type) alist, idx_type index);
update Update an element in a list returning a new list with one element
changed/updated. This function does not change the given list. This is another
form of the subscript notation (see above), mainly provided for backwards compat-
ibility. The update function may also be useful as an argument to a higher-order
function. The subscript notation is generally recommended because it will report
a more useful position for any update errors.
function List#(element_type)
update(List#(element_type) alist,
idx_type index,
element_type newElem);
oneHotSelect Select a list element with a Boolean list. The Boolean list should have exactly
one element that is True, otherwise the result is undefined. The returned
element is the one in the corresponding position to the True element in the
Boolean list.
function element_type
oneHotSelect (List#(Bool) bool_list,
List#(element_type) alist);
head Extract the first element of a list. The input list must have at least 1 element, or
an error will be returned.
last Extract the last element of a list. The input list must have at least 1 element, or
an error will be returned.
tail Remove the head element of a list leaving the remaining elements in a smaller list.
The input list must have at least 1 element, or an error will be returned.
init Remove the last element of a list the remaining elements in a smaller list. The
input list must have at least one element, or an error will be returned.
take Take a number of elements from a list starting from index 0. The number to take
is specified by the argument n. If the argument is greater than the number of
elements on the list, the function stops taking at the end of the list and returns
the entire input list.
function List#(element_type)
take (Integer n, List#(element_type) alist);
drop Drop a number of elements from a list starting from index 0. The number to drop
is specified by the argument n. If the argument is greater than the number of
elements on the list, the entire input list is dropped, returning an empty list.
function List#(element_type)
drop (Integer n, List#(element_type) alist);
filter Create a new list from a given list where the new list has only the elements which
satisfy the predicate function.
function List#(element_type)
filter (function Bool pred(element_type),
List#(element_type) alist);
takeWhile Returns the first set of elements of a list which satisfy the predicate function.
function List#(element_type)
takeWhile (function Bool pred(element_type x),
List#(element_type) alist);
takeWhileRev Returns the last set of elements on a list which satisfy the predicate function.
function List#(element_type)
takeWhileRev (function Bool pred(element_type x),
List#(element_type) alist);
dropWhile Removes the first set of elements on a list which satisfy the predicate function,
returning a list with the remaining elements.
function List#(element_type)
dropWhile (function Bool pred(element_type x),
List#(element_type) alist);
dropWhileRev Removes the last set of elements on a list which satisfy the predicate function,
returning a list with the remaining elements.
function List#(element_type)
dropWhileRev (function Bool pred(element_type x),
List#(element_type) alist);
//newvalue = 4
newvalue = head(my_list);
//newvalue = 1
Create a list, my_list2, of size 4 by removing the head (zeroth) element of the list my_list1.
//my_list1 is a list with 5 elements, {0,1,2,3,4}
//my_list2 = {1,2,3,4}
//my_list3 = Nil
Create a 2 element list, my_list2, by taking the first two elements of the list my_list1.
//my_list1 is list with 5 elements, {0,1,2,3,4}
List #(Int #(4)) my_list2 = take (2,my_list1);
//my_list2 = {0,1}
The number of elements specified to take in take can be greater than the number of elements on
the list, in which case the entire input list will be returned.
//my_list1 is list with 5 elements, {0,1,2,3,4}
List #(Int #(4)) my_list2 = take (7,my_list1);
//my_list2 = {0,1,2,3,4}
//result = 3
Create a list by removing the initial segment of a list that meets a predicate.
//the predicate function is a < 2
//my_list1 = {0,1,2,0,1,7,8}
//my_result = {2,0,1,7,8}
rotate Move the first element to the last and shift each element to the next higher index.
rotateR Move last element to the beginning and shift each element to the next lower index.
function List#(List#(element_type))
transpose ( List#(List#(element_type)) matrix );
my_list2 = transpose(my_list1);
== Lists can be compared for equality if the elements in the list can be compared.
!=
instance Eq #( List#(element_type) )
provisos( Eq#( element_type ) ) ;
isNull Check if a list is empty. Returns True if the list is empty, that is if there are zero
elements.
length Determine the length of a list. Can be done at elaboration time only.
if (all(isPositive, my_list1))
$display ("List contains all negative values");
if (any(pos, my_list1))
$display ("List contains some negative values");
The family of zip functions takes two or more lists and combines them into one list of Tuples.
Several variations are provided for different resulting Tuples. All variants can handle input lists of
different sizes. The resulting lists will be the size of the smallest list.
unzip Separate a list of pairs (i.e. a Tuple2#(a,b)) into a pair of two lists.
//my_list2 is ({0,1,2,3,4},{5,6,7,8,9})
A function can be applied to all elements of a list, using high-order functions such as map. These
functions take as an argument a function, which is applied to the elements of the list.
The zipWith functions combine two or more lists with a function and generate a new list. These
functions combine features of map and zip functions.
zipWith Combine two lists with a function. The lists do not have to have the same number
of elements.
function List#(c_type)
zipWith (function c_type func(a_type x, b_type y),
List#(a_type) listx,
List#(b_type) listy );
zipWith3 Combine three lists with a function. The lists do not have to have the same
number of elements.
function List#(d_type)
zipWith3(function d_type func(a_type x, b_type y, c_type z),
List#(a_type) listx,
List#(b_type) listy,
List#(c_type) listz );
zipWith4 Combine four lists with a function. The lists do not have to have the same number
of elements.
Examples - ZipWith
//my_list1 = {0,1,2,3,4}
//my_list2 = {5,6,7,8,9}
//my_list3 = {10,11,12,13,14}
//my_list4 = {15,18,21,24,27}
The fold family of functions reduces a list to a single result by applying a function over all its
elements. That is, given a list of element_type, L0 , L1 , L2 , ..., Ln−1 , a seed of type b_type, and a
function func, the reduction for foldr is given by
Note that foldr start processing from the highest index position to the lowest, while foldl starts
from the lowest index (zero), i.e.,
foldr Reduce a list by applying a function over all its elements. Start processing from
the highest index to the lowest.
foldl Reduce a list by applying a function over all its elements. Start processing from
the lowest index (zero).
The functions foldr1 and foldl1 use the first element as the seed. This means they only work on
lists of at least one element. Since the result type will be the same as the element type, there is no
b_type as there is in the foldr and foldl functions.
foldr1 foldr function for a non-zero sized list. Uses element Ln−1 as the seed. List must
have at least 1 element.
foldl1 foldl function for a non-zero sized list. Uses element L0 as the seed. List must
have at least 1 element.
The fold function also operates over a non-empty list, but processing is accomplished in a binary
tree-like structure. Hence the depth or delay through the resulting function will be O(log2 (lsize)
rather than O(lsize).
fold Reduce a list by applying a function over all its elements, using a binary tree-like
structure. The function returns the same type as the arguments.
mapPairs Map a function over a list consuming two elements at a time. Any straggling
element is processed by the second function.
function List#(b_type)
mapPairs (
function b_type func1(a_type x, a_type y),
function b_type func2(a_type x),
List#(a_type) alist );
Example - Folds
// my_list1 is a list of five integers {1,2,3,4,5}
// \+ is a function which returns the sum of the elements
// my_sum = 15
// my_max = 45
Create a new list using mapPairs. The function sum is applied to each pair of elements (the first
and second, the third and fourth, etc.). If there is an uneven number of elements, the function pass
is applied to the remaining element.
//sum is defined as c = a+b
function Int#(4) sum (Int #(4) a,Int #(4) b);
Int#(4) c = a + b;
return(c);
endfunction
//pass is defined as a
function Int#(4) pass (Int #(4) a);
return(a);
endfunction
my_list2 = mapPairs(sum,pass,my_list1);
The scan family of functions applies a function over a list, creating a new List result. The scan
function is similar to fold, but the intermediate results are saved and returned in a list, instead
of returning just the last result. The result of a scan function is a list. That is, given a list
of element_type, L0 , L1 , ..., Ln−1 , an initial value initb of type b_type, and a function func,
application of the scanr functions creates a new list W , where
Wn = init;
Wn−1 = f unc(Ln−1 , Wn );
Wn−2 = f unc(Ln−2 , Wn−1 );
...
W1 = f unc(L1 , W2 );
W0 = f unc(L0 , W1 );
scanr Apply a function over a list, creating a new list result. Processes elements from
the highest index position to the lowest, and fills the resulting list in the same
way. The result list is one element longer than the input list.
function List#(b_type)
scanr(function b_type func(a_type x1, b_type x2),
b_type initb,
List#(a_type) alist);
sscanr Apply a function over a list, creating a new list result. The elements are processed
from the highest index position to the lowest. Drops the Wn element from the
result. Input and output lists are the same size.
function List#(b_type)
sscanr(function b_type func(a_type x1, b_type x2),
b_type initb,
List#(a_type) alist );
The scanl function creates the resulting list in a similar way as scanr except that the processing
happens from the zeroth element up to the nth element.
W0 = init;
W1 = f unc(W0 , L0 );
W2 = f unc(W1 , L1 );
...
Wn−1 = f unc(Wn−2 , Ln−2 );
Wn = f unc(Wn−1 , Ln−1 );
The sscanl function drops the first result, init, shifting the result index by one.
scanl Apply a function over a list, creating a new list result. Processes elements from
the zeroth element up to the nth element. The result list is 1 element longer than
the input list.
function List#(a_type)
scanl(function a_type func(a_type x1, b_type x2),
a_type inita,
List#(b_type) alist);
sscanl Apply a function over a list, creating a new list result. Processes elements from
the zeroth element up to the nth element. Drop the first result, init, shifting the
result index by one. The length of the input and output lists are the same.
function List#(a_type)
sscanl(function a_type func(a_type x1, b_type x2),
a_type inita,
List#(b) alist );
Examples - Scan
Create a list of factorials
//the function my_mult multiplies element a by element b
function Bit #(16) my_mult (Bit #(16) b, Bit #(8) a);
return (extend (a) * b);
endfunction
//my_list2 = {1,1,2,6,24,120,720,5040}
Within Bluespec, there are some functions which can only be invoked in certain contexts. Two
common examples are: ActionValue, and module instantiation. ActionValues can only be invoked
within an Action context, such as a rule block or an Action method, and can be considered as two
parts - the action and the value. Module instantiation can similarly be considered, modules can only
be instantiated in the module context, while the two parts are the module instantiation (the action
performed) and the interface (the result returned). These situations are considered monadic.
When a monadic function is to be applied over a list using map-like functions such as map, zipWith,
or replicate, the monadic versions of these functions must be used. Moreover, the context require-
ments of the applied function must hold.
mapM Takes a monadic function and a list, and applies the function to all list elements
returning the list of corresponding results.
function m#(List#(b_type))
mapM ( function m#(b_type) func(a_type x),
List#(a_type) alist )
provisos (Monad#(m));
mapM_ Takes a monadic function and a list, applies the function to all list elements, and
throws away the resulting list leaving the action in its context.
zipWithM Take a monadic function (which takes two arguments) and two lists; the function
applied to the corresponding element from each list would return an action and
result. Perform all those actions and return the list of corresponding results.
function m#(List#(c_type))
zipWithM( function m#(c_type) func(a_type x, b_type y),
List#(a_type) alist,
List#(b_type) blist )
provisos (Monad#(m));
zipWith3M Same as zipWithM but combines three lists with a function. The function is
applied to the corresponding element from each list and returns an action and the
list of corresponding results.
function m#(List#(d_type))
zipWith3M( function m#(d_type)
func(a_type x, b_type y, c_type z),
List#(a_type) alist ,
List#(b_type) blist,
List#(c_type) clist )
provisos (Monad#(m));
replicateM Generate a list of elements by using the given monadic value repeatedly.
function m#(List#(element_type))
replicateM( Integer n, m#(element_type) c)
provisos (Monad#(m));
C.4 Math
C.4.1 Real
Package Name
import Real :: * ;
Description
The Real library package defines functions to operate on and manipulate real numbers. Real numbers
are numbers with a fractional component. They are also of limited precision. The Real data type
is described in section B.2.6.
Constants
The constant pi (π) is defined.
Real pi;
Trigonometric Functions
The following trigonometric functions are provided: sin, cos, tan, sinh, cosh, tanh, asin, acos,
atan, asinh, acosh, atanh, and atan2.
Arithmetic Functions
pow The element x is raised to the y power. An alias for **. pow(x,y) = x**y =
xy .
Conversion Functions
The following four functions are used to convert a Real to an Integer.
There are also two system functions $realtobits and $bitstoreal, defined in the Prelude (section
B.2.6) which provide conversion to and from IEEE 64-bit vectors (Bit#(64)).
Introspection Functions
splitReal Returns a Tuple containing the whole (n) and fractional (f ) parts of x such
that n + f = x. Both values have the same sign as x. The absolute value of
the fractional part is guaranteed to be in the range [0,1).
decodeReal Returns a Tuple3 containing the sign, the fraction, and the exponent of a real
number. The Bool represents the sign and is True for positive and False for
negative. The second part (the first Integer) represents the fractional part as
a signed Integer value. This can be converted to an Int#(54) (52 bits, plus
hidden plus sign). The last value is a signed Integer representing the exponent,
which can be be converted to an Int#(12) . The real number is represented
exactly as (f ractional × 2exp ).
C.4.2 Complex
Package Name
import Complex :: * ;
Description
The Complex package provides a representation for complex numbers plus functions to operate on
variables of this type. The basic representation is the Complex structure, which is polymorphic
on the type of data it holds. For example, one can have complex numbers of type Int or of type
FixedPoint. A Complex number is represented in two part, the real part (rel) and the imaginary
part (img). These fields are accessible though standard structure addressing, i.e., foo.rel and
foo.img where foo is of type Complex.
typedef struct {
any_t rel ;
any_t img ;
} Complex#(type any_t)
deriving ( Bits, Eq ) ;
Arith The type Complex belongs to the Arith type class, hence the common infix operators (+,
-, *, and /) are defined and can be used to manipulate variables of type Complex. The remaining
arithmetic operators are not defined for the Complex type. Note however, that some functions
generate more hardware than may be expected. The complex multiplication (*) produces four
multipliers in a combinational function; some other modules could accomplish the same function with
less hardware but with greater latency. The complex division operator (/) produces 6 multipliers,
and a divider and may not always be synthesizable with downstream tools.
instance Arith#( Complex#(any_type) )
provisos( Arith#(any_type) ) ;
Literal The Complex type is a member of the Literal class, which defines a conversion from the
compile-time Integer type to Complex type with the fromInteger function. This function converts
the Integer to the real part, and sets the imaginary part to 0.
instance Literal#( Complex#(any_type) )
provisos( Literal#(any_type) );
Functions
cmplxMap Applies a function to each part of the complex structure. This is useful for
operations such as extend, truncate, etc.
cmplxWrite Displays a complex number given a prefix string, an infix string, a postscript
string, and an Action function which writes each part. cmplxWrite is of type
Action and can only be invoked in Action contexts such as Rules and Actions
methods.
// Set the fields of the complex number using the constructor function cmplx
Complex#(Int#(6)) complex_value = cmplx(-2,7) ;
// Display complex_value as ( -2 + 7i ).
// Note that writeInt is passed as an argument to the cmplxWrite function.
cmplxWrite( "( ", " + ", "i)", writeInt, complex_value );
C.4.3 FixedPoint
Package Name
import FixedPoint :: * ;
Description
The FixedPoint library package defines a type for representing fixed-point numbers and correspond-
ing functions to operate and manipulate variables of this type.
A fixed-point number represents signed real numbers which have a fixed number of binary digits
(bits) before and after the binary point. The type constructor for a fixed-point number takes two
numeric types as argument; the first (isize) defines the number of bits to the left of the binary point
(the integer part), while the second (fsize) defines the number of bits to the right of the binary point,
(the fractional part).
The following data structure defines this type, while some utility functions provide the reading of
the integer and fractional parts.
typedef struct {
Int#(TAdd#(isize,fsize)) fxpt ;
}
FixedPoint#(numeric type isize, numeric type fsize )
deriving( Eq, Bits ) ;
Literal The type FixedPoint belongs to the Literal type class, which allows conversion from
(compile-time) type Integer to type FixedPoint. Note that only the integer part is assigned.
instance Literal#( FixedPoint#(isize, fsize) )
provisos( Add#(isize, fsize, TAdd#(isize,fsize) ),
Add#(1, xxx, isize) ) ; // isize >= 1
RealLiteral The type FixedPoint belongs to the RealLiteral type class, which allows conversion
from type Real to type FixedPoint.
Example:
FixedPoint#(3,10) p = 3.14159;
Arith The type FixedPoint belongs to the Arith type class, hence the common infix operators (+,
-, and *) are defined and can be used to manipulate variables of type FixedPoint. The arithmetic
operators / and % are not defined.
instance Arith#( FixedPoint#(isize, fsize) )
provisos( Add#(isize, fsize, TAdd#(isize,fsize) ),
Add#(1, xxx, isize) ) ; // isize >= 1
Ord In addition to equality and inequality comparisons, FixedPoint variables can be compared
by the relational operators provided by the Ord type class. i.e., <, >, <=, and >=.
instance Ord#( FixedPoint#(isize, fsize) )
provisos( Add#(1, xxx, isize) ) ; // isize >= 1
Bounded The type FixedPoint belongs to the Bounded type class. The range of values, v, rep-
resentable with a signed fixed-point number of type FixedPoint#(isize, fsize) is +(2isize−1 −
2−f size ) ≤ v ≤ −2isize−1 . The function epsilon returns the smallest representable quantum by
a specific type, 2−f size . For example, a variable v of type FixedPoint#(2,3) type can repre-
sent numbers from 1.875 (1 87 ) to −2.0 in intervals of 18 = 0.125, i.e. epsilon is 0.125. The type
FixedPoint#(5,0) is equivalent to Int#(5).
instance Bounded#( FixedPoint#(isize, fsize) ) ;
provisos( Add#(1, xxx, isize) ) ; // isize >= 1
Bitwise Left and right shifts are provided for FixedPoint variables as part of the Bitwise type
class. Note that the shift right (>>) function does an arithmetic shift, thus preserving the sign of
the operand. Note that a right shift of 1 is equivalent to a division by 2, except when the operand
is equal to −epsilon. The other methods of Bitwise type class are not provided since they have
no operational meaning on FixedPoint variables; the use of these generates an error message.
instance Bitwise#( FixedPoint#(isize, fsize) )
provisos( Add#(1, xxx, isize) ) ; // isize >= 1
Functions
Utility functions are provided to extract the integer and fractional parts.
To convert run-time Int and UInt values to type FixedPoint, the following conversion functions
are provided. Both of these functions invoke the necessary extension of the source operand.
Non-integer compile time constants may be specified by a rational number which is a ratio of two
integers. For example, one-third may be specified by fromRational(1,3).
fromRational Specify a FixedPoint with a rational number which is the ratio of two
integers.
At times, a full precision multiplication may be required, where the result is sum of the field sizes
of the operands. Note that the operand do not have to be the same type (sizes), as is required for
the infix multiplication (*) operator.
fxptMult Function for full precision multiplication, where the result is the sum of the
field sizes of the operands.
fxptTruncate Truncates bits as appropriate from the most significant integer bits and the
least significant fractional bits.
fxptSignExtend is a general sign extend function which converts variables of type FixedPoint#(ai,af)
to type FixedPoint#(ri,rf), where ai ≤ ri and af ≤ rf . The integer part is sign extended, while
additional 0 bits are added to least significant end of the fractional part.
fxptSignExtend General sign extend function where the integer part is sign extended while
additional 0 bits are added to the least significant end of the fractional part.
Displaying FixedPoint values in a simple bit notation would result in a difficult to read pattern.
The following write utility function is provided to ease in their display. Note that the use of this
function adds many multipliers and adders into the design which are only used for generating the
output and not the actual circuit.
fxptWrite Displays a FixedPoint value in a decimal format, where fwidth give the
number of digits to the right of the decimal point. fwidth must be in
the inclusive range of 0 to 10. The displayed result is truncated without
rounding.
FixedPoint#(2,3) x = 1.625 ;
C.4.4 OInt
Package Name
import OInt :: * ;
Description
The OInt#(n) type is an abstract type that can store a number in the range “0..n-1”. The repre-
sentation of a OInt#(n) takes up n bits, where exactly one bit is a set to one, and the others are
zero, i.e., it is a one-hot decoded version of the number. The reason to use a OInt number is that
the select operation is more efficient than for a binary-encoded number; the code generated for
select takes advantage of the fact that only one of the bits may be set at a time.
Types and type classes
Definition of OInt
typedef ... OInt #(numeric type n) ... ;
Functions
A binary-encoded number can be converted to an OInt.
select The Vector select function, where the type of the index is an
OInt.
function a_type select(Vector#(vsize, a_type) vecta,
OInt#(vsize) index)
provisos (Bits#(a_type, sizea));
C.5 FSM
C.5.1 StmtFSM
Package Name
import StmtFSM :: * ;
Description
The StmtFSM package provides a procedural way of defining finite state machines (FSMs) which are
automatically synthesized.
First, one uses the Stmt sublanguage to compose the actions of an FSM using sequential, parallel,
conditional and looping structures. This sublanguage is within the expression syntactic category,
i.e., a term in the sublanguage is an expression whose value is of type Stmt. This value can be bound
to identifiers, passed as arguments and results of functions, held in static data structures, etc., like
any other value. Finally, the FSM can be instantiated into hardware, multiple times if desired, by
passing the Stmt value to the module constructor mkFSM. The resulting module interface has type
FSM, which has methods to start the FSM and to wait until it completes.
The Stmt sublanguage
The state machine is automatically constructed from the procedural description given in the Stmt
definition. Appropriate state counters are created and rules are generated internally, corresponding
to the transition logic of the state machine. The use of rules for the intermediate state machine
generation ensures that resource conflicts are identified and resolved, and that implicit conditions
are properly checked before the execution of any action.
The names of generated rules (which may appear in conflict warnings) have suffixes of the form
“l<nn>c<nn>”, where the <nn> are line or column numbers, referring to the statement which gave
rise to the rule.
A term in the Stmt sublanguage is an expression, introduced at the outermost level by the keywords
seq or par. Note that within the sublanguage, if, while and for statements are interpreted
as statements in the sublanguage and not as ordinary statements, except when enclosed within
action/endaction keywords.
exprPrimary ::= seqFsmStmt | parFsmStmt
fsmStmt ::= exprFsmStmt
| seqFsmStmt
| parFsmStmt
| ifFsmStmt
| whileFsmStmt
| repeatFsmStmt
| forFsmStmt
| returnFsmStmt
exprFsmStmt ::= regWrite ;
| expression ;
seqFsmStmt ::= seq fsmStmt { fsmStmt } endseq
parFsmStmt ::= par fsmStmt { fsmStmt } endpar
ifFsmStmt ::= if expression fsmStmt
[ else fsmStmt ]
whileFsmStmt ::= while ( expression )
loopBodyFsmStmt
forFsmStmt ::= for ( fsmStmt ; expression ; fsmStmt )
loopBodyFsmStmt
returnFsmStmt ::= return ;
repeatFsmStmt ::= repeat ( expression )
loopBodyFsmStmt
loopBodyFsmStmt ::= fsmStmt
| break ;
| continue ;
The simplest kind of statement is an exprFsmStmt, which can be a register assignment or, more
generally, any expression of type Action (including action method calls and action-endaction
blocks or of type Stmt. Statements of type Action execute within exactly one clock cycle, but of
course the scheduling semantics may affect exactly which clock cycle it executes in. For example, if
the actions in a statement interfere with actions in some other rule, the statement may be delayed
by the schedule until there is no interference. In all the descriptions of statements below, the
descriptions of time taken by a construct are minimum times; they could take longer because of
scheduling semantics.
Statements can be composed into sequential, parallel, conditional and loop forms. In the sequential
form (seq-endseq), the contained statements are executed one after the other. The seq block
terminates when its last contained statement terminates, and the total time (number of clocks) is
equal to the sum of the individual statement times.
In the parallel form (par-endpar), the contained statements (“threads”) are all executed in parallel.
Statements in each thread may or may not be executed simultaneously with statements in other
threads, depending on scheduling conflicts; if they cannot be executed simultaneously they will be
interleaved, in accordance with normal scheduling. The entire par block terminates when the last
of its contained threads terminates, and the minimum total time (number of clocks) is equal to the
maximum of the individual thread times.
In the conditional form (if (b) s1 else s2 ), the boolean expression b is first evaluated. If true,
s1 is executed, otherwise s2 (if present) is executed. The total time taken is t cycles, if the chosen
branch takes t cycles.
In the while (b) s loop form, the boolean expression b is first evaluated. If true, s is executed, and
the loop is repeated. Each time the condition evaluates true , the loop body is executed, so the total
time is n × t cycles, where n is the number of times the loop is executed (possibly zero) and t is the
time for the loop body statement.
The for (s1 ;b;s2 ) sB loop form is equivalent to:
i.e., the initializer s1 is executed first. Then, the condition b is executed and, if true, the loop body
sB is executed followed by the “increment” statement s2 . The b, sB , s2 sequence is repeated as long
as b evaluates true.
Similarly, the repeat (n) sB loop form is equivalent to:
where the value of repeat count is initialized to 0. During execution, the condition (repeat count <
n) is executed and, if true, the loop body sB is executed followed by the “increment” statement
repeat count <= repeat count + 1. The sequence is repeated as long as repeat count < n evaluates
true.
In all the loop forms, the loop body statements can contain the keywords continue or break, with
the usual semantics, i.e., continue immediately jumps to the start of the next iteration, whereas
break jumps out of the loop to the loop sequel.
It is important to note that this use of loops, within a Stmt context, expresses time-based (temporal)
behavior.
Interfaces and Methods
Two interfaces are defined with this package, FSM and Once. The FSM interface defines a basic state
machine interface while the Once interface encapsulates the notion of an action that should only be
performed once. A Stmt value can be instatiated into a module that presents an interface of type
FSM.
Interfaces
Name Description
FSM The state machine interface
Once Used when an action should only be performed once
• FSM Interface
The FSM interface provides three methods; start, waitTillDone, and done. Once instantiated,
the FSM can be started by calling the start method. One can wait for the FSM to stop running
by waiting explicitly on the boolean value returned by the done method. Alternatively, one
can use the waitTillDone method in any action context (including from within another FSM),
which (because of an implicit condition) cannot execute until this FSM is done. The user must
not use waitTillDone until after the FSM has been started because the FSM comes out of a
reset as done.
interface FSM;
method Action start();
method Action waitTillDone();
method Bool done();
endinterface: FSM
FSM Interface
Methods
Name Type Description
start Action Begins state machine execution. This can only be called
when the state machine is not executing.
waitTillDone Action Does not do any action, but is only ready when the state
machine is done.
done Bool Asserted when the state machine is done and is ready to
rerun.
• Once Interface
The Once interface encapsulates the notion of an action that should only be performed once.
The start method performs the action that has been encapuslated in the Once module. After
start has been called start cannot be called again (an implicit condition will enforce this).
If the clear method is called, the start method can be called once again.
interface Once;
method Action start();
method Action clear();
method Bool done() ;
endinterface: Once
Once Interface
Methods
Name Type Description
start Action Performs the action that has been encapsulated in the
Once module, but once start has been called it cannot
be called again (an implicit condition will enforce this).
clear Action If the clear method is called, the start method can be
called once again.
done Bool Asserted when the state machine is done and is ready to
rerun.
Modules
Instantiation is performed by passing a Stmt value into the module constructor mkFSM. The state
machine is automatically constructed from the procedural decription given in the definition described
by state machine of type Stmt named seq_stmt. During construction, one or more registers of
appropriate widths are created to track state execution. Upon start action, the registers are loaded
and subsequent state changes then decrement the registers.
The mkFSMWithPred module is like mkFSM above, except that the module constructor takes an ad-
ditional boolean argument (the predicate). The predicate condition is added to the condition of
each rule generated to create the FSM. This capability is useful when using the FSM in conjuction
with other rules and/or FSMs. It allows the designer to explicitly specify to the compiler the condi-
tions under which the FSM will run. This can be used to eliminate spurious rule conflict warnings
(between rules in the FSM and other rules in the design).
The mkAutoFSM module is also like mkFSM above, except the state machine runs automatically im-
mediately after reset and a $finish(0) is called upon completion. This is useful for test benches.
Thus, it has no interface, that is, it has an empty interface.
The mkOnce function is used to create a Once interface where the action argument has been encap-
sulated and will be performed when start is called.
The implementation for Once is a 1 bit state machine (with a state register named onceReady)
allowing the action argument to occur only one time. The ready bit is initially True and then
cleared when the action is performed. It might not be performed right away, because of implicit
conditions or scheduling conflicts.
Functions
There are two functions, await and delay, provided by the StmtFSM package.
The await function is used to create an action which can only execute when the condition is True.
The action does not do anything. await is useful to block the execution of an action until a condition
becomes True.
The delay function is used to execute noAction for a specified number of cycles. The function is
provided the value of the delay and returns a Stmt.
...
When the start_reset signal is true, the rule kicks off the SRAM initialization. Other rules can
wait on fsm.done, if necessary, for the SRAM initialization to be completed.
In this example, the seq-endseq brackets are used to enter the Stmt sublanguage, and then for
represents Stmt sequencing (instead of its usual role of static generation). Since seq-endseq contains
only one statement (the loop nest), par-endpar brackets would have worked just as well.
Example - Defining and instantiating a state machine.
import StmtFSM :: *;
import FIFO :: *;
module testSizedFIFO();
// Instantiation of DUT
FIFO#(Bit#(16)) dut <- mkSizedFIFO(5);
seq
while (i < 5)
seq
noAction;
endseq
while (i <= 10)
action
dut.deq;
$display("Value read %d", dut.first);
endaction
endseq
endpar
$finish(0);
endseq);
// stmt instantiation
FSM test <- mkFSM(driversMonitors);
// This rule kicks off the test FSM, which then runs to completion.
rule start (!going);
going <= True;
test.start;
endrule
endmodule
interface SC_FSM_ifc;
method Speed xcvrspeed;
method Bool devices_ready;
method Bool out_of_reset;
endinterface
// the following lines define the FSM using the Stmt sublanguage
// the state machine is of type Stmt, with the name speed_change_stmt
Stmt speed_change_stmt =
(seq
action outofReset_reg <= False; devices_ready_reg <= False; endaction
noAction; noAction; // same as: delay(2);
endseq
// Wait a little while:
for (i <= 0; i < 200; i <= i+1)
action
endaction
// Set an interrupt:
action
inrpt.set;
endaction
endseq
);
// end of the state machine definition
// The rule which starts the FSM, provided it hasn’t been started
// previously and the brick is enabled:
rule start_Afsm (notStarted && enabled);
brickAfsm.start; //start the state machine
notStarted <= False;
endrule
The RStmt type is a polymorphic generalization of the Stmt type. A sequence of type RStmt#(a)
allows valued return statements (where the return value is of type a). Note that the Stmt type is
equivalent to RStmt#(Bit#(0)).
The FSMServer interface has one subinterface of type Server#(a, b) (from the ClientServer
package) as well as an Action method called abort; The abort method allows the FSM inside the
FSMServer module to be halted if the client FSM is halted.
An FSMServer module is accessed using the callServer function. callServer takes two arguments.
The first is the interface of the FSMServer module. The second is the input value being passed to
the module.
Note the special left arrow notation that is used pass the server result to a register (or more generally
to any state element with a Reg interface). A simple example follows showing the definition and use
of a mkFSMServer module.
Example - Defining and instantiating an FSM Server Module
C.6 Connectivity
The packages in this section provide useful components, primarily interfaces, to connect hardware
elements in a design.
The basic interfaces, Get and Put are defined in the package GetPut. The typeclass Connectable
indicates that two related types can be connected together. The package ClientServer provides
interfaces using Get and Put for modules that have a request-response type of interface. The package
CGetPut defines a type of the Get and Put interfaces that is implemented with a credit based FIFO.
C.6.1 GetPut
Package Name
import GetPut :: *;
Description
Get and Put are simple interfaces, consisting of one method each, get and put, respectively. This
package provides the interfaces Get, Put, and GetPut. This package also provides modules which
provide the GetPut interface as a FIFO implementation, but these interfaces can be used in many
additional hardware implementations.
Typeclasses
The GetPut package defines two typeclasses; ToGet and ToPut.
ToGet defines the class to which the function toGet can be applied to create an associated Get
interface.
ToPut defines the class to which the function toPut can be applied to create an associated Put
interface.
Instances of ToGet and ToPut are defined for the following interfaces. The toGet and toPut functions
convert these interfaces to a Get and Put interface respectively.
FIFO
FIFOF
SyncFIFOIfc
FIFOLevelIfc
SyncFIFOLevelIfc
FIFOCountIfc
SyncFIFOCountIfc
Interfaces
Interface Name Parameter Parameter Description Restrictions
name
Get element type type of the element must be in Bits class
being retrieved by the Get
Put element type type of the element must be in Bits class
being added by the Put
GetPut element type type of the element must be in Bits class
being retrieved and added
Get
The Get interface is where you retrieve (get) data from an object. The Get interface is provides
a single method, get, which retrieves an item of data from an interface and removes it from the
object. A get is similar to a dequeue, but it can be associated with any interface. A Get interface
is more abstract than a FIFO interface; it does not describe the underlying hardware.
Get
Method Argument
Name Type Description Name Description
get ActionValue returns an item from an
interface and removes it
from the object
Put
The Put interface is where you can give (put) data to an object. The Put interface provices a single
method, put, which gives an item to an interface. A put is similar to a enqueue, but it can be
associated with any interface. A Put interface is more abstract than a FIFO interface; it does not
describe the underlying hardware.
Put
Method Argument
Name Type Description Name Description
put Action gives an item to an interface x1 data to be added to the object
must be of type element_type
GetPut
The library also defines an interface GetPut which associates Get and Put interfaces into a Tuple2.
typedef Tuple2#(Get#(element_type), Put#(element_type)) GetPut#(type element_type);
Type classes
The class Connectable (Section C.6.2) is meant to indicate that two related types can be connected
in some way. It does not specify the nature of the connection.
A Get and Put is an example of connectable items. One object will put an element into the interface
and the other object will get the element from the interface.
Modules
There are three modules provided by the GetPut package which provide the GetPut interface with
a type of FIFO. These FIFOs use Get and Put interfaces instead of the usual enq interfaces. To use
any of these modules the FIFO package must be imported. You can also write your own modules
providing a GetPut interface for other hardware structures.
Functions
There are two functions defined in the GetPut package that change a FIFO interface to a Get or
Put interface. Given a FIFO we can use the function fifoToGet to obtain a Get interface, which
is a combination of deq and first. Given a FIFO we can use the function fifoToPut to obtain a
Put interface using enq. The functions toGet and toPut (C.6.1) are recommended instead of the
fifoToGet and fifoToPut functions.
The package defines an additional function, peekGet, which returns the first item without removing
it from the object. There are scheduling concerns when using peekGet; because of the implicit
condition, it will only fire if there is data available.
fifoToGet Returns a Get interface. It is recommended that you use the function toGet
(C.6.1) instead of this function.
function Get#(element_type) fifoToGet(FIFO#(element_type) f);
fifoToPut Returns a Put interface. It is recommended that you use the function toPut
(C.6.1) instead of this function.
function Put#(element_type) fifoToPut(FIFO#(element_type) f);
peekGet Returns first item without removing it from the object. Will not fire if data
is not available.
function element_type peekGet(Get#(element_type) g;)
...
module mkMyModule (MyInterface);
GetPut#(StatusInfo) aFifoOfStatusInfoStructures <- mkGPFIFO;
...
endmodule: mkMyModule
C.6.2 Connectable
Package Name
import Connectable :: * ;
Description
The Connectable package contains the definitions for the class Connectable and two instances of
Connectables; Tuples and Vectors.
Types and Type-Classes
The class Connectable is meant to indicate that two related types can be connected in some way.
It does not specify the nature of the connection. The Connectables type class defines the module
mkConnection, which is used to connect the pairs.
Instances
Get and Put One instance of the typeclass of Connectable is Get and Put. One object will put
an element into an interface and the other object will get the element from the interface.
Tuples If we have Tuple2 of connectable items then the pair is also connectable, simply by con-
necting the individual items.
instance Connectable#(Tuple2#(a, c), Tuple2#(b, d))
provisos (Connectable#(a, b), Connectable#(c, d));
The proviso shows that the first component of one tuple connects to the first component of the other
tuple, likewise, the second components connect as well. In the above statement, a connects to b and
c connects to d. This is used by ClientServer (Section C.6.3) to connect the Get of the Client to
the Put of the Server and visa-versa.
This is extensible to all Tuples (Tuple3, Tuple4, etc.). As long as the items are connectable, the
Tuples are connectable.
InOut Inouts are connectable via the Connectable typeclass. The use of mkConnection instan-
tiates a Verilog module InoutConnect. The Inouts must be on the same clock and the same reset.
The clock and reset of the Inouts may be different than the clock and reset of the parent module of
the mkConnection.
instance Connectable#(Inout#(a, x1), Inout#(a, x2))
provisos (Bit#(a,sa));
C.6.3 ClientServer
Package Name
import ClientServer :: * ;
Description
The ClientServer package provides two interfaces, Client and Server which can be used to define
modules which have a request-response type of interface. The GetPut package must be imported
when using this package because the Get and Put interface types are used.
Interfaces and methods
The interfaces Client and Server can be used for modules that have a request-response type of
interface (e.g. a RAM). The server accepts requests and generates responses, the client accepts
responces and generates requests. There are no assumptions about how many (if any) responses a
request generates
Interfaces
Interface Name Parameter name Parameter Description Restrictions
Client req type type of the client request must be in the Bits class
resp type type of the client response must be in the Bits class
Server req type type of the server request must be in the Bits class
resp type type of the server response must be in the Bits class
Client
The Client interface provides two sub-interfaces, request and response. From a Client, one gets
a request and puts a response.
Client SubInterface
Name Type Description
request Get#(req_type) the interface through which the outside world
retrieves (gets) a request
response Put#(resp_type) the interface through which the outside world
returns (puts) a response
Server
The Server interface provides two sub-interfaces, request and response. From a Server, one puts
a request and gets a response.
Server SubInterface
Name Type Description
request Put#(req_type) the interface through which the outside world
returns (puts) a request
response Get#(resp_type) the interface through which the outside world
retrieves (gets) a response
ClientServer
A Client can be connected to a Server and vice versa. The request (which is a Get interface)
of the client will connect to response (which is a Put interface) of the Server. By making the
ClientServer tuple an instance of the Connectable typeclass, you can connect the Get of the client
to the Put of the server, and the Put of the client to the Get of the server.
instance Connectable#(Client#(req_type, resp_type), Server#(req_type, resp_type));
instance Connectable#(Server#(req_type, resp_type), Client#(req_type, resp_type));
interface Bus_Ifc;
interface Server#(RQ, RS) to_targ ;
interface Client#(RQ, RS) to_initor;
endinterface
// Connect bus and targ ("to_targ" is a Get ifc, targ is a Put ifc)
Empty x <- mkConnection (bus.to_targ, targ);
// Connect bus and initiator ("to_initor" is a Out ifc, initor is a Get ifc)
mkConnection (bus.to_initor, initor);
// Since mkConnection returns an interface of type Empty, it does
// not need to be specified (but may be as above)
...
endmodule: mkSys
C.6.4 CGetPut
Package Name
import CGetPut :: * ;
Description
The interfaces CGet and CPut are similar to Get and Put, but the interconnection of them (via
Connectable) is implemented with a credit-based FIFO. This means that the CGet and CPut inter-
faces have completely registered input and outputs, and furthermore that additional register buffers
can be introduced in the connection path without any ill effect (except an increase in latency, of
course).
In the absence of additional register buffers, the round-trip time for communication between the two
interfaces is 4 clock cycles. Call this number r. The first argument to the type, n, specifies that
transfers will occur for a fraction n/r of clock cycles (note that the used cycles will not necessarily be
evenly spaced). n also specifies the depth of the buffer used in the receiving interface (the transmitter
side always has only a single buffer). So (in the absence of additional buffers) use n = 4 to allow
full-bandwidth transmission, at the cost of sufficient registers for quadruple buffering at one end;
use n = 1 for minimal use of registers, at the cost of reducing the bandwidth to one quarter; use
intermediate values to select the optimal trade-off if appropriate.
Interfaces and methods
The interface types are abstract to avoid any non-proper use of the credit signaling protocol.
Interfaces
Interface Name Parameter Parameter Description Restrictions
name
CGet n depth of the buffer used in the re- must be a numeric
ceiving interface type
element type type of the element must be in Bits class
being retrieved by the CGet
CPut n depth of the buffer used in the re- must be a numeric
ceiving interface type
element type type of the element must be in Bits class
being added by the CPut
• CGet
c 2008 Bluespec, Inc. All rights reserved 267
Reference Guide Bluespec SystemVerilog
• CPut
interface CPut#(numeric type n, type element_type);
...Abstract...
• Connectables
The CGet and CPut interfaces are connectable.
instance Connectable#(CGet#(n, element_type), CPut#(n, element_type));
Modules
mkCGetPut Create an n depth FIFO with a CGet interface on the dequeue side and a
Put interface on the enqueue side.
module mkCGetPut(Tuple2#(CGet#(n, element_type),
Put#(element_type)))
provisos (Bits#(element_type));
mkGetCPut Create an n depth FIFO with a Get interface on the dequeue side and a
CPut interface on the enqueue side.
module mkGetCPut(Tuple2#(Get#(element_type),
CPut#(n, element_type)))
provisos (Bits#(element_type));
C.7 Utilities
C.7.1 LFSR
Package
import LFSR :: * ;
Description
The LFSR package implements Linear Feedback Shift Registers (LFSRs). LFSRs can be used to
obtain reasonable pseudo-random numbers for many purposes (though not good enough for cryp-
tography). The seed method must be called first, to prime the algorithm. Then values may be
read using the value method, and the algorithm stepped on to the next value by the next method.
When a LFSR is created the start value, or seed, is 1.
Interfaces and Methods
The LFSR package provides an interface, LFSR, which contains three methods; seed, value, and
next. To prime the LFSR the seed method is called with the parameter seed_value, of datatype
a_type. The value is read with the value method. The next method is used to shift the register
on to the next value.
LFSR Interface
Method Arguments
Name Type Description Name Description
seed Action Sets the value of the shift register. a_type datatype of the
seed value
seed_value the initial value
value a_type returns the value of the shift register
next Action signals the shift register to shift to
the next value.
Modules
The module mkFeedLFSR creates a LFSR where the polynomial is specified by the mask used for
feedback.
mkFeedLFSR Creates a LFSR where the polynomial is specified by the mask (feed ) used
for feedback.
module mkFeedLFSR#( Bit#(n) feed )( LFSR#(Bit#(n)) );
For example, the polynominal x7 +x3 +x2 +x+1 is defined by the expression mkFeedLFSR#(8’b1000_1111)
Using the module mkFeedLFSR, the following maximal length LFSR’s are defined in this package.
For example,
mkLFSR_4 = mkFeedLFSR( 4’h9 );
The module mkLFSR_4 instantiates the interface LFSR with the value Bit#(4) to produce a 4 bit
shift register. The module uses the polynomial defined by the mask 4’h9 (x3 + 1) and the module
mkFeedLFSR.
The mkRCounter function creates a counter with a LFSR interface. This is useful during debugging
when a non-random sequence is desired. This function can be used in place of the other mkLFSR
module constructors, without changing any method calls or behavior.
import GetPut::*;
import FIFO::*;
import LFSR::*;
module mkRn_6(RandI#(6));
// A boolean flag for ensuring that we first seed the LFSR module
Reg#(Bool) starting <- mkReg(True) ;
// This rule fires first, and sends a suitable seed to the module.
rule start (starting);
starting <= False;
lfsr.seed(’h11);
endrule: start
// The interface for mkRn_6 is a Get interface. We can produce this from a
// FIFO using the fifoToGet function. We therefore don’t need to define any
// new methods explicitly in this module: we can simply return the produced
// Get interface as the "result" of this module instantiation.
return fifoToGet(fi);
endmodule
C.7.2 Randomizable
Description
The Randomizable package includes interfaces and modules to generate random values of a given
data type.
This package is provided as both a compiled library package and as BSV source code to facilitate
customization. The source code file can be found in the $BLUESPECDIR/BSVSource directory. To
customize a package, copy the file into a local directory and then include the local directory in the
path when compiling. This is done by specifying the path with the -p option as described in the
BSV Users Guide.
Packages
To include a package in your design, use the import syntax.
import Randomizable :: * ;
Randomize Interface
Name Type Description
cntrl Interface Control interface provided by the module.
next ActionValue Returns the next value of type a.
Control Interface
Name Type Description
init Control Action method to initialize the randomizer.
interface Control ;
method Action init();
endinterface
Modules
The Randomizable package includes two modules which return random values of type a. The
difference between the two modules is how the min and max values are determined. The module
mkGenericRandomizer uses the min and max values of the type, while the module mkConstrainedRandomizer
uses arguments to set the min and max values. The type a must be in the Bounded class for both
modules.
mkGenericRandomizer This module provides a Randomize interface, which will return the next ran-
dom value when the next method is invoked. The min and max values are
the values defined by the type a which must be in the Bounded class.
mkConstrainedRandomizer This module provides a Randomize interface, which will give the next random
value when the next method is invoked. When instantiated, the min and max
values are provided as arguments. Type a must be in the Bounded class.
Example
The mkTLMRandomizer module, defined within the TLM package (Section C.10.1), uses the Random-
ize package to generate random values for TLM packets. The mkConstrainedRandomizer module is
for fields with specific allowed values or ranges, while the mkGenericRandomizer module is for field
where all values of the type are allowed.
Bounded#(RequestData#(‘TLM_TYPES))
);
...
// Use mkGeneric Randomizer - entire range valid
Randomize#(RequestDescriptor#(‘TLM_TYPES)) descriptor_gen <- mkGenericRandomizer;
Randomize#(Bit#(2)) log_wrap_gen <- mkGenericRandomizer;
Randomize#(RequestData#(‘TLM_TYPES)) data_gen <- mkGenericRandomizer;
C.7.3 Arbiter
Description
The Arbiter package includes interfaces and modules to implement two different arbiters: a fair
arbiter with changing priorities (round robin) and a sticky arbiter, also round robin, but which gives
the current owner priority.
This package is provided as both a compiled library package and as BSV source code to facilitate
customization. The source code file can be found in the $BLUESPECDIR/BSVSource directory. To
customize a package, copy the file into a local directory and then include the local directory in the
path when compiling. This is done by specifying the path with the -p option as described in the
BSV Users Guide.
Packages
To include a package in your design, use the import syntax.
import Arbiter :: * ;
ArbiterClient IFC The ArbiterClient_IFC interface has two methods: an Action method to
make the request and a Boolean value method to indicate the request was granted. The lock method
is unused in this implementation.
interface ArbiterClient_IFC;
method Action request();
method Action lock();
method Bool grant();
endinterface
ArbiterRequest IFC The ArbiterRequest_IFC interface has two methods: an Action method
to grant the request and a Boolean value method to indicate there is a request. The lock method is
unused in this implementation.
interface ArbiterRequest_IFC;
method Bool request();
method Bool lock();
method Action grant();
endinterface
Arbiter IFC The Arbiter_IFC has a subinterface which is a vector of ArbiterClient_IFC in-
terfaces. The number of items in the vector equals the number of clients.
Modules
The mkArbiter module is a fair arbiter with changing priorities (round robin). The mkStickyArbiter
gives the current owner priority - they can hold priority as long as they keep requesting it. The
modules all provide a Arbiter_IFC interface.
mkArbiter This module is a fair arbiter with changing priorities (round robin). If fixed is
True, the current client holds the priority, if fixed is False, it moves to the next
client. mkArbiter provides a Arbiter_IFC interface. Initial priority is given to
client 0.
mkStickyArbiter As long as the client currently with the grant continues to assert request, it can
hold the grant. It provides a Arbiter_IFC interface.
C.7.4 GrayCounter
Description
The GrayCounter package provides an interface and a module to implement a gray-coded counter
with methods for both binary and Gray code. This package is designed for use in the BRAMFIFO
module, Section C.1.9. Since BRAMs have registered address inputs, the binary outputs are not
registered. The counter has two domains, source and destination. Binary and Gray code values are
written in the source domain. Both types of values can be read from the source and the destination
domains.
This package is provided as both a compiled library package and as BSV source code to facilitate
customization. The source code file can be found in the $BLUESPECDIR/BSVSource directory. To
customize a package, copy the file into a local directory and then include the local directory in the
path when compiling. This is done by specifying the path with the -p option as described in the
BSV Users Guide.
Package Name
To include a package in your design, use the import syntax.
import GrayCounter :: * ;
Types
The GrayCounter package uses the type Gray, defined in the Gray package, Section C.7.5. The Gray
package is imported by the GrayCounter package.
Interfaces and Methods
The GrayCounter package includes one interface, GrayCounter.
Modules
The module mkGrayCounter instantiates a Gray code counter with methods for both binary and
Gray code.
C.7.5 Gray
Description
The Gray package defines a datatype, Gray and functions for working with the Gray type. This type
is used by the GrayCounter package.
This package is provided as both a compiled library package and as BSV source code to facilitate
customization. The source code file can be found in the $BLUESPECDIR/BSVSource directory. To
customize a package, copy the file into a local directory and then include the local directory in the
path when compiling. This is done by specifying the path with the -p option as described in the
BSV Users Guide.
Package Name
import Gray :: * ;
The datatype Gray is a representation for Gray code values. The basic representation is the Gray
structure, which is polymorphic on the size of the value.
typedef struct {
Bit#(n) code;
} Gray#(numeric type n) deriving (Bits, Eq);
Functions
grayEncode This function takes a binary value of type Bit#(n) and returns a Gray
type with the Gray code value.
function Gray#(n) grayEncode(Bit#(n) value)
provisos(Add#(1, msb, n));
grayDecode This function takes a Gray code value of size n and returns the binary
value.
function Bit#(n) grayDecode(Gray#(n) value)
provisos(Add#(1, msb, n));
grayIncrDecr This functions takes a Gray code value and a Boolean, decrement. If
decrement is True, the value returned is one less than the input value.
If decrement is False, the value returned is one greater.
function Gray#(n) grayIncrDecr(Bool decrement,
Gray#(n) value)
provisos(Add#(1, msb, n));
grayIncr Takes a Gray code value and returns a Gray code value incremented
by 1.
function Gray#(n) grayIncr(Gray#(n) value)
provisos(Add#(1, msb, n));
grayDecr Takes a Gray code value a returns a Gray code value decremented by
1.
function Gray#(n) grayDecr(Gray#(n) value)
provisos(Add#(1, msb, n));
C.7.6 CompletionBuffer
Package
import CompletionBuffer :: * ;
Description
A CompletionBuffer is like a FIFO except that the order of the elements in the buffer is independent
of the order in which the elements are entered. Each element obtains a token, which reserves a slot
in the buffer. Once the element is ready to be entered into the buffer, the token is used to place the
element in the correct position. When removing elements from the buffer, the elements are delivered
in the order specified by the tokens, not in the order that the elements were written.
Completion Buffers are useful when multiple tasks are running, which may complete at different
times, in any order. By using a completion buffer, the order in which the elements are placed in the
buffer can be controlled, independent of the order in which the data becomes available.
Interface and Methods
The CompletionBuffer interface provides three subinterfaces. The reserve interface, a Get, allows
the caller to reserve a slot in the buffer by returning a token holding the identity of the slot. When
data is ready to be placed in the buffer, it is added to the buffer using the complete interface of type
Put. This interface takes a pair of values as its argument - the token identifying its slot, and the
data itself. Finally, using the drain interface, of type Get, data may be retrieved from the buffer in
the order in which the tokens were originally allocated. Thus the results of quick tasks might have
to wait in the buffer while a lengthy task ahead of them completes.
The type of the elements to be stored is element_type. The type of the required size of the buffer
is a numeric type n, which is also the type argument for the type for the tokens issued, CBToken.
This allows the type-checking phase of the synthesis to ensure that the tokens are the appropriate
size for the buffer, and that all the buffer’s internal registers are of the correct sizes as well.
CompletionBuffer Interface
Name Type Description
reserve Get Used to reserve a slot in the buffer. Returns a token, CBToken #(n),
identifying the slot in the buffer.
complete Put Enters the element into the buffer. Takes as arguments the slot in the
buffer, CBToken#(n), and the element to be stored in the buffer.
drain Get Removes an element from the buffer. The elements are returned in the
order the tokens were allocated.
Datatypes
The CBToken type is abstract to avoid misuse.
typedef union tagged { ... } CBToken #(numeric type n) ...;
Modules
The mkCompletionBuffer module is used to instantiate a completion buffer. It takes no size argu-
ments, as all that information is already contained in the type of the interface it produces.
import List::*;
import FIFO::*;
import GetPut::*;
import CompletionBuffer::*;
// Multiplier interface
interface Mult_IFC;
method Action start (Tin m1, Tin m2);
method ActionValue#(Tout) result();
endinterface
Mult_IFC s = mults[i];
// The rules for sending tasks to this particular server, and for
// dealing with returned results:
rule start_server (f); // start only if flag says it’s free
// Get a token
CBToken#(BuffSize) new_t <- cbuff.reserve.get;
Args a = infifo.first;
Tin a1 = tpl_1(a);
Tin a2 = tpl_2(a);
infifo.deq;
f <= False;
t <= new_t;
s.start(a1,a2);
endrule
C.7.7 UniqueWrappers
Package
import UniqueWrappers :: * ;
Description
The UniqueWrappers package takes a piece of combinational logic which is to be shared and puts it
into its own protective shell or wrapper to prevent its duplication. This is used in instances where a
separately synthesized module is not possible. It allows the designer to use a piece of logic at several
places in a design without duplicating it at each site.
There are times where it is desired to use a piece of logic at several places in a design, but it is too
bulky or otherwise expensive to duplicate at each site. Often the right thing to do is to make the
piece of logic into a separately synthesized module – then, if this module is instantiated only once,
it will not be duplicated, and the tool will automatically generate the scheduling and multiplexing
logic to share it among the sites which use its methods. Sometimes, however, this is not convenient.
One reason might be that the logic is to be incorporated into a sub-module of the design which is
itself polymorphic – this will probably cause difficulties in observing the constraints necessary for a
module which is to be separately synthesized. And if a module is not separately synthesized, the
tool will inline its logic freely wherever it is used, and thus duplication will not be prevented as
desired.
This package covers the case where the logic to be shared is combinational and cannot be put
into a separately synthesized module. It may be thought of as surrounding this combinational
function with a protective shell, a unique wrapper, which will prevent its duplication. The module
mkUniqueWrapper takes a one-argument function as a parameter; both the argument type a and the
result type b must be representable as bits, that is, they must both be in the Bits typeclass.
Interfaces
The UniqueWrappers package provides an interface, Wrapper, with one actionvalue method, func,
which takes an argument of type a and produces a method of type ActionValue#(b). If the module
is instantiated only once, the logic implementing its parameter will be instantiated just once; the
module’s method may, however, be used freely at several places.
Although the function supplied as the parameter is purely combinational and does not change state,
the method is of type ActionValue. This is because actionvalue methods have enable signals and
these signals are needed to organize the scheduling and multiplexing between the calling sites.
Variants of the interface Wrapper are also provided for handling functions of two or three arguments;
the interfaces have one and two extra parameters respectively. In each case the result type is the
final parameter, following however many argument type parameters are required.
Wrapper Interfaces
Wrapper This interface has one actionvalue method, func, which takes an argument of type
a_type and produces an actionvalue of type ActionValue#(b_type).
interface Wrapper#(type a_type, type b_type);
method ActionValue#(b_type) func (a_type x);
Modules
The interfaces Wrapper, Wrapper2, and Wrapper3 are provided by the modules mkUniqueWrapper,
mkUniqueWrapper2, and mkUniqueWrapper3. These modules vary only in the number of aguments
in the parameter function.
If a function has more than three arguments, it can always be rewritten or wrapped as one which
takes the arguments as a single tuple; thus the one-argument version mkUniqueWrapper can be used
with this function.
mkUniqueWrapper
Takes a function, func, with a single parameter x and provides the interface Wrapper.
module mkUniqueWrapper#(function b_type func(a_type x))
(Wrapper#(a_type, b_type))
provisos (Bits#(a_type, sizea), Bits#(b_type, sizeb));
mkUniqueWrapper2
Takes a function, func, with a two parameters, x and y, and provides the interface
Wrapper2.
module mkUniqueWrapper2#(function b_type func(a1_type x, a2_type y))
(Wrapper2#(a1_type, a2_type, b_type))
provisos (Bits#(a1_type, sizea1), Bits#(a2_type, sizea2),
Bits#(b_type, sizeb));
mkUniqueWrapper3
Takes a function, func, with a three parameters, x, y, and z, and provides the interface
Wrapper3.
module mkUniqueWrapper3#(function b_type
func(a1_type x, a2_type y, a3_type z))
(Wrapper3#(a1_type, a2_type, a3_type, b_type))
provisos (Bits#(a1_type, sizea1), Bits#(a2_type, sizea2),
Bits#(a3_type, sizea3), Bits#(b_type, sizeb));
rr <= mr ;
endaction
action
let mr <- smult.func( arg1.img, arg2.img ) ;
ii <= mr ;
endaction
action
// Do the first add in this step
let mr <- smult.func( arg1.img, arg2.rel ) ;
ir <= mr ;
rr <= rr - ii ;
endaction
action
let mr <- smult.func( arg1.rel, arg2.img );
ri <= mr ;
// We are done with the inputs so deq the in fifos
infifo1.deq ;
infifo2.deq ;
endaction
action
let ii2 = ri + ir ;
let res = Complex{ rel: rr , img: ii2 } ;
outfifo.enq( res ) ;
endaction
endseq;
C.7.8 FShow
Package
import FShow :: * ;
Description
The FShow package defines the typeclass FShow. FShow includes a single member function, fshow.
When applied to an object which is an instance of FShow, the fshow function returns an object of
type Fmt (Section B.2.8).
This package is provided as both a compiled library package and as BSV source code to facilitate
customization. The source code file can be found in the $BLUESPECDIR/BSVSource directory. To
customize a package, copy the file into a local directory and then include the local directory in the
path when compiling. This is done by specifying the path with the -p option as described in the
BSV Users Guide.
Typeclasses
FShow defines the class of types to which the function fshow can be applied to create an associated
Fmt representation.
The package defines instances of FShow for many commonly used datatypes. Users can create their
own FShow instances for other types (or redefine the instances included in the FShow package).
FShow Instances
String Returns a Fmt object showing the value of the string.
instance FShow#(String);
Maybe#(a) Returns a Fmt object showing Valid and the value, or just Invalid.
instance FShow#(Maybe#(a))
provisos(FShow#(a));
Returns a Fmt object showing the first element and Empty/Full state of the
FIFOF_#(a)
FIFO.
FIFOF#(a)
instance FShow#(FIFOF_#(a))
provisos(FShow#(a));
Vector#(n, a) Returns a Fmt object showing <V elem1 elem2 ...>, where the elemn are
the elements of the vector.
instance FShow#(Vector#(n, a))
provisos(FShow#(a));
List#(a) Returns a Fmt object showing <List elem1 elem2 ...>, where the elemn
are the elements of the list.
instance FShow#(List#(a))
provisos(FShow#(a));
FixedPoint#(i,f) Returns a Fmt object showing FP int.frac where int is the integer part
and frac is the fractional part of the fixed point number.
instance FShow#(FixedPoint#(i,f))
provisos(Add#(i,f, TAdd#(i,f)),Add#(1,ignore, i));
Functions
concatWith Concantenates a String (x) with two other arguments a and b, both of
type Fmt.
function Fmt concatWith(String x, Fmt a, Fmt b);
return (a + $format(x) + b);
Modules
dbgProbe This module is used like a Probe except that the sampled value (to be
viewed in waves) is the ascii representation of fshow(value).
module dbgProbe (Probe#(a))
provisos(FShow#(a));
Example
package FShowExample;
import Probe::*;
import FShow::*;
import Vector::*;
/// Define FShow instances for the ones that aren’t already in FShow.bsv
instance FShow#(OpCommand);
function Fmt fshow (OpCommand label);
case (label)
READ: return fshow("READ ");
WRITE: return fshow("WRITE");
UNKNOWN: return fshow("UNKNOWN");
endcase
endfunction
endinstance
instance FShow#(Header);
function Fmt fshow (Header value);
return ($format("<HEAD ")
+
fshow(value.command)
+
$format(" (%0d)", value.length)
+
$format(" A:%h", value.addr)
+
$format(" D:%h>", value.data));
endfunction
endinstance
instance FShow#(Request);
function Fmt fshow (Request request);
case (request) matches
tagged Descriptor .a:
return fshow(a);
tagged Data .a:
return $format("<DATA %h>", a);
endcase
endfunction
endinstance
(* synthesize *)
module mkFShowExample (Empty);
rule every;
// generate some values
VOB v_of_bools = unpack(truncate(count));
TUP a_tuple = unpack(truncate(count));
Request request = unpack(truncate(value));
// update values
value <= (value << 1) | {0, (value[31] ^ value[21] ^ value[1] ^ value[01])};
count <= count + 1;
if (count == 30) $finish;
endrule
endmodule
C.7.9 Assert
Package
import Assert :: *;
Description
Functions
dynamicAssert Run time assertion. Can be used anywhere an Action is valid, and is
tested whenever it is executed.
import Assert:: *;
module mkAssert_Example ();
// A static assert is checked at compile-time
// This code checks that the indices are within range
for (Integer i=0; i<length(cs); i=i+1)
begin
Integer new_index = (cs[i]).index;
staticAssert(new_index < valueOf(n),
strConcat("Assertion index out of range: ", integerToString(new_index)));
end
C.7.10 Probe
Package
import Probe :: * ;
Description
A Probe is a primitive used to ensure that a signal of interest is not optimized away by the compiler
and that it is given a known name. In terms of BSV syntax, the Probe primitive it used just like
a register except that only a write method exists. Since reads are not possible, the use of a Probe
has no effect on scheduling. In the generated Verilog, the associated signal will be named just like
the port of any Verilog module, in this case <instance_name>$PROBE. No actual Probe instance will
be created however. The only side effects of a BSV Probe instantiation relate to the naming and
retention of the associated signal in the generated Verilog.
Interfaces
interface Probe #(type a_type);
method Action _write(a_type x1);
endinterface: Probe
Modules
The module mkProbe is used to instantiate a Probe.
module mkMesaHwLpm(ILpm);
// Create registers for requestB32 and responseB32
Reg#(LuRequest) requestB32 <- mkRegU();
Reg#(LuResponse) responseB32 <- mkRegU();
endmethod: get
endinterface: response
.....
endmodule
C.7.11 Reserved
Package
import Reserved :: * ;
Description
Reserved defines an abstract data type which only has the purpose of taking up space. It is useful
when defining a struct where you need to enforce a certain layout and want to use the type checker
to enfoce that the value is not accidently used. One can enforce a layout unsafely with Bit#(n),
but Reserved#(n) gives safety. A value of type Reserved#(n) takes up exactly n bits.
typedef · · · abstract · · · Reserved#(type n);
Type classes
• Bits
Converting Reserved to or from bits yields a don’t care (?).
The only purpose is to allow the value to exist in hardware (at port boundaries and in states).
The user should have no reason to use pack/unpack directly.
• Eq and Ord
Any two Reserved values are considered to be equal.
• Bounded
The upper and lower bound return don’t care (?) values.
typedef struct {
Bit#(8) header; // Frame.header
Vector#(2, Bit#(8)) payload; // Frame.payload
Reserved#(8) dummy; // Can’t access 8 bits reserved
Bit#(8) trailer; // Frame.trailer
} Frame;
C.7.12 TriState
Package
import TriState :: * ;
Description
The TriState package implements a tri-state buffer, as shown in Figure 4. Depending on the value
of the output_enable, inout can be an input or an output.
The buffer has two inputs, an input of type value_type and a Boolean output_enable which
determines the direction of inout. If output_enable is True, the signal is coming in from input
and out through inout and output. If output_enable is False, then a value can be driven in from
inout, and the output value will be the value of inout. The behavior is described in the tables
below.
output enable = 0
output = inout
Inputs
input inout output
0 0 0
0 1 1
1 0 0
1 1 1
output enable = 1
output = in
inout = in
Outputs
input inout output
0 0 0
1 1 1
TriState Interface
Name Type Description
inout InOut#(value_type) Inout subinterface providing a value of type
value_type
_read value_type Returns the value of output
(* always_ready, always_enabled *)
interface TriState#(type value_type);
interface Inout#(value_type) inout;
method value_type _read;
endinterface: TriState
Verilog Modules
The TriState module is implemented by the Verilog module TriState.v which can be found in the
Bluespec Verilog library, $BLUESPECDIR/Verilog/.
C.7.13 ZBus
Package
import ZBus :: * ;
Description
BSV provides the ZBus library to allow users to implement and use tri-state buses. Since BSV does
not support high-impedance or undefined values internally, the library encapsulates the tri-state bus
implementation in a module that can only be accessed through predefined interfaces which do not
allow direct access to internal signals (which could potentially have high-impedance or undefined
values).
The Verilog implementation of the tri-state module includes a number of primitive sub-modules
that are implemented using Verilog tri-state wires. The BSV representation of the bus, however,
only models the values of the bus at the associated interfaces and thus the need to represent high-
impedance or undefined values in BSV is avoided.
A ZBus consists of a series of clients hanging off of a bus. The combination of the client and the
bus is provided by the ZBusDualIFC interface which consists of 2 subinterfaces, the client and the
bus. The client subinterface is provided by the ZBusClientIFC interface. The bus subinterface is
provided by the ZBusBusIFC interface. The user never needs to manipulate the bus side, this is all
done internally. The user builds the bus out of ZBusDualIFCs and then drives values onto the bus
and reads values from the bus using the ZBusClientIFC.
Interfaces and Methods
There are three interfaces are defined in this package; ZBusDualIFC, ZBusClientIFC, and ZBusBusIFC.
The ZBusDualIFC interface provides two subinterfaces; a ZBusBusIFC and a ZBusClientIFC. For a
given bus, one ZBusDualIFC interface is associated with each bus client.
ZBusDualIFC
Name Type Description
busIFC ZBusBusIFC#() The subinterface providing the bus side of the
ZBus.
clientIFC ZBusClientIFC#(t) The subinterface providing the client side to the
ZBus.
The ZBusClientIFC allows a BSV module to connect to the tri-state bus. The drive method is
used to drive a value onto the bus. The get() and fromBusValid() methods allow each bus client
to access the current value on the bus. If the bus is in an invalid state (i.e. has a high-impedance
value or an undefined value because it is being driven by more than one client simultaneously), then
the get() method will return 0 and the fromBusValid() method will return False. In all other
cases, the fromBusValid() method will return True and the get() method will return the current
value of the bus.
ZBusClientIFC
Method Argument
Name Type Description Name Description
drive Action Drives a current value on value The value being put on
to the bus the bus, datatype of
value_type.
get value_type Returns the current
value on the bus.
fromBusValid Bool Returns False if the bus
has a high-impedance
value or is undefined.
The ZBusBusIFC interface connects to the bus structure itself using tri-state values. This interface
is never accessed directly by the user.
interface ZBusBusIFC #(type value_type) ;
method Action fromBusSample(ZBit#(value_type) value, Bool isValid);
method ZBit#(t) toBusValue();
method Bool toBusCtl();
endinterface
The mkZBus module constructor function takes a list of ZBusBusIFC interfaces as arguments and
creates a module which ties them all together in a bus.
Examples - ZBus
Creating a tri-state buffer for a 32 bit signal. The interface is named buffer_0.
ZBusDualIFC#(Bit#(32)) buffer_0();
mkZBusBuffer inst_buffer_0(buffer_0);
The following code fragment demonstrates the use of the module mkZBus.
ZBusDualIFC#(Bit#(32)) buffer_0();
mkZBusBuffer inst_buffer_0(buffer_0);
ZBusDualIFC#(Bit#(32)) buffer_1();
mkZBusBuffer inst_buffer_1(buffer_1);
ZBusDualIFC#(Bit#(32)) buffer_2();
mkZBusBuffer inst_buffer_2(buffer_2);
List#(ZBusIFC#(Bit#(32))) ifc_list;
bus_ifc_list = cons(buffer_0.busIFC,
cons(buffer_1.busIFC,
cons(buffer_2.busIFC,
nil)));
Empty bus_ifc();
mkZBus#(bus_ifc_list) inst_bus(bus_ifc);
C.7.14 OVLAssertions
Package
import OVLAssertions :: * ;
Description
The OVLAssertions package provides the BSV interfaces and wrapper modules necessary to al-
low BSV designs to include assertion checkers from the Open Verification Library (OVL). The
OVL includes a set of assertion checkers that verify specific properties of a design. For more
details on the complete OVL, refer to the Accellera Standard OVL Library Reference Manual
(http://www.accellera.org).
Interfaces and Methods
The following interfaces are defined for use with the assertion modules. Each interface has one or
more Action methods. Each method takes a single argument which is either a Bool or polymorphic.
AssertTest IFC Used for assertions that check a test expression on every clock cycle.
AssertTest_IFC
Method Argument
Name Type Name Type Description
test Action test_value a_type Expression to be checked.
AssertSampleTest IFC Used for assertions that check a test expression on every clock cycle only
if the sample, indicated by the boolean value sample_test is asserted.
AssertSampleTest_IFC
Method Argument
Name Type Name Type Description
sample Action sample_test Bool Assertion only checked if sample_test is
asserted.
test Action test_value a_type Expression to be checked.
AssertStartTest IFC Used for assertions that check a test expression only subsequent to a
start event, specified by the Boolean value start_test.
AssertStartTest_IFC
Method Argument
Name Type Name Type Description
start Action start_test Bool Assertion only checked after start is as-
serted.
test Action test_value a_type Expression to be checked.
AssertStartStopTest IFC Used to check a test expression between a start event and a stop event.
AssertStartStopTest_IFC
Method Argument
Name Type Name Type Description
start Action start_test Bool Assertion only checked after start is as-
serted.
stop Action stop_test Bool Assertion only checked until the stop is
asserted.
test Action test_value a_type Expression to be checked.
AssertTransitionTest IFC Used to check a test expression that has a specified start state and
next state, i.e. a transition.
AssertTransitionTest_IFC
Method Argument
Name Type Name Type Description
test Action test_value a_type Expression that should transition to the
next_value.
start Action start_test a_type Expression that indicates the start state
for the assertion check. If the value
of start_test equals the value of
test_value, the check is performed.
next Action next_value a_type Expression that indicates the only valid
next state for the assertion check.
AssertQuiescentTest IFC Used to check that a test expression is equivalent to the specified
expression when the sample state is asserted.
AssertQuiescentTest_IFC
Method Argument
Name Type Name Type Description
sample Action sampe_test Bool Expression which initiates the quiescent
assertion check when it transistions to
true.
state Action state_value a_type Expression that should have the same
value as check_value
check Action check_value a_type Expression state_value is compared to.
AssertFifoTest_IFC
Method Argument
Name Type Name Type Description
push Action push_value a_type Expression which indicates the number of
push operations that will occur during the
current cycle.
pop Action pop_value a_type Expression which indicates the number of
pop operations that will occur during the
current cycle.
Datatypes
The parameters severity_level, property_type, msg, and coverage_level are common to all
assertion checkers.
Each assertion checker may also use some subset of the following parameters.
defaults.min_clks = 2;
defaults.max_clks = 3;
The defaults struct (created by mkOVLDefaults) includes one field for each possible parameter.
Initially each field includes the associated default value. By editing fields of the struct, individual
parameter values can be modified as needed to be non-default values. The modified defaults struct
is then provided as a module argument during instantiation.
Modules
Each module in this package corresponds to an assertion checker from the Open Verification Library
(OVL). The BSV name for each module is the same as the OVL name with bsv_ appended to the
beginning of the name.
Module bsv_assert_always
Description Concurrent assertion that the value of the expression is always True.
Interface Used AssertTest_IFC
Parameters common assertion parameters
Module Declaration
module bsv_assert_always#(OVLDefaults#(Bool) defaults)
(AssertTest_IFC#(Bool));
Module bsv_assert_always_on_edge
Description Checks that the test expression evaluates True whenever the sample
method is asserted.
Interface Used AssertSampleTest_IFC
Parameters common assertion parameters
edge_type (default value = OVL_NOEDGE)
Module Declaration
module bsv_assert_always_on_edge#(OVLDefaults#(Bool)
defaults)(AssertSampleTest_IFC#(Bool));
Module bsv_assert_change
Description Checks that once the start method is asserted, the expression will change
value within num_cks cycles.
Interface Used AssertStartTest_IFC
Parameters common assertion parameters
action_on_new_start (default value = OVL_IGNORE_NEW_START)
num_cks (default value = 1)
Module Declaration
module bsv_assert_change#(OVLDefaults#(a_type) defaults)
(AssertStartTest_IFC#(a_type))
provisos (Bits#(a_type, sizea),
Bounded#(a_type), Eq#(a_type));
Module bsv_assert_cycle_sequence
Description Ensures that if a specified necessary condition occurs,it is followed by a
specified sequence of events.
Interface Used AssertTest_IFC
Parameters common assertion parameters
necessary_condition (default value = OVL_TRIGGER_ON_MOST_PIPE)
Module Declaration
module bsv_assert_cycle_sequence#(OVLDefaults#(a_type)
defaults)(AssertTest_IFC#(a_type))
provisos (Bits#(a_type, sizea),
Bounded#(a_type), Eq#(a_type));
Module bsv_assert_decrement
Description Ensures that the expression decrements only by the value specifiedR.
Interface Used AssertTest_IFC
Parameters common assertion parameters
value (default value = 1)
Module Declaration
module bsv_assert_decrement#(OVLDefaults#(a_type) defaults)
(AssertTest_IFC#(a_type))
provisos (Bits#(a_type, sizea), Literal#(a_type),
Bounded#(a_type), Eq#(a_type));
Module bsv_assert_delta
Description Ensures that the expression always changes by a value within the range
specified by min and max.
Interface Used AssertTest_IFC
Parameters common assertion parameters
min (default value = 1)
max (default value = 1)
Module Declaration
module bsv_assert_delta#(OVLDefaults#(a_type) defaults)
(AssertTest_IFC#(a_type))
provisos (Bits#(a_type, sizea), Literal#(a_type),
Bounded#(a_type), Eq#(a_type));
Module bsv_assert_even_parity
Description Ensures that value of a specified expression has even parity, that is an
even number of bits in the expression are active high.
Interface Used AssertTest_IFC
Parameters common assertion parameters
Module Declaration
module bsv_assert_even_parity#(OVLDefaults#(a_type)
defaults) (AssertTest_IFC#(a_type))
provisos (Bits#(a_type, sizea),
Bounded#(a_type), Eq#(a_type));
Module bsv_assert_fifo_index
Description Ensures that a FIFO-type structure never overflows or underflows. This
checker can be configured to support multiple pushes (FIFO writes) and
pops (FIFO reads) during the same clock cycle.
Interface Used AssertFifoTest_IFC
Parameters common assertion parameters
depth (default value = 1)
simultaneous_push_pop (default value = True)
Module Declaration
module bsv_assert_fifo_index#(OVLDefaults#(Bit#(0))
defaults)(AssertFifoTest_IFC#(a_type, b_type))
provisos (Bits#(a_type, sizea), Bits#(b_type, sizeb));
Module bsv_assert_frame
Description Checks that once the start method is asserted, the test expression eval-
uates true not before min_cks clock cycles and not after max_cks clock
cycles.
Interface Used AssertStartTest_IFC
Parameters common assertion parameters
action_on_new_start (default value = OVL_IGNORE_NEW_START)
min_cks (default value = 1)
max_cks (default value = 1)
Module Declaration
module bsv_assert_frame#(OVLDefaults#(Bool) defaults)
(AssertStartTest_IFC#(Bool));
Module bsv_assert_handshake
Description Ensures that the specified request and acknowledge signals follow a spec-
ified handshake protocol.
Interface Used AssertStartTest_IFC
Parameters common assertion parameters
action_on_new_start (default value = OVL_IGNORE_NEW_START)
min_ack_cycle (default value = 1)
max_ack_cycle (default value = 1)
Module Declaration
module bsv_assert_handshake#(OVLDefaults#(Bool) defaults)
(AssertStartTest_IFC#(Bool));
Module bsv_assert_implication
Description Ensures that a specified consequent expression is True if the specified
antecedent expression is True.
Interface Used AssertStartTest_IFC
Parameters common assertion parameters
Module Declaration
module bsv_assert_implication#(OVLDefaults#(Bool) defaults)
(AssertStartTest_IFC#(Bool));
Module bsv_assert_increment
Description ensure that the test expression always increases by the value of specified
by value.
Interface Used AssertTest_IFC
Parameters common assertion parameters
value (default value = 1)
Module Declaration
module bsv_assert_increment#(OVLDefaults#(a_type) defaults)
(AssertTest_IFC#(a_type))
provisos (Bits#(a_type, sizea), Literal#(a_type),
Bounded#(a_type), Eq#(a_type));
Module bsv_assert_never
Description Ensures that the value of a specified expression is never True.
Interface Used AssertTest_IFC
Parameters common assertion parameters
Module Declaration
module bsv_assert_never#(OVLDefaults#(Bool) defaults)
(AssertTest_IFC#(Bool));
Module bsv_assert_never_unknown
Description Ensures that the value of a specified expression contains only 0 and 1
bits when a qualifying expression is True.
Interface Used AssertStartTest_IFC
Parameters common assertion parameters
Module Declaration
module bsv_assert_never_unknown#(OVLDefaults#(a_type)
defaults)(AssertStartTest_IFC#(a_type))
provisos (Bits#(a_type, sizea),
Bounded#(a_type), Eq#(a_type));
Module bsv_assert_never_unknown_async
Description Ensures that the value of a specified expression always contains only 0
and 1 bits
Interface Used AssertTest_IFC
Parameters common assertion parameters
Module Declaration
module bsv_assert_never_unknown_async#(OVLDefaults#(a_type)
defaults)(AssertTest_IFC#(a_type))
provisos (Bits#(a_type, sizea), Literal#(a_type),
Bounded#(a_type), Eq#(a_type));
Module bsv_assert_next
Description Ensures that the value of the specified expression is true a specified
number of cycles after a start event.
Interface Used AssertStartTest_IFC
Parameters common assertion parameters
num_cks (default value = 1)
check_overlapping (default value = True)
check_missing_start (default value = False)
Module Declaration
module bsv_assert_next#(OVLDefaults#(Bool) defaults)
(AssertStartTest_IFC#(Bool));
Module bsv_assert_no_overflow
Description Ensures that the value of the specified expression does not overflow.
Interface Used AssertTest_IFC
Parameters common assertion parameters
min (default value = minBound)
max (default value = maxBound)
Module Declaration
module bsv_assert_no_overflow#(OVLDefaults#(a_type)
defaults) (AssertTest_IFC#(a_type))
provisos (Bits#(a_type, sizea),
Bounded#(a_type), Eq#(a_type));
Module bsv_assert_no_transition
Description Ensures that the value of a specified expression does not transition from
a start state to the specified next state.
Interface Used AssertTransitionTest_IFC
Parameters common assertion parameters
Module Declaration
module bsv_assert_no_transition#(OVLDefaults#(a_type)
defaults) (AssertTransitionTest_IFC#(a_type))
provisos (Bits#(a_type, sizea),
Bounded#(a_type), Eq#(a_type));
Module bsv_assert_no_underflow
Description Ensures that the value of the specified expression does not underflow.
Interface Used AssertTest_IFC
Parameters common assertion parameters
min (default value = minBound)
max (default value = maxBound)
Module Declaration
module bsv_assert_no_underflow#(OVLDefaults#(a_type)
defaults)(AssertTest_IFC#(a_type))
provisos (Bits#(a_type, sizea),
Bounded#(a_type), Eq#(a_type));
Module bsv_assert_odd_parity
Description Ensures that the specified expression had odd parity; that an odd num-
ber of bits in the expression are active high.
Interface Used AssertTest_IFC
Parameters common assertion parameters
Module Declaration
module bsv_assert_odd_parity#(OVLDefaults#(a_type)
defaults)(AssertTest_IFC#(a_type))
provisos (Bits#(a_type, sizea),
Bounded#(a_type), Eq#(a_type));
Module bsv_assert_one_cold
Description Ensures that exactly one bit of a variable is active low.
Interface Used AssertTest_IFC
Parameters common assertion parameters
inactive (default value = OLV_ONE_COLD)
Module Declaration
module bsv_assert_one_cold#(OVLDefaults#(a_type) defaults)
(AssertTest_IFC#(a_type))
provisos (Bits#(a_type, sizea),
Bounded#(a_type), Eq#(a_type))
Module bsv_assert_one_hot
Description Ensures that exactly one bit of a variable is active high.
Interface Used AssertTest_IFC
Parameters common assertion parameters
Module Declaration
module bsv_assert_one_hot#(OVLDefaults#(a_type) defaults)
(AssertTest_IFC#(a_type))
provisos (Bits#(a_type, sizea),
Bounded#(a_type), Eq#(a_type));
Module bsv_assert_proposition
Description Ensures that the test expression is always combinationally True. Like
assert_always except that the test expression is not sampled by the
clock.
Interface Used AssertTest_IFC
Parameters common assertion parameters
Module Declaration
module bsv_assert_proposition#(OVLDefaults#(Bool) defaults)
(AssertTest_IFC#(Bool));
Module bsv_assert_quiescent_state
Description Ensures that the value of a specified state expression equals a corre-
sponding check value if a specified sample event has transitioned to
TRUE.
Interface Used AssertQuiescentTest_IFC
Parameters common assertion parameters
Module Declaration
module bsv_assert_quiescent_state#(OVLDefaults#(a_type)
defaults)(AssertQuiescentTest_IFC#(a_type))
provisos (Bits#(a_type, sizea),
Bounded#(a_type), Eq#(a_type));
Module bsv_assert_range
Description Ensure that an expression is always within a specified range.
Interface Used AssertTest_IFC
Parameters common assertion parameters
min (default value = minBound)
max (default value = maxBound)
Module Declaration
module bsv_assert_range#(OVLDefaults#(a_type) defaults)
(AssertTest_IFC#(a_type))
provisos (Bits#(a_type, sizea),
Bounded#(a_type), Eq#(a_type));
Module bsv_assert_time
Description Ensures that the expression remains True for a specified number of clock
cycles after a start event.
Interface Used AssertStartTest_IFC
Parameters common assertion parameters
action_on_new_start (default value = OVL_IGNORE_NEW_START)
num_cks (default value = 1)
Module Declaration
module bsv_assert_time#(OVLDefaults#(Bool) defaults)
(AssertStartTest_IFC#(Bool));
Module bsv_assert_transition
Description Ensures that the value of a specified expression transitions properly
froma start state to the specified next state.
Interface Used AssertTransitionTest_IFC
Parameters common assertion parameters
Module Declaration
module bsv_assert_transition#(OVLDefaults#(a_type)
defaults)(AssertTransitionTest_IFC#(a_type))
provisos (Bits#(a_type, sizea),
Bounded#(a_type), Eq#(a_type));
Module bsv_assert_unchange
Description Ensures that the value of the specified expression does not change during
a specified number of clock cycles after a start event initiates checking.
Interface Used AssertStartTest_IFC
Parameters common assertion parameters
action_on_new_start (default value = OVL_IGNORE_NEW_START)
num_cks (default value = 1)
Module Declaration
module bsv_assert_unchange#(OVLDefaults#(a_type) defaults)
(AssertStartTest_IFC#(a_type))
provisos (Bits#(a_type, sizea),
Bounded#(a_type), Eq#(a_type));
Module bsv_assert_width
Description Ensures that when the test expression goes high it stays high for at least
min and at most max clock cycles.
Interface Used AssertTest_IFC
Parameters common assertion parameters
min_cks (default value = 1)
max_cks (default value = 1)
Module Declaration
module bsv_assert_width#(OVLDefaults#(Bool) defaults)
(AssertTest_IFC#(Bool));
Module bsv_assert_win_change
Description Ensures that the value of a specified expression changes in a specified
window between a start event and a stop event.
Interface Used AssertStartStopTest_IFC
Parameters common assertion parameters
Module Declaration
module bsv_assert_win_change#(OVLDefaults#(a_type)
defaults)(AssertStartStopTest_IFC#(a_type))
provisos (Bits#(a_type, sizea),
Bounded#(a_type), Eq#(a_type));
Module bsv_assert_win_unchange
Description Ensures that the value of a specified expression does not change in a
specified window between a start event and a stop event.
Interface Used AssertStartStopTest_IFC
Parameters common assertion parameters
Module Declaration
module bsv_assert_win_unchange#(OVLDefaults#(a_type)
defaults)(AssertStartStopTest_IFC#(a_type))
provisos (Bits#(a_type, sizea),
Bounded#(a_type), Eq#(a_type));
Module bsv_assert_window
Description Ensures that the value of a specified event is True between a specified
window between a start event and a stop event.
Interface Used AssertStartStopTest_IFC
Parameters common assertion parameters
Module Declaration
module bsv_assert_window#(OVLDefaults#(Bool) defaults)
(AssertStartStopTest_IFC#(Bool));
Module bsv_assert_zero_one_hot
Description ensure that exactly one bit of a variable is active high or zero.
Interface Used AssertTest_IFC
Parameters common assertion parameters
Module Declaration
module bsv_assert_zero_one_hot#(OVLDefaults#(a_type)
defaults)(AssertTest_IFC#(a_type))
provisos (Bits#(a_type, sizea),
Bounded#(a_type), Eq#(a_type));
If the bsc compiler is being used to generate the Verilog simulation executable, the BSC_VSIM_FLAGS
environment variable can be used to set the required simulator flags that enable use of the OVL
library.
For instance, if the iverilog simulator is being used and the OVL library is located in the directory
shared/std_ovl, the BSC_VSIM_FLAGS environment variable can be set to -̈I shared/std_ovl -Y
.vlib -y shared/std_ovl -DOVL_VERILOG=1 -DOVL_ASSERT_ON=1¨. These flags:
The exact flags to be used will differ based on what OVL behavior is desired and which Verilog
simulator is being used.
Package Name
Import Clocks :: * ;
Description
The BSV Clocks library provide features to access and change the default clock. Moreover, there
are hardware primitives to generate clocks of various shapes, plus several primitives which allow the
safe crossing of signals and data from one clock domain to another.
The Clocks package uses the data types Clock and Reset as well as clock functions which are
described below but defined in the Prelude package.
Each section describes a related group of modules, followed by a table indicating the Verilog modules
used to implement the BSV modules.
Types and typeclasses
The Clocks package uses the abstract data types Clock and Reset, which are defined in the Prelude
package. These are first class objects. Both Clock and Reset are in the Eq type class, meaning two
values can be compared for equality.
Clock is an abstract type of two components: a single Bit oscillator and a Bool gate.
typedef ... Clock ;
Functions
The following functions are defined in the Prelude package but are used with multiple clock domains.
Clock Functions
exposeCurrentClock This function returns a value of type Clock, which is the current clock
of the module.
module exposeCurrentClock ( Clock c );
exposeCurrentReset This function returns a value of type Reset, which is the current reset
of the module.
module exposeCurrentReset ( Reset r );
Both exposeCurrentClock and exposeCurrentReset use the module instantiation syntax (<-) to
return the value. Hence these can only be used from within a module.
Example: setting a reset to the current reset
Reset reset_value <- exposeCurrentReset;
sameFamily A Boolean function which returns True if the clocks are in the same
family, False if the clocks are not in the same family. Clocks in the
same family have the same oscillator but may have different gate con-
ditions.
function Bool sameFamily ( Clock clka, Clock clkb ) ;
noClock Specifies a null clock, a clock where the oscillator never rises.
function Clock noClock() ;
Description
This section provides modules to generate new clocks and to modify the existing clock.
The modules mkAbsoluteClock, mkAbsoluteClockFull, mkClock, and mkUngatedClock all define a
new clock, one not based on the current clock. Both mkAbsoluteClock and mkAbsoluteClockFull
define new oscillators and are not synthesizable. mkClock and mkUngatedClock use an existing oscil-
lator to create a clock, and is synthesizable. The modules, mkGatedClock and mkGatedClockFromCC
use existing clocks to generate another clock in the same family.
Interfaces and Methods
The MakeClockIfc supports user-defined clocks with irregular waveforms created with mkClock
and mkUngatedClock, as opposed to the fixed-period waveforms created with the mkAbsoluteClock
family.
MakeClockIfc Interface
Method and subinterfaces Arguments
Name Type Description Name Description
setClockValue Action Changes the value of the value Value the clock will
clock at the next edge of be set to, must be a
the clock one bit type
getClockValue one_bit_type Retrieves the last value of
the clock
setGateCond Action Changes the gating condi- gate Must be of the type
tion Bool
getGateCond Bool Retrieves the last gating
condition set
new_clk Interface Clock interface provided
by the module
GatedClockIfc Interface
Method and subinterfaces Arguments
Name Type Description Name Description
setGateCond Action Changes the gating condi- gate Must be of the type
tion Bool
getGateCond Bool Retrieves the last gating
condition set
new_clk Interface Clock interface provided
by the module
interface GatedClockIfc ;
method Action setGateCond(Bool gate) ;
method Bool getGateCond() ;
interface Clock new_clk ;
endinterface
Modules
The mkClock module creates a Clock type from a one-bit oscillator and a Boolean gate condition.
There is no family relationship between the current clock and the clock generated by this module.
The initial values of the oscillator and gate are passed as parameters to the module. When the
module is out of reset, the oscillator value can be changed using the setClockValue method and the
gate condition can be changed by calling the setGateCond method. The oscillator value and gate
condition can be queried with the getClockValue and getGateCond methods, respectively. The
clock created by mkClock is available as the new_clk subinterface. When setting the gate condition,
the change does not affect the generated clock until it is low, to prevent glitches.
The mkUngatedClock module is an ungated version of the mkClock module. It takes only an oscillator
argument (no gate argument) and returns the same new_clock interface. Since there is no gate,
an error is returned if the design calls the setGetCond method. The getGateCond method always
returns True.
mkClock Creates a Clock type from a one-bit oscillator input, and a Boolean gate
condition. There is no family relationship between the current clock and the
clock generated by this module.
module mkClock #( one_bit_type initVal, Bool initGate)
( MakeClockIfc#(one_bit_type) ifc )
provisos( Bits#(one_bit_type, 1) ) ;
mkUngatedClock Creates an ungated Clock type from a one-bit oscillator input. There is no
family relationship between the current clock and the clock generated by this
module.
module mkUngatedClock #( one_bit_type initVal)
( MakeClockIfc#(one_bit_type) ifc )
provisos( Bits#(one_bit_type, 1) ) ;
The mkGatedClock module adds (logic and) a Boolean gate condition to an existing clock, thus
creating another clock in the same family. The source clock is provided as the argument clk_in.
The gate condition is controlled by an asynchronously-reset register inside the module. The register
is set with the setGateCond Action method of the interface and can be read with getGateCond
method. The reset value of the gate condition register is provided as an instantiation parameter.
The clock for the register (and thus these set and get methods) is the default clock of the module;
to specify a clock other than the default clock, use the clocked_by directive.
mkGatedClock Creates another clock in the same family by adding logic and a Boolean gate
condition to the current clock.
module mkGatedClock#(Bool v) ( Clock clk_in, GatedClockIfc ifc );
For convenience, we provide an alternate version in which the source clock is the default clock of the
module
mkGatedClockFromCC An alternate interface for the module mkGatedClock in which the source
clock is the default clock of the module.
module mkGatedClockFromCC#(Bool v) ( GatedClockIfc ifc );
mkAbsoluteClock The first rising edge (start) and period are defined by parameters.
This module is not synthesizable.
module mkAbsoluteClock #( Integer start,
Integer period )
( Clock );
mkAbsoluteClockFull The value initValue is held until time start, and then the clock
oscillates. The value not(initValue) is held for time compValTime,
followed by initValue held for time initValTime. Hence the clock
period after startup is compValTime + initValTime. This module is
not synthesizable.
module mkAbsoluteClockFull #( Integer start,
Bit#(1) initValue,
Integer compValTime,
Integer initValTime )
( Clock );
Verilog Modules
The BSV modules correspond to the following Verilog modules, which are found in the Bluespec
Verilog library, $BLUESPECDIR/Verilog/.
mkAbsoluteClock ClockGen.v
mkAbsoluteClockFull
mkClock MakeClock.v
mkUngatedClock
mkGatedClock GatedClock.v
mkGatedClockFromCC
Description
Bluespec provides two gated clock multiplexing primitives: a simple combinational multiplexor and
a stateful module which generates an appropriate reset signal when the clock changes. The first
multiplexor uses the interface MuxClockIfc, which includes an Action method to select the clock
along with a Clock subinterface. The second multiplexor uses the interface SelectClockIfc which
also has a Reset subinterface.
Ungated versions of these modules are also provided. The ungated versions are identical to the gated
versions, except that the input and output clocks are ungated.
Interfaces and Methods
MuxClockIfc Interface
Method and subinterfaces Arguments
Name Type Description Name Description
select Action Method used to select the ab if True, clock_out is
clock based on the Boolean taken from aclk
value ab
clock_out Interface Clock interface
interface MuxClkIfc ;
method Action select ( Bool ab ) ;
interface Clock clock_out ;
endinterface
SelectClockIfc Interface
Method and subinterfaces Arguments
Name Type Description Name Description
select Action Method used to select the ab if True, clock out is
clock based on the Boolean taken from aclk
value ab
clock_out Interface Clock interface
reset_out Interface Reset interface
interface SelectClkIfc ;
method Action select ( Bool ab ) ;
interface Clock clock_out ;
interface Reset reset_out ;
endinterface
Modules
The mkClockMux module is a simple combinational multiplexor with a registered clock selection
signal, which selects between clock inputs aClk and bClk. The provided Verilog module does not
provide any glitch detection or removal logic; it is the responsibility of the user to provide additional
logic to provide glitch-free behavior. The mkClockMux module uses two arguments and provides a
Clock interface. The aClk is selected if ab is True, while bClk is selected otherwise.
The mkUngatedClockMux module is identical to the mkClockMux module except that the input and
output clocks are ungated. The signals aClkgate, bClkgate, and outClkgate in figure 7 don’t exist.
The mkClockSelect module is a clock multiplexor containing additional logic which generates a
reset whenever a new clock is selected. As such, the interface for the module includes an Action
method to select the clock (if ab is True clock out is taken from aClk), provides a Clock interface,
and also a Reset interface.
The constructor for the module uses two clock arguments, and provides the MuxClockIfc interface.
The underlying Verilog module is ClockSelect.v; it is expected that users can substitute their own
modules to meet any additional requirements they may have. The parameter stages is the number
of clock cycles in which the reset is asserted after the clock selection changes.
The mkUngatedClockSelect module is identical to the mkClockSelect module except that the input
and output clocks are ungated. The signals aClkgate, bClkgate, and outClk_gate in figure 8 don’t
exist.
Verilog Modules
The BSV modules correspond to the following Verilog modules, which are found in the Bluespec
Verilog library, $BLUESPECDIR/Verilog/.
mkClockMux ClockMux.v
mkClockSelect ClockSelect.v
mkUngatedClockMux UngatedClockMux.v
mkUngatedClockSelect UngatedClockSelect.v
Description
A clock divider provides a derived clock and also a ClkNextRdy signal, which indicates that the
divided clock will rise in the next cycle. This signal is associated with the input clock, and can only
be used within that clock domain.
See mkSyncRegToSlow, mkSyncRegToFast, mkSyncFIFOToSlow, and mkSyncFIFOToFast in Section
C.8.10 for some specialized synchronizers which can be used with divided clocks, and other systems
when the clock edges are known to be aligned.
Data Types
The ClkNextRdy is a Boolean signal which indicates that the slow clock will rise in the next cycle.
ClockDividerIfc Interface
Name Type Description
fastClock Interface The original clock
slowClock Interface The derived clock
clockReady Bool Boolean value which indicates that the slow clock will rise
in the next cycle. The method is in the clock domain of the
fast clock.
interface ClockDividerIfc ;
interface Clock fastClock ;
interface Clock slowClock ;
method ClkNextRdy clockReady() ;
endinterface
Modules
The divider parameter may be any integer greater than 1. For even dividers the generated clock’s
duty cycle is 50%, while for odd dividers, the duty cycle is (divider/2)/divider. The current clock
(or the clocked_by argument) is used as the source clock.
The mkClockDividerOffset module provides a clock divider where the rising edge can be defined
relative to other clock dividers which have the same divisor. An offset of value 2 will produce a rising
edge one fast clock after a divider with offset 1. mkClockDivider is just mkClockDividerOffset
with an offset of value 0.
mkClockDividerOffset Provides a clock divider, where the rising edge can be defined rel-
ative to other clock dividers which have the same divisor.
module mkClockDividerOffset #( Integer divisor,
Integer offset )
( ClockDividerIfc ) ;
The mkClockInverter and mkGatedClockInverter modules generate an inverted clock having the
same period but opposite phase as the current clock. The mkGatedClockInverter is a gated version
of mkClockInverter. The output clock includes a gate signal derived from the gate of the input
clock.
mkClockInverter Generates an inverted clock having the same period but opposite
phase as the current clock.
module mkClockInverter ( ClockDividerIfc ) ;
Verilog Modules
The BSV modules correspond to the following Verilog modules, which are found in the Bluespec
Verilog library, $BLUESPECDIR/Verilog/.
mkClockDivider ClockDiv.v
mkClockDividerOffset
mkGatedClockDivider GatedClockDiv.v
mkClockInverter ClockInverter.v
mkGatedClockInverter GatedClockInverter.v
Description
Bit synchronizers are used to safely transfer one bit of data from one clock domain to another. More
complicated synchronizers are provided in later sections.
Interfaces and Methods
The SyncBitIfc interface provides a send method which transmits one bit of information from one
clock domain to the read method in a second domain.
SyncBitIfc Interface
Methods Arguments
Name Type Description Name Description
send Action Transmits information from bitData One bit of information
one clock domain to the sec- transmitted
ond domain
read one_bit_type Reads one bit of data sent
from a different clock domain
Modules
The mkSyncBit, mkSyncBitFromCC and mkSyncBitToCC modules provide a SyncBitIfc across clock
domains. The send method is in one clock domain, and the read method is in a second clock
domain, as shown in Figure 10. The FromCC and ToCC versions differ in that the FromCC module
moves data from the current clock (module’s clock), while the ToCC module moves data to the current
clock domain. The hardware implementation is a two register synchronizer, which can be found in
SyncBit.v in the Bluespec Verilog library directory.
mkSyncBit Moves data across clock domains. The in and out clocks, along with
the input reset, are explicitly provided. The default clock and reset
are ignored.
module mkSyncBit #( Clock sClkIn, Reset sRst,
Clock dClkIn )
( SyncBitIfc #(one_bit_type) )
provisos( Bits#(one_bit_type, 1)) ;
mkSyncBitFromCC Moves data from the current clock (the module’s clock) to a different
clock domain. The input clock and reset are the current clock and
reset.
module mkSyncBitFromCC #( Clock dClkIn )
( SyncBitIfc #(one_bit_type) )
provisos( Bits#(one_bit_type, 1)) ;
mkSyncBitToCC Moves data into the current clock domain. The output clock is the
current clock. The current reset is ignored.
module mkSyncBitToCC #( Clock sClkIn, Reset sRstIn )
( SyncBitIfc #(one_bit_type) )
provisos( Bits#(one_bit_type, 1)) ;
The mkSyncBit15 module (one and a half) and its variants provide the same interface as the
mkSyncBit modules, but the underlying hardware is slightly modified, as shown in Figure 11. For
these synchronizers, the first register clocked by the destination clock triggers on the falling edge of
the clock.
Figure 11: Bit Synchronizer 1.5 - first register in destination domain triggers on falling edge
mkSyncBit15 Similar to mkSyncBit except it triggers on the falling edge of the clock.
The in and out clocks, along with the input reset, are explicitly pro-
vided. The default clock and reset are ignored.
module mkSyncBit15 #( Clock sClkIn, Reset sRst,
Clock dClkIn )
( SyncBitIfc #(one_bit_type) )
provisos( Bits#(one_bit_type, 1)) ;
mkSyncBit15FromCC Moves data from the current clock and is triggered on the falling edge
of the clock. The input clock and reset are the current clock and reset.
module mkSyncBit15FromCC #(Clock dClkIn)
(SyncBitIfc #(one_bit_type))
provisos( Bits#(one_bit_type, 1)) ;
mkSyncBit15ToCC Moves data into the current clock domain and is triggered on the falling
edge of the clock. The output clock is the current clock. The current
reset is ignored.
module mkSyncBit15ToCC #( Clock sClkIn, Reset sRstIn )
( SyncBitIfc #(one_bit_type) )
provisos( Bits#(one_bit_type, 1)) ;
The mkSyncBit1 module, shown in Figure 12, also provides the same interface but only uses one
register in the destination domain. Synchronizers like this, which use only one register, are not
generally used since meta-stable output is more probable. However, one can use this synchronizer
provided special meta-stable resistant flops are selected during physical synthesis or (for example) if
the output is immediately registered.
mkSyncBit1 Moves data from one clock domain to another clock domain, with only
one register in the destination domain. The in and out clocks, along
with the input reset, are explicitly provided. The default clock and
reset are ignored.
module mkSyncBit1 #( Clock sClkIn, Reset sRst,
Clock dClkIn )
( SyncBitIfc #(one_bit_type) )
provisos( Bits#(one_bit_type, 1)) ;
mkSyncBit1FromCC Moves data from the current clock domain, with only one register in
the destination domain. The input clock and reset are the current
clock and reset.
module mkSyncBit1FromCC #( Clock dClkIn )
( SyncBitIfc #(one_bit_type) )
provisos( Bits #(one_bit_type, 1)) ;
mkSyncBit1ToCC Moves data into the current clock domain, with only one register in
the destination domain. The output clock is the current clock. The
current reset is ignored.
module mkSyncBit1ToCC #( Clock sClkIn, Reset sRstIn )
( SyncBitIfc #(one_bit_type) )
provisos( Bits#(one_bit_type, 1)) ;
The mkSyncBit05 module is similar to mkSyncBit1, but the destination register triggers on the
falling edge of the clock, as shown in Figure 13.
Figure 13: Bit Synchronizer .5 - first register in destination domain triggers on falling edge
mkSyncBit05 Moves data from one clock domain to another clock domain, with
only one register in the destination domain. The destination register
triggers on the falling edge of the clock. The in and out clocks, along
with the input reset, are explicitly provided. The default clock and
reset are ignored.
module mkSyncBit05 #( Clock sClkIn, Reset sRst,
Clock dClkIn )
( SyncBitIfc #(one_bit_type) )
provisos( Bits#(one_bit_type, 1)) ;
mkSyncBit05FromCC Moves data from the current clock domain, with only one register in
the destination domain, the destination register triggers on the falling
edge of the clock. The input clock and reset are the current clock and
reset.
module mkSyncBit05FromCC #( Clock dClkIn )
(SyncBitIfc #(one_bit_type) )
provisos( Bits#(one_bit_type, 1)) ;
mkSyncBit05ToCC Moves data into the current clock domain, with only one register in
the destination domain, the destination register triggers on the falling
edge of the clock. The output clock is the current clock. The current
reset is ignored.
module mkSyncBit05ToCC #( Clock sClkIn, Reset sRstIn )
( SyncBitIfc #(one_bit_type) )
provisos( Bits#(one_bit_type, 1)) ;
Verilog Modules
The BSV modules correspond to the following Verilog modules, which are found in the Bluespec
Verilog library, $BLUESPECDIR/Verilog/.
mkSyncBit SyncBit.v
mkSyncBitFromCC
mkSyncBitToCC
mkSyncBit15 SyncBit15.v
mkSyncBit15FromCC
mkSyncBit15ToCC
mkSyncBit1 SyncBit1.v
mkSyncBit1FromCC
mkSyncBit1ToCC
mkSyncBit05 SyncBit05.v
mkSyncBit05FromCC
mkSyncBit05ToCC
Description
Pulse synchronizers are used to transfer a pulse from one clock domain to another.
Interfaces and Methods
The SyncPulseIfc interface provides an Action method, send, which when invoked generates a True
value on the pulse method in a second clock domain.
SyncPulseIfc Interface
Methods
Name Type Description
send Action Starts transmittling a pulse from one clock domain to the
second clock domain.
pulse Bool Where the pulse is received in the second domain. pulse is
True if a pulse is recieved in this cycle.
interface SyncPulseIfc ;
method Action send () ;
method Bool pulse () ;
endinterface
Modules
The mkSyncPulse, mkSyncPulseFromCC and mkSyncPulseToCC modules provide clock domain cross-
ing modules for pulses. When the send method is called from the one clock domain, a pulse will be
seen on the read method in the second. Note that there is no handshaking between the domains,
so when sending data from a fast clock domain to a slower one, not all pulses sent may be seen in
the slower receiving clock domain. The pulse delay is two destination clocks cycles.
mkSyncPulse Sends a pulse from one clock domain to another. The in and out
clocks, along with the input reset, are explicitly provided. The default
clock and reset are ignored.
module mkSyncPulse #( Clock sClkIn, Reset sRstIn,
Clock dClkIn )
( SyncPulseIfc ) ;
mkSyncPulseFromCC Sends a pulse from the current clock domain to the other clock domain.
The input clock and reset are the current clock and reset.
module mkSyncPulseFromCC #( Clock dClkIn )
( SyncPulseIfc ) ;
mkSyncPulseToCC Sends a pulse from the other clock domain to the current clock domain.
The output clock is the current clock. The current reset is ignored.
module mkSyncPulseToCC #( Clock sClkIn, Reset sRstIn )
( SyncPulseIfc ) ;
mkSyncHandshake Sends a pulse from one clock domain to another clock domain with
handshaking. The in and out clocks, along with the input reset, are
explicitly provided. The default clock and reset are ignored.
module mkSyncHandshake #( Clock sClkIn, Reset sRstIn,
Clock dClkIn )
( SyncPulseIfc ) ;
mkSyncHandShakeFromCC Sends a pulse with a handshake from the current clock domain.
The input clock and reset are the current clock and reset.
module mkSyncHandshakeFromCC #( Clock dClkIn )
( SyncPulseIfc ) ;
mkSyncHandshakeToCC Sends a pulse with a handshake to the current clock domain. The
output clock is the current clock. The current reset is ignored.
module mkSyncHandshakeToCC #( Clock sClkIn,
Reset sRstIn )
( SyncPulseIfc ) ;
Verilog Modules
The BSV modules correspond to the following Verilog modules, which are found in the Bluespec
Verilog library, $BLUESPECDIR/Verilog/.
mkSyncPulse SyncPulse.v
mkSyncPulseFromCC
mkSyncPulseToCC
mkSyncHandshake SyncHandshake.v
mkSyncHandshakeFromCC
mkSyncHandshakeToCC
Description
Word synchronizers are used to provide word synchronization across clock domains. The crossings
are handshaked, such that a second write cannot occur until the first is acknowledged (that the data
has been received, but the value may not have been read) by the destination side. The destination
read is registered.
Interfaces and Methods
Word synchronizers use the common Reg interface (redescribed below), but there are a few subtle
differences which the designer should be aware. First, the _read and _write methods are in different
clock domains and, second, the _write method has an implicit “ready” condition which means that
some synchronization modules cannot be written every clock cycle. Both of these conditions are
handled automatically by the Bluespec compiler relieving the designer of these tedious checks.
Reg Interface
Method Arguments
Name Type Description Name Description
_write Action Writes a value x1 x1 Data to be written
_read a_type Returns the value of the reg-
ister
Modules
The mkSyncReg, mkSyncRegToCC and mkSyncRegFromCC modules provide word synchronization across
clock domains.
Figure 16: Register Synchronization Module (see Figure 15 for the pulse synchronizer with hand-
shake)
mkSyncReg Provides word synchronization across clock domains. The in and out
clocks, along with the input reset, are explicitly provided. The default
clock and reset are ignored.
module mkSyncReg #( a_type initValue,
Clock sClkIn, Reset sRstIn,
Clock dClkIn )
( Reg #(a_type) )
provisos (Bits#(a_type, sa) ) ;
mkSyncRegFromCC Provides word synchronization from the current clock domain. The
input clock and reset are the current clock and reset.
module mkSyncRegFromCC #( a_type initValue,
Clock dClkIn )
( Reg #(a_type) )
provisos (Bits#(a_type, sa)) ;
mkSyncRegToCC Provides word synchronization to the current clock domain. The out-
put clock is the current clock. The current reset is ignored.
module mkSyncRegToCC #( a_type initValue,
Clock sClkIn, Reset sRstIn )
( Reg #(a_type) )
provisos (Bits#(a_type, sa)) ;
Verilog Modules
The BSV modules correspond to the following Verilog modules, which are found in the Bluespec
Verilog library, $BLUESPECDIR/Verilog/.
mkSyncReg SyncRegister.v
mkSyncRegFromCC
mkSyncRegToCC
Description
The FIFO synchronizers use FIFOs to synchronize data being sent across clock domains. Additional
FIFO synchronizers, SyncFIFOLevel and SyncFIFOCount can be found in the FIFOLevel package
(Section C.1.4).
The sync FIFO interface defines an interface similar to the FIFOF interface, except it does not have
a clear method.
SyncFIFOIfc Interface
Method Arguments
Name Type Description Name Description
enq Action Adds an entry to the FIFO sendData Data to be added
deq Action Removes the first entry from
the FIFO
first a_type Returns the first entry
notFull Bool Returns True if there is space
and you can enq into the
FIFO
notEmpty Bool Returns True if there are el-
ements in the FIFO and you
can deq from the FIFO
Modules
The mkSyncFIFO, mkSyncFIFOFromCC and mkSyncFIFOToCC modules provide FIFOs for sending data
across clock domains. Data items enqueued on the source side will arrive at the destination side and
remain there until they are dequeued. The depth of the FIFO is specified by the depth parameter.
mkSyncFIFO Provides a FIFO for sending data across clock domains. The enq
method is in the source (sClkIn) domain, while the deq and first
methods are in the destination (dClkIn) domain. The in and out
clocks, along with the input reset, are explicitly provided. The default
clock and reset are ignored.
module mkSyncFIFO #( Integer depth,
Clock sClkIn, Reset sRstIn,
Clock dClkIn )
( SyncFIFOIfc #(a_type) )
provisos (Bits#(a_type, sa));
mkSyncFIFOFromCC Provides a FIFO to send data from the current clock domain into a
second clock domain. The input clock and reset are the current clock
and reset.
module mkSyncFIFOFromCC #( Integer depth,
Clock dClkIn )
( SyncFIFOIfc #(a_type) )
provisos (Bits#(a_type, sa));
mkSyncFIFOToCC Provides a FIFO to send data from a second clock domain into the
current clock domain. The output clock is the current clock. The
current reset is ignored.
module mkSyncFIFOToCC #( Integer depth,
Clock sClkIn, Reset sRstIn )
( SyncFIFOIfc #(a_type) )
provisos (Bits#(a_type, sa));
The sync FIFOFull modules are a variation of the Sync FIFO which allow the empty and full signals
to be registered. Registering the signals can give better synthesis results, since a comparator is
removed from the empty or full path. However, there is an additional cycle of latency before the
empty or full signal is visible.
mkSyncFIFOFull Provides a registered FIFO for sending data across clock domains. The
in and out clocks, along with the input reset, are explicitly provided.
The default clock and reset are ignored.
module mkSyncFIFOFull #( Integer depth,
Bool regEmpty,
Bool regFull,
Clock sClkIn, Reset sRstIn,
Clock dClkIn )
( SyncFIFOIfc #(a_type) )
provisos (Bits#(a_type, sa));
mkSyncFIFOFromCCFull Provides a registered FIFO to send data from the current clock domain
into a second clock domain. The input clock and reset are the current
clock and reset.
module mkSyncFIFOFromCCFull #( Integer depth,
Bool regEmpty,
Bool regFull,
Clock dClkIn )
( SyncFIFOIfc #(a_type) )
provisos (Bits#(a_type, sa));
mkSyncFIFOToCCFull Provides a registered FIFO to send data from a second clock domain
into the current clock domain. The output clock is the current clock.
The current reset is ignored.
module mkSyncFIFOToCCFull #( Integer depth,
Bool regEmpty,
Bool regFull,
Clock sClkIn, Reset sRstIn )
( SyncFIFOIfc #(a_type) )
provisos (Bits#(a_type, sa));
Verilog Modules
The BSV modules correspond to the following Verilog modules, which are found in the Bluespec
Verilog library, $BLUESPECDIR/Verilog/.
mkSyncFIFO SyncFIFO.v
mkSyncFIFOFromCC
mkSyncFIFOFromCC
mkSyncFIFOFull
mkSyncFIFOFromCCFull
mkSyncFIFOToCCFull
Description
An asynchronous RAM provides a domain crossing by having its read and write methods in separate
clock domains.
DualPortRamIfc Interface
Method Arguments
Name Type Description Name Description
write Action Writes data to a an ad- wr_addr Address of datatype addr_t
dress in a RAM
din Data of datatype data_t
read data_d Reads the data from the rd_addr Address to be read from
RAM
mkDualRam Provides an asynchronous RAM for when the read and the write meth-
ods are in separate clock domains. The write method is clocked by the
default clock, the read method is not clocked.
module mkDualRam( DualPortRamIfc #(addr_t, data_t) )
provisos ( Bits#(addr_t, sa),
Bits#(data_t, da) ) ;
Verilog Modules
The BSV modules correspond to the following Verilog modules, which are found in the Bluespec
Verilog library, $BLUESPECDIR/Verilog/.
mkDualRam DualPortRam.v
Description
In these primitives, no synchronization is actually done. It is up to the designer to verify that it is
safe for the signal to be used in the other domain. The mkNullCrossingWire is a wire synchronizer.
The older mkNullCrossing primitive is deprecated.
Modules
The mkNullCrossingWire module, shown in Figure 19, uses the ReadOnly interface which is defined
in the Prelude library B.4.7.
Note: no synchronization is actually done. This is purely a way to tell BSC that it is safe to use the
signal in the other domain. It is the responsibility of the designer to verify that this is correct.
There are some restrictions on the use of a mkNullCrossingWire. The expression used as the data
argument must not have an implicit condition, and there cannot be another rule which is required
to schedule before any method called in the expression.
mkNullCrossingWires may not be used in sequence to pass a signal across multiple clock boundaries
without synchronization. Once a signal has been crossed from one domain to a second domain
without synchronization, it cannot be subsequently passed unsynchronized to a third domain (or
back to the first domain).
Verilog Modules
The BSV modules correspond to the following Verilog modules, which are found in the Bluespec
Verilog library, $BLUESPECDIR/Verilog/.
mkNullCrossingWire BypassWire.v
Description
The mkSyncRegToSlow and mkSyncRegToFast are specialized crossing primitives which can be used
to transport data when clock edges are aligned, between the domains. The divided clocks and the
appropriate interface needed for the module would typically be generated using the mkClockDivider
module (Section C.8.3).
The crossing primitive is implemented via a single register, clocked by the slower (divided) clock. For
a fast to slow crossing, the register is only writable when the clockReady bit of the divider interface
is asserted. This is an implicit condition of the write method module which prevents erroneous
writes. For a slow to fast crossing both the read and write methods are always available.
Modules
mkSyncRegToSlow Provides a register to transport data when the clock edges are aligned
between domains. This module moves data from a fast to a slow
domain. The register is only writable when the clockReady bit of the
divider is asserted.
module mkSyncRegToSlow #( a_type initValue,
ClockDividerIfc divider,
Reset slowRstIn )
( Reg #(a_type) )
provisos (Bits#(a_type, sa)) ;
mkSyncRegToFast Provides a register to transport data when the clock edges are aligned
between domains. This module moves data from a slow to a fast
domain. The read and write methods are always available.
module mkSyncRegToFast #( a_type initValue,
ClockDividerIfc divider,
Reset slowRstIn )
( Reg #(a_type) )
provisos (Bits#(a_type, sa)) ;
The mkSyncFIFOToSlow and mkSyncFIFOToFast modules are specialized crossing primitives which
can be used to transport data when clock edges are aligned, between a fast clock domain and a slower
clock domain. The derived clock and the ClkNextRdy signal would typically be generated using the
mkClockDivider module. The synchronous FIFOs are clocked by the slower (divided) clock. The
SyncFIFOIfc is detailed in Section C.8.7.
mkSyncFIFOToSlow Provides a FIFO with specified depth to transport data from a fast
clock domain to a slower clock domain when clock edges are aligned.
The crossing primitive is implemented via a FIFO with the speci-
fied depth clocked by dClkIn. The FIFO is enqueued only when the
syncBit is asserted and the FIFO is not full.
module mkSyncFIFOToSlow #( Integer depth,
ClockDividerIfc divider,
Reset slowRstIn )
( SyncFIFOIfc #(a_type) )
provisos (Bits#(a_type, sa)) ;
mkSyncFIFOToFast Provides a FIFO with specified depth to transport data from a slower
clock domain to a faster clock domain when clock edges are aligned.
The crossing primitive is implemented via a FIFO with the specified
depth clocked by sClkIn (the source clock is the slower clock). The
FIFO is dequeued only when the syncBit is asserted and the FIFO is
not empty.
module mkSyncFIFOToFast #( Integer depth,
ClockDividerIfc divider,
Reset slowRstIn )
( SyncFIFOIfc #(a_type) )
provisos (Bits#(a_type, sa)) ;
Verilog Modules
The BSV modules correspond to the following Verilog modules, which are found in the Bluespec
Verilog library, $BLUESPECDIR/Verilog/.
mkSyncRegToSlow RegA.v
mkSyncRegToFast
mkSyncFIFOToSlow FIFO2.v
mkSyncFIFOToFast SizedFIFO.v
Description
This section describes the interfaces and modules used to synchronize reset signals from one clock
domain to another and to create reset signals. Reset generation converts a Boolean type to a Reset
type, where the reset is associated with the default or clocked_by clock domain.
The MakeResetIfc interface is provided by the reset generators mkReset and mkResetSync.
MakeResetIfc Interface
Method
Name Type Description
assertReset Action Method used to assert the reset
isAsserted Bool Indicates whether the reset is asserted
new_rst Reset Generated output reset
interface MakeResetIfc;
method Action assertReset();
method Bool isAsserted();
interface Reset new_rst;
endinterface
MuxRstIfc Interface
Method Arguments
Name Type Description Name Description
select Action Method used to select ab Value determines which
the reset based on the input reset to select
Boolean value ab
reset_out Reset Generated output reset
interface MuxRstIfc;
method Action select ( Bool ab );
interface Reset reset_out;
endinterface
Modules
Reset Synchronization To synchronize resets from one clock domain to another, both syn-
chronous and asynchronous modules are provided. The stages argument is the number of full clock
cycles the output reset is held for after the input reset is deasserted. This is shown as the number of
flops in figures 24 and 25. Specifying a 0 for the stages argument results in the creation of a simple
wire between sRst and dRstOut.
The less common mkSyncReset modules are provided for convenience, but these modules require
that sRst be held during a positive edge of dClkIn for the reset assertion to be detected. Both
mkSyncReset and mkSyncResetFromCR use the model in figure 25.
Reset Generation Two modules are provided for reset generation, mkReset and mkResetSync,
where each module has one parameter, stages. The stages parameter is the number of full clock
cycles the output reset is held after the inRst, as seen in figure 26, is deasserted. Specifying a 0
for the stages parameter results in the creation of a simple wire between the input register and
the output reset. That is, the reset is asserted immediately and not held after the input reset is
deasserted. It becomes the designer’s responsibility to ensure that the input reset is asserted for
sufficient time to allow the design to reset properly. The reset is controlled using the assertReset
method of the MakeResetIfc interface.
The difference between mkReset and mkResetSync is that for the former, the assertion of reset
is immediate, while the later asserts reset at the next rising edge of the clock. Note that use of
mkResetSync is less common, since the reset requires clock edges to take effect; failure to assert
reset for a clock edge will result in a reset not being seen at the output reset.
mkReset Provides conversion of a Boolean type to a Reset type, where the reset
is associated with dClkIn. This module uses the model in figure 26.
startInRst indicates the reset value of the register. If startInRst
is True, the reset value of the register is 0, which means the output
reset will be asserted whenever the currentReset (sRst) is asserted.
rst_out will remain asserted for the number of clock cycles given
by the stages parameter after sRst is deasserted. If startInRst is
False, the output reset will not be asserted when sRst is asserted,
but only when the assert_reset method is invoked. At the start of
simulation rst_out will only be asserted if startinRst is True and
sRst is initially asserted.
module mkReset #( Integer stages,
Bool startInRst,
Clock dClkIn )
( MakeResetIfc ) ;
mkResetSync Provides conversion of a Boolean type to a Reset type, where the reset
is associated with dClkIn and the assertion of reset is at the next
rising edge of the clock. This module uses the model in figure 26.
startInRst indicates the reset value of the register. If startInRst
is True, the reset value of the register is 0, which means the output
reset will be asserted whenever the currentReset (sRst) is asserted.
rst_out will remain asserted for the number of clock cycles given
by the stages parameter after sRst is deasserted. If startInRst is
False, the output reset will not be asserted when sRst is asserted,
but only when the assert_reset method is invoked. At the start of
simulation rst_out will only be asserted if startinRst is True and
sRst is initially asserted.
module mkResetSync #( Integer stages,
Bool startInRst,
Clock dClkIn )
( MakeResetIfc ) ;
A reset multiplexor mkResetMux, as seen in figure 27, creates one reset signal by selecting between
two existing reset signals.
mkResetMux Multiplexor which selects between two input resets, aRst and bRst,
to create a single output reset rst_out. The reset is selected through
a Boolean value provided to the select method where True selects
aRst.
module mkResetMux #( Reset aRst, Reset bRst )
( MuxRstIfc rst_out ) ;
For testbenches, in which an absolute clock is being created, it is helpful to generate a reset for
that clock. The module mkInitialReset is available for this purpose. It generates a reset which is
asserted at the start of simulation. The reset is asserted for the number of cycles specified by the
parameter cycles, counting the start of time as 1 cycle. Therefore, a cycles value of 1 will cause
the reset to turn off at the first clock tick. This module is not synthesizable.
mkInitialReset Generates a reset for cycles cycles, where the cycles parameter must
be greater than zero. The clocked_by clause indicates the clock the
reset is associated with. This module is not synthesizable.
module mkInitialReset #( Integer cycles )
( Reset ) ;
Example:
When two reset signals need to be combined so that some logic can be reset when either input reset
is asserted, the mkResetEither module can be used.
mkResetEither Generates a reset which is asserted whenever either input reset is as-
serted.
module mkResetEither ( Reset aRst,
Reset bRst)
( Reset out_ifc );
Example:
Verilog Modules
The BSV modules correspond to the following Verilog modules, which are found in the Bluespec
Verilog library, $BLUESPECDIR/Verilog/.
C.9.1 ModuleCollect
Package
import ModuleCollect :: * ;
Description
The ModuleCollect package provides the capability of adding additional items, such as configuration
bus connections, to a design in such a way that it does not change the structure of the design. This
section provides a brief overview of the package. For more description of its usage, see the CBus
package (C.9.2), which utilizes ModuleCollect. There is also a detailed example and more complete
discussion of the CBus package in the configbus tutorial in the BSV/tutorials directory.
An ordinary Bluespec module, when instantiated, adds its own state elements and rules to the grow-
ing accumulation of state elements and rules defined in the design. In some designs, for example a
configuration bus, additional items, such as the logic for the bus address decoding must be accumu-
lated as well. While there is a need to add these items, it is also desirable to keep these additional
design details separate from the main design, keeping the natural structure of the design intact.
The ModuleCollect mechanism allows the designer to hide the details of the additional interfaces. A
module which is going to be synthesized must contain only rules and state elements, as the compiler
does not know how to handle the additional items. Therefore, the collection must be brought into
the open, or exposed, before the module can be synthesized. The ModuleCollect package provides
the mechanisms to allow these additional items to be collected, processed and exposed.
Types and Type Classes
The ModuleCollect type is a variation on Module that allows additional items, other than states and
rules, to be collected while elaborating the module structure. A module defining the accumulation
of a special collection will have the type of ModuleCollect which is defined as follows:
struct ModuleCollect#(a_type, ifc)
· · · abstract · · ·
where a_type defines the type of the items being collected. The collection is kept as a List, therfore
each item in the collection must have the same type. The collection is associated with ifc, the
device module interface.
Your new type of module is a ModuleCollect defined to collect a specific type. It is often convenient
to give a name to your new type of module using the typedef keyword.
For example:
typedef ModuleCollect#(element_type, ifc_device)
MyModuleType#(type ifc_device)
Since only modules of type Module can be synthesized the collection be exposed before synthesis,
by applying the function exposeCollection. The module type of the function exposeCollection
is Module, so once the collection has been exposed the design is ready for synthesis.
Interfaces
The IWithCollection interface couples the normal module interface (the device interface) with
the collection of collected items (the collection interface). This is the interface provided by the
exposeCollection function. It separates the collection list and the device module interface, to allow
the module to be synthesized.
Once a set of items has been collected, those items must be exposed before synthesis. The exposeCollection
module constructor is used to bring the collection out into the open. The exposeCollection
module takes as an argument a ModuleCollect module (m) with interface ifc, and provides an
IWithCollection interface.
mapCollection Apply a function to each item added to the collection within the second
argument.
import ModuleCollect::*;
import List::*;
import Vector::*;
import Assert::*;
// The "wires" method tells which conditions have been set, and the
// "clear" method resets them all to 0.
// The items in our extra collection will be interfaces of the
// following type:
interface AssertionWire;
method Integer index; //Indicates which wire is to be set if
method Bool fail; // fail method ever returns true.
method Action clear;
endinterface
...
// The next definition shows how items are added to the collection.
// This is the module which will be instantiated at various places in
// the design, to test various conditions. It takes one static
// parameter, "ix", to specify which wire is to carry this condition,
// and one dynamic parameter (one varying at run-time) "c", giving the
// value of the condition itself.
interface AssertionReg;
method Action set;
method Action clear;
endinterface
// return the values in the collection, and the ifc of the device
return(tuple2(c_ifc, dut_ifc));
endmodule
C.9.2 CBus
Package
import CBus :: * ;
Description
The CBus package provides the interface, types and modules to implement a configuration bus
capability providing access to the control and status registers in a given module hierarchy. This
package utilizes the ModuleCollect package and functionality, as described in section C.9.1. The
ModuleCollect package allows items in addition to usual state elements and rules to be accumulated.
This is required to collect up the interfaces of the control status registers included in a module and
to add the associated logic and ports required to allow them to be accessed via a configuration bus.
This package is provided as both a compiled library package and as BSV source code to facilitate
customization. The source code file can be found in the $BLUESPECDIR/BSVSource directory. To
customize a package, copy the file into a local directory and then include the local directory in the
path when compiling. This is done by specifying the path with the -p option as described in the
BSV Users Guide.
For a more complete discussion of the CBus package, consult the configbus tutorial in the BSV/tutorials
directory.
Types and Type Classes
The type CBusItem defines the type of item to be collected by ModuleCollect. The items to be
collected are the same as the ifc which we will later expose, so we use a type alias:
The type ModWithCBus defines the type of module which is collecting CBusItems. An ordinary
module, one not collecting anything other than state elements and rules, has the type Module. Since
CBusItems are being collected, a module type ModWithCBus is defined. When the module type is
not Module, the type must be specified in square brackets immediately after the module keyword in
the module definition.
CBus Interface
Name Description
write Writes the data value to the register if and only if the value of
addr matches the address of the register.
read Returns the value of the associated register if and only if addr
matches the register address. In all other cases the read method
returns an Invalid value.
The IWithCBus interface combines the CBus interface with a normal module interface. It is defined as
a structured interface with two sub-interfaces: cbus_ifc (the associated configuration bus interface)
and device_ifc (the associated device interface). It is polymorphic in terms of the type of the
configuation bus interface and the type of the device interface.
Modules
The collectCBusIFC module takes as an argument a module with an IWithCBus interface, adds the
associated CBus interface to the current collection (using addToCollection from the ModuleCollect
package), and returns a module with the normal interface. Note that collectCBusIFC is of module
type ModWithCBus.
collectCBusIFC Adds the CBus to the collection and returns a module with just the device
interface.
module [ModWithCBus#(size_address, size_data)]
collectCBusIFC#(Module#(IWithCBus#(
CBus#(size_address,size_data),i)) m)(i);
The exposeCBusIFC module is used to create an IWithCBus interface given a module with a normal
interface and an associated collection of CBusItems. This module takes as an argument a module
of type ModWithCBus and provides an interface of type IWithCBus. The exposeCBusIFC module
exposes the collected CBusItems, processes them, and provides a new combined interface. This
module is synthesizable, because it is of type Module.
exposeCBusIFC A module wrapper that takes a module with a normal interface, processes the
collected CBusItems and provides an IWithCBus interface.
module [Module] exposeCBusIFC#(ModWithCBus#(
size_address, size_data, item) sm)
(IWithCBus#(CBus#(size_address, size_data), item));
The CBus package provides a set of module primitives each of which adds a CBus interface to the
collection and provides a normal Reg interface from the local block point of view. These modules are
used in designs where a normal register would be used, and can be read and written to as registers
from within the design.
mkCBRegR A wrapper to provide a read only CBus interface to the collection and a normal
Reg interface to the local block.
module [ModWithCBus#(size_address, size_data)]
mkCBRegR#(CRAddr#(size_address2) addr, r x)
(Reg#(r))
provisos (Bits#(r, sr), Add#(k, sr, size_data),
Add#(ignore, size_address2, size_address));
mkCBRegW A wrapper to provide a write only CBus interface to the collection and a
normal Reg interface to the local block.
module [ModWithCBus#(size_address, size_data)]
mkCBRegW#(CRAddr#(size_address2) addr, r x)
(Reg#(r))
provisos (Bits#(r, sr), Add#(k, sr, size_data),
Add#(ignore, size_address2, size_address));
The mkCBRegFile module wrapper adds a CBus interface to the collection and provides a RegFile
interface to the design. This module is used in designs as a normal RegFile would be used.
mkCBRegFile A wrapper to provide a normal RegFile interface and automatically add the
CBus interface to the collection.
module [ModWithCBus#(size_address, size_data)]
mkCBRegFile#(Bit#(size_address) reg_addr,
Bit#(size_address) size)
(RegFile#(Bit#(size_address), r))
provisos (Bits#(r, sr), Add#(k, sr, size_data));
Example
Provided here is a simple example of a CBus implementation. The example is comprised of three
packages: CfgDefines, Block, and Tb. The CfgDefines package contains the definition for the
configuration bus, Block is the design block, and Tb is the testbench which executes the block.
The Block package contains the local design. As seen in Figure 29, the configuration bus registers
look like a single field from the CBus (cfgResetAddr, cfgStateAddr, cfgStatusAddr), while each
field (reset, init, cnt, etc.) in the configuration bus registers looks like a regular register from
from the local block point of view.
interface Block;
// TODO: normally this block would have at least a few methods
// Cbus interface is hidden, but it is there
endinterface
// In order to access the CBus at this parent, we need to expose the bus.
// Only modules of type [Module] can be synthesized.
module [Module] mkBlock(IWithCBus#(DCBus, Block));
let ifc <- exposeCBusIFC( mkBlockInternal );
return ifc;
endmodule
The CfgDefines package contains the user defines describing how the local registers are combined
into the configuration bus.
package CfgDefines;
import CBus::*;
////////////////////////////////////////////////////////////////////////////////
/// basic defines
////////////////////////////////////////////////////////////////////////////////
// width of the address bus, it’s easiest to use only the width of the bits needed
// but you may have other reasons for passing more bits around (even if some address
// bits are always 0)
typedef 2 DCBusAddrWidth; // roof( log2( number_of_config_registers ) )
////////////////////////////////////////////////////////////////////////////////
// Define the CBus
////////////////////////////////////////////////////////////////////////////////
typedef CBus#( DCBusAddrWidth,DCBusDataWidth) DCBus;
////////////////////////////////////////////////////////////////////////////////
/// Configuration Register Types
////////////////////////////////////////////////////////////////////////////////
// these are configuration register from your design. The basic
// idea is that you want to define types for each individual field
// and later on we specify which address and what offset bits these
// go to. This means that config register address fields can
// actually be split across modules if need be.
//
typedef bit TCfgReset;
////////////////////////////////////////////////////////////////////////////////
/// configuration bus addresses
////////////////////////////////////////////////////////////////////////////////
Bit#(DCBusAddrWidth) cfgResetAddr = 0; //
Bit#(DCBusAddrWidth) cfgStateAddr = 1; //
Bit#(DCBusAddrWidth) cfgStatusAddr = 2; // maybe you really want this to be 0,4,8 ???
////////////////////////////////////////////////////////////////////////////////
/// Configuration Register Locations
////////////////////////////////////////////////////////////////////////////////
// DCAddr is a structure with two fields
// DCBusAddrWidth a ; // this is the address
// // this does a pure comparison
// Bit#(n) o ; // this is the offset that this register
// // starts reading and writting at
////////////////////////////////////////////////////////////////////////////////
///
////////////////////////////////////////////////////////////////////////////////
endpackage
(* synthesize *)
module mkTb ();
// In order to access this cfg bus we need to use IWithCBus type
IWithCBus#(DCBus,Block) dut <- mkBlock;
Stmt test =
seq
// write the bits need to the proper address
// generally this comes from software or some other packing scheme
// you can, of course, create functions to pack up several fields
// and drive that to bits of the correct width
// For that matter, you could have your own shadow config registers
// up here in the testbench to do the packing and unpacking for you
dut.cbus_ifc.write( cfgResetAddr, unpack(’1) );
// but the ’ones’ bit was set when it saw all ones on the count
// so read it to see that...
$display("TOP: status = %x at ", dut.cbus_ifc.read( cfgStatusAddr ), $time);
// now clear it
dut.cbus_ifc.write( cfgStatusAddr, 1 );
// and if we had other interface methods, that where not part of CBUS
// we would access them via dut.device_ifc
endseq;
mkAutoFSM( test );
endmodule
This section describes the Bluespec AzureIP library components. These components can be used
to build complex, fully synthesizable designs. Each component is provided in one or more BSV
packages, defining the interfaces and data structures used to communicate to other components.
These library components are provided as BSV source code to facilitate customization. Users can
easily understand and then extend the IP to implement additional features as required for their
applications. The source code files can be found in the $BLUESPECDIR/BSVSource directory. To
modify the files, copy the files into a local directory and use the -p compile option, as described in
the BSV Users Guide, to include the local directory in your path.
C.10.1 TLM
Description
The TLM package includes definitions of interfaces, data structures, and module constructors which
allow users to create and modify bus-based designs in a manner that is independent of any one
specific bus protocol. Bus operations are defined in terms of generic bus payload data structures.
Other protocol specific packages include transactor modules that convert a stream of TLM bus
operations into corresponding operations in a specific bus protocol. Designs created using the TLM
package are thus more portable (because that they allow the core design to be easily applied to
multiple bus protocols). In addition, since the specific signalling details of each bus protocol are
encapsulated in pre-designed transactors, users are not required to learn, re-implement, and re-verify
existing standard protocols.
Packages
The elements of the TLM library are defined within TLM package.
import TLM :: * ;
Data Structures
The two basic data structures defined in the TLM package are TLMRequest and TLMResponse. By
using these types in a design, the underlying bus protocol can be changed without having to modify
the interactions with the TLM objects.
TLMRequest A TLM request contains either control information and data, or data alone. A
TLMRequest is tagged as either a RequestDescriptor or RequestData. A RequestDescriptor
contains control information and data while a RequestData contains only data.
RequestDescriptor The table below describes the components of a RequestDescriptor and the
valid values for each of its members.
RequestDescriptor
Member Name DataType Valid Values
command TLMCommand READ, WRITE, UNKNOWN
mode TLMMode REGULAR, DEBUG, CONTROL
addr TLMAddr#(‘TLM_TYPES) Bit#(addr_size)
data TLMData#(‘TLM_TYPES) Bit#(data_size)
burst_length TLMUint#(‘TLM_TYPES) UInt#(uint_size)
byte_enable TLMByteEn#(‘TLM_TYPES) Bit#(TDiv#(data_size, 8))
burst_mode TLMBurstMode INCR, CNST, WRAP, UNKNOWN
burst_size TLMBurstSize#(‘TLM_TYPES) Bit#(TLog#(TDiv#(data_size, 8)))
prty TLMUInt#(‘TLM_TYPES) UInt#(uint_size)
thread_id TLMId#(‘TLM_TYPES) Bit#(id_size)
transaction_id TLMId#(‘TLM_TYPES) Bit#(id_size)
export_id TLMId#(‘TLM_TYPES) Bit#(id_size)
custom TLMCustom#(‘TLM_TYPES) cstm_type
RequestData The table below describes the components of a RequestData and the valid values
for its members.
RequestData
Member Name DataType Valid Values
data TLMData#(‘TLM_TYPES) Bit#(data_size)
transaction_id TLMId#(‘TLM_TYPES) Bit#(id_size)
custom TLMCustom#(‘TLM_TYPES) cstm_type
TLMResponse The table below describes the components of a TLMResponse and the valid values
for its members.
TLMResponse
Member Name DataType Valid Values
command TLMCommand READ, WRITE, UNKNOWN
data TLMData#(‘TLM_TYPES) Bit#(data_size)
status TLMStatus SUCCESS, ERROR, NO RESPONSE
prty TLMUInt#(‘TLM_TYPES) UInt#(uint_size)
thread_id TLMId#(‘TLM_TYPES) Bit#(id_size)
transaction_id TLMId#(‘TLM_TYPES) Bit#(id_size)
export_id TLMId#(‘TLM_TYPES) Bit#(id_size)
custom TLMCustom#(‘TLM_TYPES) cstm_type
Configurable Parameters
In the above BSV code definitions the compiler macros ‘TLM_TYPE_PRMS and ‘TLM_TYPES are used in
the typedef statements. A ’define statement is a preprocessor construct used to place prepackaged
text values into a file, as described in Section 2.7.1. In this case, the macros contain parameters
to be used in the data definitions. Placing the parameters in a separate file allows them to be
easily modified for different protocol requirements. For convenience, we have predefined a few useful
definitions for use in the TLM package.
The TLM_TYPE_PRMS macro contains type definition parameters which are used in the interface
definitions or as arguments to TLM types and interfaces.
The TLM_TYPES macro is used when providing the interface or using the data type. TLM_TYPES is
still polymorphic.
The macro TLM_STD_TYPES provides specific values for the polymorphic values defined above. The
values defined in TLM_STD_TYPES are common values. The user can change any of the values or
define other corresponding macros (with different values) as appropriate for a given design.
The macros are found in the file TLM.defines. A sample of the contents of the file are displayed
below.
Interfaces
The TLM interfaces define how TLM blocks interconnect and communicate. The TLM package
includes two basic interfaces: The TLMSendIFC interface and the TLMRecvIFC interface. These inter-
faces use basic Get and Put subinterfaces as the requests and responses, as described in Section C.6.1.
The TLMSendIFC interface generates (Get) requests and receives (Put) responses. The TLMRecvIFC
interface receives (Put) requests and generates (Get) responses. Additional TLM interfaces are built
up from these basic blocks.
TLMSendIFC The TLMSendIFC interface transmits the requests and receives the responses.
TLMSendIFC Interface
Name Type Description
tx Get#(TLMRequest#(‘TLM_TYPES)) Transmits a request through the Get interface
rx Put#(TLMResponse#(‘TLM_TYPES)) Receives a response through the Put interface
interface TLMSendIFC#(‘TLM_TYPE_PRMS);
interface Get#(TLMRequest#(‘TLM_TYPES)) tx;
interface Put#(TLMResponse#(‘TLM_TYPES)) rx;
endinterface
TLMRecvIFC The TLMRecvIFC interface receives the requests and transmits the responses.
TLMRecvIFC Interface
Name Type Description
tx Get#(TLMResponse#(‘TLM_TYPES)) Transmits the response through the Get interface
rx Put#(TLMRequest#(‘TLM_TYPES)) Receives the request through the Put interface
interface TLMRecvIFC#(‘TLM_TYPE_PRMS);
interface Get#(TLMResponse#(‘TLM_TYPES)) tx;
interface Put#(TLMRequest#(‘TLM_TYPES)) rx;
endinterface
A module with a TLMSendIFC interface creates a stream of requests. A module with a TLMRecvIFC
interface receives the requests and transmits responses. Some bus protocols have separate channels
for read and write operations. In these cases it is useful to have interfaces which bundle together
two sends or two receives. The TLMReadWriteSendIFC interface includes two send interfaces while
the TLMReadWriteRecvIFC interface bundles two receives.
interface TLMReadWriteSendIFC#(‘TLM_TYPE_PRMS);
interface TLMSendIFC#(‘TLM_TYPES) read;
interface TLMSendIFC#(‘TLM_TYPES) write;
endinterface
interface TLMReadWriteRecvIFC#(‘TLM_TYPE_PRMS);
interface TLMRecvIFC#(‘TLM_TYPES) read;
interface TLMRecvIFC#(‘TLM_TYPES) write;
endinterface
interface TLMTransformIFC#(‘TLM_TYPE_PRMS);
interface TLMRecvIFC#(‘TLM_TYPES) in;
interface TLMSendIFC#(‘TLM_TYPES) out;
endinterface
Modules
The TLM package includes modules for creating and modifying TLM objects: mkTLMRandomizer,
mkTLMSource, and mkTLMReducer. Two TLM RAM modules are also provided: mkTLMRam which
provides a single read/write port and mkTLMReadWriteRam which provides two ports, a separate one
for reads and a separate one for writes.
mkTLMRandomizer Creates a stream of random TLM operations. The argument m_command is a Maybe
type which determines if the TLMRequests will be reads, writes, or both. A value
of Valid READ will generate only reads, a value of Valid WRITE will generate only
writes, and an Invalid value will generate both reads and writes. The Randomize
interface is defined in the Randomizable package.
mkTLMSource Creates a wrapper around the mkTLMRandomize module. The provided inter-
face is now a TLMSendIFC interface which both sends TLMRequests and re-
ceives TLMResponses. The argument m_command has the same meaning as in
mkTLMRandomizer. The verbose argument controls whether or not $display out-
puts are provide when sending and receiving TLM objects.
mkTLMReducer Converts a stream of (arbitrary) TLM operations into a stream with only single
reads and single writes.
mkTLMRam Creates a TLM RAM with a single port for read and write operations. Provides the
TLMRecvIFC interface. The verbose argument controls whether or not $display
output is provided when performing a memory operation. The id argument pro-
vides an identifier for the instantiation which is used in the $display output if the
verbose flag is asserted.
mkTLMReadWriteRam Creates a RAM with separate ports for read and write operations. Provides the
TLMReadWriteRecvIFC interface. The verbose argument controls whether or not
$display output is provided when performing a memory operation. The id ar-
gument provides an identifier for the instantiation which is used in the $display
output if the verbose flag is asserted.
The mkTLMCBusAdapter module creates an adapter which allows the CBus (Section C.9.2) to be
accessed via a TLM interface.
module mkTLMCBusAdapterToReadWrite#
(TLMCBus#(‘TLM_TYPES, caddr_size) cfg)
(TLMReadWriteRecvIFC#(‘TLM_TYPES))
provisos(Bits#(TLMRequest#(‘TLM_TYPES), s0),
Bits#(TLMResponse#(‘TLM_TYPES), s1),
Add#(ignore, caddr_size, addr_size));
Functions
function RequestDescriptor#(‘TLM_TYPES)
createBasicRequestDescriptor()
provisos(Bits#(RequestDescriptor#(‘TLM_TYPES), s0));
C.10.2 AXI
Description
The AXI library includes interface, transactor, module and function definitions to implement the
Advanced eXtensible Interface (AXI) protocol with Bluespec SystemVerilog. The BSV AXI library
groups the AXI data and protocols into reusable, parameterized interfaces, which interact with TLM
interfaces. An AXI bus is implemented using AXI transactors to connect TLM interfaces on one
side with AXI interfaces on the other side.
The AXI library supports the following AXI Bus protocol features:
The AXI library does not support the following AXI Bus protocol features:
• Exclusive/Locked Access
• Low Power Interface
• Cache Transaction Attributes
The basic structure of an AXI write bus is show in figure 35. The structure of a read bus is similar.
(Note that the nature of the AXI protocol is such that the read and write buses operate totally
independently of each other).
The corresponding BSV AXI implementation is shown in figure 36. TLM Write requests are received
via the TLMRecvIFC interfaces of the master transactors. The request is then transmitted via the
AxiWrMaster interface out onto the AXI bus and on to the appropriate slave transactor. The slave
transactor receives the request via the AxiWrSlave interface, translates the request back into a
stream of TLM objects, and then transmits those objects via the TLMSendIFC interface. The TLM
response from the write operation follows the same path in reverse.
Figure 36: BSV AXI Write Bus Implementation Using TLM Transactors
Packages
The transactors, interfaces, data structures, modules, and functions for implementing the AXI bus
are defined in the AXI package.
To include a package in your design, use the import syntax.
import Axi :: * ;
Data Structures
Inside the transactor modules, the AXI data is organized into the following data structures: the
address data is defined by AxiAddrCmd, the read response is defined by AxiRdResp, the write data
is defined by AxiWrData and the write response is defined by AxiWrResp.
AxiAddrCmd The AXI Address Bus is defined by a structure, AxiAddrCmd, the components of
which are described in the following table.
AxiAddrCmd
Member Name DataType Valid Values
id AxiId#(‘TLM_TYPES) Bit#(id_size)
len AxiLen Bit#(4)
size AxiSize Bit#(3)
burst AxiBurst FIXED, INCR, WRAP
lock AxiLock NORMAL, EXCLUSIVE, LOCKED
cache AxiCache Bit#(4)
prot AxiProt Bit#(3)
addr AxiAddr#(‘TLM_TYPES) Bit#(addr_size)
typedef struct {
AxiId#(‘TLM_TYPES) id;
AxiLen len;
AxiSize size;
AxiBurst burst;
AxiLock lock;
AxiCache cache;
AxiProt prot;
AxiAddr#(‘TLM_TYPES) addr;
} AxiAddrCmd#(‘TLM_TYPE_PRMS) deriving(Bits,Eq);
AxiRdResp The AXI Read Bus is defined by the AxiRdResp structure, the components of which
are described in the following table.
AxiRdResp
Member Name DataType Valid Values
id AxiId#(‘TLM_TYPES) Bit#(id_size)
data AxiData#(‘TLM_TYPES) Bit#(data_size)
resp AxiResp OKAY, EXOKAY, SLVERR, DECERR
last Bool True, False
typedef struct {
AxiId#(‘TLM_TYPES) id;
AxiData#(‘TLM_TYPES) data;
AxiResp resp;
Bool last;
} AxiRdResp#(‘TLM_TYPE_PRMS) deriving(Bits,Eq);
The AXI Write Bus is defined by two structures, AxiWrData and AxiWrResp.
AxiWrData
Member Name DataType Valid Values
id AxiId#(‘TLM_TYPES) Bit#(id_size)
data AxiData#(‘TLM_TYPES) Bit#(data_size)
strb AxiByteEn#(‘TLM_TYPES) Bit#(TDiv#(data_size, 8))
last Bool True, False
typedef struct {
AxiId#(‘TLM_TYPES) id;
AxiData#(‘TLM_TYPES) data;
AxiByteEn#(‘TLM_TYPES) strb;
Bool last;
} AxiWrData#(‘TLM_TYPE_PRMS) deriving(Bits,Eq);
AxiWrResp
Member Name DataType Valid Values
id AxiId#(‘TLM_TYPES) Bit#(id_size)
resp AxiResp OKAY, EXOKAY, SLVERR, DECERR
typedef struct {
AxiId#(‘TLM_TYPES) id;
AxiResp resp;
} AxiWrResp#(‘TLM_TYPE_PRMS) deriving(Bits,Eq);
Bus Interfaces
This section describes the AXI bus master and slave interfaces used by the AXI transactor modules.
Since the AXI protocol supports read and write operations on separate buses, two flavors of each
interface exist, one for reads and one for writes.
AxiRdMaster The AxiRdMaster interface issues AXI read requests and receives AXI read re-
sponses.
interface AxiRdMaster#(‘TLM_TYPE_PRMS);
// Address Outputs
method AxiId#(‘TLM_TYPES) arID;
// Address Inputs
method Action arREADY(Bool value);
// Response Outputs
method Bool rREADY;
// Response Inputs
method Action rID (AxiId#(‘TLM_TYPES) value);
method Action rDATA (AxiData#(‘TLM_TYPES) value);
method Action rRESP (AxiResp value);
method Action rLAST (Bool value);
method Action rVALID(Bool value);
endinterface
AxiWrMaster The AxiWrMaster interface issues AXI write requests and receives AXI write re-
sponses.
interface AxiWrMaster#(‘TLM_TYPE_PRMS);
// Address Outputs
method AxiId#(‘TLM_TYPES) awID;
method AxiAddr#(‘TLM_TYPES) awADDR;
method AxiLen awLEN;
method AxiSize awSIZE;
method AxiBurst awBURST;
method AxiLock awLOCK;
method AxiCache awCACHE;
method AxiProt awPROT;
method Bool awVALID;
// Address Inputs
method Action awREADY(Bool value);
// Data Outputs
method AxiId#(‘TLM_TYPES) wID;
method AxiData#(‘TLM_TYPES) wDATA;
method AxiByteEn#(‘TLM_TYPES) wSTRB;
method Bool wLAST;
method Bool wVALID;
// Data Inputs
method Action wREADY(Bool value);
// Response Outputs
method Bool bREADY;
// Response Inputs
method Action bID (AxiId#(‘TLM_TYPES) value);
method Action bRESP (AxiResp value);
method Action bVALID(Bool value);
endinterface
AxiRdSlave The AxiRdSlave interface receives AXI read requests and returns AXI read re-
sponses.
interface AxiRdSlave#(‘TLM_TYPE_PRMS);
// Address Inputs
method Action arID (AxiId#(‘TLM_TYPES) value);
method Action arADDR (AxiAddr#(‘TLM_TYPES) value);
method Action arLEN (AxiLen value);
method Action arSIZE (AxiSize value);
method Action arBURST(AxiBurst value);
method Action arLOCK (AxiLock value);
method Action arCACHE(AxiCache value);
method Action arPROT (AxiProt value);
method Action arVALID(Bool value);
// Address Outputs
method Bool arREADY;
// Response Inputs
method Action rREADY(Bool value);
// Response Outputs
method AxiId#(‘TLM_TYPES) rID;
method AxiData#(‘TLM_TYPES) rDATA;
method AxiResp rRESP;
method Bool rLAST;
method Bool rVALID;
endinterface
AxiWrSlave The AxiWrSlave interface receives AXI write requests and returns AXI write re-
sponses.
interface AxiWrSlave#(‘TLM_TYPE_PRMS);
// Address Inputs
method Action awID (AxiId#(‘TLM_TYPES) value);
method Action awADDR (AxiAddr#(‘TLM_TYPES) value);
method Action awLEN (AxiLen value);
method Action awSIZE (AxiSize value);
method Action awBURST(AxiBurst value);
method Action awLOCK (AxiLock value);
method Action awCACHE(AxiCache value);
method Action awPROT (AxiProt value);
method Action awVALID(Bool value);
// Address Outputs
// Data Inputs
method Action wID (AxiId#(‘TLM_TYPES) value);
method Action wDATA (AxiData#(‘TLM_TYPES) value);
method Action wSTRB (AxiByteEn#(‘TLM_TYPES) value);
method Action wLAST (Bool value);
method Action wVALID(Bool value);
// Data Ouptuts
method Bool wREADY;
// Response Inputs
method Action bREADY(Bool value);
// Response Outputs
method AxiId#(‘TLM_TYPES) bID;
method AxiResp bRESP;
method Bool bVALID;
endinterface
The AxiRdMaster and AxiRdSlave interfaces as well as the AxiWrMaster and AxiWrSlave interfaces
are connectable.
Fabric Interfaces
When used in the context of a bus or switch, AXI transactor modules must communicate with address
decoding logic. As with the BSV implementation of the AHB bus, bus fabric interfaces are provided
to support this communication. Unlike the AHB protocol however, with the AXI bus protocol no
explicit communication between the arbiter and the master transactor modules is required. Thus
the AxiRdFabricMaster and AxiWrFabricMaster interfaces are simply wrappers around the bus
interfaces themselves.
interface AxiRdFabricMaster#(‘TLM_TYPE_PRMS);
interface AxiRdMaster#(‘TLM_TYPES) bus;
endinterface
interface AxiWrFabricMaster#(‘TLM_TYPE_PRMS);
interface AxiWrMaster#(‘TLM_TYPES) bus;
endinterface
The AxiRdFabricSlave and AxiWrFabricSlave interfaces each provide an addrMatch method which
given an AXI address returns an Boolean value indicating whether the given address maps to the
associated slave. By polling this method for each slave on the bus, the decoding logic can determine
the appropriate destination for each bus transaction.
interface AxiRdFabricSlave#(‘TLM_TYPE_PRMS);
interface AxiRdSlave#(‘TLM_TYPES) bus;
method Bool addrMatch(AxiAddr#(‘TLM_TYPES) value);
endinterface
interface AxiWrFabricSlave#(‘TLM_TYPE_PRMS);
interface AxiWrSlave#(‘TLM_TYPES) bus;
method Bool addrMatch(AxiAddr#(‘TLM_TYPES) value);
endinterface
Transactor Interfaces
Each AXI transactor module provides AXI and TLM interfaces to implement a translation between
a stream of TLM operations and the AXI bus protocol. Each transactor has two subinterfaces:
a subinterface for the connection with the AXI bus and a subinterface to send and receive TLM
objects. The AXI library package includes two master transactor interfaces and two slave trans-
actor interfaces; The AXIRdMasterXActor and AXIWrMasterXActor interfaces for masters and the
AXIRdSlaveXActor and AXIWrSlaveXActor interfaces for slaves. Since the AXI protocol supports
read and write transaction on separate buses, two transactor implementations are required for mas-
ters and two implementations for slaves. The AXI subinterface definitions can be found in section
C.10.2. The TLM interfaces are described in Section C.10.1.
interface AxiRdMasterXActorIFC#(‘TLM_TYPE_PRMS);
interface TLMRecvIFC#(‘TLM_TYPES) tlm;
interface AxiRdFabricMaster#(‘TLM_TYPES) fabric;
endinterface
interface AxiWrMasterXActorIFC#(‘TLM_TYPE_PRMS);
interface TLMRecvIFC#(‘TLM_TYPES) tlm;
interface AxiWrFabricMaster#(‘TLM_TYPES) fabric;
endinterface
interface AxiRdSlaveXActorIFC#(‘TLM_TYPE_PRMS);
interface TLMSendIFC#(‘TLM_TYPES) tlm;
interface AxiRdFabricSlave#(‘TLM_TYPES) fabric;
endinterface
interface AxiWrSlaveXActorIFC#(‘TLM_TYPE_PRMS);
interface TLMSendIFC#(‘TLM_TYPES) tlm;
interface AxiWrFabricSlave#(‘TLM_TYPES) fabric;
endinterface
Modules
The following constructors are used to create AXI transactor modules. Versions with associated syn-
thesis boundaries are also available. These versions are called mkAxiRdMasterStd, mkAxiWrMasterStd,
mkAxiRdSlaveStd, and mkAxiWrSlaveStd. The specific TLM parameter values for these synthesized
versions are as specified by the preprocessor macro TLM_STD_TYPES (see section C.10.1).
The following two module constructors are each used to create an AXI bus fabric. mkAxiRdBus is
used to create a read bus while mkAxiWrBus is used to create a write bus.
module mkAxiRdBus#(Vector#(master_count,
AxiRdFabricMaster#(‘TLM_TYPES)) masters,
Vector#(slave_count,
AxiRdFabricSlave#(‘TLM_TYPES))slaves) (Empty);
module mkAxiWrBus#(Vector#(master_count,
AxiWrMaster#(‘TLM_TYPES)) masters,
Vector#(slave_count,
AxiWrSlave#(‘TLM_TYPES)) slaves) (Empty);
The following module is used to add probe signals for each of the AXI bus signals. This facilitates
debugging and waveform viewing of the created bus fabric.
mkAxiMonitor Adds a probe module for each of the AXI bus signals. The include_pc value
indicates whether or not the monitor module should include an instantiation of an
AXI protocol checker module (available from ARM). If the protocol checker is not
available, the value of include_pc should be set to False.
C.10.3 AHB
Description
The AHB library includes interface, transactor, module and function definitions to implement the
AHB protocol with Bluespec SystemVerilog. The BSV AHB library groups the AHB data and
protocols into reusable, parameterized interfaces, which interact with TLM interfaces. An AHB bus
is implemented using AHB transactors - interfaces which connect TLM interfaces on one side with
AHB interfaces on the other side.
The AHB library supports the following AHB Bus protocol features:
• Locked Transfers
The AHB library does not support the following AHB Bus protocol features:
• Split Transfers
• Retry Transfers
Packages
The transactors, interfaces, data structures, and modules for the AHB bus are defined within the
AHB package.
import AHB :: * ;
Data Structures
Inside the transactor modules, the AHB data is organized into the following data structures: the
address and control information is defined by AHBCntrl, the write data is defined by AHBData.
These two structures are bundled into an AHBRequest. Finally, the response data is defined by
AHBResponse.
AHBRequest
Member DataType Valid Values
cntrl AHBCntrl#(‘TLM_TYPES) see above
data AHBData Bit#(data_size)
typedef struct {
AHBCntrl#(‘TLM_TYPES) cntrl;
AHBData#(‘TLM_TYPES) data;
} AHBRequest#(‘TLM_TYPE_PRMS) ‘dv;
AHBCntrl The control fields in an AHBRequest are described by the AHBCntrl structure, the
components of which are defined in the following table.
AHBCntrl
Member DataType Valid Values
command AHBWrite READ, WRITE
size AHBSize BITS8, BITS16, BITS32, BITS64, BITS128,
BITS256, BITS512, BITS1024
burst AHBBurst SINGLE, INCR, WRAP4, INCR4, WRAP8, INCR8,
WRAP16, INCR16
transfer AHBTransfer IDLE, BUSY, NONSEQ, SEQ
prot AHBProt Bit#(4)
addr AHBAddr#(‘TLM_TYPES) Bit#(addr_size)
typedef struct {
AHBWrite command;
AHBSize size;
AHBBurst burst;
AHBTransfer transfer;
AHBProt prot;
AHBAddr#(‘TLM_TYPES) addr;
} AHBCntrl#(‘TLM_TYPE_PRMS) ‘dv;
AHBResponse An AHBResponse consists of a status fields and data (when responding to a read
request). The components of the structure are described in the following table.
AHBResponse
Member DataType Valid Values
status AHBResp OKAY, ERROR, RETRY, SPLIT
data AHBData Bit#(data_size)
typedef struct {
AHBResp status;
AHBData#(‘TLM_TYPES) data;
} AHBResponse#(‘TLM_TYPE_PRMS) ‘dv;
Bus Interfaces
The two basic bus interfaces included in the AHB library are the AHBMaster interface and the
AHBSlave interface.
AHBMaster The AHBMaster interface issues AHB requests and receives AHB responses.
interface AHBMaster#(‘TLM_TYPE_PRMS);
// Outputs
method AHBAddr#(‘TLM_TYPES) hADDR;
method AHBData#(‘TLM_TYPES) hWDATA;
method AHBWrite hWRITE;
method AHBTransfer hTRANS;
method AHBBurst hBURST;
method AHBSize hSIZE;
method AHBProt hPROT;
// Inputs
method Action hRDATA(AHBData#(‘TLM_TYPES) data);
method Action hREADY(Bool value);
method Action hRESP (AHBResp response);
endinterface
AHBSlave The AHBSlave interface receives AHB requests and returns AHB responses.
interface AHBSlave#(‘TLM_TYPE_PRMS);
// Inputs
method Action hADDR (AHBAddr#(‘TLM_TYPES) addr);
method Action hWDATA(AHBData#(‘TLM_TYPES) data);
method Action hWRITE(AHBWrite value);
method Action hTRANS(AHBTransfer value);
method Action hBURST(AHBBurst value);
method Action hSIZE (AHBSize value);
method Action hPROT (AHBProt value);
// Outputs
method AHBData#(‘TLM_TYPES) hRDATA;
method Bool hREADY;
method AHBResp hRESP;
endinterface
Fabric Interfaces
When used in the context of a bus or switch, AHB Master and Slave modules must communicate
with the arbiter and with address decoding logic. Two additional interfaces are provided to support
this communication.
AHBMasterArbiter The AHBMasterArbiter interface connects the master module with the bus
arbiter. Through this interface, the master can request control of the bus and determine when
control has been granted.
interface AHBMasterArbiter;
method Bool hBUSREQ();
method Bool hLOCK ();
method Action hGRANT (Bool value);
endinterface
interface AHBSlaveSelector#(‘TLM_TYPE_PRMS);
method Bool addrMatch(AHBAddr#(‘TLM_TYPES) value);
method Action select (Bool value);
endinterface
interface AHBFabricMaster#(‘TLM_TYPE_PRMS);
interface AHBMaster#(‘TLM_TYPES) bus;
interface AHBMasterArbiter arbiter;
endinterface
interface AHBFabricSlave#(‘TLM_TYPE_PRMS);
interface AHBSlave#(‘TLM_TYPES) bus;
interface AHBSlaveSelector#(‘TLM_TYPES) selector;
endinterface
Transactor Interfaces
An AHB transactor module provides AHB and TLM interfaces to implement a translation between
a stream of TLM operations and the AHB bus protocol. Each transactor has two subinterfaces:
a subinterface for the connection with the AHB bus and a subinterface to send and receive TLM
objects.
The AHB library package includes two transactor interfaces; The AHBMasterXActor interface for
the master and AHBSlaveXActor interface for the slave. The AHB protocol doesn’t separate read
and write transactions, so there is a single transactor implementation for masters and a single
implementation for slaves.
interface AHBMasterXActorIFC#(‘TLM_TYPE_PRMS)
interface TLMRecvIFC#(‘TLM_TYPES) tlm;
interface AHBFabricMaster#(‘TLM_TYPES) fabric;
endinterface
interface AHBSlaveXActorIFC#(‘TLM_TYPE_PRMS);
interface TLMSendIFC#(‘TLM_TYPES) tlm;
interface AHBFabricSlave#(‘TLM_TYPES) fabric;
endinterface
Modules
The following constructors are used to create AHB transactor modules. Versions with associated syn-
thesis boundaries are also available. These versions are called mkAHBMasterStd, and mkAHBSlaveStd.
The specific TLM parameter values for these synthesized versions are as specified by the preprocessor
macro TLM_STD_TYPES (see section C.10.1).
module mkAHBBus#(Vector#(master_count,
AHBFabricMaster#(‘TLM_TYPES)) masters,
Vector#(slave_count,
AHBFabricSlave#(‘TLM_TYPES)) slaves) (Empty);
The following module is used to add probe signals for each of the AHB bus signals. This facilitates
debugging and waveform viewing of the created bus fabric.
mkAHBMonitor Adds a probe module for each of the AHB bus signals. The include_pc value
indicates whether or not the monitor module should include an instantiation of an
AHB protocol checker module (available from ARM). If the protocol checker is not
available, the value of include_pc should be set to False.
376
Bluespec SystemVerilog Reference Guide
Valid
tagged union member ofMaybe type, 51
Valid (type constructor), 158
valueOf (pseudo-function of size types), 22,
164
valueof (pseudo-function of size types), 164
variable assignment, 52
variable declaration, 52
variable initialization, 52
variables, 52
Vector, 199
vectorToArray (Vector function), 222
void (type, in tagged unions), 49
Xilinx
BRAM (package), 196
BRAMFIFO (package), 197
GrayCounter (package), 274
387
Reference Guide Bluespec SystemVerilog
readVReg, 210
replicate, 200
replicateM, 220
reverse, 206
rotate, 205
rotateBitsBy, 210
rotateBy, 206
rotateR, 206
scanl, 217
scanr, 216
select, 202
shiftInAt0, 206
shiftInAtN, 206
sscanl, 217
sscanr, 217
tail, 203
take, 203
takeAt, 204
toList, 222
transpose, 206
transposeLN, 207
unzip, 211
update, 202
vectorToArray, 222
writeVReg, 210
zip, 210
zip3, 210
zip4, 211
zipAny, 211
zipWith, 212
zipWith3, 213
zipWith3M, 219
zipWithAny, 212
zipWithAny3, 213
zipWithM, 219
AHB, 369
AHBBus, 369
AHBDefines, 369
AHBMaster, 369
AHBMonitor, 369
AHBPC, 369
AHBSlave, 369
Arbiter, 273
AXI, 359
AxiDefines, 359
AxiMaster, 359
AxiMonitor, 359
AxiPC, 359
AxiRam, 359
AxiRdBus, 359
AxiSlave, 359
AxiWrBus, 359
BRAMFIFO, 197
CBus, 345
FShow, 283
Gray, 276
GrayCounter, 274
Randomizable, 271
TLM, 352
TLMCBusAdapter, 352
TLMDefines, 352
TLMRam, 352
TLMReadWriteRam, 352
TLMReduce, 352
TLMUtils, 352
392