// the module CacheBus implements communication between caches
// if more than one cache demands data from other caches, they can not do it at the same time.
// the CacheBus does the arbitration for the caches, so that only one of them gets the bus and
// can broadcast requirements to other caches. the rest requesting caches have to try again at
// the next cycle.
// 
// the design reflects the fact that communication matters in a multi-processor architecture.
// with the original design where only one bus is available for all caches and the main memory,
// the competition was realy intensive. To use an extra bus for inter-cache communication relaxes
// the communication. A more costly design would be using more complex connections among caches.

macro NumOfProcs   = 2;     // number of processors, or caches


module CacheBus(event [NumProcs]bool ?reqBusCache, !ackBusCache) {
    
    nat{NumProcs} token;
    // by default the first CPU has the token, 
    // i.e. the right to access the bus
    token = 0;

    loop {
        for(i=0..NumProcs-1) {
            ackBusCache[(token+i)%NumProcs] = reqBusCache[(token+i)%NumProcs] & (i==0 ? true : forall(j=0..i-1) !ackBusCache[(token+j)%NumProcs]); 
        }
        // pass token around
        next(token) = (token+1) % NumProcs;
        
        pause;
    }
}