Commit b758d4fc authored by Lukas Sommer's avatar Lukas Sommer
Browse files

Merge branch 'feature/issue30-consider-slots-dse' into develop

parents 62596420 cfe59f6e
Pipeline #1242 passed with stages
in 192 minutes and 56 seconds
......@@ -199,5 +199,17 @@ if {$wns < 0} {
# report power
report_power -quiet -file @@REPORT_POWER@@
# extract AXI interfaces
# requires a block design
set bd [create_bd_design bd]
set pe [create_bd_cell pe -vlnv @@VLNV@@]
set interfaces_file [open @@REPORT_PORT@@ "w"]
puts @@REPORT_PORT@@
set axi_master_ports [tapasco::get_aximm_interfaces $pe "Master"]
set axi_slave_ports [tapasco::get_aximm_interfaces $pe "Slave"]
puts $interfaces_file "AXI_MASTER_PORTS\t[llength $axi_master_ports]"
puts $interfaces_file "AXI_SLAVE_PORTS\t[llength $axi_slave_ports]"
close $interfaces_file
# done!
exit
......@@ -23,4 +23,9 @@
<AchievedClockPeriod>@@MIN_PERIOD@@</AchievedClockPeriod>
</TimingReport>
<PortReport>
<NumMasters>@@MASTERS@@</NumMasters>
<NumSlaves>@@SLAVES@@</NumSlaves>
</PortReport>
</profile>
AXI_MASTER_PORTS 42
AXI_SLAVE_PORTS 25
......@@ -67,6 +67,7 @@ object EvaluateIP {
lazy val rpt_timing = reportFile.resolveSibling("timing.rpt")
lazy val rpt_util = reportFile.resolveSibling("utilization.rpt")
lazy val rpt_power = reportFile.resolveSibling("power.rpt")
lazy val rpt_port = reportFile.resolveSibling("port.rpt")
lazy val s_dcp = reportFile.resolveSibling("out-of-context_synth.dcp")
lazy val i_dcp = reportFile.resolveSibling("out-of-context_impl.dcp")
lazy val zip = zipFile
......@@ -121,7 +122,8 @@ object EvaluateIP {
logger.trace("%s: Vivado finished successfully".format(runPrefix))
val ur = UtilizationReport(files.rpt_util).get
val dpd = TimingReport(files.rpt_timing).get.dataPathDelay
writeXMLReport(reportFile, ur, dpd, targetPeriod)
val portReport = PortReport(files.rpt_port).get
writeXMLReport(reportFile, ur, dpd, targetPeriod, portReport.numMasters, portReport.numSlaves)
logger.info("{} finished successfully, report in {}", runPrefix: Any, reportFile)
// clean up files on exit
deleteOnExit(files.baseDir.toFile)
......@@ -155,6 +157,7 @@ object EvaluateIP {
"REPORT_TIMING" -> files.rpt_timing.toString,
"REPORT_UTILIZATION" -> files.rpt_util.toString,
"REPORT_POWER" -> files.rpt_power.toString,
"REPORT_PORT" -> files.rpt_port.toString,
"SYNTH_CHECKPOINT" -> files.s_dcp.toString,
"IMPL_CHECKPOINT" -> files.i_dcp.toString,
"OPTIMIZATION" -> optimization.toString,
......@@ -177,7 +180,7 @@ object EvaluateIP {
* @param targetPeriod Target operating period.
**/
private def writeXMLReport(reportFile: Path, ur: UtilizationReport, dataPathDelay: Double,
targetPeriod: Double): Unit = {
targetPeriod: Double, masters : Int, slaves : Int): Unit = {
val needles = scala.collection.mutable.Map[String, String](
"SLICE" -> ur.used.SLICE.toString,
"SLICES" -> ur.available.SLICE.toString,
......@@ -190,7 +193,9 @@ object EvaluateIP {
"DSP" -> ur.used.DSP.toString,
"DSPS" -> ur.available.DSP.toString,
"PERIOD" -> targetPeriod.toString,
"MIN_PERIOD" -> dataPathDelay.toString
"MIN_PERIOD" -> dataPathDelay.toString,
"MASTERS" -> masters.toString,
"SLAVES" -> slaves.toString
)
// write final report
......
......@@ -37,11 +37,13 @@
* @authors J. Korinth, TU Darmstadt (jk@esa.cs.tu-darmstadt.de)
**/
package de.tu_darmstadt.cs.esa.tapasco.dse
import de.tu_darmstadt.cs.esa.tapasco.base._
import de.tu_darmstadt.cs.esa.tapasco.filemgmt.FileAssetManager
import de.tu_darmstadt.cs.esa.tapasco.util.LogFormatter._
import java.nio.file.{Paths}
import Heuristics._
import de.tu_darmstadt.cs.esa.tapasco.base._
import de.tu_darmstadt.cs.esa.tapasco.filemgmt.FileAssetManager
import de.tu_darmstadt.cs.esa.tapasco.util.LogFormatter._
import java.nio.file.Paths
import Heuristics._
import de.tu_darmstadt.cs.esa.tapasco.util.SlotOccupation
class DesignSpace(
bd: Composition,
......@@ -80,34 +82,40 @@ class DesignSpace(
val cores = bd.composition flatMap (ce => FileAssetManager.entities.core(ce.kernel, target))
val srs = cores flatMap { c: Core => FileAssetManager.reports.synthReport(c.name, target) }
val areaEstimates = srs flatMap (_.area)
val slotOccupations = srs map (r => SlotOccupation(r.slaves.get, target.pd.slotCount))
val targetUtil = target.pd.targetUtilization.toDouble
logger.trace("target util = " + targetUtil)
def slotUtil(counts : Seq[Int]) : SlotOccupation = (slotOccupations zip counts).map(o => o._1 * o._2).reduce(_ + _)
// check if there is any feasible composition
if (minCounts.reduce(_ + _) > target.pd.slotCount) {
if (!slotUtil(minCounts).isFeasible) {
throw new Exception("Composition infeasible! Exceeds maximal slot count of " + target.pd.slotCount
+ " for " + target + "." + NL + bd)
}
def areaUtil(counts: Seq[Int]) = (areaEstimates zip counts) map (a => a._1 * a._2) reduce (_ + _)
val currUtil = areaUtil(minCounts).utilization
def areaUtil(counts: Seq[Int]) = (areaEstimates zip counts) map (a => a._1 * a._2) reduce(_ + _)
// compute number of steps
val currUtil = areaUtil(minCounts).utilization
val df: Int = Seq(java.lang.Math.round((targetUtil - currUtil) / currUtil).toInt, 1).max
val currSlots = slotUtil(minCounts).slots
val targetSlots = target.pd.slotCount
val sf : Int = Math.max(Math.round(targetSlots.toDouble/currSlots.toDouble), 1).toInt
val steps = Math.min(sf, df)
logger.trace("minCounts = " + minCounts + " currUtil = " + currUtil)
// compute feasible sequences as multiples of minCounts
val seqs = (for (i <- df to 1 by -1)
yield minCounts map (n => i * n)) filter (c => areaUtil(c).isFeasible)
val seqs = (for (i <- steps to 1 by -1)
yield minCounts map (n => i * n)) filter (c => areaUtil(c).isFeasible && slotUtil(c).isFeasible)
logger.trace("number of feasible counts: " + seqs.length)
if (seqs.length == 0) {
if (seqs.isEmpty) {
logger.warn("No feasible composition found; please check starting composition ratios: " + NL + bd)
}
// make sequence of CompositionEntries
val ces = for {
s <- seqs filter (_.reduce(_ + _) <= target.pd.slotCount)
s <- seqs filter (_.sum <= target.pd.slotCount)
} yield bd.composition.map(_.kernel) zip s map (x => Composition.Entry(x._1, x._2))
// make full composition
......@@ -123,7 +131,7 @@ class DesignSpace(
feasibleAlternatives(bd) map (feasibleCompositions(_)) reduce (_++_)
lazy val enumerate: Seq[DesignSpace.Element] = (for {
bd <- compositions(bd);
bd <- compositions(bd)
f <- feasibleFreqs(bd)
} yield DesignSpace.Element(bd, f, heuristic(bd, f, target)(cfg))) sortBy (_.h) reverse
......@@ -162,7 +170,7 @@ object DesignSpace {
val cores = bd.composition flatMap (ce => FileAssetManager.entities.core(ce.kernel, target))
val srs = cores flatMap { c: Core => FileAssetManager.reports.synthReport(c.name, target) }
val cps = srs flatMap (_.timing) map (_.clockPeriod)
val fmax = if (cps.length > 0) 1000.0 / cps.max else Double.PositiveInfinity
val fmax = if (cps.nonEmpty) 1000.0 / cps.max else Double.PositiveInfinity
target.pd.supportedFrequencies map (_.toDouble) filter (_ <= fmax) sortWith (_>_)
}
......@@ -172,39 +180,45 @@ object DesignSpace {
val cores = bd.composition flatMap (ce => FileAssetManager.entities.core(ce.kernel, target))
val srs = cores flatMap { c: Core => FileAssetManager.reports.synthReport(c.name, target) }
val areaEstimates = srs flatMap (_.area)
val slotOccupations = srs map (r => SlotOccupation(r.slaves.get, target.pd.slotCount))
val targetUtil = target.pd.targetUtilization.toDouble
logger.trace("target util = " + targetUtil)
def slotUtil(counts : Seq[Int]) : SlotOccupation = (slotOccupations zip counts).map(o => o._1 * o._2).reduce(_ + _)
// check if there is any feasible composition
if ((minCounts fold 0) (_ + _) > target.pd.slotCount) {
if (!slotUtil(minCounts).isFeasible) {
throw new Exception("Composition infeasible! Exceeds maximal slot count of " + target.pd.slotCount
+ " for " + target + "." + NL + bd)
+ " for " + target + "." + NL + bd)
}
def areaUtil(counts: Seq[Int]) = (areaEstimates zip counts) map (a => a._1 * a._2) reduce (_ + _)
val currUtil = areaUtil(minCounts).utilization
def areaUtil(counts: Seq[Int]) = (areaEstimates zip counts) map (a => a._1 * a._2) reduce(_ + _)
// compute number of steps
val currUtil = areaUtil(minCounts).utilization
val df: Int = Seq(java.lang.Math.round((targetUtil - currUtil) / currUtil).toInt, 1).max
val currSlots = slotUtil(minCounts).slots
val targetSlots = target.pd.slotCount
val sf : Int = Math.max(Math.round(targetSlots.toDouble/currSlots.toDouble), 1).toInt
val steps = Math.min(sf, df)
logger.trace("minCounts = " + minCounts + " currUtil = " + currUtil)
// compute feasible sequences as multiples of minCounts
val seqs = (for (i <- df to 1 by -1)
yield minCounts map (n => i * n)) filter (c => areaUtil(c).isFeasible)
val seqs = (for (i <- steps to 1 by -1)
yield minCounts map (n => i * n)) filter (c => areaUtil(c).isFeasible && slotUtil(c).isFeasible)
logger.trace("number of feasible counts: " + seqs.length)
if (seqs.length == 0) {
if (seqs.isEmpty) {
logger.warn("No feasible composition found; please check starting composition ratios: " + NL + bd)
}
// make sequence of CompositionEntries
val ces = for {
s <- seqs filter (_.reduce(_ + _) <= target.pd.slotCount)
} yield bd.composition.map(_.kernel) zip s map (x => Composition.Entry(x._1, x._2))
s <- seqs filter (_.sum <= target.pd.slotCount)
} yield bd.composition.map(_.kernel) zip s map (x => Composition.Entry(x._1, x._2))
// make full composition
ces map (Composition(Paths.get("N/A"), Some("Generated composition."), _))
} else {
Seq()
Seq.empty
}
}
package de.tu_darmstadt.cs.esa.tapasco.reports
import java.nio.file.Path
import de.tu_darmstadt.cs.esa.tapasco.util.SequenceMatcher
import scala.io.Source
final case class PortReport(override val file : Path, numMasters : Int, numSlaves : Int) extends Report(file)
object PortReport {
private[this] val logger = de.tu_darmstadt.cs.esa.tapasco.Logging.logger(this.getClass)
def apply(pr : Path) : Option[PortReport] = extract(pr)
private def masterPortMatcher : SequenceMatcher[Int] = new SequenceMatcher[Int](
"""AXI_MASTER_PORTS\s+(\d+)""".r
)(true, ms => ms.head.group(1).toInt)
private def slavePortMatcher : SequenceMatcher[Int] = new SequenceMatcher[Int](
"""AXI_SLAVE_PORTS\s+(\d+)""".r
)(true, ms => ms.head.group(1).toInt)
private def extract(pr : Path) : Option[PortReport] = try {
val numMasters = masterPortMatcher
val numSlaves = slavePortMatcher
val source = Source.fromFile(pr.toString)
source.getLines foreach { line =>
numMasters.update(line)
numSlaves.update(line)
}
if(numMasters.matched && numSlaves.matched){
Some(PortReport(pr, numMasters.result.get, numSlaves.result.get))
}
else {
None
}
} catch { case e : Exception =>
logger.warn("Could not extract port information from %s: %s".format(pr, e))
None
}
}
......@@ -30,9 +30,11 @@ import java.nio.file.Path
final case class SynthesisReport(
override val file: Path,
area: Option[AreaEstimate],
timing: Option[TimingEstimate]) extends Report(file) {
timing: Option[TimingEstimate],
masters : Option[Int],
slaves : Option[Int]) extends Report(file) {
require(file.toFile.exists, "file %s does not exist".format(file.toString))
require(area.nonEmpty || timing.nonEmpty, "no synthesis results found in %s".format(file.toString))
require(area.nonEmpty || timing.nonEmpty || masters.nonEmpty || slaves.nonEmpty, "no synthesis results found in %s".format(file.toString))
}
object SynthesisReport {
......@@ -50,8 +52,8 @@ object SynthesisReport {
val ff: Integer = ((xml \\ "AreaReport" \\ e \\ "FF") text).toInt
val dsp: Integer = ((xml \\ "AreaReport" \\ e \\ "DSP") text).toInt
val bram: Integer = ((xml \\ "AreaReport" \\ e \\ "BRAM") text).toInt
new ResourcesEstimate(slice, lut, ff, dsp, bram)
}).grouped(2).map(x => new AreaEstimate(x.head, x.tail.head)).toList.headOption
ResourcesEstimate(slice, lut, ff, dsp, bram)
}).grouped(2).map(x => AreaEstimate(x.head, x.tail.head)).toList.headOption
} catch { case e: Exception => logger.warn("parsing utilization report failed: " + e); None }
/** Extracts the timing estimation from the given synthesis report file.
......@@ -64,9 +66,19 @@ object SynthesisReport {
((xml \\ "TimingReport" \\ "TargetClockPeriod") text).toDouble))
} catch { case e: Exception => logger.warn("parsing timing report failed: " + e); None }
def extractMasterPorts(sr : Path) : Option[Int] = try {
val xml = scala.xml.XML.loadFile(sr.toAbsolutePath.toString)
Some(((xml \\ "PortReport" \\ "NumMasters") text).toInt)
} catch { case e : Exception => logger.warn("parsing port report failed: %s".format(e)); None}
def extractSlavePorts(sr : Path) : Option[Int] = try {
val xml = scala.xml.XML.loadFile(sr.toAbsolutePath.toString)
Some(((xml \\ "PortReport" \\ "NumSlaves") text).toInt)
} catch { case e : Exception => logger.warn("parsing port report failed: %s".format(e)); None}
/** Produce SynthesisReport instance from file. **/
def apply(sr: Path): Option[SynthesisReport] = catchAllDefault(None: Option[SynthesisReport],
"failed to read synthesis report %s: ".format(sr.toString)) {
Some(SynthesisReport(sr, extractArea(sr), extractTiming(sr)))
Some(SynthesisReport(sr, extractArea(sr), extractTiming(sr), extractMasterPorts(sr), extractSlavePorts(sr)))
}
}
......@@ -90,3 +90,17 @@ final case class AreaEstimate(
def isFeasible: Boolean = List(slice, lut, ff, dsp, bram).map(x => x <= 100.0).reduce(_&&_)
def compare(that: AreaEstimate): Int = this.resources compare that.resources
}
/**
* Occupation of slots of a hardware design
* @param slots Number of occupied slots
* @param available Number of available slots
*/
final case class SlotOccupation(slots : Int, available : Int) extends Ordered[SlotOccupation] {
override def toString: String = "SlotOccupation: %d/%d\n".format(slots, available)
def *(n : Int) : SlotOccupation = SlotOccupation(slots * n, available)
def +(s : SlotOccupation) : SlotOccupation = SlotOccupation(slots + s.slots, available)
def isFeasible : Boolean = slots <= available
override def compare(that: SlotOccupation): Int = this.slots compare that.slots
}
package de.tu_darmstadt.cs.esa.tapasco.reports
import java.nio.file.Paths
import org.scalatest.{FlatSpec, Matchers}
class PortReportSpec extends FlatSpec with Matchers {
val reportPath = Paths.get("report-examples").toAbsolutePath
"A missing PortReport file" should "result in None" in {
PortReport(reportPath.resolve("missing.rpt")) shouldBe empty
}
"An invalid PortReport file" should "result in None" in {
PortReport(reportPath.resolve("correct-timing.rpt")) shouldBe empty
}
"A correct PortReport file" should "be parsed to Some(PortReport)" in {
PortReport(reportPath.resolve("correct-port.rpt")) should not be empty
}
"A correct PortReport file" should "be parsed correctly" in {
val pr = PortReport(reportPath.resolve("correct-port.rpt"))
pr should not be empty
pr.get.numMasters shouldBe 42
pr.get.numSlaves shouldBe 25
}
}
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