diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Compiler.v | 100 | ||||
-rw-r--r-- | src/VericertClflags.ml | 2 | ||||
-rw-r--r-- | src/common/IntegerExtra.v | 287 | ||||
-rw-r--r-- | src/extraction/Extraction.v | 17 | ||||
-rw-r--r-- | src/hls/Array.v (renamed from src/verilog/Array.v) | 0 | ||||
-rw-r--r-- | src/hls/AssocMap.v (renamed from src/verilog/AssocMap.v) | 0 | ||||
-rw-r--r-- | src/hls/HTL.v (renamed from src/verilog/HTL.v) | 0 | ||||
-rw-r--r-- | src/hls/HTLBlockgen.v | 655 | ||||
-rw-r--r-- | src/hls/HTLSchedulegen.v | 42 | ||||
-rw-r--r-- | src/hls/HTLgen.v (renamed from src/translation/HTLgen.v) | 8 | ||||
-rw-r--r-- | src/hls/HTLgenproof.v (renamed from src/translation/HTLgenproof.v) | 114 | ||||
-rw-r--r-- | src/hls/HTLgenspec.v (renamed from src/translation/HTLgenspec.v) | 0 | ||||
-rw-r--r-- | src/hls/Partition.ml | 125 | ||||
-rw-r--r-- | src/hls/PrintHTL.ml (renamed from src/verilog/PrintHTL.ml) | 0 | ||||
-rw-r--r-- | src/hls/PrintRTLBlock.ml | 119 | ||||
-rw-r--r-- | src/hls/PrintVerilog.ml (renamed from src/verilog/PrintVerilog.ml) | 0 | ||||
-rw-r--r-- | src/hls/PrintVerilog.mli (renamed from src/verilog/PrintVerilog.mli) | 0 | ||||
-rw-r--r-- | src/hls/RTLBlock.v | 170 | ||||
-rw-r--r-- | src/hls/RTLBlockgen.v | 32 | ||||
-rw-r--r-- | src/hls/RTLPar.v | 99 | ||||
-rw-r--r-- | src/hls/Schedule.ml | 610 | ||||
-rw-r--r-- | src/hls/Value.v (renamed from src/verilog/Value.v) | 0 | ||||
-rw-r--r-- | src/hls/ValueInt.v (renamed from src/verilog/ValueInt.v) | 0 | ||||
-rw-r--r-- | src/hls/ValueVal.v | 209 | ||||
-rw-r--r-- | src/hls/Verilog.v (renamed from src/verilog/Verilog.v) | 4 | ||||
-rw-r--r-- | src/hls/Veriloggen.v (renamed from src/translation/Veriloggen.v) | 0 | ||||
-rw-r--r-- | src/hls/Veriloggenproof.v (renamed from src/translation/Veriloggenproof.v) | 0 |
27 files changed, 2574 insertions, 19 deletions
diff --git a/src/Compiler.v b/src/Compiler.v index 6efd7a2..8eeea6b 100644 --- a/src/Compiler.v +++ b/src/Compiler.v @@ -51,12 +51,16 @@ From vericert Require Verilog Veriloggen Veriloggenproof - HTLgen. + HTLgen + RTLBlock + RTLBlockgen + HTLSchedulegen. From compcert Require Import Smallstep. Parameter print_RTL: Z -> RTL.program -> unit. Parameter print_HTL: HTL.program -> unit. +Parameter print_RTLBlock: RTLBlock.program -> unit. Definition print {A: Type} (printer: A -> unit) (prog: A) : A := let unused := printer prog in prog. @@ -80,13 +84,45 @@ Proof. intros. destruct x; simpl. rewrite print_identity. auto. auto. Qed. +Definition total_if {A: Type} + (flag: unit -> bool) (f: A -> A) (prog: A) : A := + if flag tt then f prog else prog. + +Definition partial_if {A: Type} + (flag: unit -> bool) (f: A -> res A) (prog: A) : res A := + if flag tt then f prog else OK prog. + +Definition time {A B: Type} (name: string) (f: A -> B) : A -> B := f. + +Definition transf_backend_opt (r : RTL.program) : res Verilog.program := + OK r + @@@ Inlining.transf_program + @@ print (print_RTL 1) + @@ Renumber.transf_program + @@ print (print_RTL 2) + @@ total_if Compopts.optim_constprop (time "Constant propagation" Constprop.transf_program) + @@ print (print_RTL 3) + @@ total_if Compopts.optim_constprop (time "Renumbering" Renumber.transf_program) + @@ print (print_RTL 4) + @@@ partial_if Compopts.optim_CSE (time "CSE" CSE.transf_program) + @@ print (print_RTL 5) + @@@ partial_if Compopts.optim_redundancy (time "Redundancy elimination" Deadcode.transf_program) + @@ print (print_RTL 6) + @@@ time "Unused globals" Unusedglob.transform_program + @@ print (print_RTL 7) + @@@ HTLgen.transl_program + @@ print print_HTL + @@ Veriloggen.transl_program. + Definition transf_backend (r : RTL.program) : res Verilog.program := OK r @@@ Inlining.transf_program - @@ print (print_RTL 1) + @@ print (print_RTL 1) + @@ Renumber.transf_program + @@ print (print_RTL 2) @@@ HTLgen.transl_program - @@ print print_HTL - @@ Veriloggen.transl_program. + @@ print print_HTL + @@ Veriloggen.transl_program. Definition transf_hls (p : Csyntax.program) : res Verilog.program := OK p @@ -96,9 +132,48 @@ Definition transf_hls (p : Csyntax.program) : res Verilog.program := @@@ Cminorgen.transl_program @@@ Selection.sel_program @@@ RTLgen.transl_program - @@ print (print_RTL 0) + @@ print (print_RTL 0) @@@ transf_backend. +Definition transf_hls_opt (p : Csyntax.program) : res Verilog.program := + OK p + @@@ SimplExpr.transl_program + @@@ SimplLocals.transf_program + @@@ Cshmgen.transl_program + @@@ Cminorgen.transl_program + @@@ Selection.sel_program + @@@ RTLgen.transl_program + @@ print (print_RTL 0) + @@@ transf_backend_opt. + +Definition transf_hls_temp (p : Csyntax.program) : res Verilog.program := + OK p + @@@ SimplExpr.transl_program + @@@ SimplLocals.transf_program + @@@ Cshmgen.transl_program + @@@ Cminorgen.transl_program + @@@ Selection.sel_program + @@@ RTLgen.transl_program + @@@ Inlining.transf_program + @@ print (print_RTL 1) + @@ Renumber.transf_program + @@ print (print_RTL 2) + @@ total_if Compopts.optim_constprop (time "Constant propagation" Constprop.transf_program) + @@ print (print_RTL 3) + @@ total_if Compopts.optim_constprop (time "Renumbering" Renumber.transf_program) + @@ print (print_RTL 4) + @@@ partial_if Compopts.optim_CSE (time "CSE" CSE.transf_program) + @@ print (print_RTL 5) + @@@ partial_if Compopts.optim_redundancy (time "Redundancy elimination" Deadcode.transf_program) + @@ print (print_RTL 6) + @@@ time "Unused globals" Unusedglob.transform_program + @@ print (print_RTL 7) + @@@ RTLBlockgen.transl_program + @@ print print_RTLBlock + @@@ HTLSchedulegen.transl_program + @@ print print_HTL + @@ Veriloggen.transl_program. + Local Open Scope linking_scope. Definition CompCert's_passes := @@ -109,6 +184,7 @@ Definition CompCert's_passes := ::: mkpass Selectionproof.match_prog ::: mkpass RTLgenproof.match_prog ::: mkpass Inliningproof.match_prog + ::: mkpass Renumberproof.match_prog ::: (@mkpass _ _ HTLgenproof.match_prog (HTLgenproof.TransfHTLLink HTLgen.transl_program)) ::: mkpass Veriloggenproof.match_prog ::: pass_nil _. @@ -132,8 +208,9 @@ Proof. destruct (RTLgen.transl_program p5) as [p6|e] eqn:P6; simpl in T; try discriminate. unfold transf_backend in T. simpl in T. rewrite ! compose_print_identity in T. destruct (Inlining.transf_program p6) as [p7|e] eqn:P7; simpl in T; try discriminate. - destruct (HTLgen.transl_program p7) as [p8|e] eqn:P8; simpl in T; try discriminate. - set (p9 := Veriloggen.transl_program p8) in *. + set (p8 := Renumber.transf_program p7) in *. + destruct (HTLgen.transl_program p8) as [p9|e] eqn:P9; simpl in T; try discriminate. + set (p10 := Veriloggen.transl_program p9) in *. unfold match_prog; simpl. exists p1; split. apply SimplExprproof.transf_program_match; auto. exists p2; split. apply SimplLocalsproof.match_transf_program; auto. @@ -142,8 +219,9 @@ Proof. exists p5; split. apply Selectionproof.transf_program_match; auto. exists p6; split. apply RTLgenproof.transf_program_match; auto. exists p7; split. apply Inliningproof.transf_program_match; auto. - exists p8; split. apply HTLgenproof.transf_program_match; auto. - exists p9; split. apply Veriloggenproof.transf_program_match; auto. + exists p8; split. apply Renumberproof.transf_program_match; auto. + exists p9; split. apply HTLgenproof.transf_program_match; auto. + exists p10; split. apply Veriloggenproof.transf_program_match; auto. inv T. reflexivity. Qed. @@ -171,7 +249,7 @@ Ltac DestructM := destruct H as (p & M & MM); clear H end. repeat DestructM. subst tp. - assert (F: forward_simulation (Cstrategy.semantics p) (Verilog.semantics p9)). + assert (F: forward_simulation (Cstrategy.semantics p) (Verilog.semantics p10)). { eapply compose_forward_simulations. eapply SimplExprproof.transl_program_correct; eassumption. @@ -188,6 +266,8 @@ Ltac DestructM := eapply compose_forward_simulations. eapply Inliningproof.transf_program_correct; eassumption. eapply compose_forward_simulations. + eapply Renumberproof.transf_program_correct; eassumption. + eapply compose_forward_simulations. eapply HTLgenproof.transf_program_correct. eassumption. eapply Veriloggenproof.transf_program_correct; eassumption. } diff --git a/src/VericertClflags.ml b/src/VericertClflags.ml index ca591de..26b4053 100644 --- a/src/VericertClflags.ml +++ b/src/VericertClflags.ml @@ -4,3 +4,5 @@ let option_hls = ref true let option_debug_hls = ref false let option_initial = ref false let option_dhtl = ref false +let option_drtlblock = ref false +let option_hls_schedule = ref false diff --git a/src/common/IntegerExtra.v b/src/common/IntegerExtra.v index c9b5dbd..f44cac2 100644 --- a/src/common/IntegerExtra.v +++ b/src/common/IntegerExtra.v @@ -362,4 +362,291 @@ Module IntExtra. rewrite testbit_repr; auto. Abort. + Lemma div_divs_equiv : + forall x y, + signed x >= 0 -> + signed y >= 0 -> + divs x y = divu x y. + Proof. + unfold divs, divu. + intros. + rewrite !signed_eq_unsigned; + try rewrite Zquot.Zquot_Zdiv_pos; try reflexivity; + lazymatch goal with + | |- unsigned _ <= max_signed => + solve [rewrite <- signed_positive; assumption] + | |- 0 <= unsigned _ => solve [apply unsigned_range_2] + end. + Qed. + + Lemma neg_signed' : + forall x : int, + unsigned x <> 2147483648 -> + signed (neg x) = - signed x. + Proof. + intros x Hhalf. + Transparent repr. + unfold signed. + simpl. + rewrite Z_mod_modulus_eq. + replace modulus with 4294967296; auto. + replace half_modulus with 2147483648; auto. + repeat match goal with | |- context[if ?x then _ else _] => destruct x end. + - destruct (Z.eq_dec (unsigned x) 0). + + rewrite e. auto. + + pose proof (Z.mod_opp_l_nz (unsigned x) 4294967296). + assert (4294967296 <> 0) by lia. + apply H in H0. + rewrite H0 in l. + pose proof (Z.mod_small (unsigned x) 4294967296). + assert (0 <= unsigned x < 4294967296). + pose proof (unsigned_range_2 x). lia. + apply H1 in H2. rewrite H2 in l. lia. + rewrite Z.mod_small. assumption. + pose proof (unsigned_range_2 x). lia. + - destruct (Z.eq_dec (unsigned x) 0). + + lia. + + rewrite Z.mod_opp_l_nz; try lia. + rewrite Z.opp_sub_distr. + rewrite Z.mod_small. lia. + pose proof (unsigned_range_2 x). + simplify; lia. + rewrite Z.mod_small. assumption. + pose proof (unsigned_range_2 x). + simplify; lia. + - destruct (Z.eq_dec (unsigned x) 0). + + rewrite e in *. rewrite Z.opp_0 in *. rewrite Zmod_0_l in g. lia. + + rewrite Z.mod_opp_l_nz; try lia. + rewrite Z.mod_small. lia. + pose proof (unsigned_range_2 x). lia. + rewrite Z.mod_small. assumption. + pose proof (unsigned_range_2 x). lia. + - destruct (Z.eq_dec (unsigned x) 0). + + lia. + + rewrite Z.mod_opp_l_nz in g; try lia. + rewrite Z.mod_small in g. + assert (unsigned x < 2147483648) by lia. lia. + pose proof (unsigned_range_2 x). + replace max_unsigned with 4294967295 in * by auto. lia. + rewrite Z.mod_small. assumption. + pose proof (unsigned_range_2 x). + replace max_unsigned with 4294967295 in * by auto. lia. + Qed. + + Lemma neg_divs_distr_l : + forall x y, + unsigned x <> 2147483648 -> + neg (divs x y) = divs (neg x) y. + Proof. + intros x y Hhalf. unfold divs, neg. + set (x' := signed x). set (y' := signed y). + apply eqm_samerepr. + apply eqm_trans with (- (Z.quot x' y')). + auto with ints. + replace (- (Z.quot x' y')) with (Z.quot (- x') y') + by (rewrite Zquot.Zquot_opp_l; auto). + unfold x'. + rewrite <- neg_signed'. + auto with ints. + assumption. + Qed. + + Lemma neg_signed : + forall x : int, + unsigned x <> 2147483648 -> + signed x < 0 -> + signed (neg x) >= 0. + Proof. + intros. + rewrite neg_signed'. lia. + assumption. + Qed. + + Lemma shl_signed_positive : + forall y, + unsigned y <= 30 -> + signed (shl one y) >= 0. + Proof. + intros. + unfold signed, shl. + destruct (zlt (unsigned (repr (Z.shiftl (unsigned one) (unsigned y)))) half_modulus). + - rewrite unsigned_repr. + + rewrite Z.shiftl_1_l. + apply Z.le_ge. apply Z.pow_nonneg. lia. + + rewrite Z.shiftl_1_l. split. + apply Z.pow_nonneg. lia. + simplify. + replace (4294967295) with (2 ^ 32 - 1); try lia. + transitivity (2 ^ 31); try lia. + apply Z.pow_le_mono_r; lia. + - simplify. rewrite Z.shiftl_1_l in g. + unfold half_modulus, modulus, wordsize, + Wordsize_32.wordsize in *. unfold two_power_nat in *. simplify. + unfold Z_mod_modulus in *. + destruct (2 ^ unsigned y) eqn:?. + apply Z.ge_le in g. exfalso. + replace (4294967296 / 2) with (2147483648) in g; auto. + rewrite Z.shiftl_1_l. rewrite Heqz. + unfold wordsize in *. unfold Wordsize_32.wordsize in *. + rewrite Zbits.P_mod_two_p_eq in *. + replace (4294967296 / 2) with (2147483648) in g; auto. + rewrite <- Heqz in g. + rewrite Z.mod_small in g. + replace (2147483648) with (2 ^ 31) in g. + pose proof (Z.pow_le_mono_r 2 (unsigned y) 30). + apply Z.ge_le in g. + assert (0 < 2) by lia. apply H0 in H1. lia. assumption. lia. + split. lia. rewrite two_power_nat_equiv. + apply Z.pow_lt_mono_r; lia. + + pose proof (Zlt_neg_0 p). + pose proof (pow2_nonneg (unsigned y)). rewrite <- Heqz in H0. + lia. + Qed. + + Lemma is_power2_shl : + forall y, + unsigned y <= 30 -> + is_power2 (shl one y) = Some y. + Proof. + intros. + unfold is_power2, shl. + destruct (Zbits.Z_is_power2 (unsigned (repr (Z.shiftl (unsigned one) (unsigned y))))) eqn:?. + - simplify. + rewrite Z_mod_modulus_eq in Heqo. + rewrite Z.mod_small in Heqo. rewrite Z.shiftl_1_l in Heqo. + rewrite <- two_p_correct in Heqo. + rewrite Zbits.Z_is_power2_complete in Heqo. inv Heqo. + rewrite repr_unsigned. auto. + pose proof (unsigned_range_2 y). lia. + rewrite Z.shiftl_1_l. unfold modulus, wordsize, Wordsize_32.wordsize. + rewrite two_power_nat_equiv. + split. apply pow2_nonneg. + apply Z.pow_lt_mono_r; lia. + - simplify. + rewrite Z_mod_modulus_eq in Heqo. + rewrite Z.mod_small in Heqo. rewrite Z.shiftl_1_l in Heqo. + rewrite <- two_p_correct in Heqo. + rewrite Zbits.Z_is_power2_complete in Heqo. discriminate. + pose proof (unsigned_range_2 y). lia. + rewrite Z.shiftl_1_l. unfold modulus, wordsize, Wordsize_32.wordsize. + rewrite two_power_nat_equiv. + split. apply pow2_nonneg. + apply Z.pow_lt_mono_r; lia. + Qed. + + Definition shrx_alt (x y : int) : int := + if zlt (signed x) 0 + then neg (shru (neg x) y) + else shru x y. + + Lemma shrx_shrx_alt_equiv_ne : + forall x y, + unsigned x <> 2147483648 -> + unsigned y <= 30 -> + shrx x y = shrx_alt x y. + Proof. + intros x y Hhalf H. + unfold shrx, shrx_alt, lt. + destruct (Z_ge_lt_dec (signed x) 0); + [rewrite zlt_false | rewrite zlt_true]; + + repeat lazymatch goal with + | |- is_power2 _ = Some _ => apply is_power2_shl + | |- signed (shl one _) >= 0 => apply shl_signed_positive + | |- signed (neg _) >= 0 => apply neg_signed + | |- divs _ _ = divu _ _ => apply div_divs_equiv + | |- divs ?x (shl one ?y) = neg (shru (neg ?x) ?y) => + rewrite <- neg_involutive at 1; rewrite neg_divs_distr_l; + try assumption; f_equal + | |- divs ?x (shl one ?y) = shru ?x ?y => + let H := fresh "H" in + pose proof (divu_pow2 x (shl one y) y) as H; + rewrite <- H + end; try assumption. + Qed. + + Lemma shrx_shrx_alt_equiv_eq : + forall x y, + unsigned x = 2147483648 -> + unsigned y <= 30 -> + shrx x y = shrx_alt x y. + Proof. + intros. + repeat unfold shrx, shrx_alt, signed, divs, neg. + replace half_modulus with 2147483648 by auto. + replace modulus with 4294967296 by auto. + simplify. + rewrite !Z_mod_modulus_eq. + rewrite !H. + simplify. + assert (Hshl: Z.shiftl 1 (unsigned y) mod 4294967296 = Z.shiftl 1 (unsigned y)). + { apply Z.mod_small. + rewrite Z.shiftl_1_l. + split. + apply pow2_nonneg. + replace 4294967296 with (2^32) by auto. + apply Z.le_lt_trans with (m := 2 ^ 31); try lia. + apply Z.pow_le_mono_r; lia. + } + rewrite !Hshl. + f_equal. + assert ((Z.shiftl 1 (unsigned y)) < 2147483648). + rewrite Z.shiftl_1_l. + replace 2147483648 with (2^31) by auto. + apply Z.le_lt_trans with (m := 2 ^ 30); try lia. + apply Z.pow_le_mono_r; lia. + destruct (zlt (Z.shiftl 1 (unsigned y)) 2147483648); try lia. + replace (-2147483648 mod 4294967296) with 2147483648 by auto. + assert (Hmodeq : Z.shiftr 2147483648 (unsigned y) mod 4294967296 + = Z.shiftr 2147483648 (unsigned y)). + { apply Z.mod_small. split. + apply Z.shiftr_nonneg. lia. + rewrite Z.shiftr_div_pow2. + replace 4294967296 with (Z.succ 4294967295); auto. + apply Zle_lt_succ. + replace 4294967295 with (4294967295 * (2 ^ unsigned y) / (2 ^ unsigned y)). + 2: { + apply Z.div_mul. + pose proof (Z.pow_pos_nonneg 2 (unsigned y)). + apply not_eq_sym. + apply Z.le_neq. apply H2; try lia. + apply unsigned_range_2. + } + + apply Z.div_le_mono. + apply Z.pow_pos_nonneg. lia. + apply unsigned_range_2. + transitivity 4294967295; try lia. + apply Z.le_mul_diag_r; try lia. + replace 1 with (Z.succ 0) by auto. + apply Z.le_succ_l. + apply Z.pow_pos_nonneg; try lia. + apply unsigned_range_2. apply unsigned_range_2. + } + rewrite !Hmodeq. + replace (-2147483648) with (Z.opp 2147483648) by auto. + rewrite Zquot.Zquot_opp_l. + f_equal. + rewrite Zquot.Zquot_Zdiv_pos. + rewrite Z.shiftr_div_pow2. + rewrite Z.shiftl_1_l. auto. + apply unsigned_range_2. + lia. + rewrite Z.shiftl_1_l. + apply Z.lt_le_incl. + apply Z.pow_pos_nonneg; try lia. + apply unsigned_range_2. + Qed. + + Theorem shrx_shrx_alt_equiv : + forall x y, + unsigned y <= 30 -> + shrx x y = shrx_alt x y. + Proof. + intros. + destruct (Z.eq_dec (unsigned x) 2147483648); + [ apply shrx_shrx_alt_equiv_eq | apply shrx_shrx_alt_equiv_ne]; auto. + Qed. + End IntExtra. diff --git a/src/extraction/Extraction.v b/src/extraction/Extraction.v index b1a885e..c2b3951 100644 --- a/src/extraction/Extraction.v +++ b/src/extraction/Extraction.v @@ -16,7 +16,14 @@ * along with this program. If not, see <https://www.gnu.org/licenses/>. *) -From vericert Require Verilog Value Compiler. +From vericert Require + Verilog + Value + Compiler + RTLBlockgen + RTLBlock + HTLSchedulegen + HTLgen. From Coq Require DecidableClass. @@ -128,6 +135,7 @@ Extract Constant Compiler.print_Clight => "PrintClight.print_if". Extract Constant Compiler.print_Cminor => "PrintCminor.print_if". Extract Constant driver.Compiler.print_RTL => "PrintRTL.print_if". Extract Constant Compiler.print_RTL => "PrintRTL.print_if". +Extract Constant Compiler.print_RTLBlock => "PrintRTLBlock.print_if". Extract Constant Compiler.print_HTL => "PrintHTL.print_if". Extract Constant Compiler.print_LTL => "PrintLTL.print_if". Extract Constant Compiler.print_Mach => "PrintMach.print_if". @@ -162,12 +170,19 @@ Extract Inlined Constant Binary.B2R => "fun _ -> assert false". Extract Inlined Constant Binary.round_mode => "fun _ -> assert false". Extract Inlined Constant Bracket.inbetween_loc => "fun _ -> assert false". +Extract Constant RTLBlockgen.partition => "Partition.partition". +Extract Constant HTLSchedulegen.transl_module => "Schedule.transl_module". + (* Needed in Coq 8.4 to avoid problems with Function definitions. *) Set Extraction AccessOpaque. Cd "src/extraction". Separate Extraction Verilog.module Value.uvalueToZ vericert.Compiler.transf_hls + vericert.Compiler.transf_hls_temp + vericert.Compiler.transf_hls_opt + RTLBlockgen.transl_program RTLBlock.successors_instr + HTLgen.tbl_to_case_expr Compiler.transf_c_program Compiler.transf_cminor_program Cexec.do_initial_state Cexec.do_step Cexec.at_final_state diff --git a/src/verilog/Array.v b/src/hls/Array.v index fe0f6b2..fe0f6b2 100644 --- a/src/verilog/Array.v +++ b/src/hls/Array.v diff --git a/src/verilog/AssocMap.v b/src/hls/AssocMap.v index 8d8788a..8d8788a 100644 --- a/src/verilog/AssocMap.v +++ b/src/hls/AssocMap.v diff --git a/src/verilog/HTL.v b/src/hls/HTL.v index 620ef14..620ef14 100644 --- a/src/verilog/HTL.v +++ b/src/hls/HTL.v diff --git a/src/hls/HTLBlockgen.v b/src/hls/HTLBlockgen.v new file mode 100644 index 0000000..5f40962 --- /dev/null +++ b/src/hls/HTLBlockgen.v @@ -0,0 +1,655 @@ +(* + * Vericert: Verified high-level synthesis. + * Copyright (C) 2020 Yann Herklotz <yann@yannherklotz.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + *) + +(*From compcert Require Import Maps. +From compcert Require Errors Globalenvs Integers. +From compcert Require Import AST. +From vericert Require Import RTLBlock Verilog HTL Vericertlib AssocMap ValueInt Statemonad. + +Hint Resolve AssocMap.gempty : htlh. +Hint Resolve AssocMap.gso : htlh. +Hint Resolve AssocMap.gss : htlh. +Hint Resolve Ple_refl : htlh. +Hint Resolve Ple_succ : htlh. + +Record state: Type := mkstate { + st_st : reg; + st_freshreg: reg; + st_freshstate: node; + st_scldecls: AssocMap.t (option io * scl_decl); + st_arrdecls: AssocMap.t (option io * arr_decl); + st_datapath: datapath; + st_controllogic: controllogic; +}. + +Definition init_state (st : reg) : state := + mkstate st + 1%positive + 1%positive + (AssocMap.empty (option io * scl_decl)) + (AssocMap.empty (option io * arr_decl)) + (AssocMap.empty stmnt) + (AssocMap.empty stmnt). + +Module HTLState <: State. + + Definition st := state. + + Inductive st_incr: state -> state -> Prop := + state_incr_intro: + forall (s1 s2: state), + st_st s1 = st_st s2 -> + Ple s1.(st_freshreg) s2.(st_freshreg) -> + Ple s1.(st_freshstate) s2.(st_freshstate) -> + (forall n, + s1.(st_datapath)!n = None \/ s2.(st_datapath)!n = s1.(st_datapath)!n) -> + (forall n, + s1.(st_controllogic)!n = None + \/ s2.(st_controllogic)!n = s1.(st_controllogic)!n) -> + st_incr s1 s2. + Hint Constructors st_incr : htlh. + + Definition st_prop := st_incr. + Hint Unfold st_prop : htlh. + + Lemma st_refl : forall s, st_prop s s. Proof. auto with htlh. Qed. + + Lemma st_trans : + forall s1 s2 s3, st_prop s1 s2 -> st_prop s2 s3 -> st_prop s1 s3. + Proof. + intros. inv H. inv H0. apply state_incr_intro; eauto using Ple_trans; intros; try congruence. + - destruct H4 with n; destruct H8 with n; intuition congruence. + - destruct H5 with n; destruct H9 with n; intuition congruence. + Qed. + +End HTLState. +Export HTLState. + +Module HTLMonad := Statemonad(HTLState). +Export HTLMonad. + +Module HTLMonadExtra := Monad.MonadExtra(HTLMonad). +Import HTLMonadExtra. +Export MonadNotation. + +Definition state_goto (st : reg) (n : node) : stmnt := + Vnonblock (Vvar st) (Vlit (posToValue n)). + +Definition state_cond (st : reg) (c : expr) (n1 n2 : node) : stmnt := + Vnonblock (Vvar st) (Vternary c (posToExpr n1) (posToExpr n2)). + +Definition check_empty_node_datapath: + forall (s: state) (n: node), { s.(st_datapath)!n = None } + { True }. +Proof. + intros. case (s.(st_datapath)!n); tauto. +Defined. + +Definition check_empty_node_controllogic: + forall (s: state) (n: node), { s.(st_controllogic)!n = None } + { True }. +Proof. + intros. case (s.(st_controllogic)!n); tauto. +Defined. + +Lemma add_instr_state_incr : + forall s n n' st, + (st_datapath s)!n = None -> + (st_controllogic s)!n = None -> + st_incr s + (mkstate + s.(st_st) + s.(st_freshreg) + (st_freshstate s) + s.(st_scldecls) + s.(st_arrdecls) + (AssocMap.set n st s.(st_datapath)) + (AssocMap.set n (state_goto s.(st_st) n') s.(st_controllogic))). +Proof. + constructor; intros; + try (simpl; destruct (peq n n0); subst); + auto with htlh. +Qed. + +Lemma declare_reg_state_incr : + forall i s r sz, + st_incr s + (mkstate + s.(st_st) + s.(st_freshreg) + s.(st_freshstate) + (AssocMap.set r (i, VScalar sz) s.(st_scldecls)) + s.(st_arrdecls) + s.(st_datapath) + s.(st_controllogic)). +Proof. auto with htlh. Qed. + +Definition declare_reg (i : option io) (r : reg) (sz : nat) : mon unit := + fun s => OK tt (mkstate + s.(st_st) + s.(st_freshreg) + s.(st_freshstate) + (AssocMap.set r (i, VScalar sz) s.(st_scldecls)) + s.(st_arrdecls) + s.(st_datapath) + s.(st_controllogic)) + (declare_reg_state_incr i s r sz). + +Definition add_instr (n : node) (n' : node) (st : stmnt) : mon unit := + fun s => + match check_empty_node_datapath s n, check_empty_node_controllogic s n with + | left STM, left TRANS => + OK tt (mkstate + s.(st_st) + s.(st_freshreg) + (st_freshstate s) + s.(st_scldecls) + s.(st_arrdecls) + (AssocMap.set n st s.(st_datapath)) + (AssocMap.set n (state_goto s.(st_st) n') s.(st_controllogic))) + (add_instr_state_incr s n n' st STM TRANS) + | _, _ => Error (Errors.msg "HTL.add_instr") + end. + +Lemma add_instr_skip_state_incr : + forall s n st, + (st_datapath s)!n = None -> + (st_controllogic s)!n = None -> + st_incr s + (mkstate + s.(st_st) + s.(st_freshreg) + (st_freshstate s) + s.(st_scldecls) + s.(st_arrdecls) + (AssocMap.set n st s.(st_datapath)) + (AssocMap.set n Vskip s.(st_controllogic))). +Proof. + constructor; intros; + try (simpl; destruct (peq n n0); subst); + auto with htlh. +Qed. + +Definition add_instr_skip (n : node) (st : stmnt) : mon unit := + fun s => + match check_empty_node_datapath s n, check_empty_node_controllogic s n with + | left STM, left TRANS => + OK tt (mkstate + s.(st_st) + s.(st_freshreg) + (st_freshstate s) + s.(st_scldecls) + s.(st_arrdecls) + (AssocMap.set n st s.(st_datapath)) + (AssocMap.set n Vskip s.(st_controllogic))) + (add_instr_skip_state_incr s n st STM TRANS) + | _, _ => Error (Errors.msg "HTL.add_instr") + end. + +Lemma add_node_skip_state_incr : + forall s n st, + (st_datapath s)!n = None -> + (st_controllogic s)!n = None -> + st_incr s + (mkstate + s.(st_st) + s.(st_freshreg) + (st_freshstate s) + s.(st_scldecls) + s.(st_arrdecls) + (AssocMap.set n Vskip s.(st_datapath)) + (AssocMap.set n st s.(st_controllogic))). +Proof. + constructor; intros; + try (simpl; destruct (peq n n0); subst); + auto with htlh. +Qed. + +Definition add_node_skip (n : node) (st : stmnt) : mon unit := + fun s => + match check_empty_node_datapath s n, check_empty_node_controllogic s n with + | left STM, left TRANS => + OK tt (mkstate + s.(st_st) + s.(st_freshreg) + (st_freshstate s) + s.(st_scldecls) + s.(st_arrdecls) + (AssocMap.set n Vskip s.(st_datapath)) + (AssocMap.set n st s.(st_controllogic))) + (add_node_skip_state_incr s n st STM TRANS) + | _, _ => Error (Errors.msg "HTL.add_instr") + end. + +Definition nonblock (dst : reg) (e : expr) := Vnonblock (Vvar dst) e. +Definition block (dst : reg) (e : expr) := Vblock (Vvar dst) e. + +Definition bop (op : binop) (r1 r2 : reg) : expr := + Vbinop op (Vvar r1) (Vvar r2). + +Definition boplit (op : binop) (r : reg) (l : Integers.int) : expr := + Vbinop op (Vvar r) (Vlit (intToValue l)). + +Definition boplitz (op: binop) (r: reg) (l: Z) : expr := + Vbinop op (Vvar r) (Vlit (ZToValue l)). + +Definition translate_comparison (c : Integers.comparison) (args : list reg) : mon expr := + match c, args with + | Integers.Ceq, r1::r2::nil => ret (bop Veq r1 r2) + | Integers.Cne, r1::r2::nil => ret (bop Vne r1 r2) + | Integers.Clt, r1::r2::nil => ret (bop Vlt r1 r2) + | Integers.Cgt, r1::r2::nil => ret (bop Vgt r1 r2) + | Integers.Cle, r1::r2::nil => ret (bop Vle r1 r2) + | Integers.Cge, r1::r2::nil => ret (bop Vge r1 r2) + | _, _ => error (Errors.msg "Htlgen: comparison instruction not implemented: other") + end. + +Definition translate_comparison_imm (c : Integers.comparison) (args : list reg) (i: Integers.int) + : mon expr := + match c, args with + | Integers.Ceq, r1::nil => ret (boplit Veq r1 i) + | Integers.Cne, r1::nil => ret (boplit Vne r1 i) + | Integers.Clt, r1::nil => ret (boplit Vlt r1 i) + | Integers.Cgt, r1::nil => ret (boplit Vgt r1 i) + | Integers.Cle, r1::nil => ret (boplit Vle r1 i) + | Integers.Cge, r1::nil => ret (boplit Vge r1 i) + | _, _ => error (Errors.msg "Htlgen: comparison_imm instruction not implemented: other") + end. + +Definition translate_comparisonu (c : Integers.comparison) (args : list reg) : mon expr := + match c, args with + | Integers.Clt, r1::r2::nil => ret (bop Vltu r1 r2) + | Integers.Cgt, r1::r2::nil => ret (bop Vgtu r1 r2) + | Integers.Cle, r1::r2::nil => ret (bop Vleu r1 r2) + | Integers.Cge, r1::r2::nil => ret (bop Vgeu r1 r2) + | _, _ => error (Errors.msg "Htlgen: comparison instruction not implemented: other") + end. + +Definition translate_comparison_immu (c : Integers.comparison) (args : list reg) (i: Integers.int) + : mon expr := + match c, args with + | Integers.Clt, r1::nil => ret (boplit Vltu r1 i) + | Integers.Cgt, r1::nil => ret (boplit Vgtu r1 i) + | Integers.Cle, r1::nil => ret (boplit Vleu r1 i) + | Integers.Cge, r1::nil => ret (boplit Vgeu r1 i) + | _, _ => error (Errors.msg "Htlgen: comparison_imm instruction not implemented: other") + end. + +Definition translate_condition (c : Op.condition) (args : list reg) : mon expr := + match c, args with + | Op.Ccomp c, _ => translate_comparison c args + | Op.Ccompu c, _ => translate_comparisonu c args + | Op.Ccompimm c i, _ => translate_comparison_imm c args i + | Op.Ccompuimm c i, _ => translate_comparison_immu c args i + | Op.Cmaskzero n, _ => error (Errors.msg "Htlgen: condition instruction not implemented: Cmaskzero") + | Op.Cmasknotzero n, _ => error (Errors.msg "Htlgen: condition instruction not implemented: Cmasknotzero") + | _, _ => error (Errors.msg "Htlgen: condition instruction not implemented: other") + end. + +Definition check_address_parameter_signed (p : Z) : bool := + Z.leb Integers.Ptrofs.min_signed p + && Z.leb p Integers.Ptrofs.max_signed. + +Definition check_address_parameter_unsigned (p : Z) : bool := + Z.leb p Integers.Ptrofs.max_unsigned. + +Definition translate_eff_addressing (a: Op.addressing) (args: list reg) : mon expr := + match a, args with (* TODO: We should be more methodical here; what are the possibilities?*) + | Op.Aindexed off, r1::nil => + if (check_address_parameter_signed off) + then ret (boplitz Vadd r1 off) + else error (Errors.msg "Veriloggen: translate_eff_addressing (Aindexed): address out of bounds") + | Op.Ascaled scale offset, r1::nil => + if (check_address_parameter_signed scale) && (check_address_parameter_signed offset) + then ret (Vbinop Vadd (boplitz Vmul r1 scale) (Vlit (ZToValue offset))) + else error (Errors.msg "Veriloggen: translate_eff_addressing (Ascaled): address out of bounds") + | Op.Aindexed2 offset, r1::r2::nil => + if (check_address_parameter_signed offset) + then ret (Vbinop Vadd (bop Vadd r1 r2) (Vlit (ZToValue offset))) + else error (Errors.msg "Veriloggen: translate_eff_addressing (Aindexed2): address out of bounds") + | Op.Aindexed2scaled scale offset, r1::r2::nil => (* Typical for dynamic array addressing *) + if (check_address_parameter_signed scale) && (check_address_parameter_signed offset) + then ret (Vbinop Vadd (Vvar r1) (Vbinop Vadd (boplitz Vmul r2 scale) (Vlit (ZToValue offset)))) + else error (Errors.msg "Veriloggen: translate_eff_addressing (Aindexed2scaled): address out of bounds") + | Op.Ainstack a, nil => (* We need to be sure that the base address is aligned *) + let a := Integers.Ptrofs.unsigned a in + if (check_address_parameter_unsigned a) + then ret (Vlit (ZToValue a)) + else error (Errors.msg "Veriloggen: translate_eff_addressing (Ainstack): address out of bounds") + | _, _ => error (Errors.msg "Veriloggen: translate_eff_addressing unsuported addressing") + end. + +(** Translate an instruction to a statement. FIX mulhs mulhu *) +Definition translate_instr (op : Op.operation) (args : list reg) : mon expr := + match op, args with + | Op.Omove, r::nil => ret (Vvar r) + | Op.Ointconst n, _ => ret (Vlit (intToValue n)) + | Op.Oneg, r::nil => ret (Vunop Vneg (Vvar r)) + | Op.Osub, r1::r2::nil => ret (bop Vsub r1 r2) + | Op.Omul, r1::r2::nil => ret (bop Vmul r1 r2) + | Op.Omulimm n, r::nil => ret (boplit Vmul r n) + | Op.Omulhs, r1::r2::nil => error (Errors.msg "Htlgen: Instruction not implemented: mulhs") + | Op.Omulhu, r1::r2::nil => error (Errors.msg "Htlgen: Instruction not implemented: mulhu") + | Op.Odiv, r1::r2::nil => ret (bop Vdiv r1 r2) + | Op.Odivu, r1::r2::nil => ret (bop Vdivu r1 r2) + | Op.Omod, r1::r2::nil => ret (bop Vmod r1 r2) + | Op.Omodu, r1::r2::nil => ret (bop Vmodu r1 r2) + | Op.Oand, r1::r2::nil => ret (bop Vand r1 r2) + | Op.Oandimm n, r::nil => ret (boplit Vand r n) + | Op.Oor, r1::r2::nil => ret (bop Vor r1 r2) + | Op.Oorimm n, r::nil => ret (boplit Vor r n) + | Op.Oxor, r1::r2::nil => ret (bop Vxor r1 r2) + | Op.Oxorimm n, r::nil => ret (boplit Vxor r n) + | Op.Onot, r::nil => ret (Vunop Vnot (Vvar r)) + | Op.Oshl, r1::r2::nil => ret (bop Vshl r1 r2) + | Op.Oshlimm n, r::nil => ret (boplit Vshl r n) + | Op.Oshr, r1::r2::nil => ret (bop Vshr r1 r2) + | Op.Oshrimm n, r::nil => ret (boplit Vshr r n) + | Op.Oshrximm n, r::nil => error (Errors.msg "Htlgen: Instruction not implemented: Oshrximm") + (*ret (Vbinop Vdiv (Vvar r) + (Vbinop Vshl (Vlit (ZToValue 1)) + (Vlit (intToValue n))))*) + | Op.Oshru, r1::r2::nil => ret (bop Vshru r1 r2) + | Op.Oshruimm n, r::nil => ret (boplit Vshru r n) + | Op.Ororimm n, r::nil => error (Errors.msg "Htlgen: Instruction not implemented: Ororimm") + (*ret (Vbinop Vor (boplit Vshru r (Integers.Int.modu n (Integers.Int.repr 32))) + (boplit Vshl r (Integers.Int.sub (Integers.Int.repr 32) (Integers.Int.modu n (Integers.Int.repr 32)))))*) + | Op.Oshldimm n, r::nil => ret (Vbinop Vor (boplit Vshl r n) (boplit Vshr r (Integers.Int.sub (Integers.Int.repr 32) n))) + | Op.Ocmp c, _ => translate_condition c args + | Op.Osel c AST.Tint, r1::r2::rl => + do tc <- translate_condition c rl; + ret (Vternary tc (Vvar r1) (Vvar r2)) + | Op.Olea a, _ => translate_eff_addressing a args + | _, _ => error (Errors.msg "Htlgen: Instruction not implemented: other") + end. + +Lemma add_branch_instr_state_incr: + forall s e n n1 n2, + (st_datapath s) ! n = None -> + (st_controllogic s) ! n = None -> + st_incr s (mkstate + s.(st_st) + (st_freshreg s) + (st_freshstate s) + s.(st_scldecls) + s.(st_arrdecls) + (AssocMap.set n Vskip (st_datapath s)) + (AssocMap.set n (state_cond s.(st_st) e n1 n2) (st_controllogic s))). +Proof. + intros. apply state_incr_intro; simpl; + try (intros; destruct (peq n0 n); subst); + auto with htlh. +Qed. + +Definition add_branch_instr (e: expr) (n n1 n2: node) : mon unit := + fun s => + match check_empty_node_datapath s n, check_empty_node_controllogic s n with + | left NSTM, left NTRANS => + OK tt (mkstate + s.(st_st) + (st_freshreg s) + (st_freshstate s) + s.(st_scldecls) + s.(st_arrdecls) + (AssocMap.set n Vskip (st_datapath s)) + (AssocMap.set n (state_cond s.(st_st) e n1 n2) (st_controllogic s))) + (add_branch_instr_state_incr s e n n1 n2 NSTM NTRANS) + | _, _ => Error (Errors.msg "Htlgen: add_branch_instr") + end. + +Definition translate_arr_access (mem : AST.memory_chunk) (addr : Op.addressing) + (args : list reg) (stack : reg) : mon expr := + match mem, addr, args with (* TODO: We should be more methodical here; what are the possibilities?*) + | Mint32, Op.Aindexed off, r1::nil => + if (check_address_parameter_signed off) + then ret (Vvari stack (Vbinop Vdivu (boplitz Vadd r1 off) (Vlit (ZToValue 4)))) + else error (Errors.msg "HTLgen: translate_arr_access address out of bounds") + | Mint32, Op.Aindexed2scaled scale offset, r1::r2::nil => (* Typical for dynamic array addressing *) + if (check_address_parameter_signed scale) && (check_address_parameter_signed offset) + then ret (Vvari stack + (Vbinop Vdivu + (Vbinop Vadd (boplitz Vadd r1 offset) (boplitz Vmul r2 scale)) + (Vlit (ZToValue 4)))) + else error (Errors.msg "HTLgen: translate_arr_access address out of bounds") + | Mint32, Op.Ainstack a, nil => (* We need to be sure that the base address is aligned *) + let a := Integers.Ptrofs.unsigned a in + if (check_address_parameter_unsigned a) + then ret (Vvari stack (Vlit (ZToValue (a / 4)))) + else error (Errors.msg "HTLgen: eff_addressing out of bounds stack offset") + | _, _, _ => error (Errors.msg "HTLgen: translate_arr_access unsuported addressing") + end. + +Fixpoint enumerate (i : nat) (ns : list node) {struct ns} : list (nat * node) := + match ns with + | n :: ns' => (i, n) :: enumerate (i+1) ns' + | nil => nil + end. + +Definition tbl_to_case_expr (st : reg) (ns : list node) : list (expr * stmnt) := + List.map (fun a => match a with + (i, n) => (Vlit (natToValue i), Vnonblock (Vvar st) (Vlit (posToValue n))) + end) + (enumerate 0 ns). + +Definition transf_instr (fin rtrn stack: reg) (ni: node * instruction) : mon unit := + match ni with + (n, i) => + match i with + | Inop n' => + if Z.leb (Z.pos n') Integers.Int.max_unsigned then + add_instr n n' Vskip + else error (Errors.msg "State is larger than 2^32.") + | Iop op args dst n' => + if Z.leb (Z.pos n') Integers.Int.max_unsigned then + do instr <- translate_instr op args; + do _ <- declare_reg None dst 32; + add_instr n n' (nonblock dst instr) + else error (Errors.msg "State is larger than 2^32.") + | Iload mem addr args dst n' => + if Z.leb (Z.pos n') Integers.Int.max_unsigned then + do src <- translate_arr_access mem addr args stack; + do _ <- declare_reg None dst 32; + add_instr n n' (nonblock dst src) + else error (Errors.msg "State is larger than 2^32.") + | Istore mem addr args src n' => + if Z.leb (Z.pos n') Integers.Int.max_unsigned then + do dst <- translate_arr_access mem addr args stack; + add_instr n n' (Vnonblock dst (Vvar src)) (* TODO: Could juse use add_instr? reg exists. *) + else error (Errors.msg "State is larger than 2^32.") + | Icall _ _ _ _ _ => error (Errors.msg "Calls are not implemented.") + | Itailcall _ _ _ => error (Errors.msg "Tailcalls are not implemented.") + | Ibuiltin _ _ _ _ => error (Errors.msg "Builtin functions not implemented.") + | Icond cond args n1 n2 => + if Z.leb (Z.pos n1) Integers.Int.max_unsigned && Z.leb (Z.pos n2) Integers.Int.max_unsigned then + do e <- translate_condition cond args; + add_branch_instr e n n1 n2 + else error (Errors.msg "State is larger than 2^32.") + | Ijumptable r tbl => + (*do s <- get; + add_node_skip n (Vcase (Vvar r) (tbl_to_case_expr s.(st_st) tbl) (Some Vskip))*) + error (Errors.msg "Ijumptable: Case statement not supported.") + | Ireturn r => + match r with + | Some r' => + add_instr_skip n (Vseq (block fin (Vlit (ZToValue 1%Z))) (block rtrn (Vvar r'))) + | None => + add_instr_skip n (Vseq (block fin (Vlit (ZToValue 1%Z))) (block rtrn (Vlit (ZToValue 0%Z)))) + end + end + end. + +Lemma create_reg_state_incr: + forall s sz i, + st_incr s (mkstate + s.(st_st) + (Pos.succ (st_freshreg s)) + (st_freshstate s) + (AssocMap.set s.(st_freshreg) (i, VScalar sz) s.(st_scldecls)) + s.(st_arrdecls) + (st_datapath s) + (st_controllogic s)). +Proof. constructor; simpl; auto with htlh. Qed. + +Definition create_reg (i : option io) (sz : nat) : mon reg := + fun s => let r := s.(st_freshreg) in + OK r (mkstate + s.(st_st) + (Pos.succ r) + (st_freshstate s) + (AssocMap.set s.(st_freshreg) (i, VScalar sz) s.(st_scldecls)) + (st_arrdecls s) + (st_datapath s) + (st_controllogic s)) + (create_reg_state_incr s sz i). + +Lemma create_arr_state_incr: + forall s sz ln i, + st_incr s (mkstate + s.(st_st) + (Pos.succ (st_freshreg s)) + (st_freshstate s) + s.(st_scldecls) + (AssocMap.set s.(st_freshreg) (i, VArray sz ln) s.(st_arrdecls)) + (st_datapath s) + (st_controllogic s)). +Proof. constructor; simpl; auto with htlh. Qed. + +Definition create_arr (i : option io) (sz : nat) (ln : nat) : mon (reg * nat) := + fun s => let r := s.(st_freshreg) in + OK (r, ln) (mkstate + s.(st_st) + (Pos.succ r) + (st_freshstate s) + s.(st_scldecls) + (AssocMap.set s.(st_freshreg) (i, VArray sz ln) s.(st_arrdecls)) + (st_datapath s) + (st_controllogic s)) + (create_arr_state_incr s sz ln i). + +Definition stack_correct (sz : Z) : bool := + (0 <=? sz) && (sz <? Integers.Ptrofs.modulus) && (Z.modulo sz 4 =? 0). + +Definition max_pc_map (m : Maps.PTree.t stmnt) := + PTree.fold (fun m pc i => Pos.max m pc) m 1%positive. + +Lemma max_pc_map_sound: + forall m pc i, m!pc = Some i -> Ple pc (max_pc_map m). +Proof. + intros until i. unfold max_pc_function. + apply PTree_Properties.fold_rec with (P := fun c m => c!pc = Some i -> Ple pc m). + (* extensionality *) + intros. apply H0. rewrite H; auto. + (* base case *) + rewrite PTree.gempty. congruence. + (* inductive case *) + intros. rewrite PTree.gsspec in H2. destruct (peq pc k). + inv H2. xomega. + apply Ple_trans with a. auto. xomega. +Qed. + +Lemma max_pc_wf : + forall m, Z.pos (max_pc_map m) <= Integers.Int.max_unsigned -> + map_well_formed m. +Proof. + unfold map_well_formed. intros. + exploit list_in_map_inv. eassumption. intros [x [A B]]. destruct x. + apply Maps.PTree.elements_complete in B. apply max_pc_map_sound in B. + unfold Ple in B. apply Pos2Z.pos_le_pos in B. subst. + simplify. transitivity (Z.pos (max_pc_map m)); eauto. +Qed. + +Definition transf_module (f: function) : mon module := + if stack_correct f.(fn_stacksize) then + do fin <- create_reg (Some Voutput) 1; + do rtrn <- create_reg (Some Voutput) 32; + do (stack, stack_len) <- create_arr None 32 (Z.to_nat (f.(fn_stacksize) / 4)); + do _ <- collectlist (transf_instr fin rtrn stack) (Maps.PTree.elements f.(RTL.fn_code)); + do _ <- collectlist (fun r => declare_reg (Some Vinput) r 32) f.(RTL.fn_params); + do start <- create_reg (Some Vinput) 1; + do rst <- create_reg (Some Vinput) 1; + do clk <- create_reg (Some Vinput) 1; + do current_state <- get; + match zle (Z.pos (max_pc_map current_state.(st_datapath))) Integers.Int.max_unsigned, + zle (Z.pos (max_pc_map current_state.(st_controllogic))) Integers.Int.max_unsigned with + | left LEDATA, left LECTRL => + ret (mkmodule + f.(RTL.fn_params) + current_state.(st_datapath) + current_state.(st_controllogic) + f.(fn_entrypoint) + current_state.(st_st) + stack + stack_len + fin + rtrn + start + rst + clk + current_state.(st_scldecls) + current_state.(st_arrdecls) + (conj (max_pc_wf _ LECTRL) (max_pc_wf _ LEDATA))) + | _, _ => error (Errors.msg "More than 2^32 states.") + end + else error (Errors.msg "Stack size misalignment."). + +Definition max_state (f: function) : state := + let st := Pos.succ (max_reg_function f) in + mkstate st + (Pos.succ st) + (Pos.succ (max_pc_function f)) + (AssocMap.set st (None, VScalar 32) (st_scldecls (init_state st))) + (st_arrdecls (init_state st)) + (st_datapath (init_state st)) + (st_controllogic (init_state st)). + +Definition transl_module (f : function) : Errors.res module := + run_mon (max_state f) (transf_module f). + +Definition transl_fundef := transf_partial_fundef transl_module. + +(* Definition transl_program (p : RTL.program) := transform_partial_program transl_fundef p. *) + +(*Definition transl_main_fundef f : Errors.res HTL.fundef := + match f with + | Internal f => transl_fundef (Internal f) + | External f => Errors.Error (Errors.msg "Could not find internal main function") + end. + +(** Translation of a whole program. *) + +Definition transl_program (p: RTL.program) : Errors.res HTL.program := + transform_partial_program2 (fun i f => if Pos.eqb p.(AST.prog_main) i + then transl_fundef f + else transl_main_fundef f) + (fun i v => Errors.OK v) p. +*) + +Definition main_is_internal (p : RTLBlock.program) : bool := + let ge := Globalenvs.Genv.globalenv p in + match Globalenvs.Genv.find_symbol ge p.(AST.prog_main) with + | Some b => + match Globalenvs.Genv.find_funct_ptr ge b with + | Some (AST.Internal _) => true + | _ => false + end + | _ => false + end. + +Definition transl_program (p : RTLBlock.program) : Errors.res HTL.program := + if main_is_internal p + then transform_partial_program transl_fundef p + else Errors.Error (Errors.msg "Main function is not Internal."). +*) diff --git a/src/hls/HTLSchedulegen.v b/src/hls/HTLSchedulegen.v new file mode 100644 index 0000000..1ae3bf6 --- /dev/null +++ b/src/hls/HTLSchedulegen.v @@ -0,0 +1,42 @@ +(* + * Vericert: Verified high-level synthesis. + * Copyright (C) 2020 Yann Herklotz <yann@yannherklotz.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + *) + +From compcert Require Import Maps. +From compcert Require Errors Globalenvs Integers. +From compcert Require Import AST. +From vericert Require Import RTLBlock Verilog HTL Vericertlib AssocMap ValueInt Statemonad. + +Parameter transl_module : function -> Errors.res module. + +Definition transl_fundef := transf_partial_fundef transl_module. + +Definition main_is_internal (p : RTLBlock.program) : bool := + let ge := Globalenvs.Genv.globalenv p in + match Globalenvs.Genv.find_symbol ge p.(AST.prog_main) with + | Some b => + match Globalenvs.Genv.find_funct_ptr ge b with + | Some (AST.Internal _) => true + | _ => false + end + | _ => false + end. + +Definition transl_program (p : RTLBlock.program) : Errors.res HTL.program := + if main_is_internal p + then transform_partial_program transl_fundef p + else Errors.Error (Errors.msg "Main function is not Internal."). diff --git a/src/translation/HTLgen.v b/src/hls/HTLgen.v index 68e0293..5f0f8bf 100644 --- a/src/translation/HTLgen.v +++ b/src/hls/HTLgen.v @@ -358,10 +358,10 @@ Definition translate_instr (op : Op.operation) (args : list reg) : mon expr := | Op.Oshlimm n, r::nil => ret (boplit Vshl r n) | Op.Oshr, r1::r2::nil => ret (bop Vshr r1 r2) | Op.Oshrimm n, r::nil => ret (boplit Vshr r n) - | Op.Oshrximm n, r::nil => error (Errors.msg "Htlgen: Instruction not implemented: Oshrximm") - (*ret (Vbinop Vdiv (Vvar r) - (Vbinop Vshl (Vlit (ZToValue 1)) - (Vlit (intToValue n))))*) + | Op.Oshrximm n, r::nil => + ret (Vternary (Vbinop Vlt (Vvar r) (Vlit (ZToValue 0))) + (Vunop Vneg (Vbinop Vshru (Vunop Vneg (Vvar r)) (Vlit n))) + (Vbinop Vshru (Vvar r) (Vlit n))) | Op.Oshru, r1::r2::nil => ret (bop Vshru r1 r2) | Op.Oshruimm n, r::nil => ret (boplit Vshru r n) | Op.Ororimm n, r::nil => error (Errors.msg "Htlgen: Instruction not implemented: Ororimm") diff --git a/src/translation/HTLgenproof.v b/src/hls/HTLgenproof.v index bf63800..afc827d 100644 --- a/src/translation/HTLgenproof.v +++ b/src/hls/HTLgenproof.v @@ -1,4 +1,4 @@ -(* + (* * Vericert: Verified high-level synthesis. * Copyright (C) 2020 Yann Herklotz <yann@yannherklotz.com> * @@ -470,7 +470,11 @@ Section CORRECTNESS. | |- context[match ?d with _ => _ end] => destruct d eqn:?; try discriminate | H : match ?d with _ => _ end = _ |- _ => repeat unfold_match H | H : match ?d with _ => _ end _ = _ |- _ => repeat unfold_match H - | |- Verilog.expr_runp _ _ _ _ _ => econstructor + | |- Verilog.expr_runp _ _ _ ?f _ => + match f with + | Verilog.Vternary _ _ _ => idtac + | _ => econstructor + end | |- val_value_lessdef (?f _ _) _ => unfold f | |- val_value_lessdef (?f _) _ => unfold f | H : ?f (Registers.Regmap.get _ _) _ = Some _ |- _ => @@ -716,6 +720,71 @@ Section CORRECTNESS. repeat unfold_match TR_INSTR; inv TR_INSTR; repeat econstructor. Qed. + Lemma eval_correct_Oshrximm : + forall s sp rs m v e asr asa f f' stk s' i pc res0 pc' args res ml st n, + match_states (RTL.State stk f sp pc rs m) (HTL.State res ml st asr asa) -> + (RTL.fn_code f) ! pc = Some (RTL.Iop (Op.Oshrximm n) args res0 pc') -> + Op.eval_operation ge sp (Op.Oshrximm n) + (List.map (fun r : BinNums.positive => + Registers.Regmap.get r rs) args) m = Some v -> + translate_instr (Op.Oshrximm n) args s = OK e s' i -> + exists v', Verilog.expr_runp f' asr asa e v' /\ val_value_lessdef v v'. + Proof. + intros s sp rs m v e asr asa f f' stk s' i pc pc' res0 args res ml st n MSTATE INSTR EVAL TR_INSTR. + pose proof MSTATE as MSTATE_2. inv MSTATE. + inv MASSOC. unfold translate_instr in TR_INSTR; repeat (unfold_match TR_INSTR); inv TR_INSTR; + unfold Op.eval_operation in EVAL; repeat (unfold_match EVAL); inv EVAL. + (*repeat (simplify; eval_correct_tac; unfold valueToInt in * ). + destruct (Z_lt_ge_dec (Int.signed i0) 0). + econstructor.*) + unfold Values.Val.shrx in *. + destruct v0; try discriminate. + destruct (Int.ltu n (Int.repr 31)) eqn:?; try discriminate. + inversion H1. clear H1. + assert (Int.unsigned n <= 30). + { unfold Int.ltu in *. destruct (zlt (Int.unsigned n) (Int.unsigned (Int.repr 31))); try discriminate. + rewrite Int.unsigned_repr in l by (simplify; lia). + replace 31 with (Z.succ 30) in l by auto. + apply Zlt_succ_le in l. + auto. + } + rewrite IntExtra.shrx_shrx_alt_equiv in H2 by auto. + unfold IntExtra.shrx_alt in *. + destruct (zlt (Int.signed i0) 0). + - repeat econstructor; unfold valueToBool, boolToValue, uvalueToZ, natToValue; + repeat (simplify; eval_correct_tac). + inv_lessdef. unfold valueToInt in *. rewrite H3 in H1. + inv H1. + unfold Int.lt in *. rewrite zlt_true in Heqb0. simplify. + rewrite Int.unsigned_repr in Heqb0. discriminate. + simplify; lia. + unfold ZToValue. rewrite Int.signed_repr by (simplify; lia). + auto. + rewrite H3 in H1; discriminate. + rewrite H3 in H2; discriminate. + rewrite IntExtra.shrx_shrx_alt_equiv; auto. unfold IntExtra.shrx_alt. rewrite zlt_true; try lia. + simplify. inv_lessdef. unfold valueToInt in *. + rewrite H3 in H1. auto. inv H1. auto. + rewrite H3 in H1. discriminate. + rewrite H3 in H2. discriminate. + - econstructor; econstructor; [eapply Verilog.erun_Vternary_false|idtac]; repeat econstructor; unfold valueToBool, boolToValue, uvalueToZ, natToValue; + repeat (simplify; eval_correct_tac). + inv_lessdef. unfold valueToInt in *. rewrite H3 in H1. + inv H1. + unfold Int.lt in *. rewrite zlt_false in Heqb0. simplify. + rewrite Int.unsigned_repr in Heqb0. lia. + simplify; lia. + unfold ZToValue. rewrite Int.signed_repr by (simplify; lia). + auto. + rewrite H3 in H1; discriminate. + rewrite H3 in H2; discriminate. + rewrite IntExtra.shrx_shrx_alt_equiv; auto. unfold IntExtra.shrx_alt. rewrite zlt_false; try lia. + simplify. inv_lessdef. unfold valueToInt in *. + rewrite H3 in H1. auto. inv H1. auto. + rewrite H3 in H1. discriminate. + rewrite H3 in H2. discriminate. + Qed. + Lemma eval_correct : forall s sp op rs m v e asr asa f f' stk s' i pc res0 pc' args res ml st, match_states (RTL.State stk f sp pc rs m) (HTL.State res ml st asr asa) -> @@ -754,6 +823,47 @@ Section CORRECTNESS. - rewrite Heqb in Heqb0. discriminate. (*- unfold Int.ror. unfold Int.or. unfold Int.shru, Int.shl, Int.sub. unfold intToValue. unfold Int.modu, repeat (rewrite Int.unsigned_repr). auto.*) + - assert (Int.unsigned n <= 30). + { unfold Int.ltu in *. destruct (zlt (Int.unsigned n) (Int.unsigned (Int.repr 31))); try discriminate. + rewrite Int.unsigned_repr in l by (simplify; lia). + replace 31 with (Z.succ 30) in l by auto. + apply Zlt_succ_le in l. + auto. + } + destruct (zlt (Int.signed i0) 0). + + repeat econstructor; unfold valueToBool, boolToValue, uvalueToZ, natToValue; + repeat (simplify; eval_correct_tac). + rewrite IntExtra.shrx_shrx_alt_equiv; auto. unfold IntExtra.shrx_alt. rewrite zlt_true; try lia. + simplify. inv_lessdef. unfold valueToInt in *. + rewrite Heqv0 in H0. auto. inv H0. auto. + rewrite Heqv0 in H2. discriminate. + unfold valueToInt in l. auto. + inv_lessdef. unfold valueToInt in *. rewrite Heqv0 in H0. + inv H0. + unfold Int.lt in *. rewrite zlt_true in Heqb0. simplify. + rewrite Int.unsigned_repr in Heqb0. discriminate. + simplify; lia. + unfold ZToValue. rewrite Int.signed_repr by (simplify; lia). + auto. + rewrite Heqv0 in H0; discriminate. + rewrite Heqv0 in H2; discriminate. + + eapply Verilog.erun_Vternary_false; repeat econstructor; unfold valueToBool, boolToValue, uvalueToZ, natToValue; + repeat (simplify; eval_correct_tac). + rewrite IntExtra.shrx_shrx_alt_equiv; auto. unfold IntExtra.shrx_alt. rewrite zlt_false; try lia. + simplify. inv_lessdef. unfold valueToInt in *. + rewrite Heqv0 in H0. auto. inv H0. auto. + rewrite Heqv0 in H2. discriminate. + unfold valueToInt in *. auto. + inv_lessdef. unfold valueToInt in *. + rewrite Heqv0 in H0. + inv H0. + unfold Int.lt in *. rewrite zlt_false in Heqb0. simplify. + rewrite Int.unsigned_repr in Heqb0. lia. + simplify; lia. + unfold ZToValue. rewrite Int.signed_repr by (simplify; lia). + auto. + rewrite Heqv0 in H0; discriminate. + rewrite Heqv0 in H2; discriminate. - unfold Op.eval_addressing32 in *. repeat (unfold_match H2); inv H2. + unfold translate_eff_addressing in *. repeat (unfold_match H1). destruct v0; inv Heql; rewrite H2; inv H1; repeat eval_correct_tac. diff --git a/src/translation/HTLgenspec.v b/src/hls/HTLgenspec.v index 541f9fa..541f9fa 100644 --- a/src/translation/HTLgenspec.v +++ b/src/hls/HTLgenspec.v diff --git a/src/hls/Partition.ml b/src/hls/Partition.ml new file mode 100644 index 0000000..d9bccad --- /dev/null +++ b/src/hls/Partition.ml @@ -0,0 +1,125 @@ + (* + * Vericert: Verified high-level synthesis. + * Copyright (C) 2020 Yann Herklotz <yann@yannherklotz.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + *) + +open Printf +open Clflags +open Camlcoq +open Datatypes +open Coqlib +open Maps +open AST +open Kildall +open Op +open RTLBlock + +(* Assuming that the nodes of the CFG [code] are numbered in reverse postorder (cf. pass + [Renumber]), an edge from [n] to [s] is a normal edge if [s < n] and a back-edge otherwise. *) +let find_edge i n = + let succ = RTL.successors_instr i in + let filt = List.filter (fun s -> P.lt n s || P.lt s (P.pred n)) succ in + ((match filt with [] -> [] | _ -> [n]), filt) + +let find_edges c = + PTree.fold (fun l n i -> + let f = find_edge i n in + (List.append (fst f) (fst l), List.append (snd f) (snd l))) c ([], []) + +let prepend_instr i = function + | {bb_body = bb; bb_exit = e} -> {bb_body = (i :: bb); bb_exit = e} + +let translate_inst = function + | RTL.Inop _ -> Some RBnop + | RTL.Iop (op, ls, dst, _) -> Some (RBop (op, ls, dst)) + | RTL.Iload (m, addr, ls, dst, _) -> Some (RBload (m, addr, ls, dst)) + | RTL.Istore (m, addr, ls, src, _) -> Some (RBstore (m, addr, ls, src)) + | _ -> None + +let translate_cfi = function + | RTL.Icall (s, r, ls, dst, n) -> Some (RBcall (s, r, ls, dst, n)) + | RTL.Itailcall (s, r, ls) -> Some (RBtailcall (s, r, ls)) + | RTL.Ibuiltin (e, ls, r, n) -> Some (RBbuiltin (e, ls, r, n)) + | RTL.Icond (c, ls, dst1, dst2) -> Some (RBcond (c, ls, dst1, dst2)) + | RTL.Ijumptable (r, ls) -> Some (RBjumptable (r, ls)) + | RTL.Ireturn r -> Some (RBreturn r) + | _ -> None + +let rec next_bblock_from_RTL is_start e (c : RTL.code) s i = + let succ = List.map (fun i -> (i, PTree.get i c)) (RTL.successors_instr i) in + let trans_inst = (translate_inst i, translate_cfi i) in + match trans_inst, succ with + | (None, Some i'), _ -> + if List.exists (fun x -> x = s) (snd e) && not is_start then + Errors.OK { bb_body = []; bb_exit = Some (RBgoto s) } + else + Errors.OK { bb_body = []; bb_exit = Some i' } + | (Some i', None), (s', Some i_n)::[] -> + if List.exists (fun x -> x = s) (fst e) then + Errors.OK { bb_body = [i']; bb_exit = Some (RBgoto s') } + else if List.exists (fun x -> x = s) (snd e) && not is_start then + Errors.OK { bb_body = []; bb_exit = Some (RBgoto s) } + else begin + match next_bblock_from_RTL false e c s' i_n with + | Errors.OK bb -> + Errors.OK (prepend_instr i' bb) + | Errors.Error msg -> Errors.Error msg + end + | _, _ -> + Errors.Error (Errors.msg (coqstring_of_camlstring "next_bblock_from_RTL went wrong.")) + +let rec traverseacc f l c = + match l with + | [] -> Errors.OK c + | x::xs -> + match f x c with + | Errors.Error msg -> Errors.Error msg + | Errors.OK x' -> + match traverseacc f xs x' with + | Errors.Error msg -> Errors.Error msg + | Errors.OK xs' -> Errors.OK xs' + +let rec translate_all edge c s res = + let c_bb, translated = res in + if List.exists (fun x -> P.eq x s) translated then Errors.OK (c_bb, translated) else + (match PTree.get s c with + | None -> Errors.Error (Errors.msg (coqstring_of_camlstring "Could not translate all.")) + | Some i -> + match next_bblock_from_RTL true edge c s i with + | Errors.Error msg -> Errors.Error msg + | Errors.OK {bb_exit = None; _} -> + Errors.Error (Errors.msg (coqstring_of_camlstring "Error in translate all")) + | Errors.OK {bb_body = bb; bb_exit = Some e} -> + let succ = List.filter (fun x -> P.lt x s) (successors_instr e) in + (match traverseacc (translate_all edge c) succ (c_bb, s :: translated) with + | Errors.Error msg -> Errors.Error msg + | Errors.OK (c', t') -> + Errors.OK (PTree.set s {bb_body = bb; bb_exit = Some e} c', t'))) + +(* Partition a function and transform it into RTLBlock. *) +let function_from_RTL f = + let e = find_edges f.RTL.fn_code in + match translate_all e f.RTL.fn_code f.RTL.fn_entrypoint (PTree.empty, []) with + | Errors.Error msg -> Errors.Error msg + | Errors.OK (c, _) -> + Errors.OK { fn_sig = f.RTL.fn_sig; + fn_stacksize = f.RTL.fn_stacksize; + fn_params = f.RTL.fn_params; + fn_entrypoint = f.RTL.fn_entrypoint; + fn_code = c + } + +let partition = function_from_RTL diff --git a/src/verilog/PrintHTL.ml b/src/hls/PrintHTL.ml index 44f8b01..44f8b01 100644 --- a/src/verilog/PrintHTL.ml +++ b/src/hls/PrintHTL.ml diff --git a/src/hls/PrintRTLBlock.ml b/src/hls/PrintRTLBlock.ml new file mode 100644 index 0000000..45bd253 --- /dev/null +++ b/src/hls/PrintRTLBlock.ml @@ -0,0 +1,119 @@ +(* *********************************************************************) +(* *) +(* The Compcert verified compiler *) +(* *) +(* Xavier Leroy, INRIA Paris-Rocquencourt *) +(* *) +(* Copyright Institut National de Recherche en Informatique et en *) +(* Automatique. All rights reserved. This file is distributed *) +(* under the terms of the INRIA Non-Commercial License Agreement. *) +(* *) +(* *********************************************************************) + +(** Pretty-printers for RTL code *) + +open Printf +open Camlcoq +open Datatypes +open Maps +open AST +open RTLBlock +open PrintAST + +(* Printing of RTL code *) + +let reg pp r = + fprintf pp "x%d" (P.to_int r) + +let rec regs pp = function + | [] -> () + | [r] -> reg pp r + | r1::rl -> fprintf pp "%a, %a" reg r1 regs rl + +let ros pp = function + | Coq_inl r -> reg pp r + | Coq_inr s -> fprintf pp "\"%s\"" (extern_atom s) + +let print_bblock_body pp i = + fprintf pp "\t\t"; + match i with + | RBnop -> fprintf pp "nop\n" + | RBop(op, ls, dst) -> + fprintf pp "%a = %a\n" + reg dst (PrintOp.print_operation reg) (op, ls) + | RBload(chunk, addr, args, dst) -> + fprintf pp "%a = %s[%a]\n" + reg dst (name_of_chunk chunk) + (PrintOp.print_addressing reg) (addr, args) + | RBstore(chunk, addr, args, src) -> + fprintf pp "%s[%a] = %a\n" + (name_of_chunk chunk) + (PrintOp.print_addressing reg) (addr, args) + reg src + +let print_bblock_exit pp i = + fprintf pp "\t\t"; + match i with + | Some(RBcall(_, fn, args, res, _)) -> + fprintf pp "%a = %a(%a)\n" + reg res ros fn regs args; + | Some(RBtailcall(_, fn, args)) -> + fprintf pp "tailcall %a(%a)\n" + ros fn regs args + | Some(RBbuiltin(ef, args, res, _)) -> + fprintf pp "%a = %s(%a)\n" + (print_builtin_res reg) res + (name_of_external ef) + (print_builtin_args reg) args + | Some(RBcond(cond, args, s1, s2)) -> + fprintf pp "if (%a) goto %d else goto %d\n" + (PrintOp.print_condition reg) (cond, args) + (P.to_int s1) (P.to_int s2) + | Some(RBjumptable(arg, tbl)) -> + let tbl = Array.of_list tbl in + fprintf pp "jumptable (%a)\n" reg arg; + for i = 0 to Array.length tbl - 1 do + fprintf pp "\tcase %d: goto %d\n" i (P.to_int tbl.(i)) + done + | Some(RBreturn None) -> + fprintf pp "return\n" + | Some(RBreturn (Some arg)) -> + fprintf pp "return %a\n" reg arg + | Some(RBgoto n) -> + fprintf pp "goto %d\n" (P.to_int n) + | None -> fprintf pp "\n" + +let print_bblock pp (pc, i) = + fprintf pp "%5d:{\n" pc; + List.iter (print_bblock_body pp) i.bb_body; + print_bblock_exit pp i.bb_exit; + fprintf pp "\t}\n\n" + +let print_function pp id f = + fprintf pp "%s(%a) {\n" (extern_atom id) regs f.fn_params; + let instrs = + List.sort + (fun (pc1, _) (pc2, _) -> compare pc2 pc1) + (List.rev_map + (fun (pc, i) -> (P.to_int pc, i)) + (PTree.elements f.fn_code)) in + List.iter (print_bblock pp) instrs; + fprintf pp "}\n\n" + +let print_globdef pp (id, gd) = + match gd with + | Gfun(Internal f) -> print_function pp id f + | _ -> () + +let print_program pp (prog: program) = + List.iter (print_globdef pp) prog.prog_defs + +let destination : string option ref = ref None + +let print_if prog = + match !destination with + | None -> () + | Some f -> + let oc = open_out f in + print_program oc prog; + close_out oc diff --git a/src/verilog/PrintVerilog.ml b/src/hls/PrintVerilog.ml index 44710b8..44710b8 100644 --- a/src/verilog/PrintVerilog.ml +++ b/src/hls/PrintVerilog.ml diff --git a/src/verilog/PrintVerilog.mli b/src/hls/PrintVerilog.mli index 6a15ee9..6a15ee9 100644 --- a/src/verilog/PrintVerilog.mli +++ b/src/hls/PrintVerilog.mli diff --git a/src/hls/RTLBlock.v b/src/hls/RTLBlock.v new file mode 100644 index 0000000..dc505ed --- /dev/null +++ b/src/hls/RTLBlock.v @@ -0,0 +1,170 @@ +(* + * Vericert: Verified high-level synthesis. + * Copyright (C) 2020 Yann Herklotz <yann@yannherklotz.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + *) + +Require Import Coqlib Maps. +Require Import AST Integers Values Events Memory Globalenvs Smallstep. +Require Import Op Registers. + +Definition node := positive. + +Inductive instruction : Type := +| RBnop : instruction +| RBop : operation -> list reg -> reg -> instruction +| RBload : memory_chunk -> addressing -> list reg -> reg -> instruction +| RBstore : memory_chunk -> addressing -> list reg -> reg -> instruction. + +Definition bblock_body : Type := list instruction. + +Inductive control_flow_inst : Type := +| RBcall : signature -> reg + ident -> list reg -> reg -> node -> control_flow_inst +| RBtailcall : signature -> reg + ident -> list reg -> control_flow_inst +| RBbuiltin : external_function -> list (builtin_arg reg) -> + builtin_res reg -> node -> control_flow_inst +| RBcond : condition -> list reg -> node -> node -> control_flow_inst +| RBjumptable : reg -> list node -> control_flow_inst +| RBreturn : option reg -> control_flow_inst +| RBgoto : node -> control_flow_inst. + +Record bblock : Type := mk_bblock { + bb_body: bblock_body; + bb_exit: option control_flow_inst + }. + +Definition code : Type := PTree.t bblock. + +Record function: Type := mkfunction { + fn_sig: signature; + fn_params: list reg; + fn_stacksize: Z; + fn_code: code; + fn_entrypoint: node +}. + +Definition fundef := AST.fundef function. + +Definition program := AST.program fundef unit. + +Definition funsig (fd: fundef) := + match fd with + | Internal f => fn_sig f + | External ef => ef_sig ef + end. + +Definition successors_instr (i : control_flow_inst) : list node := + match i with + | RBcall sig ros args res s => s :: nil + | RBtailcall sig ros args => nil + | RBbuiltin ef args res s => s :: nil + | RBcond cond args ifso ifnot => ifso :: ifnot :: nil + | RBjumptable arg tbl => tbl + | RBreturn optarg => nil + | RBgoto n => n :: nil + end. + +(* Definition genv := Genv.t fundef unit. +Definition regset := Regmap.t val. + +Fixpoint init_regs (vl: list val) (rl: list reg) {struct rl} : regset := + match rl, vl with + | r1 :: rs, v1 :: vs => Regmap.set r1 v1 (init_regs vs rs) + | _, _ => Regmap.init Vundef + end. + +Inductive stackframe : Type := + | Stackframe: + forall (res: reg) (**r where to store the result *) + (f: function) (**r calling function *) + (sp: val) (**r stack pointer in calling function *) + (pc: node) (**r program point in calling function *) + (rs: regset), (**r register state in calling function *) + stackframe. + +Inductive cont : Type := + | C + | N. + +Inductive state : Type := + | State: + forall (stack: list stackframe) (**r call stack *) + (f: function) (**r current function *) + (sp: val) (**r stack pointer *) + (pc: node) (**r current program point in [c] *) + (rs: regset) (**r register state *) + (m: mem) (**r memory state *) + (bblock: bblock) (**r bblock being executed *) + (c : cont), + state + | Callstate: + forall (stack: list stackframe) (**r call stack *) + (f: fundef) (**r function to call *) + (args: list val) (**r arguments to the call *) + (m: mem), (**r memory state *) + state + | Returnstate: + forall (stack: list stackframe) (**r call stack *) + (v: val) (**r return value for the call *) + (m: mem), (**r memory state *) + state. + +Section RELSEM. + +Variable ge: genv. + +Definition find_function + (ros: reg + ident) (rs: regset) : option fundef := + match ros with + | inl r => Genv.find_funct ge rs#r + | inr symb => + match Genv.find_symbol ge symb with + | None => None + | Some b => Genv.find_funct_ptr ge b + end + end. + +Inductive step : state -> trace -> state -> Prop := + | exec_RBnop : + forall s f sp pc rs m ls ci, + step (State s f sp pc rs m (mk_bblock (RBnop :: ls) ci) C) E0 + (State s f sp (Pos.succ pc) rs m (mk_bblock ls ci) C) + | exec_RBop : + forall s f sp pc rs m ls args op res ci v, + eval_operation ge sp op rs##args m = Some v -> + step (State s f sp pc rs m (mk_bblock (RBop op args res :: ls) ci) C) E0 + (State s f sp (Pos.succ pc) rs m (mk_bblock ls ci) C) + | exec_RBload: + forall s f sp pc rs m chunk addr args dst a v ls ci, + eval_addressing ge sp addr rs##args = Some a -> + Mem.loadv chunk m a = Some v -> + step (State s f sp pc rs m (mk_bblock (RBload chunk addr args dst :: ls) ci) C) + E0 (State s f sp (Pos.succ pc) (rs#dst <- v) m (mk_bblock ls ci) C) + | exec_RBstore: + forall s f sp pc rs m chunk addr args src a m' ls ci, + eval_addressing ge sp addr rs##args = Some a -> + Mem.storev chunk m a rs#src = Some m' -> + step (State s f sp pc rs m (mk_bblock (RBstore chunk addr args src :: ls) ci) C) + E0 (State s f sp (Pos.succ pc) rs m' (mk_bblock ls ci) C) + | exec_RBcond: + forall s f sp pc rs m cond args ifso ifnot b pc', + eval_condition cond rs##args m = Some b -> + pc' = (if b then ifso else ifnot) -> + step (State s f sp pc rs m (mk_bblock nil (RBcond cond args ifso ifnot)) C) + E0 (State s f sp pc' rs m (mk_bblock nil (RBcond cond args ifso ifnot)) N) +. + +End RELSEM. +*) diff --git a/src/hls/RTLBlockgen.v b/src/hls/RTLBlockgen.v new file mode 100644 index 0000000..6b3e9c3 --- /dev/null +++ b/src/hls/RTLBlockgen.v @@ -0,0 +1,32 @@ +(* + * Vericert: Verified high-level synthesis. + * Copyright (C) 2020 Yann Herklotz <yann@yannherklotz.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + *) + +From compcert Require + RTL. +From compcert Require Import + AST + Maps. +From vericert Require Import + RTLBlock. + +Parameter partition : RTL.function -> Errors.res function. + +Definition transl_fundef := transf_partial_fundef partition. + +Definition transl_program : RTL.program -> Errors.res program := + transform_partial_program transl_fundef. diff --git a/src/hls/RTLPar.v b/src/hls/RTLPar.v new file mode 100644 index 0000000..af3f28b --- /dev/null +++ b/src/hls/RTLPar.v @@ -0,0 +1,99 @@ +(* + * Vericert: Verified high-level synthesis. + * Copyright (C) 2020 Yann Herklotz <yann@yannherklotz.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + *) + +From compcert Require Import Coqlib Maps. +From compcert Require Import AST Integers Values Events Memory Globalenvs Smallstep. +From compcert Require Import Op Registers. + +Definition node := positive. + +Inductive instruction : Type := +| RPnop : instruction +| RPop : operation -> list reg -> reg -> instruction +| RPload : memory_chunk -> addressing -> list reg -> reg -> instruction +| RPstore : memory_chunk -> addressing -> list reg -> reg -> instruction. + +Definition bblock_body : Type := list (list instruction). + +Inductive control_flow_inst : Type := +| RPcall : signature -> reg + ident -> list reg -> reg -> node -> control_flow_inst +| RPtailcall : signature -> reg + ident -> list reg -> control_flow_inst +| RPbuiltin : external_function -> list (builtin_arg reg) -> + builtin_res reg -> node -> control_flow_inst +| RPcond : condition -> list reg -> node -> node -> control_flow_inst +| RPjumptable : reg -> list node -> control_flow_inst +| RPreturn : option reg -> control_flow_inst +| RPgoto : node -> control_flow_inst. + +Record bblock : Type := mk_bblock { + bb_body: bblock_body; + bb_exit: option control_flow_inst + }. + +Definition code : Type := PTree.t bblock. + +Record function: Type := mkfunction { + fn_sig: signature; + fn_params: list reg; + fn_stacksize: Z; + fn_code: code; + fn_entrypoint: node +}. + +Definition fundef := AST.fundef function. + +Definition program := AST.program fundef unit. + +Definition funsig (fd: fundef) := + match fd with + | Internal f => fn_sig f + | External ef => ef_sig ef + end. + +Definition successors_instr (i : control_flow_inst) : list node := + match i with + | RPcall sig ros args res s => s :: nil + | RPtailcall sig ros args => nil + | RPbuiltin ef args res s => s :: nil + | RPcond cond args ifso ifnot => ifso :: ifnot :: nil + | RPjumptable arg tbl => tbl + | RPreturn optarg => nil + | RPgoto n => n :: nil + end. + +(*Inductive state : Type := + | State: + forall (stack: list stackframe) (**r call stack *) + (f: function) (**r current function *) + (sp: val) (**r stack pointer *) + (pc: node) (**r current program point in [c] *) + (rs: regset) (**r register state *) + (m: mem), (**r memory state *) + state + | Callstate: + forall (stack: list stackframe) (**r call stack *) + (f: fundef) (**r function to call *) + (args: list val) (**r arguments to the call *) + (m: mem), (**r memory state *) + state + | Returnstate: + forall (stack: list stackframe) (**r call stack *) + (v: val) (**r return value for the call *) + (m: mem), (**r memory state *) + state. +*) diff --git a/src/hls/Schedule.ml b/src/hls/Schedule.ml new file mode 100644 index 0000000..982aa0e --- /dev/null +++ b/src/hls/Schedule.ml @@ -0,0 +1,610 @@ +(* + * Vericert: Verified high-level synthesis. + * Copyright (C) 2020 Yann Herklotz <yann@yannherklotz.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + *) + +open Printf +open Clflags +open Camlcoq +open Datatypes +open Coqlib +open Maps +open AST +open Kildall +open Op +open RTLBlock +open HTL +open Verilog +open HTLgen +open HTLMonad +open HTLMonadExtra + +module IMap = Map.Make (struct + type t = int + + let compare = compare +end) + +type dfg = { nodes : instruction list; edges : (int * int) list } +(** The DFG type defines a list of instructions with their data dependencies as [edges], which are + the pairs of integers that represent the index of the instruction in the [nodes]. The edges + always point from left to right. *) + +let print_list f out_chan a = + fprintf out_chan "[ "; + List.iter (fprintf out_chan "%a " f) a; + fprintf out_chan "]" + +let print_tuple out_chan a = + let l, r = a in + fprintf out_chan "(%d,%d)" l r + +let print_dfg out_chan dfg = + fprintf out_chan "{ nodes = %a, edges = %a }" + (print_list PrintRTLBlock.print_bblock_body) + dfg.nodes (print_list print_tuple) dfg.edges + +let read_process command = + let buffer_size = 2048 in + let buffer = Buffer.create buffer_size in + let string = Bytes.create buffer_size in + let in_channel = Unix.open_process_in command in + let chars_read = ref 1 in + while !chars_read <> 0 do + chars_read := input in_channel string 0 buffer_size; + Buffer.add_substring buffer (Bytes.to_string string) 0 !chars_read + done; + ignore (Unix.close_process_in in_channel); + Buffer.contents buffer + +(** Add a dependency if it uses a register that was written to previously. *) +let add_dep i tree deps curr = + match PTree.get curr tree with None -> deps | Some ip -> (ip, i) :: deps + +(** This function calculates the dependencies of each instruction. The nodes correspond to previous + register that were allocated and show which instruction caused it. + + This function only gathers the RAW constraints, and will therefore only be active for operations + that modify registers, which is this case only affects loads and operations. *) +let accumulate_RAW_deps dfg curr = + let i, dst_map, { edges; nodes } = dfg in + let acc_dep_instruction rs dst = + ( i + 1, + PTree.set dst i dst_map, + { + nodes; + edges = List.append (List.fold_left (add_dep i dst_map) [] rs) edges; + } ) + in + let acc_dep_instruction_nodst rs = + ( i + 1, + dst_map, + { + nodes; + edges = List.append (List.fold_left (add_dep i dst_map) [] rs) edges; + } ) + in + match curr with + | RBop (_, rs, dst) -> acc_dep_instruction rs dst + | RBload (_mem, _addr, rs, dst) -> acc_dep_instruction rs dst + | RBstore (_mem, _addr, rs, src) -> acc_dep_instruction_nodst (src :: rs) + | _ -> (i + 1, dst_map, { edges; nodes }) + +(** Finds the next write to the [dst] register. This is a small optimisation so that only one + dependency is generated for a data dependency. *) +let rec find_next_dst_write i dst i' curr = + let check_dst dst' curr' = + if dst = dst' then Some (i, i') + else find_next_dst_write i dst (i' + 1) curr' + in + match curr with + | [] -> None + | RBop (_, _, dst') :: curr' -> check_dst dst' curr' + | RBload (_, _, _, dst') :: curr' -> check_dst dst' curr' + | _ :: curr' -> find_next_dst_write i dst (i' + 1) curr' + +let rec find_all_next_dst_read i dst i' curr = + let check_dst rs curr' = + if List.exists (fun x -> x = dst) rs + then (i, i') :: find_all_next_dst_read i dst (i' + 1) curr' + else find_all_next_dst_read i dst (i' + 1) curr' + in + match curr with + | [] -> [] + | RBop (_, rs, _) :: curr' -> check_dst rs curr' + | RBload (_, _, rs, _) :: curr' -> check_dst rs curr' + | RBstore (_, _, rs, src) :: curr' -> check_dst (src :: rs) curr' + | RBnop :: curr' -> find_all_next_dst_read i dst (i' + 1) curr' + +let drop i lst = + let rec drop' i' lst' = + match lst' with + | _ :: ls -> if i' = i then ls else drop' (i' + 1) ls + | [] -> [] + in + if i = 0 then lst else drop' 1 lst + +let take i lst = + let rec take' i' lst' = + match lst' with + | l :: ls -> if i' = i then [ l ] else l :: take' (i' + 1) ls + | [] -> [] + in + if i = 0 then [] else take' 1 lst + +let rec next_store i = function + | [] -> None + | RBstore (_, _, _, _) :: _ -> Some i + | _ :: rst -> next_store (i + 1) rst + +let rec next_load i = function + | [] -> None + | RBload (_, _, _, _) :: _ -> Some i + | _ :: rst -> next_load (i + 1) rst + +let accumulate_RAW_mem_deps dfg curr = + let i, { nodes; edges } = dfg in + match curr with + | RBload (_, _, _, _) -> ( + match next_store 0 (take i nodes |> List.rev) with + | None -> (i + 1, { nodes; edges }) + | Some d -> (i + 1, { nodes; edges = (i - d - 1, i) :: edges }) ) + | _ -> (i + 1, { nodes; edges }) + +let accumulate_WAR_mem_deps dfg curr = + let i, { nodes; edges } = dfg in + match curr with + | RBstore (_, _, _, _) -> ( + match next_load 0 (take i nodes |> List.rev) with + | None -> (i + 1, { nodes; edges }) + | Some d -> (i + 1, { nodes; edges = (i - d - 1, i) :: edges }) ) + | _ -> (i + 1, { nodes; edges }) + +let accumulate_WAW_mem_deps dfg curr = + let i, { nodes; edges } = dfg in + match curr with + | RBstore (_, _, _, _) -> ( + match next_store 0 (take i nodes |> List.rev) with + | None -> (i + 1, { nodes; edges }) + | Some d -> (i + 1, { nodes; edges = (i - d - 1, i) :: edges }) ) + | _ -> (i + 1, { nodes; edges }) + +(** This function calculates the WAW dependencies, which happen when two writes are ordered one + after another and therefore have to be kept in that order. This accumulation might be redundant + if register renaming is done before hand, because then these dependencies can be avoided. *) +let accumulate_WAW_deps dfg curr = + let i, { edges; nodes } = dfg in + let dst_dep dst = + match find_next_dst_write i dst (i + 1) (drop (i + 1) nodes) with + | Some d -> (i + 1, { nodes; edges = d :: edges }) + | _ -> (i + 1, { nodes; edges }) + in + match curr with + | RBop (_, _, dst) -> dst_dep dst + | RBload (_, _, _, dst) -> dst_dep dst + | RBstore (_, _, _, _) -> ( + match next_store (i + 1) (drop (i + 1) nodes) with + | None -> (i + 1, { nodes; edges }) + | Some i' -> (i + 1, { nodes; edges = (i, i') :: edges }) ) + | _ -> (i + 1, { nodes; edges }) + +let accumulate_WAR_deps dfg curr = + let i, { edges; nodes } = dfg in + let dst_dep dst = + let dep_list = find_all_next_dst_read i dst 0 (take i nodes |> List.rev) + |> List.map (function (d, d') -> (i - d' - 1, d)) + in + (i + 1, { nodes; edges = List.append dep_list edges }) + in + match curr with + | RBop (_, _, dst) -> dst_dep dst + | RBload (_, _, _, dst) -> dst_dep dst + | _ -> (i + 1, { nodes; edges }) + +let assigned_vars vars = function + | RBnop -> vars + | RBop (_, _, dst) -> dst :: vars + | RBload (_, _, _, dst) -> dst :: vars + | RBstore (_, _, _, _) -> vars + +(** All the nodes in the DFG have to come after the source of the basic block, and should terminate + before the sink of the basic block. After that, there should be constraints for data + dependencies between nodes. *) +let gather_bb_constraints debug bb = + let _, _, dfg = + List.fold_left accumulate_RAW_deps + (0, PTree.empty, { nodes = bb.bb_body; edges = [] }) + bb.bb_body + in + if debug then printf "DFG : %a\n" print_dfg dfg else (); + let _, dfg' = List.fold_left accumulate_WAW_deps (0, dfg) bb.bb_body in + if debug then printf "DFG': %a\n" print_dfg dfg' else (); + let _, dfg'' = List.fold_left accumulate_WAR_deps (0, dfg') bb.bb_body in + if debug then printf "DFG'': %a\n" print_dfg dfg'' else (); + let _, dfg''' = + List.fold_left accumulate_RAW_mem_deps (0, dfg'') bb.bb_body + in + if debug then printf "DFG''': %a\n" print_dfg dfg''' else (); + let _, dfg'''' = + List.fold_left accumulate_WAR_mem_deps (0, dfg''') bb.bb_body + in + if debug then printf "DFG'''': %a\n" print_dfg dfg'''' else (); + let _, dfg''''' = + List.fold_left accumulate_WAW_mem_deps (0, dfg'''') bb.bb_body + in + if debug then printf "DFG''''': %a\n" print_dfg dfg''''' else (); + match bb.bb_exit with + | None -> assert false + | Some e -> (List.length bb.bb_body, dfg''''', successors_instr e) + +let gen_bb_name s i = sprintf "bb%d%s" (P.to_int i) s + +let gen_bb_name_ssrc = gen_bb_name "ssrc" + +let gen_bb_name_ssnk = gen_bb_name "ssnk" + +let gen_var_name s c i = sprintf "v%d%s_%d" (P.to_int i) s c + +let gen_var_name_b = gen_var_name "b" + +let gen_var_name_e = gen_var_name "e" + +let print_lt0 = sprintf "%s - %s <= 0;\n" + +let print_bb_order i c = if P.to_int c < P.to_int i then + print_lt0 (gen_bb_name_ssnk i) (gen_bb_name_ssrc c) else + "" + +let print_src_order i c = + print_lt0 (gen_bb_name_ssrc i) (gen_var_name_b c i) + ^ print_lt0 (gen_var_name_e c i) (gen_bb_name_ssnk i) + ^ sprintf "%s - %s = 1;\n" (gen_var_name_e c i) (gen_var_name_b c i) + +let print_src_type i c = + sprintf "int %s;\n" (gen_var_name_e c i) + ^ sprintf "int %s;\n" (gen_var_name_b c i) + +let print_data_dep_order c (i, j) = + print_lt0 (gen_var_name_e i c) (gen_var_name_b j c) + +let rec gather_cfg_constraints (completed, (bvars, constraints, types)) c curr = + if List.exists (fun x -> P.eq x curr) completed then + (completed, (bvars, constraints, types)) + else + match PTree.get curr c with + | None -> assert false + | Some (num_iters, dfg, next) -> + let constraints' = + constraints + ^ String.concat "" (List.map (print_bb_order curr) next) + ^ String.concat "" + (List.map (print_src_order curr) + (List.init num_iters (fun x -> x))) + ^ String.concat "" (List.map (print_data_dep_order curr) dfg.edges) + in + let types' = + types + ^ String.concat "" + (List.map (print_src_type curr) + (List.init num_iters (fun x -> x))) + ^ sprintf "int %s;\n" (gen_bb_name_ssrc curr) + ^ sprintf "int %s;\n" (gen_bb_name_ssnk curr) + in + let bvars' = + List.append + (List.map + (fun x -> gen_var_name_b x curr) + (List.init num_iters (fun x -> x))) + bvars + in + let next' = List.filter (fun x -> P.lt x curr) next in + List.fold_left + (fun compl curr' -> gather_cfg_constraints compl c curr') + (curr :: completed, (bvars', constraints', types')) + next' + +let rec intersperse s = function + | [] -> [] + | [ a ] -> [ a ] + | x :: xs -> x :: s :: intersperse s xs + +let update_schedule v = function Some l -> Some (v :: l) | None -> Some [ v ] + +let parse_soln tree s = + let r = Str.regexp "v\([0-9]+\)b_\([0-9]+\)[ ]+\([0-9]+\)" in + if Str.string_match r s 0 then + IMap.update + (Str.matched_group 1 s |> int_of_string) + (update_schedule + ( Str.matched_group 2 s |> int_of_string, + Str.matched_group 3 s |> int_of_string )) + tree + else tree + +let solve_constraints vars constraints types = + let oc = open_out "lpsolve.txt" in + fprintf oc "min: "; + List.iter (fprintf oc "%s") (intersperse " + " vars); + fprintf oc ";\n"; + fprintf oc "%s" constraints; + fprintf oc "%s" types; + close_out oc; + Str.split (Str.regexp_string "\n") (read_process "lp_solve lpsolve.txt") + |> drop 3 + |> List.fold_left parse_soln IMap.empty + +let find_min = function + | [] -> assert false + | l :: ls -> + let rec find_min' current = function + | [] -> current + | l' :: ls' -> + if snd l' < current then find_min' (snd l') ls' + else find_min' current ls' + in + find_min' (snd l) ls + +let find_max = function + | [] -> assert false + | l :: ls -> + let rec find_max' current = function + | [] -> current + | l' :: ls' -> + if snd l' > current then find_max' (snd l') ls' + else find_max' current ls' + in + find_max' (snd l) ls + +type registers = { reg_finish : reg; reg_return : reg; reg_stk : reg } + +let ( >>= ) = bind + +let translate_control_flow r curr_st instr = + match instr with + | RBgoto n -> + fun state -> + OK + ( (), + { + state with + st_controllogic = + PTree.set curr_st (state_goto state.st_st n) + state.st_controllogic; + } ) + | RBcond (c, rs, n1, n2) -> + translate_condition c rs >>= fun ce state -> + OK + ( (), + { + state with + st_controllogic = + PTree.set curr_st + (state_cond state.st_st ce n1 n2) + state.st_controllogic; + } ) + | RBreturn ret -> ( + let new_state state = state.st_freshstate in + match ret with + | None -> + let fin = + Vseq + ( HTLgen.block r.reg_finish + (Vlit (ValueInt.coq_ZToValue (Z.of_uint 1))), + HTLgen.block r.reg_return + (Vlit (ValueInt.coq_ZToValue (Z.of_uint 0))) ) + in + fun state -> + OK + ( (), + { + state with + st_datapath = + PTree.set (new_state state) fin state.st_datapath; + st_controllogic = + PTree.set curr_st + (state_goto state.st_st (new_state state)) + state.st_controllogic; + st_freshstate = P.succ state.st_freshstate; + } ) + | Some ret' -> + let fin = + Vseq + ( HTLgen.block r.reg_finish + (Vlit (ValueInt.coq_ZToValue (Z.of_uint 1))), + HTLgen.block r.reg_return (Vvar ret') ) + in + fun state -> + OK + ( (), + { + state with + st_datapath = + PTree.set (new_state state) fin state.st_datapath; + st_controllogic = + PTree.set curr_st + (state_goto state.st_st (new_state state)) + state.st_controllogic; + st_freshstate = P.succ state.st_freshstate; + } ) ) + | RBjumptable (r, tbl) -> + fun state -> + OK ( (), + { + state with + st_controllogic = PTree.set curr_st (Vcase (Vvar r, HTLgen.tbl_to_case_expr state.st_st tbl, Some Vskip)) state.st_controllogic + }) + | _ -> + error + (Errors.msg + (coqstring_of_camlstring + "Control flow instructions not implemented.")) + +let translate_instr r curr_st instr = + let prev_instr state = + match PTree.get curr_st state.st_datapath with None -> Vskip | Some v -> v + in + match instr with + | RBnop -> + fun state -> + OK + ( (), + { + state with + st_datapath = + PTree.set curr_st (prev_instr state) state.st_datapath; + } ) + | RBop (op, rs, dst) -> + translate_instr op rs >>= fun instr -> + declare_reg None dst (Nat.of_int 32) >>= fun _ state -> + let stmnt = Vseq (prev_instr state, nonblock dst instr) in + OK + ( (), + { state with st_datapath = PTree.set curr_st stmnt state.st_datapath } + ) + | RBload (mem, addr, rs, dst) -> + translate_arr_access mem addr rs r.reg_stk >>= fun src -> + declare_reg None dst (Nat.of_int 32) >>= fun _ state -> + let stmnt = Vseq (prev_instr state, nonblock dst src) in + OK + ( (), + { state with st_datapath = PTree.set curr_st stmnt state.st_datapath } + ) + | RBstore (mem, addr, rs, src) -> + translate_arr_access mem addr rs r.reg_stk >>= fun dst state -> + OK + ( (), + { + state with + st_datapath = + PTree.set curr_st + (Vseq (prev_instr state, Vnonblock (dst, Vvar src))) + state.st_datapath; + } ) + +let combine_bb_schedule schedule s = + let i, st = s in + IMap.update st (update_schedule i) schedule + +let add_schedules r bb_body min_sched curr ischedule = + let i, schedule = ischedule in + let curr_state = curr - i + min_sched in + let instrs = List.map (List.nth bb_body) schedule in + if curr_state = 20 then printf "HII: curr_state: %d : curr: %d\n" curr_state curr; + collectlist (translate_instr r (P.of_int curr_state)) instrs >>= fun _ -> + translate_control_flow r (P.of_int curr_state) + (RBgoto (curr_state - 1 |> P.of_int)) + +(** Should generate the [HTL] code based on the input [RTLBlock] description. *) +let transf_htl r c (schedule : (int * int) list IMap.t) = + let f bbs = + let i, bb = bbs in + match bb with + | { bb_body = []; bb_exit = Some c } -> translate_control_flow r i c + | { bb_body = bb_body'; bb_exit = Some ctrl_flow } -> + let i_sched = + try IMap.find (P.to_int i) schedule + with Not_found -> ( + printf "Could not find %d\n" (P.to_int i); + IMap.iter + (fun d -> printf "%d: %a\n" d (print_list print_tuple)) + schedule; + assert false + ) + in + let min_state = find_min i_sched in + let max_state = find_max i_sched in + let i_sched_tree = + List.fold_left combine_bb_schedule IMap.empty i_sched + in + collectlist + (add_schedules r bb_body' min_state (P.to_int i)) + (IMap.to_seq i_sched_tree |> List.of_seq) + >>= fun _ -> + printf "--------------- curr: %d, max: %d, min: %d, next: %d\n" (P.to_int i) max_state min_state (P.to_int i - max_state + min_state - 1); + printf "HIIIII: %d orig: %d\n" (P.to_int i - max_state + min_state - 1) (P.to_int i); + (match ctrl_flow with + | RBgoto _ -> + translate_control_flow r (P.of_int (P.to_int i - max_state + min_state)) ctrl_flow + | _ -> + translate_control_flow r (P.of_int (P.to_int i - max_state + min_state - 1)) ctrl_flow) + | _ -> + coqstring_of_camlstring "Illegal state reached in scheduler" + |> Errors.msg |> error + in + collectlist f c + +let second = function (_, a, _) -> a + +let schedule entry r (c : code) = + let debug = true in + let c' = PTree.map1 (gather_bb_constraints false) c in + let _ = if debug then PTree.map (fun r o -> printf "##### %d #####\n%a\n\n" (P.to_int r) print_dfg (second o)) c' else PTree.empty in + let _, (vars, constraints, types) = + gather_cfg_constraints ([], ([], "", "")) c' entry + in + let schedule' = solve_constraints vars constraints types in + IMap.iter (fun a b -> printf "##### %d #####\n%a\n\n" a (print_list print_tuple) b) schedule'; + (*printf "Schedule: %a\n" (fun a x -> IMap.iter (fun d -> fprintf a "%d: %a\n" d (print_list print_tuple)) x) schedule';*) + transf_htl r (PTree.elements c) schedule' + +let transl_module' (f : RTLBlock.coq_function) : HTL.coq_module mon = + create_reg (Some Voutput) (Nat.of_int 1) >>= fun fin -> + create_reg (Some Voutput) (Nat.of_int 32) >>= fun rtrn -> + create_arr None (Nat.of_int 32) (Nat.of_int (Z.to_int f.fn_stacksize / 4)) + >>= fun stack_var -> + let stack, stack_len = stack_var in + schedule f.fn_entrypoint + { reg_finish = fin; reg_return = rtrn; reg_stk = stack } + f.fn_code + >>= fun _ -> + collectlist (fun r -> declare_reg (Some Vinput) r (Nat.of_int 32)) f.fn_params + >>= fun _ -> + create_reg (Some Vinput) (Nat.of_int 1) >>= fun start -> + create_reg (Some Vinput) (Nat.of_int 1) >>= fun rst -> + create_reg (Some Vinput) (Nat.of_int 1) >>= fun clk current_state -> + let m = + { + HTL.mod_params = f.fn_params; + HTL.mod_datapath = current_state.st_datapath; + HTL.mod_controllogic = current_state.st_controllogic; + HTL.mod_entrypoint = f.fn_entrypoint; + HTL.mod_st = current_state.st_st; + HTL.mod_stk = stack; + HTL.mod_stk_len = stack_len; + HTL.mod_finish = fin; + HTL.mod_return = rtrn; + HTL.mod_start = start; + HTL.mod_reset = rst; + HTL.mod_clk = clk; + HTL.mod_scldecls = current_state.st_scldecls; + HTL.mod_arrdecls = current_state.st_arrdecls; + } + in + OK (m, current_state) + +let max_state f = + let st = P.of_int 10000 in + { + (init_state st) with + st_st = st; + st_freshreg = P.succ st; + st_freshstate = P.of_int 10000; + st_scldecls = + AssocMap.AssocMap.set st (None, Nat.of_int 32) (init_state st).st_scldecls; + } + +let transl_module (f : RTLBlock.coq_function) : HTL.coq_module Errors.res = + run_mon (max_state f) (transl_module' f) diff --git a/src/verilog/Value.v b/src/hls/Value.v index d6a3d8b..d6a3d8b 100644 --- a/src/verilog/Value.v +++ b/src/hls/Value.v diff --git a/src/verilog/ValueInt.v b/src/hls/ValueInt.v index f1fd056..f1fd056 100644 --- a/src/verilog/ValueInt.v +++ b/src/hls/ValueInt.v diff --git a/src/hls/ValueVal.v b/src/hls/ValueVal.v new file mode 100644 index 0000000..b882dc6 --- /dev/null +++ b/src/hls/ValueVal.v @@ -0,0 +1,209 @@ +(* + * Vericert: Verified high-level synthesis. + * Copyright (C) 2020 Yann Herklotz <yann@yannherklotz.com> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + *) + +(* begin hide *) +From bbv Require Import Word. +From bbv Require HexNotation WordScope. +From Coq Require Import ZArith.ZArith FSets.FMapPositive Lia. +From compcert Require Export lib.Integers common.Values. +From vericert Require Import Vericertlib. +(* end hide *) + +(** * Value + +A [value] is a bitvector with a specific size. We are using the implementation +of the bitvector by mit-plv/bbv, because it has many theorems that we can reuse. +However, we need to wrap it with an [Inductive] so that we can specify and match +on the size of the [value]. This is necessary so that we can easily store +[value]s of different sizes in a list or in a map. + +Using the default [word], this would not be possible, as the size is part of the type. *) + +(* Definition value : Type := val. + +(** ** Value conversions + +Various conversions to different number types such as [N], [Z], [positive] and +[int], where the last one is a theory of integers of powers of 2 in CompCert. *) + +Definition valueToNat (v : value) : nat := + match v with + | value_bool b => Nat.b2n b + | value_int i => Z.to_nat (Int.unsigned i) + | value_int64 i => Z.to_nat (Int64.unsigned i) + end. + +Definition natToValue (n : nat) : value := + value_int (Int.repr (Z.of_nat n)). + +Definition natToValue64 (n : nat) : value := + value_int64 (Int64.repr (Z.of_nat n)). + +Definition valueToN (v : value) : N := + match v with + | value_bool b => N.b2n b + | value_int i => Z.to_N (Int.unsigned i) + | value_int64 i => Z.to_N (Int64.unsigned i) + end. + +Definition NToValue (n : N) : value := + value_int (Int.repr (Z.of_N n)). + +Definition NToValue64 (n : N) : value := + value_int64 (Int64.repr (Z.of_N n)). + +Definition ZToValue (z : Z) : value := + value_int (Int.repr z). + +Definition ZToValue64 (z : Z) : value := + value_int64 (Int64.repr z). + +Definition valueToZ (v : value) : Z := + match v with + | value_bool b => Z.b2z b + | value_int i => Int.signed i + | value_int64 i => Int64.signed i + end. + +Definition uvalueToZ (v : value) : Z := + match v with + | value_bool b => Z.b2z b + | value_int i => Int.unsigned i + | value_int64 i => Int64.unsigned i + end. + +Definition posToValue (p : positive) : value := + value_int (Int.repr (Z.pos p)). + +Definition posToValue64 (p : positive) : value := + value_int64 (Int64.repr (Z.pos p)). + +Definition valueToPos (v : value) : positive := + match v with + | value_bool b => 1%positive + | value_int i => Z.to_pos (Int.unsigned i) + | value_int64 i => Z.to_pos (Int64.unsigned i) + end. + +Definition intToValue (i : Integers.int) : value := value_int i. + +Definition int64ToValue (i : Integers.int64) : value := value_int64 i. + +Definition valueToInt (v : value) : Integers.int := + match v with + | value_bool b => Int.repr (if b then 1 else 0) + | value_int i => i + | value_int64 i => Int.repr (Int64.unsigned i) + end. + +(*Definition ptrToValue (i : ptrofs) : value := + value_int (Ptrofs.to_int i). + +Definition valueToPtr (i : value) : Integers.ptrofs := + Ptrofs.of_int i. + +Definition valToValue (v : Values.val) : option value := + match v with + | Values.Vint i => Some (intToValue i) + | Values.Vint64 i => Some (intToValue i) + | Values.Vptr b off => Some (ptrToValue off) + | Values.Vundef => Some (ZToValue 0%Z) + | _ => None + end. + +(** Convert a [value] to a [bool], so that choices can be made based on the +result. This is also because comparison operators will give back [value] instead +of [bool], so if they are in a condition, they will have to be converted before +they can be used. *) + +Definition valueToBool (v : value) : bool := + if Z.eqb (uvalueToZ v) 0 then false else true. + +Definition boolToValue (b : bool) : value := + natToValue (if b then 1 else 0). + +(** ** Arithmetic operations *) + +Definition unify_word (sz1 sz2 : nat) (w1 : word sz2): sz1 = sz2 -> word sz1. +intros; subst; assumption. Defined. + +Lemma unify_word_unfold : + forall sz w, + unify_word sz sz w eq_refl = w. +Proof. auto. Qed. + +Inductive val_value_lessdef: val -> value -> Prop := +| val_value_lessdef_int: + forall i v', + i = valueToInt v' -> + val_value_lessdef (Vint i) v' +| val_value_lessdef_ptr: + forall b off v', + off = valueToPtr v' -> + val_value_lessdef (Vptr b off) v' +| lessdef_undef: forall v, val_value_lessdef Vundef v. + +Inductive opt_val_value_lessdef: option val -> value -> Prop := +| opt_lessdef_some: + forall v v', val_value_lessdef v v' -> opt_val_value_lessdef (Some v) v' +| opt_lessdef_none: forall v, opt_val_value_lessdef None v. + +Lemma valueToZ_ZToValue : + forall z, + (Int.min_signed <= z <= Int.max_signed)%Z -> + valueToZ (ZToValue z) = z. +Proof. auto using Int.signed_repr. Qed. + +Lemma uvalueToZ_ZToValue : + forall z, + (0 <= z <= Int.max_unsigned)%Z -> + uvalueToZ (ZToValue z) = z. +Proof. auto using Int.unsigned_repr. Qed. + +Lemma valueToPos_posToValue : + forall v, + 0 <= Z.pos v <= Int.max_unsigned -> + valueToPos (posToValue v) = v. +Proof. + unfold valueToPos, posToValue. + intros. rewrite Int.unsigned_repr. + apply Pos2Z.id. assumption. +Qed. + +Lemma valueToInt_intToValue : + forall v, + valueToInt (intToValue v) = v. +Proof. auto. Qed. + +Lemma valToValue_lessdef : + forall v v', + valToValue v = Some v' -> + val_value_lessdef v v'. +Proof. + intros. + destruct v; try discriminate; constructor. + unfold valToValue in H. inversion H. + unfold valueToInt. unfold intToValue in H1. auto. + inv H. symmetry. unfold valueToPtr, ptrToValue. apply Ptrofs.of_int_to_int. trivial. +Qed. + +Ltac simplify_val := repeat (simplify; unfold uvalueToZ, valueToPtr, Ptrofs.of_int, valueToInt, intToValue, + ptrToValue in *) + +(*Ltac crush_val := simplify_val; try discriminate; try congruence; try lia; liapp; try assumption.*) +*) diff --git a/src/verilog/Verilog.v b/src/hls/Verilog.v index 3b0dd0a..e5f86d5 100644 --- a/src/verilog/Verilog.v +++ b/src/hls/Verilog.v @@ -29,7 +29,7 @@ Require Import Lia. Import ListNotations. -From vericert Require Import common.Vericertlib common.Show verilog.ValueInt AssocMap Array. +From vericert Require Import Vericertlib Show ValueInt AssocMap Array. From compcert Require Events. From compcert Require Import Integers Errors Smallstep Globalenvs. @@ -149,7 +149,7 @@ Inductive binop : Type := (** ** Unary Operators *) Inductive unop : Type := -| Vneg (** negation ([~]) *) +| Vneg (** negation ([-]) *) | Vnot. (** not operation [!] *) (** ** Expressions *) diff --git a/src/translation/Veriloggen.v b/src/hls/Veriloggen.v index a0be0fa..a0be0fa 100644 --- a/src/translation/Veriloggen.v +++ b/src/hls/Veriloggen.v diff --git a/src/translation/Veriloggenproof.v b/src/hls/Veriloggenproof.v index 9abbd4b..9abbd4b 100644 --- a/src/translation/Veriloggenproof.v +++ b/src/hls/Veriloggenproof.v |