---------------------------------------------------------------------- -- Filename: i.m -- Content: i-protocol -- Version: Murphi 3.0 -- Author: translate to Murphi by Yifei Dong, 1998 ---------------------------------------------------------------------- CONST WindowSize : 2; MaxSequence : 2*WindowSize - 1; fixed : true; candrop : true; cancorrupt : true; TYPE SequenceNumber : 0..MaxSequence; PacketType : ENUM { DATA, ACK, NAK }; Packet : RECORD pty : PacketType; seq : SequenceNumber; ack : SequenceNumber; END; Channel : RECORD full : boolean; herr : boolean; -- negative of checksum derr : boolean; -- convenient for CLEAR body : Packet; END; ChanID : 0..1; SenderState : ENUM { S_START, S_SENDMSG_1, S_SENDMSG_2, S_SENDMSG_3, S_TIMEOUT_1, S_GETPKT_1 }; ReceiverState : ENUM { R_START, R_GETPKT_1, R_GETPKT_2, R_GETPKT_3, R_GETPKT_31, R_GETPKT_4, R_TIMEOUT_1 }; Action : ENUM { tau, lsend, rrecv, drop, corrupt, timeout, progress }; VAR -- Channels Channels : array [ChanID] of Channel; -- Sender states SState : SenderState; SNext : SenderState; SSendSeq : SequenceNumber; SRack : SequenceNumber; Sseq : SequenceNumber; -- Receiver states RState : ReceiverState; RSendSeq : SequenceNumber; RRecSeq : SequenceNumber; RLack : SequenceNumber; RRack : SequenceNumber; RNakd : array[SequenceNumber] of boolean; RRecBuf : array[SequenceNumber] of boolean; Rseq : SequenceNumber; Rtmp : SequenceNumber; -- test variables action : Action; ---------------------------------------------------------------------- ---------------- Arithmetic Functions ------------------------------ ---------------------------------------------------------------------- FUNCTION inc(s: SequenceNumber) : SequenceNumber; BEGIN IF s < MaxSequence THEN RETURN s+1; ELSE RETURN 0; END; END; FUNCTION sub(s1, s2: SequenceNumber) : SequenceNumber; BEGIN IF s1 >= s2 THEN RETURN s1-s2; ELSE RETURN MaxSequence + 1 + s1 - s2; END; END; FUNCTION in_open_interval(s, l, r: SequenceNumber) : boolean; BEGIN RETURN s != l & sub(l,s) <= WindowSize & sub(s,r) <= WindowSize; END; ---------------------------------------------------------------------- ---------- Formula monitor ----------------------------------------- ---------------------------------------------------------------------- PROCEDURE observe(a: Action); BEGIN action := a; END; ---------------------------------------------------------------------- ---------------- Communication Functions --------------------------- ---------------------------------------------------------------------- FUNCTION isfull(Chan: Channel) : boolean; BEGIN return Chan.full; END; FUNCTION isempty(Chan: Channel) : boolean; BEGIN return ! Chan.full; END; PROCEDURE input(VAR Chan: Channel; VAR Hck, Dck: boolean; VAR Pak: Packet); BEGIN assert(isfull(Chan)); Pak := Chan.body; Hck := ! Chan.herr; Dck := ! Chan.derr; CLEAR Chan; END; PROCEDURE output(VAR Chan: Channel; Pty: PacketType; Seq, Ack: SequenceNumber); BEGIN assert(isempty(Chan)); Chan.body.pty := Pty; Chan.body.seq := Seq; Chan.body.ack := Ack; Chan.full := true; Chan.herr := false; Chan.derr := false; END; ---------------------------------------------------------------------- ---------------- Processes Data and Ack Channels ------------------- ---------------------------------------------------------------------- RULESET n:ChanID DO ALIAS Chan: Channels[n] DO RULE "channel drop message" isfull(Chan) & candrop ==> BEGIN Chan.full := false; CLEAR Chan; observe(drop); END; RULE "channel corrupt message header" isfull(Chan) & cancorrupt & ! Chan.herr ==> BEGIN Chan.herr := true; observe(corrupt); END; RULE "channel corrupt message data" isfull(Chan) & cancorrupt & ! Chan.derr ==> BEGIN Chan.derr := true; observe(corrupt); END; ENDALIAS; ENDRULESET; ---------------------------------------------------------------------- ALIAS DataChan:Channels[0]; AckChan: Channels[1] DO ---------------------------------------------------------------------- ---------------------------------------------------------------------- ---------------- Process Sender ------------------------------------ ---------------------------------------------------------------------- RULE "sender send message" SState = S_START ==> BEGIN observe(lsend); SState := S_SENDMSG_1; END; RULE "sender sendmsg 1" SState = S_SENDMSG_1 ==> BEGIN IF (sub(SSendSeq, SRack) > WindowSize) | (SSendSeq = SRack) THEN SState := S_SENDMSG_3; SNext := S_SENDMSG_1; ELSE SState := S_SENDMSG_2; END; observe(tau); END; RULE "sender sendmsg 2" SState = S_SENDMSG_2 & isempty(DataChan) ==> BEGIN output(DataChan, DATA, SSendSeq, 0); SSendSeq := inc(SSendSeq); SState := S_START; SNext := S_START; observe(tau); END; RULE "sender timeout" (SState = S_START | SState = S_SENDMSG_3) & isempty(DataChan) ==> BEGIN IF SState = S_START THEN observe(progress); -- arbitary timeout as progress ELSE observe(timeout); END; output(DataChan, NAK, 1, 0); IF SSendSeq != inc(SRack) THEN SState := S_TIMEOUT_1; ELSE SState := SNext; END; END; RULE "sender timeout 1" SState = S_TIMEOUT_1 & isempty(DataChan) ==> BEGIN output(DataChan, DATA, inc(SRack), 0); SState := SNext; observe(tau); END; RULE "sender getpkt" ( SState = S_START | SState = S_SENDMSG_3) & isfull(AckChan) ==> VAR hck : boolean; dck : boolean; pak : Packet; BEGIN SState := SNext; input(AckChan, hck, dck, pak); IF hck THEN IF in_open_interval(pak.ack, SSendSeq, SRack) THEN SRack := pak.ack; ENDIF; IF pak.pty = NAK THEN IF in_open_interval(pak.seq, SSendSeq, SRack) THEN Sseq := pak.seq; SState := S_GETPKT_1; ENDIF; ENDIF; ENDIF; observe(tau); END; RULE "sender getpkt 1" -- output SState = S_GETPKT_1 & isempty(DataChan) ==> BEGIN output(DataChan, DATA, Sseq, 0); SState := SNext; observe(tau); END; ---------------------------------------------------------------------- ---------------- Process Receiver ---------------------------------- ---------------------------------------------------------------------- RULE "receiver getpkt" (RState = R_START | RState = R_TIMEOUT_1) & isfull(DataChan) ==> VAR hck : boolean; dck : boolean; pak : Packet; tmp : SequenceNumber; BEGIN -- set default action and state observe(tau); RState := R_START; input(DataChan, hck, dck, pak); IF hck THEN -- handle-ack -- proti.vpl: don't updaterack because rack === 0 /* IF in_open_interval(pak.ack, RRack, RSendSeq) THEN RRack := pak.ack; END; */ SWITCH pak.pty CASE DATA: -- handle_data IF sub(pak.seq, RLack) <= WindowSize & pak.seq != RLack THEN IF dck = false THEN IF pak.seq != RRecSeq | RRecBuf[pak.seq] | RNakd[pak.seq] THEN RState := R_GETPKT_1; Rseq := pak.seq; ENDIF; ELSE -- dck = true RNakd[pak.seq] := false; IF pak.seq = inc(RRecSeq) THEN -- expected sequence number RRecSeq := inc(RRecSeq); -- usr-recv!*; observe(rrecv); tmp := inc(RRecSeq); WHILE RRecBuf[tmp] DO RRecSeq := tmp; -- usr-recv!*; observe(rrecv); RRecBuf[tmp] := false; tmp := inc(tmp); ENDWHILE; IF sub(pak.seq, RLack) >= WindowSize/2 THEN RState := R_GETPKT_2; Rseq := pak.seq; ENDIF; ELSIF pak.seq != RRecSeq & !RRecBuf[pak.seq] THEN RRecBuf[pak.seq] := true; tmp := inc(RRecSeq); IF tmp != pak.seq THEN Rtmp := tmp; Rseq := pak.seq; RState := R_GETPKT_3; ENDIF; ENDIF; -- pak.seq = inc(RRecSeq) ENDIF; -- dck = false ENDIF; -- -- CASE ACK: skip CASE NAK: -- handle_nak fix IF fixed & (pak.seq = RSendSeq) & (inc(RRack) = RSendSeq) THEN RState := R_GETPKT_4; ENDIF; ENDSWITCH; -- pak.pty ENDIF; -- hck END; RULE "receiver getpkt 1" RState = R_GETPKT_1 & isempty(AckChan) ==> BEGIN output(AckChan, NAK, Rseq, RRecSeq); RLack := RRecSeq; RNakd[Rseq] := true; CLEAR Rseq; RState := R_START; observe(tau); END; RULE "receiver getpkt 2" RState = R_GETPKT_2 & isempty(AckChan) ==> BEGIN output(AckChan, ACK, Rseq, RRecSeq); RLack := RRecSeq; CLEAR Rseq; RState := R_START; observe(tau); END; RULE "receiver getpkt 3" RState = R_GETPKT_3 ==> BEGIN IF !RNakd[Rtmp] & !RRecBuf[Rtmp] THEN RState := R_GETPKT_31; ELSE Rtmp := inc(Rtmp); WHILE Rtmp != Rseq & (RNakd[Rtmp] | RRecBuf[Rtmp]) DO Rtmp := inc(Rtmp); ENDWHILE; IF Rtmp = Rseq THEN CLEAR Rtmp; CLEAR Rseq; RState := R_START; ELSE RState := R_GETPKT_31; ENDIF; ENDIF; observe(tau); END; RULE "receiver getpkt 31" RState = R_GETPKT_31 & isempty(AckChan) ==> BEGIN output(AckChan, NAK, Rtmp, RRecSeq); RLack := RRecSeq; RNakd[Rtmp] := true; Rtmp := inc(Rtmp); IF Rtmp = Rseq THEN CLEAR Rtmp; CLEAR Rseq; RState := R_START; ELSE RState := R_GETPKT_3; ENDIF; observe(tau); END; RULE "receiver getpkt 4" RState = R_GETPKT_4 & isempty(AckChan) ==> BEGIN output(AckChan, ACK, RRecSeq, RRecSeq); RLack := RRecSeq; RState := R_START; observe(tau); END; RULE "receiver timeout" RState = R_START & isfull(DataChan) ==> BEGIN -- FORALL s: SequenceNumber DO -- RNakd[s] := false; -- END; CLEAR RNakd; RState := R_TIMEOUT_1; observe(progress); END; RULE "receiver timeout 2" RState = R_TIMEOUT_1 & isempty(AckChan) ==> VAR Seq : SequenceNumber; BEGIN Seq := inc(RRecSeq); output(AckChan, NAK, Seq, RRecSeq); RNakd[Seq] := true; RLack := RRecSeq; RState := R_START; observe(tau); END; ---------------------------------------------------------------------- ---------------- Liveness requirements ----------------------------- ---------------------------------------------------------------------- /* LIVENESS "data channel is active" ALWAYS EVENTUALLY isfull(DataChan); LIVENESS "ack channel is active" ALWAYS EVENTUALLY isfull(AckChan); */ LIVENESS "no livelock" ALWAYS EVENTUALLY (action != tau & action != timeout); ---------------------------------------------------------------------- ENDALIAS; ---------------------------------------------------------------------- STARTSTATE -- channel states CLEAR Channels; -- sender states SState := S_START; SNext := S_START; SSendSeq := 1; SRack := 0; CLEAR Sseq; -- receiver states RState := R_START; RSendSeq := 1; RRecSeq := 0; RLack := 0; RRack := 0; -- FORALL s:SequenceNumber DO -- RNakd[s] := false; -- RRecBuf[s] := false; -- END; CLEAR RNakd; CLEAR RRecBuf; CLEAR Rseq; CLEAR Rtmp; -- test variables action := tau; END;