Arm rules3
¤
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;
assert ~prevents(T,lt);
evs(lt).serialized := true;
evs(lt).time := gt;
evs(lt).op := nop;
gt := gt.incr();
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<T { # A2
isd_st(p,a,t) := true;
call dcs.st(p,a,d);
call ref.create(t,p,st,a,d);
call ref.perform(p,t);
pnd_st(p,a,t) := false;
isd_st(p,a,t) := false;
}
}
}
} else if ph=arm_stl_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_ld(p,A,T) | pnd_lda(p,A,T) | pnd_st(p,A,T) | pnd_stl(p,A,T)) { # R1
pnd_stl(p,a,lt) := true;
isd_stl(p,a,lt) := true;
call dcs.st(p,a,d);
call ref.create(lt,p,stl,a,d);
call ref.perform(p,lt);
pnd_stl(p,a,lt) := false;
isd_stl(p,a,lt) := false;
} else {
pnd_stl(p,a,lt) := true;
isd_stl(p,a,lt) := false;
};
lt := lt.incr();
}
} else if ph=arm_stl_isd_ph {
if pnd_stl(p,a,t) {
if ~isd_stl(p,a,t) {
if forall A. forall T. ~(pnd_ld(p,A,T) | pnd_lda(p,A,T) | pnd_st(p,A,T) | pnd_stl(p,A,T)) | t<T | t=T { # R1
isd_stl(p,a,t) := true;
call dcs.st(p,a,d);
call ref.create(t,p,stl,a,d);
call ref.perform(p,t);
pnd_stl(p,a,t) := false;
isd_stl(p,a,t) := false;
}
}
}
} else if ph=arm_ld_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_ld(p,a,lt) := true;
isd_ld(p,a,lt) := true;
call ref.create(lt,p,ld,a,d);
} else {
pnd_ld(p,a,lt) := true;
isd_ld(p,a,lt) := false;
};
lt := lt.incr();
}
} else if ph=arm_ld_isd_ph {
if pnd_ld(p,a,t) {
if ~isd_ld(p,a,t) {
if forall A. forall T. ~pnd_lda(p,A,T) | t<T { # A2
isd_ld(p,a,t) := true;
call ref.create(t,p,ld,a,d);
}
}
}
} else if ph=arm_lda_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) & ~pnd_ld(p,A,T) & ~pnd_st(p,A,T) & ~pnd_stl(p,A,T) { # A1
pnd_lda(p,a,lt) := true;
isd_lda(p,a,lt) := true;
call ref.create(lt,p,lda,a,d);
} else {
pnd_lda(p,a,lt) := true;
isd_lda(p,a,lt) := false;
};
lt := lt.incr();
}
} else if ph=arm_lda_isd_ph {
if pnd_lda(p,a,t) {
if ~isd_lda(p,a,t) {
if forall A. forall T. ~pnd_lda(p,A,T) & ~pnd_ld(p,A,T) & ~pnd_st(p,A,T) & ~pnd_stl(p,A,T) | t<T | t=T { # A2
isd_lda(p,a,t) := true;
call ref.create(t,p,lda,a,d);
}
}
}
}
}
action recv(ph:arm_ph_type, p:proc.t, a:addr_type.t, t:ltime.t) = {
if ph=arm_rsp_ph {
if isd_ld(p,a,t) {
call ref.perform(p,t);
pnd_ld(p,a,t) := false;
isd_ld(p,a,t) := false;
}
} else if ph=arm_rspa_ph {
if isd_lda(p,a,t) {
call ref.perform(p,t);
pnd_lda(p,a,t) := false;
isd_lda(p,a,t) := false;
}
}
}
}
¤
DCS+DRAM
¤
object dcs = {
individual data(A:addr_type.t) : data_type.t
action st(p:proc.t, a:addr_type.t, d:data_type.t) = {
data(a) := d;
}
action ld(a:addr_type.t) returns (d:data_type.t) = {
d := data(a)
}
}
initialize the state to nothing outstanding
after init {
arm.pnd_st(P,A,T) := false;
arm.pnd_stl(P,A,T) := false;
arm.pnd_ld(P,A,T) := false;
arm.pnd_lda(P,A,T) := false;
arm.isd_st(P,A,T) := false;
arm.isd_stl(P,A,T) := false;
arm.isd_ld(P,A,T) := false;
arm.isd_lda(P,A,T) := false;
ref.evs(T).op := nop;
ref.evs(T).serialized := false;
}
conjecture ~arm.isd_st(P,A,T)
conjecture ~arm.isd_stl(P,A,T)
conjecture (arm.pnd_st(P,A,T) & arm.isd_st(P,A,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)
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<T)
conjecture arm.pnd_lda(P,A,T) -> ~((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