• Top
    • Documentation
    • Books
    • Recursion-and-induction
    • Boolean-reasoning
    • Projects
    • Debugging
    • Std
    • Proof-automation
    • Macro-libraries
    • ACL2
    • Interfacing-tools
    • Hardware-verification
      • Gl
      • Esim
      • Vl2014
      • Sv
        • Svex-stvs
        • Svex-fixpoint-decomposition-methodology
          • Def-svtv-idealized-thm
          • Def-svtv-ideal
        • Sv-versus-esim
        • Svex-decomp
        • Svex-compose-dfs
        • Moddb
        • Svex-compilation
        • Svmods
        • Svstmt
        • Sv-tutorial
        • Expressions
        • Symbolic-test-vector
        • Vl-to-svex
      • Vwsim
      • Fgl
      • Vl
      • X86isa
      • Svl
      • Rtl
    • Software-verification
    • Testing-utilities
    • Math
  • Sv

Svex-fixpoint-decomposition-methodology

High-level description of decomposition methodology for svtvs.

In hardware verification, it is important to be able to break down proofs into pieces. An important technique for this purpose is to assume that certain internal signals have particular values at particular times, and prove something about their fanout logic. For symbolic simulation, the best performance for this method is obtained by replacing the fanin logic of these signals with free symbolic values, which we call "overriding" those signals. In order to compose together multiple proofs using this technique, it is important to be able to use a theorem proved about a design's behavior with certain signals overridden to prove a theorem about the design without (some of) those overrides.

As a high-level example, suppose we prove that the partial product summation of a multiplier is correct by overriding the partial product signals. Note: the following uses a made-up syntax, for illustration purposes only. In our real framework, run-design would be replaced by svtv-run, lookup would be replaced by svex-env-lookup, etc.

(defthm multiplier-pp-sum-correct-override
   (implies (unsigned-byte-p 128 partial-products-value)
            (let* ((run (run-design (multiplier)
                                    :inputs `((opcode . ,*multiply-opcode*))
                                    :overrides `((partial-product-signal . ,partial-products-value)))))
                (equal (lookup 'multiplier-output run)
                       (pp-sum-spec partial-products-value)))))
}

Additionally, suppose that we've proved that the same multiplier computes a correct Booth encoding as its partial products signal:

(defthm multiplier-booth-encoding-correct
   (implies (and (unsigned-byte-p 16 a) (unsigned-byte-p 16 b))
            (let* ((run (run-design (multiplier)
                                    :inputs `((opcode . ,*multiply-opcode*)
                                              (a-input . ,a)
                                              (b-input . ,b)))))
              (equal (lookup 'partial-product-signal run)
                     (booth-enc-spec a b)))))

We'd like to be able to compose these two theorems and prove the multiplier correct from end to end. But the design here has been run with two different settings for the inputs and overrides, and these can't be reconciled as-is. At a high level, our decomposition methodology works by allowing theorems such as these -- which are convenient to prove using symbolic simulation -- to be generalized into theorems that can be straightforwardly composed. Using the same made-up syntax:

(defthm multiplier-pp-sum-correct-gen
  (let* ((opcode (lookup 'opcode input-env))
         (run (run-design (multiplier) :inputs input-env))
         (partial-products-value (lookup 'partial-product-signal run))
         (result (lookup 'multiplier-output run)))
     (implies (and (equal opcode *multiply-opcode*)
                   (unsigned-byte-p 128 partial-products-value))
              (equal result (pp-sum-spec partial-products-value)))))

Notice this has been changed in two ways:

  • First, the :inputs argument, which used to be an explicit alist, is now a free variable with a hypothesis assuming that the inputs that were given before (opcode bound to *multiply-opcode*) are correctly bound there.
  • Second, the overrides are replaced by references to observations of internal signals. That is, we're no longer replacing the fanin cone of partial-product-signal with a variable, as we wanted for symbolic simulation; rather, we're looking at whatever the value of that signal happens to be, and proving that the multiplier output satisfies its spec based on that signal.

The booth encoding theorem can be changed similarly, although since it had no overrides only the first change applies here:

(defthm multiplier-booth-encoding-correct-gen
  (let* ((opcode (lookup 'opcode input-env))
         (a      (lookup 'a-input input-env))
         (b      (lookup 'b-input input-env))
         (run (run-design (multiplier) :inputs input-env))
         (partial-products-value (lookup 'partial-product-signal run)))
     (implies (and (equal opcode *multiply-opcode*)
                   (unsigned-byte-p 16 a)
                   (unsigned-byte-p 16 b))
              (equal partial-products-value (booth-enc-spec a b)))))

Notice that these theorems now can be composed! In fact, given a lemma about the bit width of booth-enc-spec, we can obtain:

(defthm multiplier-computes-pp-sum-of-booth-enc
  (let* ((opcode (lookup 'opcode input-env))
         (a      (lookup 'a-input input-env))
         (b      (lookup 'b-input input-env))
         (run (run-design (multiplier) :inputs input-env))
         (result (lookup 'multiplier-output run)))
     (implies (and (equal opcode *multiply-opcode*)
                   (unsigned-byte-p 16 a)
                   (unsigned-byte-p 16 b))
              (equal result (pp-sum-spec (booth-enc-spec a b))))))

This methodology's main user interface is def-svtv-idealized-thm. See that topic for usage. In the rest of this topic we'll discuss how this works and the solutions to certain complications.

To produce the generalized forms of the theorems above, we want a particular property of the design: that if we override a signal with the value that it would have had anyway, then the values of all signals are preserved. Stated in terms of our imaginary run-design function above, for the particular case of the partial-products-value of our multiplier design:

(defthm multiplier-partial-product-overrides-correct
  (let* ((base-run (run-design (multiplier) :inputs input-env))
         (partial-products-value (lookup 'partial-product-signal base-run))
         (override-run (run-design (multiplier)
                                   :inputs input-env
                                   :overrides `((partial-product-signal . ,partial-products-value)))))
     (all-lookups-equivalent override-run base-run)))

This theorem, along with a couple of other simpler facts that allow the generalization of the input environment would be sufficient to let us generalize multiplier-pp-sum-correct-override to multiplier-pp-sum-correct-gen.

Generally we also want to allow for multiple override signals in different combinations. potentially overridden signals:

(defthm multiplier-partial-product-overrides-correct
  (let* ((base-run (run-design (multiplier) :inputs input-env))
         (override-run (run-design (multiplier)
                                   :inputs input-env
                                   :overrides override-env)))
     (implies (override-signals-consistent-with-base-run override-env base-run)
              (all-lookups-equivalent override-run base-run))))

Here we imagine override-signals-consistent-with-base-run would check that all internal signals of the design that are bound in override-env are bound to the same value in base-run.

This overrides-correct condition seems intuitively obvious and we would like to prove it for all designs. However, it is actually a fairly deep property of the design object, and as we'll see, depending how we compose the design together it may or may not be true.

The problematic cases have to do with designs where there are 0-delay combinational loops. These can occur due to latch-based logic or clock gating logic, often enough that it isn't workable to just disallow them.

The correct way to deal with 0-delay combinational loops is to compute a fixpoint. That is, for a given setting of the circuit inputs and stateholding elements, begin at a state setting these values for the inputs/states and all internal signals set to X. Apply the internal signals' update functions to obtain a new state. Repeat this until we reach a fixpoint. It is a theorem that if all signals have finite bit widths and the update functions respect X-monotonicity, then a fixpoint is reached by the time we have repeated this n times where n is the total number of bits in all internal signals. A more in-depth exploration of this algorithm is written in least-fixpoint.

Because of the number of repetitions needed, it isn't always practical to actually compute the fixpoint. Instead we use an approximate composition method that is efficient, practially useful, and conservative with respect to the fixpoint: that is, if a signal's value in this approximate composition is non-X, then its fixpoint value must be the same.

The approximate composition does not always satisfy the overrides-correct condition above. For example, if we override a signal that is part of a 0-delay combinational loop and its fanin logic therefore appears more than once in different forms (composed with itself a different number of times), then overriding all occurrences of that fanin logic with the signal's final value isn't guaranteed to preserve the value of other signals.

However, the overrides-correct condition is true of the fixpoint. One version of this is proved in "centaur/sv/svex/fixpoint-override" as svex-alist-eval-override-fixpoint-equivalent-to-reference-fixpoint; a somewhat more general version is proved in svex-alist-eval-fixpoint-override-impl-equiv-spec.

This leads us to the following methodology for composing hardware proofs with overrides:

  • Compute a symbolic representation approx of the desired run of the design based on the approximate composition -- in particular, using defsvtv$.
  • Define a non-executable function ideal that logically computes the analogous run with the fixpoint composition. This can be done using def-svtv-ideal.
  • To prove composition-friendly facts about ideal, first prove a lemma with overrides about approx (such as multiplier-pp-sum-correct-override and use the conservativity of approx with respect to ideal along with the overrides-correct property of ideal to prove the composition-friendly fact (such as multiplier-pp-sum-correct-gen) about ideal. This is automated by def-svtv-idealized-thm.

Subtopics

Def-svtv-idealized-thm
Prove a theorem about an idealized SVTV via a symbolic simulation lemma about the SVTV itself.
Def-svtv-ideal
Define a non-executable, fixpoint-based analogue of a symbolic-test-vector.