Commit 78f02e6b authored by Simon Schulz's avatar Simon Schulz

Added source files

parent 2f6f3d1b
import AssemblyKeys._
seq(assemblySettings: _*)
name := "Smaunchy"
version := "1.0"
scalaVersion := "2.9.1"
libraryDependencies ++= Seq(
"org.scalatest" %% "scalatest" % "1.6.1" % "test",
"commons-lang" % "commons-lang" % "2.6"
)
traceLevel in run := 0
fork in run := true
scalacOptions ++= Seq("-optimize")
// The following is the class that will run when the jar is compiled!
mainClass in assembly := Some("core.Driver")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.11.2")
addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.6.0")
package core
import Programs.Test
import engine.CacheEngine
import model.CacheModel
object Driver {
def main(args: Array[String]): Unit = {
val cache = new CacheModel(32768, 16, 2)
val program = new Test
val engine = new CacheEngine(cache)
engine.simulate(program)
}
}
package Programs
import model.CacheModel
/**
* Created by work on 30.09.14.
*/
class SimulatedProgramException extends Exception{}
/**
* Representation of a memory value.
* @param address Address of the value in memory
* @param value Actual value
* @param cache Cache-model for tracking accesses
* @param sizeInByte Size of the value in memory
*/
class MemVal(var address : Int, var value : Int, var cache : CacheModel, sizeInByte : Int = 4){
/**
* Assign function triggering a cache write
* @param newval
*/
def :=(newval : Int) {cache.write(address); value = newval}
/**
* Addition with Int. Triggering a cache read
* @param myVal
* @return
*/
def +(myVal : Int) : Int = {
cache.read(address)
value + myVal
}
}
/**
* Companion object containing implicit conversion
*/
object MemVal{
/**
* Implicit conversion to Int triggering a cache read.
* @param memVal
* @return
*/
implicit def memval2Int(memVal : MemVal) : Int = {memVal.cache.read(memVal.address); memVal.value}
}
/**
* Memory object representing arrays
* Hijacking read and write by returning Memvals
* @param numElements Number of elements to be allocated
* @param base Base address in memory
* @param cache Cache model, needed to pass memory accesses
* @param dataTypeSizeInByte datatype size in byte, standard 4 byte
*/
class MemObject(numElements : Int, base : Int, cache : CacheModel, dataTypeSizeInByte : Int = 4) {
val map = new Array[MemVal](numElements)
for(i <- 1 to numElements-1)
map(i) = new MemVal(base + i * dataTypeSizeInByte, 0, cache)
def apply (pos : Int): MemVal =
if (pos < numElements) map(pos)
else throw new SimulatedProgramException
}
/**
* Abstract type to generalize the concept of programs
* @param name String name of the program
*/
abstract class Program(var name : String) {
var cache : CacheModel = null
/**
* Used to simulate memory allocation in a given program.
* Only memory allocated this way is actually tracked.
* @param size
* @param base
* @return
*/
def alloc(size : Int, base : Int) : MemObject = {
if(cache == null) throw new SimulatedProgramException
if(base % cache.lineSize != 0) throw new SimulatedProgramException
new MemObject(size , base, cache)
}
/**
* Implementation of the actual program.
* Called by the cache engine
* @return
*/
def program : Int
}
package Programs
import Programs.Program
import model.CacheModel
/**
* Created by work on 30.09.14.
*/
class Test extends Program("Test") {
override def program: Int = {
val a = alloc(10, 0)
val b = alloc(10, 16384)
val c = alloc(10, 32768)
a(1) := 10
b(1) := 20
c(1) := 30
a(1) := 10
return a(1)
}
}
package engine
import Programs.Program
import model.CacheModel
/**
* Class for executing programs using a given fix cache-model
* @param cache
*/
class CacheEngine(cache : CacheModel) {
/**
* Start the simulation of a program, print its stats.
* @param program
*/
def simulate(program : Program): Unit ={
(cache reset)
program.cache = cache
val retVal = program.program
val name = program.name
printStats(retVal, name)
}
/**
* Result printing
* @param retVal
* @param name
*/
def printStats(retVal : Int, name : String): Unit = {
print("Program " + name + " terminated with value " + retVal.toString)
print("Cache:")
print("-----------------------------------")
print("Read misses: ")
print(" Compulsory misses: " + cache.readCompulsoryMisses)
print("+ Conflict misses: " + cache.readConflictMisses)
print("= Total read-misses: " + cache.readMissCount)
print("-----------------------------------")
print("Write misses: ")
print(" Compulsory misses: " + cache.writeCompulsoryMisses)
print("+ Conflict misses: " + cache.writeConflictMisses)
print("= Total write-misses: " + cache.writeMissCount)
print("-----------------------------------")
}
/**
* Helper for printing
* @param str
*/
def print(str : String): Unit ={printf("[CacheEngine] " + str + "\n")}
}
package model
/**
* Class to dedicate which bits in the address are needed for
* adressing which part of the cache. Function to extract
* these parts from a given address are offered, too.
* @param associativity
* @param capacity
* @param lineSize
* @param addressSize
*/
class CacheLineFormatter (associativity : Int, capacity : Int, lineSize : Int, addressSize : Int) {
// Calculate the byte positions
val offsetBytes = findPowerOfTwo(lineSize)
val numOfSets = capacity / (lineSize * associativity)
val setBytes = findPowerOfTwo(numOfSets)
require(offsetBytes > 0, {printf("lineSize: " + lineSize.toString + " is no power of 2"); sys.exit(1)})
require(setBytes > 0, {printf("number of sets: " + numOfSets + " is no power of 2"); sys.exit(1)})
val tagBytes = addressSize - setBytes - offsetBytes
val OFFSET_MASK : Int = (math.pow(2, offsetBytes).toInt - 1)
val SET_MASK : Int = (math.pow(2, offsetBytes + setBytes).toInt - 1) ^ OFFSET_MASK
val TAG_MASK : Int = (math.pow(2, offsetBytes + setBytes + tagBytes).toInt - 1) ^ (OFFSET_MASK | SET_MASK)
val MASK_LIST = List(TAG_MASK, SET_MASK, OFFSET_MASK)
/**
* Helper function to find the power of two for
* a given number (e.g. 2^x = 1024 => x = 10).
* If there is not integer solution the ceiling of
* the found value is returned.
* @param n
* @return
*/
def findPowerOfTwo(n : Int) : Int = {
val lnOf2 = scala.math.log(2) // natural log of 2
val res = scala.math.log(n) / lnOf2
if(res == (res.toInt)) return res.toInt
else res.toInt + 1
}
/**
* Return a list of offset, set and tag part
* @param address
* @return
*/
def format(address : Int) : List[Int] = MASK_LIST.map(address & _)
/**
* Returns the offset-bits of a given address
* @param address
* @return
*/
def getOffset(address : Int) : Int = address & OFFSET_MASK
/**
* Return the set-bits of a given address
* @param address
* @return
*/
def getSet(address : Int) : Int = (address & SET_MASK) >> (offsetBytes)
/**
* Return the tag-bits of a given address
* @param address
* @return
*/
def getTag(address : Int) : Int = (address & TAG_MASK) >> (offsetBytes + setBytes)
}
package model
/**
* Cache Model representing an abstract cache
* @param capacity Capacity in Bytes
* @param lineSize Line-size in Bytes
* @param associativity Associativity (set-size)
* @param replacement Replacement strategy. Only LRU supported so far
* @param addressSize Size of an address to calculate address parts.
*/
class CacheModel (val capacity : Int, val lineSize : Int, val associativity : Int, replacement : String = "LRU", addressSize : Int = 32) {
// Make some checks if the variables are viable:
require(capacity % lineSize == 0, {printf("[CacheModel] Incompatible cache and linesize."); sys.exit(1)})
require((capacity / lineSize) % associativity == 0, {printf("[CacheModel] Incompatible cache, linesize and associativity."); sys.exit(1)})
val addressFormatter = new CacheLineFormatter(associativity, capacity, lineSize, addressSize)
// Initialize the cache
val numLines = capacity / lineSize
val numSets = numLines / associativity
val cacheTable = new Array[List[Int]](numSets)
reset
var readMissCount = 0
var writeMissCount = 0
var readConflictMisses = 0
var readCompulsoryMisses = 0
var writeConflictMisses = 0
var writeCompulsoryMisses = 0
var hist = List[Int]()
/**
* Read access at specific address
* @param address
*/
def read(address : Int): Unit = {
val set = addressFormatter.getSet(address)
val tag = addressFormatter.getTag(address)
updateRead(set, tag, address)
}
/**
* Write access at specific address
* @param address
*/
def write(address : Int) : Unit = {
val set = addressFormatter.getSet(address)
val tag = addressFormatter.getTag(address)
updateWrite(set, tag, address)
}
/**
* Update the cache state if a read access occurs
* @param set
* @param tag
* @param address
*/
def updateRead(set : Int, tag : Int, address : Int) : Unit = {
val miss = update(set, tag)
readMissCount += miss
if(miss == 1)
if(hist contains address)
readConflictMisses += 1
else
readCompulsoryMisses += 1
hist = address :: hist
}
/**
* Update the cache state if a write access occurs
* @param set
* @param tag
* @param address
*/
def updateWrite(set : Int, tag : Int, address : Int) : Unit = {
val miss = update(set, tag)
writeMissCount += miss
if(miss == 1)
if(hist contains address)
writeConflictMisses += 1
else
writeCompulsoryMisses += 1
hist = address :: hist
}
/**
* Generalized update function for both, read and write.
* Assumption: When not storing an actual value, no difference
* between both.
* @param set
* @param tag
* @return Cache miss occurred 1 else 0
*/
def update(set : Int, tag : Int) : Int = {
val setList = cacheTable(set)
val filtered = setList.filter(_ != tag)
val firstList = tag :: filtered
if((firstList size) == (setList size)) {
cacheTable(set) = firstList
0
}
else {
cacheTable(set) = firstList.take((firstList size) - 1)
1
}
}
/**
* Function to check if a given tag is present in
* a given set
* @param set
* @param tag
* @return
*/
def inSet(set : Int, tag : Int) = (cacheTable(set) contains tag)
/**
* Reset the cache
*/
def reset: Unit =
for(i <- 0 to numSets -1)
cacheTable(i) = List.fill(associativity)(-1)
}
/**
* Created by work on 30.09.14.
*/
class test {
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment