#lang ivy1.7 ################################################################################ # # TODO: # ################################################################################ isolate proc = { type t object impl = { interpret proc.t->bv[1] } attribute test=impl } isolate data_type = { type t object impl = { interpret data_type.t->bv[1] } attribute test=impl } isolate addr_type = { type t object impl = { interpret addr_type.t->bv[1] } attribute test=impl } isolate ltime = { type t relation (X:t < Y:t) action incr(inp:t) returns (out:t) object spec = { property X:t < Y & Y < Z -> X < Z property ~(X:t < Y & Y < X) property X:t < Y | X = Y | Y < X after incr { assume inp < out } } object impl = { interpret ltime.t->bv[8] implement incr { out := inp + 1 } } attribute test=impl } isolate gtime = { type t relation (X:t < Y:t) action incr(inp:t) returns (out:t) object spec = { property X:t < Y & Y < Z -> X < Z property ~(X:t < Y & Y < X) property X:t < Y | X = Y | Y < X after incr { assume inp < out } } object impl = { interpret gtime.t->bv[8] } attribute test=impl } type op_type = {nop,ld,st,lda,stl} module evs_type = { individual p : proc.t individual op : op_type individual addr : addr_type.t individual data : data_type.t individual serialized : bool individual time : gtime.t } ################################################################################ # Specification: ################################################################################ object ref = { instantiate evs(T:ltime.t) : evs_type ################################################################################ # current global time individual gt : gtime.t ################################################################################ # global memory state individual mem(A:addr_type.t) : data_type.t ################################################################################ # event T1 prevents T2 if it must come before T2 according to the # ordering conditions but is not yet serialized. The definition # here is strong enough to imply release consistency (ld and # st to different addresses commute, but nothing commutes with # stl and lda) # # P1) only one of ld, lda, st, stl pending for same address from same processor # # An acquire synchronization must ensure that no younger load/store executes before the # acquire is complete # # => A1) prevent lda to issue until all older ld*/st* are finished to ensure it # doesn't overtake those # A2) prevent younger ld*/st* to issue before lda # # A release synchronization must ensure that all older loads/stores complete before the # release is issued. # # => R1) prevent stl to issue until all older ld*/st* are finished to ensure it # doesn't overtake those # # P0 1) st P1 1) ld # P0 2) stl P1 2) lda # # Acquires and releases must execute atomically with respect to one another # relation prevents(T1:ltime.t,T2:ltime.t) definition prevents(T1,T2) = ~evs(T1).serialized & (T1 < T2) & (evs(T1).op=ld | evs(T1).op=lda | evs(T1).op=st | evs(T1).op=stl) & evs(T1).p = evs(T2).p & (evs(T1).addr = evs(T2).addr | evs(T2).op = stl | evs(T2).op = lda) action create(lt:ltime.t,p:proc.t,op:op_type,a:addr_type.t,d:data_type.t) = { evs(lt).p := p; evs(lt).op := op; evs(lt).addr := a; evs(lt).data := d; evs(lt).serialized := false; } action perform(p:proc.t, lt:ltime.t) = { var a : addr_type.t; var d : data_type.t; # serialization must be appropriately ordered assert ~prevents(T,lt); # serialize at current global time evs(lt).serialized := true; evs(lt).time := gt; evs(lt).op := nop; # advance global time gt := gt.incr(); # update the global memory state a := evs(lt).addr; d := evs(lt).data; if evs(lt).op = ld | evs(lt).op = lda { evs(lt).data := mem(a) } else if evs(lt).op = st | evs(lt).op = stl { mem(a) := d } } } ################################################################################ # arm: ################################################################################ type arm_ph_type = {arm_idle_ph, arm_st_ph, arm_stl_ph, arm_ld_ph, arm_lda_ph, arm_rsp_ph, arm_rspa_ph, arm_st_isd_ph, arm_ld_isd_ph, arm_stl_isd_ph, arm_lda_isd_ph} object arm = { relation pnd_st(P:proc.t, A:addr_type.t, T:ltime.t) relation pnd_stl(P:proc.t, A:addr_type.t, T:ltime.t) relation pnd_ld(P:proc.t, A:addr_type.t, T:ltime.t) relation pnd_lda(P:proc.t, A:addr_type.t, T:ltime.t) relation isd_st(P:proc.t, A:addr_type.t, T:ltime.t) relation isd_stl(P:proc.t, A:addr_type.t, T:ltime.t) relation isd_ld(P:proc.t, A:addr_type.t, T:ltime.t) relation isd_lda(P:proc.t, A:addr_type.t, T:ltime.t) individual lt : ltime.t action step(p:proc.t, ph:arm_ph_type, a:addr_type.t,d:data_type.t,t:ltime.t) = { if ph=arm_st_ph { if forall T. ~(pnd_ld(p,a,T) | pnd_lda(p,a,T) | pnd_st(p,a,T) | pnd_stl(p,a,T)) { # P1 if forall A. forall T. ~pnd_lda(p,A,T) { # A2 pnd_st(p,a,lt) := true; isd_st(p,a,lt) := true; call dcs.st(p,a,d); call ref.create(lt,p,st,a,d); call ref.perform(p,lt); pnd_st(p,a,lt) := false; isd_st(p,a,lt) := false; } else { pnd_st(p,a,lt) := true; isd_st(p,a,lt) := false; }; lt := lt.incr(); } } else if ph=arm_st_isd_ph { if pnd_st(p,a,t) { if ~isd_st(p,a,t) { if forall A. forall T. ~pnd_lda(p,A,T) | t (ref.evs(T).p=P & ref.evs(T).op=st & ref.evs(T).addr=A) conjecture (arm.pnd_stl(P,A,T) & arm.isd_stl(P,A,T)) <-> (ref.evs(T).p=P & ref.evs(T).op=stl & ref.evs(T).addr=A) conjecture (arm.pnd_ld(P,A,T) & arm.isd_ld(P,A,T)) <-> (ref.evs(T).p=P & ref.evs(T).op=ld & ref.evs(T).addr=A) conjecture (arm.pnd_lda(P,A,T) & arm.isd_lda(P,A,T)) <-> (ref.evs(T).p=P & ref.evs(T).op=lda & ref.evs(T).addr=A) conjecture arm.pnd_st(P,A,T) -> (T < arm.lt) conjecture arm.pnd_stl(P,A,T) -> (T < arm.lt) conjecture arm.pnd_ld(P,A,T) -> (T < arm.lt) conjecture arm.pnd_lda(P,A,T) -> (T < arm.lt) conjecture (arm.pnd_st(P,A,T1) & arm.pnd_st(P,A,T2)) -> T1=T2 conjecture (arm.pnd_stl(P,A,T1) & arm.pnd_stl(P,A,T2)) -> T1=T2 conjecture (arm.pnd_ld(P,A,T1) & arm.pnd_ld(P,A,T2)) -> T1=T2 conjecture (arm.pnd_lda(P,A,T1) & arm.pnd_lda(P,A,T2)) -> T1=T2 conjecture ~(arm.pnd_ld(P,A,T1) & arm.pnd_st(P,A,T2)) conjecture ~(arm.pnd_ld(P,A,T1) & arm.pnd_stl(P,A,T2)) conjecture ~(arm.pnd_ld(P,A,T1) & arm.pnd_lda(P,A,T2)) conjecture ~(arm.pnd_lda(P,A,T1) & arm.pnd_st(P,A,T2)) conjecture ~(arm.pnd_lda(P,A,T1) & arm.pnd_stl(P,A,T2)) conjecture ~(arm.pnd_st(P,A,T1) & arm.pnd_stl(P,A,T2)) conjecture arm.isd_st(P,A,T) -> arm.pnd_st(P,A,T) conjecture arm.isd_stl(P,A,T) -> arm.pnd_stl(P,A,T) conjecture arm.isd_ld(P,A,T) -> arm.pnd_ld(P,A,T) conjecture arm.isd_lda(P,A,T) -> arm.pnd_lda(P,A,T) # When lda is issued there are no older ld, lda, st, stl pending conjecture arm.isd_lda(P,A,T) -> ~((arm.pnd_ld(P,A1,T1) | arm.pnd_lda(P,A1,T1) | arm.pnd_st(P,A1,T1) | arm.pnd_stl(P,A1,T1)) & T1 ~((arm.isd_ld(P,A1,T1) | arm.isd_lda(P,A1,T1) | arm.isd_st(P,A1,T1) | arm.isd_stl(P,A1,T1)) & T1>T) conjecture (arm.pnd_ld(P1,A1,T1) & arm.pnd_ld(P2,A2,T2) & T1=T2) -> (P1=P2 & A1=A2) conjecture ~(arm.pnd_ld(P1,A1,T1) & arm.pnd_lda(P2,A2,T2) & T1=T2) conjecture ~(arm.pnd_ld(P1,A1,T1) & arm.pnd_st(P2,A2,T2) & T1=T2) conjecture ~(arm.pnd_ld(P1,A1,T1) & arm.pnd_stl(P2,A2,T2) & T1=T2) conjecture (arm.pnd_lda(P1,A1,T1) & arm.pnd_lda(P2,A2,T2) & T1=T2) -> (P1=P2 & A1=A2) conjecture ~(arm.pnd_lda(P1,A1,T1) & arm.pnd_st(P2,A2,T2) & T1=T2) conjecture ~(arm.pnd_lda(P1,A1,T1) & arm.pnd_stl(P2,A2,T2) & T1=T2) conjecture (arm.pnd_st(P1,A1,T1) & arm.pnd_st(P2,A2,T2) & T1=T2) -> (P1=P2 & A1=A2) conjecture ~(arm.pnd_st(P1,A1,T1) & arm.pnd_stl(P2,A2,T2) & T1=T2) conjecture (arm.pnd_stl(P1,A1,T1) & arm.pnd_stl(P2,A2,T2) & T1=T2) -> (P1=P2 & A1=A2) export arm.step export arm.recv