Tilelink concrete directory
¤
This is an abstract model of simple directory using TileLink.
The directory has two client ports, clnt0 and clnt1
¤
include tilelink_concrete_spec
¤
This module is a directory for a single port
¤
module dir(myid,clnt,other,ref,mem) = {
relation cache_excl(H:tl_addrhi,W:tl_addrlo)
relation cache_shrd(H:tl_addrhi,W:tl_addrlo)
relation pend_excl(A:tl_addrhi,W:tl_addrlo)
relation pend_shrd(A:tl_addrhi,W:tl_addrlo)
relation pend_prb(A:tl_addrhi,W:tl_addrlo)
relation pend_fnse(A:tl_addrhi)
relation pend_fnss(A:tl_addrhi)
individual addr_id(A:tl_addrhi) : id
individual addr_nid(A:tl_addrhi) : id
init ~cache_excl(H,W)
init ~cache_shrd(H,W)
init ~pend_excl(A,W)
init ~pend_shrd(A,W)
init ~pend_prb(A,W)
init ~pend_fnse(A)
init ~pend_fnss(A)
¤
release action called by the client side
action tl_Release(msg:rls_id) = {
local hi:tl_addrhi, lo:tl_addrlo {
hi := rlss(msg).addr_hi;
lo := rlss(msg).word;
if rlss(msg).voluntary {
assume ~pend_excl(hi,W) & ~pend_shrd(hi,W);
assume ~pend_fnse(hi) & ~pend_fnss(hi)
};
if rlss(msg).dirty {
mem(addr_cons(hi,lo)) := rlss(msg).data_
};
cache_excl(hi,lo) := false;
cache_shrd(hi,lo) := false;
if rlss(msg).voluntary {
local rsp:gnt_id {
assume gnts(rsp).id_ = rlss(msg).id_;
assume gnts(rsp).addr_hi = hi;
assume gnts(rsp).word = lo;
assume gnts(rsp).relack;
call clnt.tl_Grant(rsp)
}
}
else {
pend_prb(hi,lo) := false
}
}
}
¤
acquire action called by the client side
action tl_Acquire(msg:acq_id) = {
local hi:tl_addrhi, lo:tl_addrlo, ow:ownership, txid:id {
hi := acqs(msg).addr_hi;
ow := acqs(msg).own;
txid := acqs(msg).id_;
rule: Cannot grant if there is a pending probe with the same hi address
assume ~pend_prb(hi,W);
assume ~((pend_excl(H,W) | pend_fnse(H) | pend_shrd(H,W) | pend_fnss(H))
& addr_id(H) = txid & H ~= hi);
assume ~((pend_excl(H,W) | pend_fnse(H) | pend_shrd(H,W) | pend_fnss(H))
& addr_id(H) ~= txid & H = hi);
addr_id(hi) := txid;
if ow = excl {
pend_excl(hi,W) := true
}
else {
pend_shrd(hi,W) := true
}
}
}
action grant(hi:tl_addrhi, lo:tl_addrlo, ow:ownership) = {
assume ow ~= none;
assume ~other.cache_excl(hi,W);
if ow = excl {
assume pend_excl(hi,lo);
pend_excl(hi,lo) := false;
if forall W:tl_addrlo. ~pend_excl(hi,W:tl_addrlo) {
pend_fnse(hi) := true
};
cache_excl(hi,lo) := true
}
else {
assume pend_shrd(hi,lo);
pend_shrd(hi,lo) := false;
if forall W:tl_addrlo. ~pend_shrd(hi,W:tl_addrlo) {
pend_fnss(hi) := true
};
cache_shrd(hi,lo) := true
};
local rsp:gnt_id {
assume gnts(rsp).id_ = addr_id(hi);
assume gnts(rsp).addr_hi = hi;
assume gnts(rsp).word = lo;
assume gnts(rsp).own = ow;
assume gnts(rsp).data_ = mem(addr_cons(hi,lo));
assume ~gnts(rsp).relack;
call clnt.tl_Grant(rsp)
}
}
action tl_Finish(msg:fns_id) = {
local hi:tl_addrhi, ow:ownership, txid:id {
txid := fnss(msg).id_;
assert exists H. addr_id(H) = txid & (pend_fnss(H) | pend_fnse(H)); # look up addr by txid
assume addr_id(hi) = txid & (pend_fnss(hi) | pend_fnse(hi));
if pend_fnss(hi) {
pend_fnss(hi) := false
} else {
pend_fnse(hi) := false
}
}
}
action probe(hi:tl_addrhi) = {
if exists W1:tl_addrlo,W2:tl_addrlo.
((other.pend_excl(hi,W1:tl_addrlo) | other.pend_shrd(hi,W1:tl_addrlo))
& cache_excl(hi,W2:tl_addrlo)) {
assume ~pend_prb(hi,W);
assume ~(pend_excl(hi,W) | pend_fnse(hi) | pend_shrd(hi,W) | pend_fnss(hi));
local rsp:prb_id {
assume prbs(rsp).addr_hi = hi;
call clnt.tl_Probe(rsp)
};
pend_prb(hi,W) := true
}
}
action step = {
local hi:tl_addrhi, lo:tl_addrlo, ow:ownership {
if * {
call grant(hi,lo,ow)
} else {
call probe(hi)
}
}
}
}
module dut(myid,clnt0,clnt1,ref) = {
¤
The directory state
individual mem(A:addr) : data
init mem(A) = ref.mem(A)
instantiate dir0 : dir(myid,clnt0,dir1,ref,mem)
instantiate dir1 : dir(myid,clnt1,dir0,ref,mem)
action step = {
if * {
call dir0.step
} else {
call dir1.step
}
}
}
¤
Verify the module using the standard testbench
¤
include tilelink_concrete_two_client_testbench
1) everything cached on both ports
axiom intf0.cached(A) & intf1.cached(A)
¤
Conjectured invariants
¤
module dirinv(b,intf) = {
conjecture b.pend_prb(H,W) <-> intf.prb_addr(H,W)
conjecture ~intf.none_r(H,W)
conjecture b.cache_excl(H,W) <-> intf.excl_p(addr_cons(H,W))
conjecture b.cache_shrd(H,W) <-> intf.shrd_p(addr_cons(H,W))
conjecture b.pend_excl(H,W) -> intf.excl_r(H) | intf.excl_a(H)
conjecture intf.excl_r(H) -> b.pend_excl(H,W)
conjecture intf.excl_a(H) -> exists W. b.pend_excl(H,W)
conjecture b.pend_shrd(H,W) -> intf.shrd_r(H) | intf.shrd_a(H)
conjecture intf.shrd_r(H) -> b.pend_shrd(H,W)
conjecture intf.shrd_a(H) -> exists W. b.pend_shrd(H,W)
conjecture ((intf.excl_f(H) <-> b.pend_fnse(H)) & (intf.shrd_f(H) <-> b.pend_fnss(H)))
| ((intf.excl_f(H) <-> b.pend_fnss(H)) & (intf.shrd_f(H) <-> b.pend_fnse(H)))
conjecture (b.pend_excl(H,W) -> ~b.pend_fnse(H)) & (b.pend_shrd(H,W) -> ~b.pend_fnss(H))
conjecture (~intf.excl_f(H) & b.pend_fnse(H) -> intf.excl_p(addr_cons(H,W)))
& (~intf.shrd_f(H) & b.pend_fnss(H) -> intf.shrd_p(addr_cons(H,W)))
conjecture (intf.excl_r(H) | intf.shrd_r(H)) -> intf.addr_rid(H) = b.addr_id(H)
conjecture (intf.excl_a(H) | intf.shrd_a(H) | intf.excl_f(H) | intf.shrd_f(H)) -> intf.addr_id(H) = b.addr_id(H)
conjecture ~(b.cache_shrd(H,W) & b.pend_shrd(H,W)) & ~(b.cache_excl(H,W) & b.pend_excl(H,W))
conjecture b.pend_excl(H,W1) & ~b.cache_excl(H,W2) -> b.pend_excl(H,W2)
conjecture b.pend_shrd(H,W1) & ~b.cache_shrd(H,W2) -> b.pend_shrd(H,W2)
conjecture ~(b.pend_prb(H,W1) & (b.pend_excl(H,W2) | b.pend_fnse(H) | b.pend_shrd(H,W2) | b.pend_fnss(H)))
conjecture ~((b.pend_excl(H1,W1) | b.pend_shrd(H1,W1) | b.pend_fnse(H1) | b.pend_fnss(H1))
& (b.pend_excl(H2,W2) | b.pend_shrd(H2,W2) | b.pend_fnse(H2) | b.pend_fnss(H2))
& H1 ~= H2 & b.addr_id(H1) = b.addr_id(H2))
}
instantiate dirinv(b.dir0,intf0)
instantiate dirinv(b.dir1,intf1)
conjecture ~b.dir0.cache_excl(H,W) & ~b.dir1.cache_excl(H,W) -> b.mem(addr_cons(H,W)) = ref.mem(addr_cons(H,W))
conjecture ~intf0.dirt_p(A) & ~intf1.dirt_p(A) -> b.mem(A) = ref.mem(A)
conjecture ~(b.dir0.cache_excl(H,W) & b.dir1.cache_excl(H,W))