Initial work on representing HDL and formal verification in Rocq. #56
2 changed files with 175 additions and 0 deletions
171
rocq-demo/rocq_hdl.v
Normal file
171
rocq-demo/rocq_hdl.v
Normal file
|
|
@ -0,0 +1,171 @@
|
||||||
|
(* SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
See Notices.txt for copyright information *)
|
||||||
|
|
||||||
|
(* This file demonstrates converting simple HDL designs into Rocq, as well
|
||||||
|
as proofs by induction that parallels those of formal verification in
|
||||||
|
Symbiyosys with the "smtbmc" engine in the "prove" mode. *)
|
||||||
|
|
||||||
|
(* Models a simple register, feeding back to itself. We prove that,
|
||||||
|
when initialized to zero, it stays always at zero. *)
|
||||||
|
Module simple_register.
|
||||||
|
|
||||||
|
(* State record. Contains only one boolean variable, which we call "a" *)
|
||||||
|
Record S := { a: bool }.
|
||||||
|
(* The above automatically defines an accessor function "a: S->bool", so
|
||||||
|
we can write "a s" to get the value of "a" in state "s". *)
|
||||||
|
|
||||||
|
(* Defines an "initial" predicate, so "initial s" means
|
||||||
|
"s is an initial state". In our case, the value of "a"
|
||||||
|
in the initial state is zero (false) *)
|
||||||
|
Definition initial (s: S) :=
|
||||||
|
a s = false.
|
||||||
|
(* As an example, we construct a state where "a" is indeed false,
|
||||||
|
and prove it is an initial state. The syntax for instantiating
|
||||||
|
such a record is "{| a := false |}". *)
|
||||||
|
Example test_initial: initial {| a := false |}.
|
||||||
|
Proof. reflexivity. Qed.
|
||||||
|
|
||||||
|
(* Defines an "assert" predicate, so "assert s" means "assertions
|
||||||
|
hold in state s" or equivalently "state s is valid". In our case,
|
||||||
|
valid states correspond to those where "a" is false. *)
|
||||||
|
Definition assert (s: S) :=
|
||||||
|
a s = false.
|
||||||
|
|
||||||
|
(* The following proof by induction follows the article "Temporal
|
||||||
|
Induction by Incremental SAT Solving",
|
||||||
|
https://doi.org/10.1016/S1571-0661(05)82542-3 *)
|
||||||
|
(* Proves the base case, with one step: the initial state is a
|
||||||
|
valid state. *)
|
||||||
|
Theorem base1: forall s0, initial s0 -> assert s0.
|
||||||
|
Proof. trivial. Qed.
|
||||||
|
|
||||||
|
(* Defines a "transition" predicate, so "t s1 s2" means:
|
||||||
|
"s2 can be reached in one step from s1". In our case,
|
||||||
|
the value of "a" in s2 is the same as in s1. *)
|
||||||
|
Definition t (s1 s2 : S) :=
|
||||||
|
a s2 = a s1.
|
||||||
|
|
||||||
|
(* Induction principle: for all transitions from a valid state,
|
||||||
|
we reach a valid state. *)
|
||||||
|
Theorem induct1:
|
||||||
|
forall s0 s1, assert s0 -> t s0 s1 -> assert s1.
|
||||||
|
Proof.
|
||||||
|
unfold t, assert.
|
||||||
|
intros s0 s1 A T.
|
||||||
|
transitivity (a s0).
|
||||||
|
apply T. apply A.
|
||||||
|
Qed.
|
||||||
|
(* Here we have proven the base case and the inductive case, so we
|
||||||
|
declare success. However, we may want to formalize it from first
|
||||||
|
principles, something like "All reacheable states from an initial
|
||||||
|
state are valid", and use the above theorems to prove it (TODO). *)
|
||||||
|
|
||||||
|
(* Example: all states reacheable in one step from the initial state
|
||||||
|
are valid. *)
|
||||||
|
Example one_step_valid:
|
||||||
|
forall s0 s1, initial s0 -> t s0 s1 -> assert s1.
|
||||||
|
Proof.
|
||||||
|
unfold initial, t, assert.
|
||||||
|
intros s0 s1 I T.
|
||||||
|
transitivity (a s0).
|
||||||
|
apply T. apply I.
|
||||||
|
Qed.
|
||||||
|
(* Same as above, but we prove using only the induction theorems. *)
|
||||||
|
Example one_step_valid':
|
||||||
|
forall s0 s1, initial s0 -> t s0 s1 -> assert s1.
|
||||||
|
Proof.
|
||||||
|
apply induct1.
|
||||||
|
Qed.
|
||||||
|
End simple_register.
|
||||||
|
|
||||||
|
(* The same circuit as above, but with an additional output register.
|
||||||
|
The output is asserted to be always false, but no such restriction
|
||||||
|
is made on the inner register, a "hidden" state. It creates a
|
||||||
|
difficulty for induction: there are unreacheable valid states that
|
||||||
|
leads to invalid states. A two step induction solves the problem. *)
|
||||||
|
Module hidden_state.
|
||||||
|
(* Demonstrate state with several (two) variables. *)
|
||||||
|
Record S :=
|
||||||
|
{ a: bool
|
||||||
|
; b: bool
|
||||||
|
}.
|
||||||
|
(* Both registers start at zero (false). *)
|
||||||
|
Definition initial (s: S) :=
|
||||||
|
a s = false /\ b s = false.
|
||||||
|
(* Just b is constrained to be zero (false) *)
|
||||||
|
Definition assert (s: S) :=
|
||||||
|
b s = false.
|
||||||
|
(* "a" keeps its previous value, "b" copies "a". *)
|
||||||
|
Definition t (s1 s2 : S) :=
|
||||||
|
a s2 = a s1 /\ b s2 = a s1.
|
||||||
|
(* Base case in one step. *)
|
||||||
|
Theorem base1:
|
||||||
|
forall s0,
|
||||||
|
initial s0 -> assert s0.
|
||||||
|
Proof.
|
||||||
|
unfold initial, assert.
|
||||||
|
intros s0 [_ H].
|
||||||
|
apply H.
|
||||||
|
Qed.
|
||||||
|
(* Try proving the inductive case in one step. *)
|
||||||
|
Theorem induct1:
|
||||||
|
forall s0 s1,
|
||||||
|
assert s0 -> t s0 s1 -> assert s1.
|
||||||
|
Proof.
|
||||||
|
unfold t, assert.
|
||||||
|
intros s0 s1 A T.
|
||||||
|
destruct T as [_ Tb].
|
||||||
|
transitivity (a s0). apply Tb.
|
||||||
|
Abort.
|
||||||
|
(* Ops, we reach a dead-end. Maybe it's not provable after all?
|
||||||
|
Try proving its negation by finding a counter-example. *)
|
||||||
|
Theorem not_induct1:
|
||||||
|
not (forall s0 s1,
|
||||||
|
assert s0 -> t s0 s1 -> assert s1).
|
||||||
|
Proof.
|
||||||
|
unfold not, assert, t.
|
||||||
|
intros H.
|
||||||
|
specialize H with (s0 := {|a := true; b := false|}).
|
||||||
|
simpl in H.
|
||||||
|
specialize H with (s1 := {|a := true; b := true|}).
|
||||||
|
simpl in H.
|
||||||
|
discriminate H. reflexivity. auto.
|
||||||
|
Qed.
|
||||||
|
(* Indeed, a valid state where "a" is one (true) leads to
|
||||||
|
an invalid state. Try one more step. *)
|
||||||
|
Theorem base2:
|
||||||
|
forall s0 s1,
|
||||||
|
initial s0 -> assert s0 -> t s0 s1 -> assert s1.
|
||||||
|
Proof.
|
||||||
|
intros s0 s1 [Ia _] _ [_ Tb].
|
||||||
|
unfold assert.
|
||||||
|
transitivity (a s0). apply Tb.
|
||||||
|
apply Ia.
|
||||||
|
Qed.
|
||||||
|
Theorem induct2:
|
||||||
|
forall s0 s1 s2,
|
||||||
|
assert s0 -> t s0 s1 -> assert s1 -> t s1 s2 -> assert s2.
|
||||||
|
Proof.
|
||||||
|
intros s0 s1 s2 A0 [T0a T0b] A1 [T1a T1b].
|
||||||
|
unfold assert in *.
|
||||||
|
transitivity (a s1). apply T1b.
|
||||||
|
transitivity (a s0). apply T0a.
|
||||||
|
transitivity (b s1). symmetry. apply T0b.
|
||||||
|
apply A1.
|
||||||
|
Qed.
|
||||||
|
(* Success! Two steps indeed work. *)
|
||||||
|
|
||||||
|
(* Example: all states reacheable in two steps are valid *)
|
||||||
|
Example two_steps_valid:
|
||||||
|
forall s0 s1 s2,
|
||||||
|
initial s0 -> t s0 s1 -> t s1 s2 -> assert s2.
|
||||||
|
Proof.
|
||||||
|
intros s0 s1 s2.
|
||||||
|
intros Hi T01.
|
||||||
|
apply induct2 with (s0 := s0).
|
||||||
|
- apply base1. apply Hi.
|
||||||
|
- apply T01.
|
||||||
|
- apply base2 with (s0 := s0).
|
||||||
|
apply Hi. apply base1. apply Hi. apply T01.
|
||||||
|
Qed.
|
||||||
|
End hidden_state.
|
||||||
|
|
@ -32,6 +32,7 @@ POUND_HEADER=('^"# SPDX-License-Identifier: LGPL-3.0-or-later"$' '^"# See Notice
|
||||||
SLASH_HEADER=('^"// SPDX-License-Identifier: LGPL-3.0-or-later"$' '^"// See Notices.txt for copyright information"$')
|
SLASH_HEADER=('^"// SPDX-License-Identifier: LGPL-3.0-or-later"$' '^"// See Notices.txt for copyright information"$')
|
||||||
MD_HEADER=('^"<!--"$' '^"SPDX-License-Identifier: LGPL-3.0-or-later"$' '^"See Notices.txt for copyright information"$')
|
MD_HEADER=('^"<!--"$' '^"SPDX-License-Identifier: LGPL-3.0-or-later"$' '^"See Notices.txt for copyright information"$')
|
||||||
JSON_HEADER=('^"{"$' '^" \"license_header\": ["$' '^" \"SPDX-License-Identifier: LGPL-3.0-or-later\","$' '^" \"See Notices.txt for copyright information\""')
|
JSON_HEADER=('^"{"$' '^" \"license_header\": ["$' '^" \"SPDX-License-Identifier: LGPL-3.0-or-later\","$' '^" \"See Notices.txt for copyright information\""')
|
||||||
|
ROCQ_HEADER=('^"(* SPDX-License-Identifier: LGPL-3.0-or-later"$' '^" See Notices.txt for copyright information *)"$')
|
||||||
|
|
||||||
function main()
|
function main()
|
||||||
{
|
{
|
||||||
|
|
@ -64,6 +65,9 @@ function main()
|
||||||
*.json)
|
*.json)
|
||||||
check_file "$file" "${JSON_HEADER[@]}"
|
check_file "$file" "${JSON_HEADER[@]}"
|
||||||
;;
|
;;
|
||||||
|
*.v)
|
||||||
|
check_file "$file" "${ROCQ_HEADER[@]}"
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
fail_file "$file" 0 "unimplemented file kind -- you need to add it to $0"
|
fail_file "$file" 0 "unimplemented file kind -- you need to add it to $0"
|
||||||
;;
|
;;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue