Keywords

These keywords were added by machine and not by the authors. This process is experimental and the keywords may be updated as the learning algorithm improves.

figure a

Our format Btor2 generalizes and extends the Btor [5] format, which can be seen as a word-level generalization of the initial version of the bit-level format Aiger [2]. Btor is a format for quantifier-free formulas over bit-vectors and arrays with Smt-Lib [1] semantics but also provides sequential extensions for specifying word-level model checking problems with registers and memories. In contrast to Btor, which is tailored towards bit-vectors and one-dimensional bit-vector arrays, Btor2 has explicit sort declarations. It further allows to explicitly initialize registers and memories (instead of implicit initialization in Btor) and extends the set of sequential features with witnesses, invariant and fairness constraints, and liveness properties. All of these are word-level variants lifted from corresponding features in the latest Aiger format [4], the input format of the hardware model checking competition (HWMCC) [3, 6] since 2011. We provide an open source Btor2 tool suite, which includes a generic parser, random simulator and witness checker. We further implemented a reference bounded model checker BtorMC on top of our SMT solver Boolector. We consider Btor2 as an ideal candidate to establish a word-level hardware model checking competition.

1 Format Description

The syntax of Btor2 is shown in Fig. 1. The sort keyword is used to define arbitrary bit-vector and array sorts. This not only allows to specify multi-dimensional arrays but can be extended to support (uninterpreted) functions, floating points and other sorts. As a consequence, Btor2 is not backwards compatible with Btor. For clarity, in Fig. 1 we distinguish between node (line) identifiers and sort identifiers , and do not allow an identifier to occur in both sets. Introducing sorts renders type specific keywords such as var, array and acond from Btor obsolete. Instead, Btor2 uses the keyword input to declare bit-vector and array variables of a given sort. Bit-vector constants are created as in Btor with the keywords const[dh], one, ones and zero.

Bit-vector and array operators as supported by Btor2 and their respective sorts are shown in Table 1. We use \(\mathcal {B}^{n}\) for a bit-vector sort of width n, and \(\mathcal {I}\) and \(\mathcal {E}\) for the index and element sorts of an array sort \(\mathcal {A}^{\mathcal {I}\rightarrow \mathcal {E}}\). Note that some bit-vector operators can be interpreted as signed or unsigned. In signed context, as in Smt-Lib, bit-vectors are represented in two’s complement.

Fig. 1.
figure 1

Syntax of Btor2. Non-terminals and are indexed and non-indexed operators as defined in Table 1 (sequential part in ). (Color figure online)

2 Sequential Extension

As shown in Fig. 1, the sequential extension of Btor2 introduces a state keyword, which allows to specify registers and memories. In contrast to Btor, where registers are implicitly zero-initialized and memories are uninitialized, Btor2 provides a keyword init to explicitly define initialization functions for states. This enables us to also model partial initialization. For example, initializing a memory with a bit-vector constant zero, zero-initializes the whole memory, whereas partially initializing a register can be achieved by applying a bit-mask to an uninitialized register.

Table 1. Operators supported by Btor2, where \(\mathcal {B}^{n}\) represents a bit-vector sort of size n and \(\mathcal {A}^{\mathcal {I}\rightarrow \mathcal {E}}\) represents an array sort with index sort \(\mathcal {I}\) and element sort \(\mathcal {E}\).

Transition functions for both registers and memories are defined with the next keyword. It takes the current and next states as arguments. A state variable without associated next function is treated as a primary input, i.e., it has the same behaviour as inputs defined via keyword input. Note that Btor provides a next keyword for registers and an anext keyword for memories. Using sorts in Btor2 avoids such sort specific keyword variants.

As in the latest version of Aiger [4], Btor2 supports bad state properties, which are essentially negations of safety properties. Multiple properties can be specified by simply adding multiple bad state properties. Invariant constraints can be introduced via the constraint keyword and are assumed to hold globally. A witness for a bad state property is an initialized finite path, which reaches (actually, contains) a bad state and satisfies all invariant constraints.

Again as in Aiger [4], keywords fair and justice allow to specify (global) fairness constraints and (negations of) liveness properties. Each justice property consists of a set of Büchi conditions. A witness for a justice property is an infinite initialized path on which all Büchi conditions and all global fairness constraints are satisfied infinitely often. In addition, all global invariant constraints have to hold. The justice keyword takes a number (the number of Büchi conditions) and an arbitrary number of nodes (the Büchi conditions) as arguments.

3 Witness Format

The syntax of the Btor2 witness format is shown in Fig. 2. A Btor2 witness consists of a sequence of valid input assignments grouped by (time) frames. It starts with ‘sat’ followed by a list of properties that are satisfied by the witness. A property is identified by a prefix ‘b’ (for bad) and ‘j’ (for justice) followed by a number i, which ranges over the number of defined bad and justice properties starting from 0. For example, ‘b0 j0’ refers to the first bad and first justice property in the order as they occur in the Btor2 input. The list of properties is followed by a sequence of \(k+1\) frames at time \(t \in \{0,\ldots , k\}\). A frame is divided into a state and input part. The state part starts with and is mandatory for the first frame (\(t=0\)) and optional for later frames (\(t>0\)). It contains state assignments at time t. The input part starts with and consists of input assignments of the transition from time t to \(t+1\). If states are uninitialized (no init), their initial assignment is required to be specified in frame ‘#0’. The state part is usually omitted for \(t>0\) since state assignments can be computed from states and inputs at time \(t-1\). While don’t care inputs can be omitted, our witness checker assumes that they are zero. Input and state assignments use the same numbering scheme as properties, i.e., states and inputs are numbered separately in the order they are defined, starting from 0. For example, 0 in frame (or ) refers to the first state (or input) as defined in the Btor2 input. For justice properties we assume the witness to be lasso shaped, i.e., the next state, which can be computed from the last state and inputs at time k, is identical to one of the previous states at time \(t = 0\ldots k\). As in Aiger, a Btor2 witness is terminated with ‘.’ on a separate line.

Fig. 2.
figure 2

Btor2 model and witness format syntax (sequential part in ). (Color figure online)

Figure 3 illustrates a simple C program (left), the corresponding Btor2 model with the negation of the assertion as a bad property (center), and a Btor2 witness for the violated property (right). The Btor2 model defines one bad property (a == 3 && b == 3), which is satisfied in frame 6. The corresponding witness identifies this property as bad property ‘b0’ (first bad property defined in the model). All states are initialized, hence ‘#0’ is empty, and ‘@0’ to ‘@6’ indicate the assignments of input 0 (turn, the first input defined in the model) in frames 0 to 6, e.g., turn \(=1\) at \(t=0\), turn \(=0\) at \(t=1\) and so on. In frame 6, both states a and b reach value 3, and therefore property ‘b0’ is satisfied.

Fig. 3.
figure 3

Example C program with corresponding Btor2 model and witness.

4 Tools

We provide a generic stand-alone parser for Btor2, which features basic type checking and consists of approx. 1,500 lines of C code. We implemented a reference bounded model checker BtorMC, which currently supports checking safety (aka. bad state) properties for models with registers and memories and produces witnesses for satisfiable properties. Unrolling the model is performed by symbolic simulation, i.e., symbolic substitution of current state expressions into next state functions, and incremental SMT solving. We also implemented a simulator for randomly simulating Btor2 models. It further supports checking Btor2 witnesses. The model checker is tightly integrated into our SMT solver Boolector [18], an award-winning SMT solver for the theory of fixed-size bit-vectors with arrays and uninterpreted functions. Since the last major version [18], we extended Boolector with several new features. Most notably, Boolector 3.0 now comes with support for quantified bit-vectors [24] and two different local search strategies for quantifier-free bit-vector formulas that don’t rely on but can be combined with bit-blasting [19, 21, 22]. It further provides support for Btor2. In contrast to previous versions of Boolector, Boolector 3.0 and all Btor2 tools are released under the MIT open source license and the source code is hosted on GitHubFootnote 1.

5 Experiments

We collected ten real-world (System)Verilog designs with safety properties from various open source projects [11, 26,27,28]. The majority of these designs include memories. We used the open synthesis suite Yosys [29] to synthesize these designs into Btor2 and Smt-Lib. For Btor2, Yosys directly generates the models from a circuit description. For Smt-Lib, since the language does not support describing model checking problems, we used Yosys in combination with Yosys-SMTBMC to produce unrolled (incremental) problems.

We compared BtorMC against the most recent versions of Boolector (3.0) and Yices [10] (2.5.4), the two best solvers of the QF_ABV division of the SMT competition 2017. The Btor2 models serve as input for BtorMC, and the incremental Smt-Lib benchmarks serve as input for Boolector and Yices. All benchmarks, synthesis scripts, generated files, log files and the source code of our tools for this evaluation are available at http://fmv.jku.at/cav18-btor2.

The results in Table 2 show that our flow using Btor2 as intermediate format is competetive with simple unrolling. Note that our model checker BtorMC issues incremental calls to Boolector. However, in Boolector, sophisticated word-level rewriting is currently disabled in incremental mode. We expect a major performance boost by fully supporting incremental word-level preprocessing.

Table 2. BtorMC/Btor2 vs. unrolled Smt-Lib with a time limit of 3600 s, where k is the bound and #bad is the number of bad properties.

6 Conclusion

We propose Btor2, a new word-level model-checking and witness format. For this format we provide a generic parser implementation, a simulator that also checks witnesses, and a reference bounded model checker BtorMC, which is tightly integrated with our SMT solver Boolector. These open source tools are evaluated on new real-world benchmarks, which we synthesized from open source hardware (System) Verilog models into Btor2 and Smt-Lib with Yosys. The tool Verilog2SMV [14] translates Verilog into model-checking problems in several formats, including nuXmv [7] and Btor. However, its translation to Btor is incomplete and development discontinued.

We plan to provide a translator from Btor2 into SALLY [25], and VMT [8], which are both extensions of Smt-Lib to model symbolic transition systems. It might also be interesting to translate incremental Smt-Lib benchmarks and horn clause models (as handled by, e.g., \(\mu Z\) [13]) into Btor2 and vice versa. We hope other compilers and model checkers such as SAL [9], EBMC [15] and ABC [12, 16] will provide support to produce and read Btor2 models. We want to extend the format to other logics, in particular to support lambdas as in [23]. There is also a need for fuzzing [20] and delta-debugging tools [17].

Last but not least, we want to use this format to bootstrap a word-level model checking competition, which of course needs more benchmarks.