¤
ivy Origin protocol model
Copyright 2017-2018 Asgeir Eiriksson (asgeir.eiriksson@gmail.com)
-
write this to look as close to the SMV model structure as possible to investigate how well ivy matches the typical hardware design paradigm
-
this would enable moving the same model between ivy, SMV and verilog
-
why do we need the back-and-forth between tools/paradigms capability?
1) analyze liveness within smv 2) refine the design within smv in both directions, i.e. increase abstraction e.g. similar to the BRD to IRD proc handling, and to formally verify the actual hardware design 3) prove memory consistency model within ivy 4) generate executable model within ivy 5) mixed sw/fw/hw designs with ivy
TODO
1) change action to ph, both in the smv model 2) module p_mod "individual msg_req_rsp : msg_req_rsp_type" looks like it might be moved to net, but maybe not 3) change the SMV defines to lower case to better match ivy
¤
type proc
type data_type
type addr_type
type state_type = {inv,shd,cex,dex}
type dstate_type = {uown,shrd,excl,busys,busye}
type msg_req_rsp_type = {idle_r_r,erply,espec,nack,rdsh,rdex,srply,sspec,upack,upgrd,wack,wb,wbbak}
type msg_rpl_type = {idle_rpl,eack,eresp,sack,sresp}
type msg_req_type = {idle_req,irdex,irdsh}
type msg_trf_type = {idle_trf,dngrd,xfer}
type ph_type = {req_rsp_ph1,req_rsp_ph2,req_rsp_ph3,rpl_ph,req_ph,trf_ph,store_ph}
type send_type = {send_ex,send_evct,send_rdsh,send_none}
module req_rsp_type = {
individual kind : msg_req_rsp_type
individual data : data_type
}
module req_type = {
individual kind : msg_req_type
individual src : proc
individual dst : proc
}
module rpl_type = {
individual kind : msg_rpl_type
individual src : proc
individual dst : proc
individual data : data_type
}
module trf_type = {
individual kind : msg_trf_type
individual src : proc
individual data : data_type
}
module rrb_type = {
individual v : bool
individual a : bool
individual r : bool
}
module wrb_type = {
individual v : bool
individual a : bool
}
module irb_type = {
individual i : bool
individual kind : msg_req_type
individual src : proc
}
¤
proc issues requests and pesses responses, interventions and store requests
¤
****¤
module p_mod = {
individual state(A:addr_type) : state_type
instantiate rrb(A:addr_type) : rrb_type
instantiate irb(A:addr_type) : irb_type
instantiate wrb(A:addr_type) : wrb_type
individual data(A:addr_type) : data_type
individual wr : bool
individual error : bool
individual ivack : bool
action p_rec(p:proc,a:addr_type,ph:ph_type,send:send_type,store:bool,store_data:data_type) = {
error := true;
if ph=rpl_ph & net.ivack(p,a) {
net.ivack(p,a) := false;
if (exists X. net.ivack(X,a)) | (exists Y. net.ivec(Y,a)) | (exists P. pi(P).irb(a).i & pi(P).irb(a).kind=idle_req) {
net.last_ivack := false;
} else {
net.last_ivack := true;
};
ivack := true;
} else {
net.last_ivack := false;
ivack := false;
};
if ph=req_rsp_ph1 & ~rrb(a).v & ~wrb(a).v & state(a)=inv & send=send_rdsh {
rrb(a).v := true;
rrb(a).r := false;
rrb(a).a := false;
net.req_rsp(p,a).kind := rdsh;
error := false;
}
else if ph=req_rsp_ph1 & ~rrb(a).v & ~wrb(a).v & state(a)=inv & send=send_ex {
rrb(a).v := true;
rrb(a).r := false;
rrb(a).a := false;
net.req_rsp(p,a).kind := rdex;
error := false;
}
else {
error := false;
}
}
}
****¤
object d = {
individual dstate(A:addr_type) : dstate_type
individual ptr(A:addr_type) : proc
individual pptr(A:addr_type) : proc
individual tmp_pptr : proc
individual pptr_vld(A:addr_type) : bool
relation owner(X:proc,A:addr_type)
individual mem(A:addr_type) : data_type
individual mem_real(A:addr_type) : data_type
individual error : bool
individual shrd_only : bool
individual excl_owner : bool
¤
directory receives proc requests and ownership transfer messages and responds with response messages and optionally invalidates and interventions
¤
action dir_rec(p:proc,a:addr_type,ph:ph_type) = {
error := true;
tmp_pptr := ptr(a);
shrd_only := ~(exists P. owner(P,a) & p~=P);
excl_owner := p=ptr(a);
if ph=req_rsp_ph2 & net.req_rsp(p,a).kind=rdsh & dstate(a)=uown {
dstate(a) := shrd;
owner(p,a) := true;
net.req_rsp(p,a).kind := srply;
net.req_rsp(p,a).data := mem(a);
pptr_vld(a) := false;
error := false;
}
else if ph=req_rsp_ph2 & net.req_rsp(p,a).kind=rdsh & dstate(a)=excl & excl_owner {
dstate(a) := shrd;
owner(p,a) := true;
net.req_rsp(p,a).kind := srply;
net.req_rsp(p,a).data := mem(a);
pptr(a) := ptr(a);
pptr_vld(a) := true;
error := false;
}
else {
error := false;
}
}
after dir_rec {
assert ~error
}
}
****¤
¤
¤
module network = {
instantiate req_rsp(X:proc,A:addr_type) : req_rsp_type
instantiate req(X:proc,A:addr_type) : req_type
instantiate rpl(A:addr_type) : rpl_type
instantiate trf(A:addr_type) : trf_type
individual binv(A:addr_type) : bool
individual inval(A:addr_type) : bool
individual store_data : data_type
individual last_ivack : bool
individual ivec(X:proc,A:addr_type) : bool
individual ivack(X:proc,A:addr_type) : bool
action step(p:proc,a:addr_type,ph:ph_type,send:send_type,store:bool) = {
if ph=req_rsp_ph1 { # proc issues new request
call pi(p).p_rec(p,a,ph,send,store,store_data)
} else if ph=req_rsp_ph2 { # directory receives request/owner-ship transfer
call d.dir_rec(p,a,ph)
} else if ph=req_rsp_ph3 { # proc receives response
call pi(p).p_rec(p,a,ph,send,store,store_data)
} else if ph=rpl_ph & net.rpl(a).dst=p { # proc receives intervention/invalidate response
if net.rpl(a).kind~= idle_rpl & net.rpl(a).dst=p {
call pi(p).p_rec(p,a,ph,send,store,store_data)
} else if net.ivack(p,a) {
call pi(d.ptr(a)).p_rec(p,a,ph,send,store,store_data)
}
} else if ph=req_ph { # proc receives intervention/invalidate request
call pi(p).p_rec(p,a,ph,send,store,store_data)
} else if ph=trf_ph { # directory receives ownership transfer message
call d.dir_rec(p,a,ph)
} else if ph=store_ph { # proc writes/dirties a cex cache line
call pi(p).p_rec(p,a,ph,send,store,store_data)
}
}
action step0(p:proc,a:addr_type,send:send_type,store:bool) = {
call pi(p).p_rec(p,a,req_rsp_ph1,send,store,store_data)
}
action step1(p:proc,a:addr_type,send:send_type,store:bool) = {
call d.dir_rec(p,a,req_rsp_ph2)
}
action step2(p:proc,a:addr_type,send:send_type,store:bool) = {
call pi(p).p_rec(p,a,req_rsp_ph3,send,store,store_data)
}
action step3(p:proc,a:addr_type,send:send_type,store:bool) = {
if net.rpl(a).dst=p {
if net.rpl(a).kind~= idle_rpl & net.rpl(a).dst=p {
call pi(p).p_rec(p,a,rpl_ph,send,store,store_data)
} else if net.ivack(p,a) {
call pi(d.ptr(a)).p_rec(p,a,rpl_ph,send,store,store_data)
}
}
}
action step4(p:proc,a:addr_type,send:send_type,store:bool) = {
call pi(p).p_rec(p,a,req_ph,send,store,store_data)
}
action step5(p:proc,a:addr_type,send:send_type,store:bool) = {
call d.dir_rec(p,a,trf_ph)
}
action step6(p:proc,a:addr_type,send:send_type,store:bool) = {
call pi(p).p_rec(p,a,store_ph,send,store,store_data)
}
}
after init {
net.req_rsp(P,A).kind := idle_r_r;
net.req(P,A).kind := idle_req;
net.ivec(P,A) := false;
net.ivack(P,A) := false;
net.inval(A) := false;
net.binv(A) := false;
pi(X).state(A) := inv;
pi(X).rrb(A).v := false;
d.dstate(A) := uown;
d.owner(X,A) := false;
d.pptr_vld(A) := false;
d.mem_real(A) := d.mem(A);
net.rpl(A).kind := idle_rpl;
net.trf(A).kind := idle_trf
}
¤
¤
instantiate pi(X:proc) : p_mod
instantiate net : network
¤
¤
conjecture (net.req_rsp(P,A).kind~=rdsh)
export net.step0
export net.step1
export net.step2
export net.step3
export net.step4
export net.step5
export net.step6