blob: d05fc19fee6bf24d98de8e083ec44af2d250a128 [file] [log] [blame]
----------------------------------------------------------------------
-- BlackParrot MESI Coherence Protocol
--
-- Notes:
-- 1. Replacement is not modeled since we use the single address assumption. The LCEs are not
-- allowed to evict a block on their own, only the CCE can evict a block.
-- 2. "Message delivery cannot be assumed to be in the same order as they were sent, even for
-- the same sender and receiver pair"
-- 3. A cache is made owner or added to sharers when the CCE / Directory receives the ack. Sharers
-- are cleared at ack receipt.
--
----------------------------------------------------------------------
----------------------------------------------------------------------
-- Constants
----------------------------------------------------------------------
const
ProcCount: 2; -- number processors
ValueCount: 2; -- number of data values.
VC0: 0; -- low priority - LCE Req
VC1: 1; -- LCE Cmd
VC2: 2; -- LCE Data Cmd
VC3: 3; -- LCE Resp
VC4: 4; -- LCE Data Resp
NumVCs: VC4 - VC0 + 1;
-- TODO: what is max messages?
NetMax: (2*ProcCount)+1;
----------------------------------------------------------------------
-- Types
----------------------------------------------------------------------
type
Proc: scalarset(ProcCount); -- unordered range of processors
Value: 1..ValueCount; -- arbitrary values for tracking coherence
Home: enum { HomeType }; -- need enumeration for IsMember calls
Node: union { Home , Proc };
Count: 0..ProcCount; -- integer range of number of sharers
VCType: VC0..NumVCs-1;
MessageType: enum { LceRdReq, -- req to dir for read
LceWrReq, -- req to dir for write
InvTagCmd, -- cmd to LCE to invalidate block
TrCmd, -- cmd to LCE to send data to another LCE
SetTagCmd, -- cmd to LCE to set tag and coherence state of block
SetTagWakeupCmd, -- cmd to LCE to set tag and wakeup on upgrade (state -> M)
WritebackCmd, -- cmd to LCE to send data to CCE
DataCmdCce, -- data block to LCE from CCE
DataCmdLce, -- data block to LCE from LCE (transfer)
InvTagAck, -- ack from LCE to CCE for invalidation
CohAck, -- ack from LCE to CCE for coherence transaction
TrAck, -- ack from LCE to CCE for transfer
LceDataRespNull, -- null data from LCE to CCE on WB
LceDataResp -- data from LCE to CCE on WB
};
E_HomeState: enum { H_M, H_S, H_I, H_E, -- stable states
-- "transient" states - states that CCE steps through to process request
H_IA, -- wait for invalidation acks
H_CA, -- wait for coherence ack
H_TWB, -- wait for writeback or null writeback from transferring LCE
H_TA -- wait for transfer ack
};
E_ProcState: enum { P_M, P_S, P_I, P_E, -- stable states
P_I_DT, -- invalid waiting for data cmd or tag cmd to arrive
P_I_D, -- invalid waiting for data cmd to arrive
P_I_T, -- invalid waiting for tag cmd to arrive
P_ID, -- invalid and dirty (waiting for WB command)
P_S_U -- shared to modified upgrade
};
Message:
Record
mtype: MessageType;
src: Node;
-- do not need a destination for verification; the destination is indicated by which array entry in the Net the message is placed
-- channel for message
vc: VCType;
-- data value for data cmd and data resp
val: Value;
-- metadata for requests
upgrade: boolean;
-- metadata for commands
target: Node; -- target for transfer
nextState: E_ProcState;
End;
HomeState:
Record
state: E_HomeState;
owner: Node;
sharers: multiset [ProcCount] of Node;
val: Value;
-- metadata
ack: Count; -- invalidation ack counter
upgrade: boolean; -- does requesting LCE want an upgrade?
transfer: boolean; -- does request result in LCE to LCE transfer?
nextState: E_ProcState;
nextHomeState: E_HomeState;
reqLce: Node;
trLce: Node;
End;
ProcState:
Record
state: E_ProcState;
val: Value;
-- metadata
nextState: E_ProcState;
transfer: boolean;
blocked: boolean;
End;
----------------------------------------------------------------------
-- Variables
----------------------------------------------------------------------
var
HomeNode: HomeState;
Procs: array [Proc] of ProcState;
Net: array [Node] of multiset [NetMax] of Message; -- One multiset for each destination - messages are arbitrarily reordered by the multiset
InBox: array [Node] of array [VCType] of Message; -- If a message is not processed, it is placed in InBox, blocking that virtual channel
msg_processed: boolean;
LastWrite: Value; -- Used to confirm that writes are not lost; this variable would not exist in real hardware
----------------------------------------------------------------------
-- Procedures
----------------------------------------------------------------------
Procedure printMsgType(mtype:MessageType;);
Begin
switch (mtype)
case LceRdReq:
put "LceRdReq";
case LceWrReq:
put "LceWrReq";
case InvTagCmd:
put "InvTagCmd";
case TrCmd:
put "TrCmd";
case SetTagCmd:
put "SetTagCmd";
case SetTagWakeupCmd:
put "SetTagWakeupCmd";
case WritebackCmd:
put "WritebackCmd";
case DataCmdCce:
put "DataCmdCce";
case DataCmdLce:
put "DataCmdLce";
case InvTagAck:
put "InvTagAck";
case CohAck:
put "CohAck";
case TrAck:
put "TrAck";
case LceDataRespNull:
put "LceDataRespNull";
case LceDataResp:
put "LceDataResp";
else
put "Unhandled message type "; put mtype; put "\n";
error "Unhandled message type!";
endswitch;
end;
Procedure Send(mtype:MessageType;
dst:Node;
src:Node;
vc:VCType;
val:Value;
upgrade:boolean;
target:Node;
nextState:E_ProcState;
);
var msg:Message;
Begin
-- put "Sending msg on Net "; put dst;
-- put " msgType: "; put mtype;
-- put " msgType: "; printMsgType(mtype);
-- put " src: "; put src; put " vc: "; put vc;
-- put "\n";
Assert (MultiSetCount(i:Net[dst], true) < NetMax) "Too many messages";
msg.mtype := mtype;
msg.src := src;
msg.vc := vc;
-- data value for data cmd and data resp
msg.val := val;
-- metadata for requests
msg.upgrade := upgrade;
-- metadata for commands
msg.target := target; -- target for transfer
msg.nextState := nextState;
MultiSetAdd(msg, Net[dst]);
End;
Procedure ErrorUnhandledMsg(msg:Message; n:Node);
Begin
put "Unhandled msg "; put msg.mtype; put " from proc "; put n;
error "Unhandled message type!";
End;
Procedure ErrorUnhandledState();
Begin
error "Unhandled state!";
End;
Procedure AddToSharersList(n:Node);
Begin
if MultiSetCount(i:HomeNode.sharers, HomeNode.sharers[i] = n) = 0
then
MultiSetAdd(n, HomeNode.sharers);
endif;
End;
Function IsSharer(n:Node) : Boolean;
Begin
return MultiSetCount(i:HomeNode.sharers, HomeNode.sharers[i] = n) > 0
End;
Procedure RemoveFromSharersList(n:Node);
Begin
MultiSetRemovePred(i:HomeNode.sharers, HomeNode.sharers[i] = n);
End;
-- Sends a message to all sharers except rqst
Procedure SendInvReqToSharers(rqst:Node);
Begin
for n:Node do
if (IsMember(n, Proc) &
MultiSetCount(i:HomeNode.sharers, HomeNode.sharers[i] = n) != 0)
then
if n != rqst
then
Send(InvTagCmd, n, HomeType, VC1, UNDEFINED, false, UNDEFINED, UNDEFINED);
endif;
endif;
endfor;
End;
Procedure HomeReceive(msg:Message);
var cnt:0..ProcCount; -- for counting sharers
Begin
-- Debug output may be helpful:
-- put "Receiving "; put msg.mtype; put " on VC"; put msg.vc;
-- put " at home -- "; put HomeNode.state;
-- put "\n";
-- compiler barfs if we put this inside a switch, so it is useful to
-- pre-calculate the sharer count here
cnt := MultiSetCount(i:HomeNode.sharers, true);
-- default to 'processing' message. set to false otherwise
msg_processed := true;
-- H_M, H_S, H_I, H_E, H_IA, H_CA, H_TA, H_TWB
switch HomeNode.state
-- Stable states
-- invalid in directory - immediately reply with current data and set tag
case H_I:
assert(cnt = 0) "home invalid, but sharers count not 0";
switch msg.mtype
case LceRdReq:
HomeNode.state := H_CA;
HomeNode.ack := 0; -- invalid, no acks required
HomeNode.upgrade := false; -- not an upgrade
HomeNode.transfer := false; -- not a transfer
HomeNode.nextState := P_E; -- LCE will go to P_E
HomeNode.nextHomeState := H_E; -- HomeNode will go to H_E after receiving coh ack
HomeNode.reqLce := msg.src;
undefine HomeNode.trLce;
-- send commands
--put "sending data cmd cce and set tag cmd from H_I\n";
Send(DataCmdCce, msg.src, HomeType, VC2, HomeNode.val, false, UNDEFINED, UNDEFINED);
Send(SetTagCmd, msg.src, HomeType, VC1, UNDEFINED, false, UNDEFINED, P_E);
case LceWrReq:
HomeNode.state := H_CA;
HomeNode.ack := 0; -- invalid, no acks required
HomeNode.upgrade := false; -- not an upgrade
HomeNode.transfer := false; -- not a transfer
HomeNode.nextState := P_M; -- LCE will go to P_M
HomeNode.nextHomeState := H_M; -- HomeNode will go to H_M after receiving coh ack
HomeNode.reqLce := msg.src;
undefine HomeNode.trLce;
-- send commands
--put "sending data cmd cce and set tag cmd from H_I\n";
Send(DataCmdCce, msg.src, HomeType, VC2, HomeNode.val, false, UNDEFINED, UNDEFINED);
Send(SetTagCmd, msg.src, HomeType, VC1, UNDEFINED, false, UNDEFINED, P_M);
else
ErrorUnhandledMsg(msg, HomeType);
endswitch;
-- block in shared in directory
case H_S:
switch msg.mtype
case LceRdReq:
HomeNode.state := H_CA;
HomeNode.ack := 0; -- invalid, no acks required
HomeNode.upgrade := false; -- not an upgrade
HomeNode.transfer := false; -- not a transfer
HomeNode.nextState := P_S; -- LCE will go to P_S
HomeNode.nextHomeState := H_S; -- HomeNode will go to H_S after receiving coh ack
HomeNode.reqLce := msg.src;
undefine HomeNode.trLce;
-- send commands
--put "sending data cmd and set tag cmd from H_S\n";
Send(DataCmdCce, msg.src, HomeType, VC2, HomeNode.val, false, UNDEFINED, UNDEFINED);
Send(SetTagCmd, msg.src, HomeType, VC1, UNDEFINED, false, UNDEFINED, P_S);
case LceWrReq:
-- there are two cases for a write request when block is cached in shared:
-- 1. request is from a cache with block in P_S ==> Upgrade
-- 2. request is from a cache with block in P_I ==> Write miss
-- invalidate all the other caches regardless of upgrade or write miss
--put "sending invalidations from H_S\n";
SendInvReqToSharers(msg.src);
if (IsSharer(msg.src))
then
assert(msg.upgrade) "Upgrade detected, but request is not uprade";
-- this is an upgrade
HomeNode.upgrade := true;
HomeNode.ack := cnt - 1;
-- special case: the only sharer is the requestor
if (cnt = 1)
then
--put "sending set tag wakeup cmd from H_S\n";
Send(SetTagWakeupCmd, msg.src, HomeType, VC1, UNDEFINED, false, UNDEFINED, P_M);
HomeNode.state := H_CA;
else
HomeNode.state := H_IA;
endif;
else
-- requestor does not have block cached, not an upgrade, all sharers will ack
HomeNode.upgrade := false; -- not an upgrade
HomeNode.ack := cnt;
-- wait for acks
HomeNode.state := H_IA;
endif;
HomeNode.transfer := false; -- not a transfer
HomeNode.nextState := P_M; -- LCE will go to P_M
HomeNode.nextHomeState := H_M; -- HomeNode will go to H_M after receiving inv acks and coh ack
HomeNode.reqLce := msg.src;
undefine HomeNode.trLce;
else
ErrorUnhandledMsg(msg, HomeType);
endswitch;
case H_E:
assert(cnt = 0) "home exclusive, but sharers count not 0";
switch msg.mtype
case LceRdReq:
HomeNode.state := H_IA;
-- send invalidation to owner
--put "sending invalidations from H_E\n";
Send(InvTagCmd, HomeNode.owner, HomeType, VC1, UNDEFINED, false, UNDEFINED, UNDEFINED);
HomeNode.ack := 1; -- cached in E by single cache
HomeNode.upgrade := false; -- not an upgrade
HomeNode.transfer := true; -- transfer
HomeNode.nextState := P_S; -- LCE will go to P_S
HomeNode.nextHomeState := H_S; -- HomeNode will go to H_S after receiving tr ack
HomeNode.reqLce := msg.src;
HomeNode.trLce := HomeNode.owner;
case LceWrReq:
HomeNode.state := H_IA;
-- send invalidations
--put "sending invalidations from H_E\n";
Send(InvTagCmd, HomeNode.owner, HomeType, VC1, UNDEFINED, false, UNDEFINED, UNDEFINED);
HomeNode.ack := 1; -- cached in E by single cache
HomeNode.upgrade := false; -- not an upgrade
HomeNode.transfer := true; -- transfer
HomeNode.nextState := P_M; -- LCE will go to P_M
HomeNode.nextHomeState := H_M; -- HomeNode will go to H_M after receiving tr ack
HomeNode.reqLce := msg.src;
HomeNode.trLce := HomeNode.owner;
else
ErrorUnhandledMsg(msg, HomeType);
endswitch;
-- block in modified, cached in a single LCE
case H_M:
assert(cnt = 0) "home modified, but sharers count not 0";
switch msg.mtype
case LceRdReq:
HomeNode.state := H_IA;
--put "sending invalidations from H_M\n";
Send(InvTagCmd, HomeNode.owner, HomeType, VC1, UNDEFINED, false, UNDEFINED, UNDEFINED);
HomeNode.ack := 1; -- cached in E by single cache
HomeNode.upgrade := false; -- not an upgrade
HomeNode.transfer := true; -- transfer
HomeNode.nextState := P_S; -- LCE will go to P_S
HomeNode.nextHomeState := H_S; -- HomeNode will go to H_S after receiving tr ack
HomeNode.reqLce := msg.src;
HomeNode.trLce := HomeNode.owner;
case LceWrReq:
HomeNode.state := H_IA;
--put "sending invalidations from H_M\n";
Send(InvTagCmd, HomeNode.owner, HomeType, VC1, UNDEFINED, false, UNDEFINED, UNDEFINED);
HomeNode.ack := 1; -- cached in E by single cache
HomeNode.upgrade := false; -- not an upgrade
HomeNode.transfer := true; -- transfer
HomeNode.nextState := P_M; -- LCE will go to P_M
HomeNode.nextHomeState := H_M; -- HomeNode will go to H_M after receiving tr ack
HomeNode.reqLce := msg.src;
HomeNode.trLce := HomeNode.owner;
else
ErrorUnhandledMsg(msg, HomeType);
endswitch;
-- "Transient" states
-- waiting for invalidation acks
case H_IA:
switch msg.mtype
case LceRdReq, LceWrReq:
msg_processed := false;
case InvTagAck:
HomeNode.ack := HomeNode.ack - 1;
-- remove LCE from sharers as invalidation acks arrive
RemoveFromSharersList(msg.src);
if (HomeNode.ack = 0)
then
if (HomeNode.upgrade = true)
then
--put "sending set tag wakeup from H_IA\n";
Send(SetTagWakeupCmd, HomeNode.reqLce, HomeType, VC1, UNDEFINED, false, UNDEFINED, P_M);
HomeNode.state := H_CA;
elsif (HomeNode.transfer = true)
then
--put "sending set tag, tr, and writeback from H_IA\n";
Send(SetTagCmd, HomeNode.reqLce, HomeType, VC1, UNDEFINED, false, UNDEFINED, HomeNode.nextState);
Send(TrCmd, HomeNode.trLce, HomeType, VC1, UNDEFINED, false, HomeNode.reqLce, UNDEFINED);
Send(WritebackCmd, HomeNode.trLce, HomeType, VC1, UNDEFINED, false, UNDEFINED, UNDEFINED);
HomeNode.state := H_TWB;
else
--put "sending set tag and data cmd from H_IA\n";
Send(DataCmdCce, HomeNode.reqLce, HomeType, VC2, HomeNode.val, false, UNDEFINED, UNDEFINED);
Send(SetTagCmd, HomeNode.reqLce, HomeType, VC1, UNDEFINED, false, UNDEFINED, HomeNode.nextState);
HomeNode.state := H_CA;
endif;
endif;
else
ErrorUnhandledMsg(msg, HomeType);
endswitch;
-- waiting for coherence ack
case H_CA:
switch msg.mtype
case LceRdReq, LceWrReq:
msg_processed := false;
case CohAck:
HomeNode.state := HomeNode.nextHomeState;
assert(msg.src = HomeNode.reqLce) "Coh Ack arrived from other than requesting LCE";
if (HomeNode.nextHomeState = H_E | HomeNode.nextHomeState = H_M)
then
RemoveFromSharersList(msg.src);
undefine HomeNode.sharers;
HomeNode.owner := HomeNode.reqLce;
elsif (HomeNode.nextHomeState = H_S)
then
AddToSharersList(HomeNode.reqLce);
undefine HomeNode.owner;
endif;
else
ErrorUnhandledMsg(msg, HomeType);
endswitch;
-- waiting for transfer ack
case H_TA:
switch msg.mtype
case LceRdReq, LceWrReq:
msg_processed := false;
case TrAck:
HomeNode.state := HomeNode.nextHomeState;
HomeNode.transfer := false;
assert(msg.src = HomeNode.reqLce) "Tr Ack arrived from other than requesting LCE";
if (HomeNode.nextHomeState = H_E | HomeNode.nextHomeState = H_M)
then
RemoveFromSharersList(msg.src);
undefine HomeNode.sharers;
HomeNode.owner := HomeNode.reqLce;
elsif (HomeNode.nextHomeState = H_S)
then
AddToSharersList(HomeNode.reqLce);
undefine HomeNode.owner;
endif;
else
ErrorUnhandledMsg(msg, HomeType);
endswitch;
-- waiting for writeback from transfer LCE
case H_TWB:
switch msg.mtype
case LceRdReq, LceWrReq, TrAck:
msg_processed := false;
case LceDataResp:
HomeNode.state := H_TA;
HomeNode.val := msg.val;
case LceDataRespNull:
HomeNode.state := H_TA;
HomeNode.val := msg.val;
else
ErrorUnhandledMsg(msg, HomeType);
endswitch;
else
ErrorUnhandledState();
endswitch;
End;
Procedure ProcReceive(msg:Message; p:Proc);
Begin
-- put "Receiving "; put msg.mtype; put " on VC"; put msg.vc;
-- put " at proc "; put p; put " in state "; put Procs[p].state;
-- put "\n";
-- default to 'processing' message. set to false otherwise
msg_processed := true;
alias ps:Procs[p].state do
alias pv:Procs[p].val do
alias pns:Procs[p].nextState do
alias ptr:Procs[p].transfer do
alias pb:Procs[p].blocked do
-- Processor states
-- P_I, P_ID, P_I_DT, P_I_D, P_I_T, P_S, P_S_U, P_E, P_M
-- Send(mtype, dst, src, vc, val, upgrade, replace, lruDirty, target, nextState)
switch ps
-- invalid, block is clean
case P_I:
switch msg.mtype
case TrCmd:
--put "sending data cmd lce from P_I\n";
Send(DataCmdLce, msg.target, p, VC2, pv, false, UNDEFINED, UNDEFINED);
-- stay in invalid
case WritebackCmd:
--put "sending null wb resp from P_I\n";
Send(LceDataRespNull, msg.src, p, VC4, pv, false, UNDEFINED, UNDEFINED);
--undefine pv;
-- stay in invalid
else
ErrorUnhandledMsg(msg, p);
endswitch;
-- invalid, but still dirty (waiting for writeback)
case P_ID:
switch msg.mtype
case TrCmd:
--put "sending data cmd lce from P_ID\n";
Send(DataCmdLce, msg.target, p, VC2, pv, false, UNDEFINED, UNDEFINED);
-- stay in invalid dirty, transfer is not a writeback
case WritebackCmd:
--put "sending wb resp from P_ID\n";
Send(LceDataResp, msg.src, p, VC4, pv, false, UNDEFINED, UNDEFINED);
-- TODO: when in P_ID, becasuse no ordering on networks modeled here, could receive writbeack before transfer, thus transfer would send
-- invalid (UNDEFINED) data
--undefine pv;
-- go to invalid, no longer dirty
ps := P_I;
else
ErrorUnhandledMsg(msg, p);
endswitch;
-- invalid, waiting for tag or data
case P_I_DT:
switch msg.mtype
case TrCmd:
--put "sending data cmd lce from P_I_DT\n";
Send(DataCmdLce, msg.target, p, VC2, pv, false, UNDEFINED, UNDEFINED);
-- stay in current state
case WritebackCmd:
--put "sending null wb resp from P_I_DT\n";
Send(LceDataRespNull, msg.src, p, VC4, pv, false, UNDEFINED, UNDEFINED);
--undefine pv;
-- stay in current state
case SetTagCmd:
-- record next state
pns := msg.nextState;
ps := P_I_D;
case DataCmdCce:
-- record data value received
pv := msg.val;
ps := P_I_T;
case DataCmdLce:
-- record data value received
ptr := true;
pv := msg.val;
ps := P_I_T;
else
ErrorUnhandledMsg(msg, p);
endswitch;
-- invalid, waiting for data
case P_I_D:
switch msg.mtype
case DataCmdCce:
--put "sending coh ack from P_I_D\n";
Send(CohAck, msg.src, p, VC3, UNDEFINED, false, UNDEFINED, UNDEFINED);
-- next state was provided by set tag command already received
ps := pns;
pv := msg.val;
pb := false;
case DataCmdLce:
--put "sending tr ack from P_I_D\n";
Send(TrAck, HomeType, p, VC3, UNDEFINED, false, UNDEFINED, UNDEFINED);
-- next state was provided by set tag command already received
ps := pns;
pv := msg.val;
pb := false;
--put "received val: "; put msg.val; put " HomeNode.val is: "; put HomeNode.val; put "LastWrite is: "; put LastWrite; put "\n";
else
ErrorUnhandledMsg(msg, p);
endswitch;
-- invalid, waiting for tag
case P_I_T:
switch msg.mtype
case SetTagCmd:
if (ptr)
then
--put "sending tr ack from P_I_T\n";
Send(TrAck, msg.src, p, VC3, UNDEFINED, false, UNDEFINED, UNDEFINED);
ptr := false;
pb := false;
else
--put "sending coh ack from P_I_T\n";
Send(CohAck, msg.src, p, VC3, UNDEFINED, false, UNDEFINED, UNDEFINED);
pb := false;
endif;
ps := msg.nextState;
else
ErrorUnhandledMsg(msg, p);
endswitch;
-- shared
case P_S:
switch msg.mtype
case InvTagCmd:
--put "sending inv tag ack from P_S\n";
Send(InvTagAck, msg.src, p, VC3, UNDEFINED, false, UNDEFINED, UNDEFINED);
ps := P_I;
else
ErrorUnhandledMsg(msg, p);
endswitch;
-- shared, attempting to upgrade to modified
case P_S_U:
switch msg.mtype
case InvTagCmd:
--put "sending inv tag ack from P_S_U\n";
Send(InvTagAck, msg.src, p, VC3, UNDEFINED, false, UNDEFINED, UNDEFINED);
ps := P_I_DT;
case SetTagWakeupCmd:
--put "sending coh ack from P_S_U\n";
Send(CohAck, msg.src, p, VC3, UNDEFINED, false, UNDEFINED, UNDEFINED);
ps := P_M;
pb := false;
else
ErrorUnhandledMsg(msg, p);
endswitch;
-- exclusive
case P_E:
switch msg.mtype
case InvTagCmd:
--put "sending inv tag ack from P_E\n";
Send(InvTagAck, msg.src, p, VC3, UNDEFINED, false, UNDEFINED, UNDEFINED);
-- block is clean, so go to invalid state
ps := P_I;
else
ErrorUnhandledMsg(msg, p);
endswitch;
-- modified
case P_M:
switch msg.mtype
case InvTagCmd:
--put "sending inv tag ack from P_M\n";
Send(InvTagAck, msg.src, p, VC3, UNDEFINED, false, UNDEFINED, UNDEFINED);
-- block is dirty, go to invalid dirty state
ps := P_ID;
else
ErrorUnhandledMsg(msg, p);
endswitch;
----------------------------
-- Error catch
----------------------------
else
ErrorUnhandledState();
endswitch;
endalias;
endalias;
endalias;
endalias;
endalias;
End;
----------------------------------------------------------------------
-- Rules
----------------------------------------------------------------------
-- Processor states
-- P_M, P_S, P_I, P_E, P_I_DT, P_I_D, P_I_T, P_ID, P_S_U
-- Processor actions (affecting coherency)
ruleset n:Proc do
alias p:Procs[n] do
---------------------------------------------------------------
-- load or store hits
---------------------------------------------------------------
ruleset v:Value Do
rule "store new value"
(p.state = P_M)
==>
p.val := v;
LastWrite := v; --We use LastWrite to sanity check that reads receive the value of the last write
--put "storing value := "; put v; put " by proc "; put n; put "\n";
endrule;
endruleset;
ruleset v:Value Do
rule "store new value on exclusive"
(p.state = P_E)
==>
p.val := v;
LastWrite := v; --We use LastWrite to sanity check that reads receive the value of the last write
p.state := P_M;
--put "storing value := "; put v; put " by proc "; put n; put "\n";
endrule;
endruleset;
---------------------------------------------------------------
-- load or stores with no replacement
---------------------------------------------------------------
rule "read request"
(p.state = P_I & p.blocked = false)
==>
--put "LceRdReq from Proc "; put n; put " in state "; put p.state;
Send(LceRdReq, HomeType, n, VC0, UNDEFINED, false, UNDEFINED, UNDEFINED);
p.state := P_I_DT;
p.blocked := true;
endrule;
rule "write request"
(p.state = P_I & p.blocked = false)
==>
--put "LceWrReq from Proc "; put n; put " in state "; put p.state;
Send(LceWrReq, HomeType, n, VC0, UNDEFINED, false, UNDEFINED, UNDEFINED);
p.state := P_I_DT;
p.blocked := true;
endrule;
rule "upgrade request"
(p.state = P_S & p.blocked = false)
==>
--put "LceWrReq from Proc "; put n; put " in state "; put p.state;
Send(LceWrReq, HomeType, n, VC0, UNDEFINED, true, UNDEFINED, UNDEFINED);
p.state := P_S_U;
p.blocked := true;
endrule;
endalias;
endruleset;
-- Message delivery rules
ruleset n:Node do
choose midx:Net[n] do
alias chan:Net[n] do
alias msg:chan[midx] do
alias box:InBox[n] do
-- Pick a random message in the network and delivier it
rule "receive-net"
(isundefined(box[msg.vc].mtype))
==>
if IsMember(n, Home)
then
HomeReceive(msg);
else
ProcReceive(msg, n);
endif;
if ! msg_processed
then
-- The node refused the message, stick it in the InBox to block the VC.
box[msg.vc] := msg;
endif;
MultiSetRemove(midx, chan);
endrule;
endalias
endalias;
endalias;
endchoose;
-- Try to deliver a message from a blocked VC; perhaps the node can handle it now
ruleset vc:VCType do
rule "receive-blocked-vc"
(! isundefined(InBox[n][vc].mtype))
==>
if IsMember(n, Home)
then
HomeReceive(InBox[n][vc]);
else
ProcReceive(InBox[n][vc], n);
endif;
if msg_processed
then
-- Message has been handled, forget it
undefine InBox[n][vc];
endif;
endrule;
endruleset;
endruleset;
----------------------------------------------------------------------
-- Startstate
----------------------------------------------------------------------
startstate
msg_processed := false;
-- home node initialization
HomeNode.state := H_I;
undefine HomeNode.owner;
undefine HomeNode.sharers;
HomeNode.val := 1;
HomeNode.ack := 0;
HomeNode.upgrade := false;
HomeNode.transfer := false;
undefine HomeNode.nextState;
undefine HomeNode.reqLce;
undefine HomeNode.trLce;
-- global last write value
LastWrite := HomeNode.val;
-- processor initialization
for i:Proc do
Procs[i].state := P_I;
undefine Procs[i].val;
undefine Procs[i].nextState;
Procs[i].transfer := false;
Procs[i].blocked := false;
endfor;
-- network initialization
undefine Net;
endstartstate;
----------------------------------------------------------------------
-- Invariants
----------------------------------------------------------------------
-- Directory Invariants
invariant "Invalid implies empty owner"
HomeNode.state = H_I
->
IsUndefined(HomeNode.owner);
invariant "Modified implies empty sharers list"
HomeNode.state = H_M
->
MultiSetCount(i:HomeNode.sharers, true) = 0;
invariant "Invalid implies empty sharer list"
HomeNode.state = H_I
->
MultiSetCount(i:HomeNode.sharers, true) = 0;
invariant "Exclusive implies empty sharer list"
HomeNode.state = H_E
->
MultiSetCount(i:HomeNode.sharers, true) = 0;
invariant "Value in memory must match the value of the last write in Invalid state"
HomeNode.state = H_I
->
HomeNode.val = LastWrite;
invariant "Home in Shared state implies Proc in Invalid or Shared"
Forall n : Proc do
HomeNode.state = H_S
->
(Procs[n].state = P_S | Procs[n].state = P_S_U | Procs[n].state = P_I | Procs[n].state = P_I_DT | Procs[n].state = P_I_D
| Procs[n].state = P_I_T | Procs[n].state = P_ID)
end;
invariant "Values in shared state must match last write"
Forall n : Proc do
Procs[n].state = P_S
->
Procs[n].val = LastWrite --LastWrite is updated whenever a new value is created
end;
-- Not true in our system, CCE invalidates blocks and then requests writeback, so a block may be invalid and
-- still have a valid data value
--invariant "value is undefined while invalid"
-- Forall n : Proc do
-- Procs[n].state = P_I
-- ->
-- IsUndefined(Procs[n].val)
-- end;
invariant "Exclusive has a clean copy of data"
Forall n : Proc do
HomeNode.state = H_E & Procs[n].state = P_E
->
HomeNode.val = Procs[n].val
end;
--TODO: is there any variation of these rules that hold true?
--invariant "values in memory matches value of last write, when shared or invalid"
-- Forall n : Proc do
-- HomeNode.state = H_S | HomeNode.state = H_I
-- ->
-- HomeNode.val = LastWrite
-- end;
--invariant "values in memory matches value of last write, when shared"
-- Forall n : Proc do
-- HomeNode.state = H_S
-- ->
-- HomeNode.val = LastWrite
-- end;
invariant "values in shared state match memory"
Forall n : Proc do
HomeNode.state = H_S & Procs[n].state = P_S
->
HomeNode.val = Procs[n].val
end;