// // Firefly cache coherency simulation // // 21/02/97 first version // 10/06/97 used SimpleButton + corrected a bug // 07/12/01 added locks and parallel operation // 10/12/01 memory read bug - now flush cache line if dirty (rather than ~SD) // 07/08/02 Vivio 2.0 // 08/08/02 bug fix - from reset if CPU0 write a3 is followed by CPU0 write a1 write NOW a shared write // 20/08/02 moved positioning of bus lock in write // 21/02/05 memory read bug - handle flush of shared cache line correctly (see bug 10/12/01) // 18/01/06 Vivio 4.0 // 20/02/06 real x,y event parameters // 07/11/06 added extra wait(1) after getting bus lock // 09/11/08 revision by Djordje Jevdjic // // include files // #include "standard.vin" #include "simpleButton.vin" // // set viewport // const int LOGICALW = 1024; const int LOGICALH = 768; setViewport(0, 0, LOGICALW, LOGICALH, 1); // // set background // bgbrush = SolidBrush(vellum); setBgBrush(bgbrush); // // fonts // hintFont = Font("Times New Roman", 16, 0); titleFont = Font("Times New Roman", 24, 0); f12B = Font("Times New Roman", 12, 1); fsmall = Font("Times New Roman", 14, 0); // // title // navybrush = SolidBrush(rgb(0, 0, 102)); title = Rectangle2(0, VCENTRE, 0, navybrush, 5, 5, LOGICALW/2, 30, whitebrush, titleFont, "Firefly Cache Coherency Protocol"); title.setTxtOff(2, 1); // // useage infomation // str = "Like real hardware, the CPUs can operate\n"; str += "in parallel - try pressing a button on each\n"; str += "CPU \"simultaneously\"."; hint = Txt(0, HLEFT | VTOP, 5, 80, blackbrush, hintFont, str); const int DIRTYBIT = 2; // cache line states const int SHAREDBIT = 4; const int EMPTY = 1; // ~Shared & ~Dirty & Empty 001 const int NSND = 0; // ~Shared & ~Dirty & ~Empty 000 const int NSD = 2; // ~Shared & Dirty & ~Empty 010 const int SND = 4; // Shared & ~Dirty & ~Empty 100 const int SD = 6; // Shared & Dirty & ~Empty 110 const int NCPU = 4; int wValue = 0; // value written into cache int isshared = 0; // indicates if cache line shared int issharedstate = 0; // shared cache line state (all should be the same) int isshareddata = 0; // shared cache line data (all should be the same) int buglevel = 0; // buglevel (0 = bug free) int watchcnt = 0; cpulock[0] = 0; // CPU locks cpulock[1] = 0; cpulock[2] = 0; cpulock[3] = 0; buslock = 0; // bus lock SimpleButton lastButton[*]; // // vertical double headed bus arrrow // // separate up, down and body objects to animate bus traffic // class BusArrow(int x, int y, int whead, int wbody, int l, Brush brush) arrow = Polygon(0, AAFILL | ABSOLUTE, 0, blackbrush, x,y, 0,0, whead,whead, wbody,whead, wbody,l-whead, whead,l-whead, 0,l, -whead,l-whead, -wbody,l-whead, -wbody,whead, -whead,whead); int largewhead = whead; int largewbody = wbody; body = Rectangle2(0, 0, 0, brush, x-wbody, y, 2*wbody+1, l); body.setOpacity(0); up = Polygon(0, AAFILL | ABSOLUTE, 0, brush, 0,0, largewhead,0, 2*largewhead,whead, 0,whead); up.setOpacity(0); down = Polygon(0, AAFILL | ABSOLUTE, 0, brush, 0,0, 0,0, 2*largewhead,0, largewhead,whead); down.setOpacity(0); // // reset bus arrow // function reset() arrow.setBrush(blackbrush); up.setOpacity(0); body.setOpacity(0); down.setOpacity(0); arrow.setOpacity(255); end; // // animate up movement on vertical bus arrow // // may be called with ticks = 0 // function moveup(int ticks, int wflag) // // initial positions, size & opacity // up.setPos(x-largewhead, y+(l-largewhead)); body.setPos(x-largewbody, y+l); body.setSize(2*largewbody+1, 0); up.setOpacity(255); body.setOpacity(255); // // final positions, size & opacity // up.setPos(x-largewhead, y, ticks, 1, 0); body.setPos(x-largewbody, y+largewhead, ticks, 1, 0); body.setSize(2*largewbody+1, l+1-largewhead, ticks, 1, 0); arrow.setOpacity(0, ticks, 1, wflag); end; // // animate down movement on vertical bus arrow // // may be called with ticks = 0 // function movedown(int ticks, int wflag) // // initial positions, size & opacity // down.setPos(x-largewhead, y); body.setPos(x-largewbody, y); body.setSize(2*largewbody+1, 0); down.setOpacity(255); body.setOpacity(255); // // final positions, size & opacity // down.setPos(x-largewhead, y+l-largewhead, ticks, 1, 0); body.setSize(2*largewbody+1, l-largewhead+1, ticks, 1, 0); arrow.setOpacity(0, ticks, 1, wflag); end; end; // // memory object // // contains 4 memory locations a0, a1, a2 & a3 // class Memory(int x, int y) r = Rectangle2(0, 0, blackpen, gray192brush, x, y, 150, 100); t = Txt(0, HLEFT | VTOP, x+160, y+40, redbrush, 0, "memory"); abus = BusArrow(x+50, y+100, 10, 4, 70, bluebrush); dbus = BusArrow(x+100, y+100, 10, 4, 40, redbrush); for (int i = 0; i < 4; i++) mem[i] = 0; stale[i] = 0; memR[i] = Rectangle2(0, 0, blackpen, whitebrush, x+5, y+4+i*24, 140, 20, blackbrush, 0, "address: a%d data: %d", i, mem[i]); memR[i].setTxtOff(0, 1); end; function highlight(int addr, int flag) if (flag == 1) memR[addr].setBrush(greenbrush); else if (stale[addr]) memR[addr].setBrush(gray192brush); else memR[addr].setBrush(whitebrush); end; end; end; function reset() for (int i = 0; i < 4; i++) highlight(i, 0); end; end; end; // // horizontal double headed bus object // class Bus(int x, int y, int whead, int wbody, int l, Brush brush) arrow = Polygon(0, AAFILL | ABSOLUTE, 0, blackbrush, x,y, 0,0, whead,-whead, whead,-wbody, l-whead,-wbody, l-whead,-whead, l,0, l-whead,whead, l-whead,wbody, whead,wbody, whead,whead); function highlight(int flag) if (flag == 0) arrow.setBrush(blackbrush); else arrow.setBrush(brush); end; end; end; // // data, address and shared horizontal busses // ddbus = Bus(20, 215, 11, 5, 1000, redbrush); ddbusTxt = Txt(0,HLEFT | VTOP, 60, 194, redbrush, fsmall, "data bus"); aabus = Bus(30, 245, 11, 5, 980, bluebrush); aabusTxt = Txt(0, HLEFT | VTOP, 70, 223, bluebrush, fsmall, "address bus"); sharedBus = Bus(40, 280, 7, 3, 950, magentabrush); sharedTxt = Txt(0, HLEFT | VTOP, 200, 260, magentabrush, fsmall, "shared"); memory = Memory(325, 70); class Cache; // forward declaration Cache cache[4]; // // cache object // class Cache(int x, int y, int cpu) sharedbus = BusArrow(x+13, y-42, 7, 3, 42, magentabrush); abus = BusArrow(x+62, y-75, 10, 4, 75, bluebrush); dbus = BusArrow(x+117, y-105, 10, 4, 105, redbrush); cpuabus = BusArrow(x+50, y+50, 10, 4, 50, bluebrush); cpudbus = BusArrow(x+100, y+50, 10, 4, 50, redbrush); r = Rectangle2(0, 0, blackpen, gray192brush, x, y, 150, 50); t = Txt(0, HLEFT | VTOP, x+130, y-20, redbrush, 0, "cache %d", cpu); for (int i = 0; i < 2; i++) state[i] = EMPTY; sharedR[i] = Rectangle2(0, 0, blackpen, whitebrush, x+5, y+6+i*20, 15+1, 17+1, blackbrush, f12B, "S"); sharedX0[i] = Line2(0, AAFILL | ABSOLUTE, redpen, x+5,y+6+i*20, x+5+15+1,y+6+i*20+17+1); sharedX0[i].setOpacity(0); sharedX1[i] = Line2(0, AAFILL | ABSOLUTE, redpen, x+5,y+6+i*20+17, x+5+15+1,y+6+i*17+1); sharedX1[i].setOpacity(0); dirtyR[i] = Rectangle2(0, 0, blackpen, whitebrush, x+20, y+6+i*20, 15+1, 17+1, blackbrush, f12B, "D"); dirtyX0[i] = Line2(0, AAFILL | ABSOLUTE, redpen, x+20,y+6+i*20, x+20+15+1,y+6+i*20+17+1); dirtyX1[i] = Line2(0, AAFILL | ABSOLUTE, redpen, x+20,y+6+i*20+17, x+20+15+1,y+6+i*20); a[i] = i; aR[i] = Rectangle2(0, 0, blackpen, whitebrush, x+35, y+6+i*20, 55+1, 17+1, blackbrush, 0, "a%d", i); aR[i].setTxtOff(0, 1); d[i] = 0; dR[i] = Rectangle2(0, 0, blackpen, whitebrush, x+90, y+6+i*20, 55+1, 17+1, blackbrush, 0, "?"); dR[i].setTxtOff(0, 1); end; // // state crosses // function statetext(int set) int opacity; opacity = (state[set] & SHAREDBIT) ? 0 : 255; sharedX0[set].setOpacity(opacity); sharedX1[set].setOpacity(opacity); opacity = (state[set] & DIRTYBIT) ? 0 : 255; dirtyX0[set].setOpacity(opacity); dirtyX1[set].setOpacity(opacity); end; function highlight(int set, int flag) if (flag == 1) sharedR[set].setBrush(greenbrush); dirtyR[set].setBrush(greenbrush); aR[set].setBrush(greenbrush); dR[set].setBrush(greenbrush); else sharedR[set].setBrush(whitebrush); dirtyR[set].setBrush(whitebrush); aR[set].setBrush(whitebrush); dR[set].setBrush(whitebrush); end; end; function resetbus() memory.abus.reset(); memory.dbus.reset(); ddbus.arrow.setBrush(blackbrush); aabus.arrow.setBrush(blackbrush); sharedBus.arrow.setBrush(blackbrush); memory.reset(); for (int i = 0; i < 4; i++) cache[i].abus.reset(); cache[i].dbus.reset(); cache[i].sharedbus.reset(); end; end; function resetStart() for (int i = 0; i < 4; i++) if ((cpulock[i] == 0) || (cpu == i)) cache[i].cpuabus.reset(); cache[i].cpudbus.reset(); cache[i].highlight(0, 0); cache[i].highlight(1, 0); lastButton[i].setborderpen(blackpen); end; end; if (buslock == 0) resetbus(); end; end; function resetEnd() for (int i = 0; i < 4; i++) if (cpulock[i] == 0) cache[i].cpuabus.reset(); cache[i].cpudbus.reset(); cache[i].highlight(0, 0); cache[i].highlight(1, 0); lastButton[i].setborderpen(blackpen); end; end; end; function shared(int addr, int rw, int data) int set = addr % 2; if (a[set] != addr || state[set] == EMPTY) watchcnt++; return; end; isshared = SHAREDBIT; if (rw == 1) abus.movedown(20, 0); dbus.movedown(20, 1); d[set] = data; else abus.movedown(20, 1); end; sharedbus.moveup(20, 0); sharedBus.highlight(1); state[set] |= SHAREDBIT; issharedstate = state[set]; isshareddata = d[set]; highlight(set, 1); if (rw == 0)// && state[set] & DIRTYBIT) dbus.moveup(20, 0); end; // if (rw == 1) state[set] = state[set] & SHAREDBIT; // clear dirty bit // end; dR[set].setTxt("%d", d[set]); statetext(set); watchcnt++; end; function sharedwatch(int addr, int cpu, int rw, int data) isshared = 0; watchcnt=0; for (int i = 0; i < 4; i++) if (i != cpu) fork(cache[i].shared(addr, rw, data)); end; end; // // wait for the NCPU-1 forked shared() functions to finish // while (watchcnt < NCPU-1) wait(1); end; if(isshared>0) cache[cpu].sharedbus.movedown(20, 0); end; if (rw == 1) memory.dbus.moveup(20, 0); end; memory.abus.moveup(20, 1); if ((isshared == SHAREDBIT) && (rw == 0)) wait(20); end; end; function flush(int cpu, int addr) set = addr % 2; flushaddr = a[set]; abus.moveup(20, 0); dbus.moveup(20, 1); aabus.highlight(1); ddbus.highlight(1); sharedwatch(addr, cpu, 1, d[set]); // bug fix {joj 21/2/05} state[set] = state[set] & SHAREDBIT; // clear dirty bit ~SD statetext(set); memory.stale[flushaddr] = 0; memory.memR[flushaddr].setBrush(whitebrush); memory.mem[flushaddr] = d[set]; memory.memR[flushaddr].setTxt("address: a%d data: %d", flushaddr, memory.mem[flushaddr]); wait(10); resetbus(); end; function read(int addr, int animatecpu) int set = addr % 2; if (animatecpu) cpuabus.moveup(20, 1); end; if (a[set] == addr && state[set] != EMPTY) // hit if (animatecpu) cpudbus.movedown(20, 1); end; return; end; if ((a[set] != addr) && (state[set] & DIRTYBIT) && (buglevel != 2)) // miss + dirty while (buslock) wait(1); end; buslock = 1; wait(1); // {joj 7/11/06} resetbus(); flush(cpu, a[set]); buslock = 0; end; highlight(set, 1); aR[set].setTxt("a%d", addr); while (buslock) wait(1); end; buslock = 1; wait(1); // {joj 7/11/06} resetbus(); abus.moveup(20, 1); aabus.highlight(1); sharedwatch(addr, cpu, 0, 0); if (isshared) d[set] = isshareddata; state[set] = SND; if (issharedstate & DIRTYBIT) memory.dbus.moveup(20, 0); memory.mem[addr] = isshareddata; memory.memR[addr].setTxt("address: a%d data: %d", addr, memory.mem[addr]); memory.stale[addr] = 0; memory.highlight(addr, 1); end; else memory.highlight(addr, 1); memory.dbus.movedown(20, 1); d[set] = memory.mem[addr]; state[set] = isshared; end; ddbus.highlight(1); dbus.movedown(20, 1); a[set] = addr; dR[set].setTxt("%d", d[set]); statetext(set); if (animatecpu) cpudbus.movedown(20, 1); end; buslock = 0; end; // // cpu write operation // function write(int addr) int set = addr % 2; cpudbus.moveup(20, 0); cpuabus.moveup(20, 1); highlight(set, 1); // // if cache miss and cache line dirty then flush line to memory // if ((a[set] != addr) && (state[set] & DIRTYBIT) && (buglevel != 2)) while (buslock) wait(1); end; buslock = 1; wait(1); // {joj 7/11/06} resetbus(); flush(cpu, a[set]); // write out dirty cache line buslock = 0; end; // // if a miss read memory first before writing // if (a[set] != addr || state[set] == EMPTY) read(addr, 0); wait(20); end; // // // if shared then write through otherwise write-back // if ((state[set] & SHAREDBIT) && (buglevel != 1)) while (buslock) wait(1); end; buslock = 1; wait(1); // {joj 7/11/06} resetbus(); a[set] = addr; aR[set].setTxt("a%d", addr); wValue++; d[set] = wValue; dR[set].setTxt("%d", wValue); abus.moveup(20, 0); dbus.moveup(20, 1); aabus.highlight(1); ddbus.highlight(1); sharedwatch(addr, cpu, 1, d[set]); state[set] = isshared; statetext(set); memory.mem[addr] = d[set]; memory.memR[addr].setTxt("address: a%d data: %d", addr, memory.mem[addr]); memory.stale[addr] = 0; memory.highlight(addr, 1); buslock = 0; else a[set] = addr; aR[set].setTxt("a%d", addr); wValue++; d[set] = wValue; dR[set].setTxt("%d", wValue); state[set] = NSD; statetext(set); memory.stale[addr] = 1; memory.memR[addr].setBrush(gray192brush); end; end; end; cache[0] = Cache(50, 325, 0); cache[1] = Cache(300, 325, 1); cache[2] = Cache(550, 325, 2); cache[3] = Cache(825, 325, 3); class readOp(int x, int y, int addr, int cpu, ref Rectangle r) b = SimpleButton(x, y, 65, 22, bgbrush, gray192brush, blackbrush, 0, "read"); b.button.setTxt("read a%d", addr); when b.button.eventLB(int down, real xx, real yy) if ((down == 0) && cpulock[cpu] == 0) cpulock[cpu] = 1; r.setPen(greenpen); cache[cpu].resetStart(); b.setborderpen(redpen); start(); lastButton[cpu] = b; cache[cpu].read(addr, 1); r.setPen(blackpen); cache[cpu].resetEnd(); cpulock[cpu] = 0; checkPoint(); end; end; end; class writeOp(int x, int y, int addr, int cpu, ref Rectangle r) b = SimpleButton(x, y, 65, 22, bgbrush, gray192brush, blackbrush, 0, "read"); b.button.setTxt("write a%d", addr); when b.button.eventLB(int down, real xx, real yy) if (down == 0 && cpulock[cpu] == 0) cpulock[cpu] = 1; r.setPen(greenpen); cache[cpu].resetStart(); b.setborderpen(redpen); start(); lastButton[cpu] = b; cache[cpu].write(addr); r.setPen(blackpen); cache[cpu].resetEnd(); cpulock[cpu] = 0; checkPoint(); end; end; end; // // cpu object // class CPU(int x, int y, int cpu) r = Rectangle2(0, 0, blackpen, gray192brush, x, y, 150, 100); t = Txt(0, HLEFT | VTOP, x+130, y-20, redbrush, 0, "CPU %d", cpu); for (int i = 0; i < 4; i++) rb[i] = readOp(x+5, y+5+i*23, i, cpu, r); wb[i] = writeOp(x+80, y+5+i*23, i, cpu, r); end; end; cpu[0] = CPU(50, 425, 0); cpu[1] = CPU(300, 425, 1); cpu[2] = CPU(550, 425, 2); cpu[3] = CPU(825, 425, 3); lastButton[0] = cpu[0].rb[0].b; lastButton[1] = cpu[1].rb[0].b; lastButton[2] = cpu[2].rb[0].b; lastButton[3] = cpu[3].rb[0].b; // // user buttons and their event handlers // resetButton = SimpleButton(LOGICALW-275, LOGICALH-125, 75, 25, bgbrush, gray192brush, blackbrush, 0, "reset"); when resetButton.button.eventLB(int down, real x, real y) if (down == 0) reset(); end; end;