/* snooping cache algorithm
 */
#define QSZ	2

mtype = {	r, w, raw,
		RD, WR, RX,
		MX, MXdone,
		req0, req1,
		CtoB, BtoC,
		grant, done
	};

chan tocpu0	= [QSZ] of { mtype };
chan fromcpu0	= [QSZ] of { mtype };
chan tobus0	= [QSZ] of { mtype };
chan frombus0	= [QSZ] of { mtype };
chan grant0	= [QSZ] of { mtype };

chan tocpu1	= [QSZ] of { mtype };
chan fromcpu1	= [QSZ] of { mtype };
chan tobus1	= [QSZ] of { mtype };
chan frombus1	= [QSZ] of { mtype };
chan grant1	= [QSZ] of { mtype };

chan claim0	= [QSZ] of { mtype };
chan claim1	= [QSZ] of { mtype };
chan release0	= [QSZ] of { mtype };
chan release1	= [QSZ] of { mtype };

#define W	1
#define R	2
#define X	3

proctype cpu0()
{
	xs fromcpu0;
	xr tocpu0;
	do
	:: fromcpu0!r   -> tocpu0?done
	:: fromcpu0!w   -> tocpu0?done
	:: fromcpu0!raw -> tocpu0?done
	od
}

proctype cpu1()
{
	xs fromcpu1;
	xr tocpu1;
	do
	:: fromcpu1!r   -> tocpu1?done
	:: fromcpu1!w   -> tocpu1?done
	:: fromcpu1!raw -> tocpu1?done
	od
}

proctype cache0()
{	byte state = X;
	byte which;

	xr frombus0;
	xr fromcpu0;
	xs tocpu0;
	xs tobus0;
	xr grant0;
	xs claim0;
	xs release0;
resume:
	do
	:: frombus0?RD ->
		if
		:: (state == W) -> state = R; tobus0!CtoB
		:: (state != W) -> tobus0!done
		fi
	:: frombus0?MX -> state = X; tobus0!MXdone
	:: frombus0?RX ->
		if
		:: (state == W) -> state = X; tobus0!CtoB
		:: (state == R) -> state = X; tobus0!done
		:: (state == X) -> tobus0!done
		fi

	:: fromcpu0?r ->
		if
		:: (state != X) -> tocpu0!done
		:: (state == X) -> which = RD; goto buscycle
		fi
	:: fromcpu0?w ->
		if
		:: (state == W) -> tocpu0!done
		:: (state != W) -> which = MX; goto buscycle
		fi
	:: fromcpu0?raw ->
		if
		:: (state == W) -> tocpu0!done
		:: (state != W) -> which = RX; goto buscycle
		fi
	od;
buscycle:
	claim0!req0;
	do
	:: frombus0?RD ->
		if
		:: (state == W) -> state = R; tobus0!CtoB
		:: (state != W) -> tobus0!done
		fi
	:: frombus0?MX -> state = X; tobus0!MXdone
	:: frombus0?RX ->
		if
		:: (state == W) -> state = X; tobus0!CtoB
		:: (state == R) -> state = X; tobus0!done
		:: (state == X) -> tobus0!done
		fi
	:: grant0?grant ->
		if
		:: (which == RD) -> state = R
		:: (which == MX) -> state = W
		:: (which == RX) -> state = W
		fi;
		tocpu0!done;
		break
	od;
	release0!done;

	if
	:: (which == RD) -> tobus0!RD -> frombus0?BtoC
	:: (which == MX) -> tobus0!MX -> frombus0?done
	:: (which == RX) -> tobus0!RX -> frombus0?BtoC
	fi;
	goto resume
}

proctype cache1()
{	byte state = X;
	byte which;

	xr frombus1;
	xr fromcpu1;
	xs tobus1;
	xs tocpu1;
	xr grant1;
	xs claim1;
	xs release1;
resume:
	do
	:: frombus1?RD ->
		if
		:: (state == W) -> state = R; tobus1!CtoB
		:: (state != W) -> tobus1!done
		fi
	:: frombus1?MX -> state = X; tobus1!MXdone
	:: frombus1?RX ->
		if
		:: (state == W) -> state = X; tobus1!CtoB
		:: (state == R) -> state = X; tobus1!done
		:: (state == X) -> tobus1!done
		fi

	:: fromcpu1?r ->
		if
		:: (state != X) -> tocpu1!done
		:: (state == X) -> which = RD; goto buscycle
		fi
	:: fromcpu1?w ->
		if
		:: (state == W) -> tocpu1!done
		:: (state != W) -> which = MX; goto buscycle
		fi
	:: fromcpu1?raw ->
		if
		:: (state == W) -> tocpu1!done
		:: (state != W) -> which = RX; goto buscycle
		fi
	od;
buscycle:
	claim1!req1;
	do
	:: frombus1?RD ->
		if
		:: (state == W) -> state = R; tobus1!CtoB
		:: (state != W) -> tobus1!done
		fi
	:: frombus1?MX -> state = X; tobus1!MXdone
	:: frombus1?RX ->
		if
		:: (state == W) -> state = X; tobus1!CtoB
		:: (state == R) -> state = X; tobus1!done
		:: (state == X) -> tobus1!done
		fi
	:: grant1?grant ->
		if
		:: (which == RD) -> state = R
		:: (which == MX) -> state = W
		:: (which == RX) -> state = W
		fi;
		tocpu1!done;
		break
	od;
	release1!done;

	if
	:: (which == RD) -> tobus1!RD -> frombus1?BtoC
	:: (which == MX) -> tobus1!MX -> frombus1?done
	:: (which == RX) -> tobus1!RX -> frombus1?BtoC
	fi;
	goto resume
}

proctype busarbiter()
{
	xs grant0;
	xs grant1;
	xr claim0;
	xr claim1;
	xr release0;
	xr release1;

	do
	:: claim0?req0 -> grant0!grant; release0?done
	:: claim1?req1 -> grant1!grant; release1?done
	od
}

proctype bus()		/* models real bus + main memory */
{
	xs frombus1;
	xs frombus0;
	xr tobus0;
	xr tobus1;

	do
	:: tobus0?CtoB -> frombus1!BtoC
	:: tobus1?CtoB -> frombus0!BtoC

	:: tobus0?done -> /* M -> B */ frombus1!BtoC
	:: tobus1?done -> /* M -> B */ frombus0!BtoC

	:: tobus0?MXdone -> /* B -> M */ frombus1!done
	:: tobus1?MXdone -> /* B -> M */ frombus0!done

	:: tobus0?RD -> frombus1!RD
	:: tobus1?RD -> frombus0!RD

	:: tobus0?MX -> frombus1!MX
	:: tobus1?MX -> frombus0!MX

	:: tobus0?RX -> frombus1!RX
	:: tobus1?RX -> frombus0!RX
	od
}

init {
	atomic {
		run cpu0(); run cpu1();
		run cache0(); run cache1();
		run bus(); run busarbiter()
	}
}
