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} { ...@@ -199,5 +199,17 @@ if {$wns < 0} {
# report power # report power
report_power -quiet -file @@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! # done!
exit exit
...@@ -23,4 +23,9 @@ ...@@ -23,4 +23,9 @@
<AchievedClockPeriod>@@MIN_PERIOD@@</AchievedClockPeriod> <AchievedClockPeriod>@@MIN_PERIOD@@</AchievedClockPeriod>
</TimingReport> </TimingReport>
<PortReport>
<NumMasters>@@MASTERS@@</NumMasters>
<NumSlaves>@@SLAVES@@</NumSlaves>
</PortReport>
</profile> </profile>
AXI_MASTER_PORTS 42
AXI_SLAVE_PORTS 25
...@@ -67,6 +67,7 @@ object EvaluateIP { ...@@ -67,6 +67,7 @@ object EvaluateIP {
lazy val rpt_timing = reportFile.resolveSibling("timing.rpt") lazy val rpt_timing = reportFile.resolveSibling("timing.rpt")
lazy val rpt_util = reportFile.resolveSibling("utilization.rpt") lazy val rpt_util = reportFile.resolveSibling("utilization.rpt")
lazy val rpt_power = reportFile.resolveSibling("power.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 s_dcp = reportFile.resolveSibling("out-of-context_synth.dcp")
lazy val i_dcp = reportFile.resolveSibling("out-of-context_impl.dcp") lazy val i_dcp = reportFile.resolveSibling("out-of-context_impl.dcp")
lazy val zip = zipFile lazy val zip = zipFile
...@@ -121,7 +122,8 @@ object EvaluateIP { ...@@ -121,7 +122,8 @@ object EvaluateIP {
logger.trace("%s: Vivado finished successfully".format(runPrefix)) logger.trace("%s: Vivado finished successfully".format(runPrefix))
val ur = UtilizationReport(files.rpt_util).get val ur = UtilizationReport(files.rpt_util).get
val dpd = TimingReport(files.rpt_timing).get.dataPathDelay 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) logger.info("{} finished successfully, report in {}", runPrefix: Any, reportFile)
// clean up files on exit // clean up files on exit
deleteOnExit(files.baseDir.toFile) deleteOnExit(files.baseDir.toFile)
...@@ -155,6 +157,7 @@ object EvaluateIP { ...@@ -155,6 +157,7 @@ object EvaluateIP {
"REPORT_TIMING" -> files.rpt_timing.toString, "REPORT_TIMING" -> files.rpt_timing.toString,
"REPORT_UTILIZATION" -> files.rpt_util.toString, "REPORT_UTILIZATION" -> files.rpt_util.toString,
"REPORT_POWER" -> files.rpt_power.toString, "REPORT_POWER" -> files.rpt_power.toString,
"REPORT_PORT" -> files.rpt_port.toString,
"SYNTH_CHECKPOINT" -> files.s_dcp.toString, "SYNTH_CHECKPOINT" -> files.s_dcp.toString,
"IMPL_CHECKPOINT" -> files.i_dcp.toString, "IMPL_CHECKPOINT" -> files.i_dcp.toString,
"OPTIMIZATION" -> optimization.toString, "OPTIMIZATION" -> optimization.toString,
...@@ -177,7 +180,7 @@ object EvaluateIP { ...@@ -177,7 +180,7 @@ object EvaluateIP {
* @param targetPeriod Target operating period. * @param targetPeriod Target operating period.
**/ **/
private def writeXMLReport(reportFile: Path, ur: UtilizationReport, dataPathDelay: Double, 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]( val needles = scala.collection.mutable.Map[String, String](
"SLICE" -> ur.used.SLICE.toString, "SLICE" -> ur.used.SLICE.toString,
"SLICES" -> ur.available.SLICE.toString, "SLICES" -> ur.available.SLICE.toString,
...@@ -190,7 +193,9 @@ object EvaluateIP { ...@@ -190,7 +193,9 @@ object EvaluateIP {
"DSP" -> ur.used.DSP.toString, "DSP" -> ur.used.DSP.toString,
"DSPS" -> ur.available.DSP.toString, "DSPS" -> ur.available.DSP.toString,
"PERIOD" -> targetPeriod.toString, "PERIOD" -> targetPeriod.toString,
"MIN_PERIOD" -> dataPathDelay.toString "MIN_PERIOD" -> dataPathDelay.toString,
"MASTERS" -> masters.toString,
"SLAVES" -> slaves.toString
) )
// write final report // write final report
......
...@@ -37,11 +37,13 @@ ...@@ -37,11 +37,13 @@
* @authors J. Korinth, TU Darmstadt (jk@esa.cs.tu-darmstadt.de) * @authors J. Korinth, TU Darmstadt (jk@esa.cs.tu-darmstadt.de)
**/ **/
package de.tu_darmstadt.cs.esa.tapasco.dse package de.tu_darmstadt.cs.esa.tapasco.dse
import de.tu_darmstadt.cs.esa.tapasco.base._ import de.tu_darmstadt.cs.esa.tapasco.base._
import de.tu_darmstadt.cs.esa.tapasco.filemgmt.FileAssetManager import de.tu_darmstadt.cs.esa.tapasco.filemgmt.FileAssetManager
import de.tu_darmstadt.cs.esa.tapasco.util.LogFormatter._ import de.tu_darmstadt.cs.esa.tapasco.util.LogFormatter._
import java.nio.file.{Paths} import java.nio.file.Paths
import Heuristics._
import Heuristics._
import de.tu_darmstadt.cs.esa.tapasco.util.SlotOccupation
class DesignSpace( class DesignSpace(
bd: Composition, bd: Composition,
...@@ -80,34 +82,40 @@ class DesignSpace( ...@@ -80,34 +82,40 @@ class DesignSpace(
val cores = bd.composition flatMap (ce => FileAssetManager.entities.core(ce.kernel, target)) 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 srs = cores flatMap { c: Core => FileAssetManager.reports.synthReport(c.name, target) }
val areaEstimates = srs flatMap (_.area) val areaEstimates = srs flatMap (_.area)
val slotOccupations = srs map (r => SlotOccupation(r.slaves.get, target.pd.slotCount))
val targetUtil = target.pd.targetUtilization.toDouble val targetUtil = target.pd.targetUtilization.toDouble
logger.trace("target util = " + targetUtil) 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 // 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 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 (_ + _) def areaUtil(counts: Seq[Int]) = (areaEstimates zip counts) map (a => a._1 * a._2) reduce(_ + _)
val currUtil = areaUtil(minCounts).utilization
// compute number of steps // compute number of steps
val currUtil = areaUtil(minCounts).utilization
val df: Int = Seq(java.lang.Math.round((targetUtil - currUtil) / currUtil).toInt, 1).max 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) logger.trace("minCounts = " + minCounts + " currUtil = " + currUtil)
// compute feasible sequences as multiples of minCounts // compute feasible sequences as multiples of minCounts
val seqs = (for (i <- df to 1 by -1) val seqs = (for (i <- steps to 1 by -1)
yield minCounts map (n => i * n)) filter (c => areaUtil(c).isFeasible) yield minCounts map (n => i * n)) filter (c => areaUtil(c).isFeasible && slotUtil(c).isFeasible)
logger.trace("number of feasible counts: " + seqs.length) 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) logger.warn("No feasible composition found; please check starting composition ratios: " + NL + bd)
} }
// make sequence of CompositionEntries // make sequence of CompositionEntries
val ces = for { 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)) } yield bd.composition.map(_.kernel) zip s map (x => Composition.Entry(x._1, x._2))
// make full composition // make full composition
...@@ -123,7 +131,7 @@ class DesignSpace( ...@@ -123,7 +131,7 @@ class DesignSpace(
feasibleAlternatives(bd) map (feasibleCompositions(_)) reduce (_++_) feasibleAlternatives(bd) map (feasibleCompositions(_)) reduce (_++_)
lazy val enumerate: Seq[DesignSpace.Element] = (for { lazy val enumerate: Seq[DesignSpace.Element] = (for {
bd <- compositions(bd); bd <- compositions(bd)
f <- feasibleFreqs(bd) f <- feasibleFreqs(bd)
} yield DesignSpace.Element(bd, f, heuristic(bd, f, target)(cfg))) sortBy (_.h) reverse } yield DesignSpace.Element(bd, f, heuristic(bd, f, target)(cfg))) sortBy (_.h) reverse
...@@ -162,7 +170,7 @@ object DesignSpace { ...@@ -162,7 +170,7 @@ object DesignSpace {
val cores = bd.composition flatMap (ce => FileAssetManager.entities.core(ce.kernel, target)) 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 srs = cores flatMap { c: Core => FileAssetManager.reports.synthReport(c.name, target) }
val cps = srs flatMap (_.timing) map (_.clockPeriod) 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 (_>_) target.pd.supportedFrequencies map (_.toDouble) filter (_ <= fmax) sortWith (_>_)
} }
...@@ -172,39 +180,45 @@ object DesignSpace { ...@@ -172,39 +180,45 @@ object DesignSpace {
val cores = bd.composition flatMap (ce => FileAssetManager.entities.core(ce.kernel, target)) 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 srs = cores flatMap { c: Core => FileAssetManager.reports.synthReport(c.name, target) }
val areaEstimates = srs flatMap (_.area) val areaEstimates = srs flatMap (_.area)
val slotOccupations = srs map (r => SlotOccupation(r.slaves.get, target.pd.slotCount))
val targetUtil = target.pd.targetUtilization.toDouble val targetUtil = target.pd.targetUtilization.toDouble
logger.trace("target util = " + targetUtil) 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 // 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 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 (_ + _) def areaUtil(counts: Seq[Int]) = (areaEstimates zip counts) map (a => a._1 * a._2) reduce(_ + _)
val currUtil = areaUtil(minCounts).utilization
// compute number of steps // compute number of steps
val currUtil = areaUtil(minCounts).utilization
val df: Int = Seq(java.lang.Math.round((targetUtil - currUtil) / currUtil).toInt, 1).max 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) logger.trace("minCounts = " + minCounts + " currUtil = " + currUtil)
// compute feasible sequences as multiples of minCounts // compute feasible sequences as multiples of minCounts
val seqs = (for (i <- df to 1 by -1) val seqs = (for (i <- steps to 1 by -1)
yield minCounts map (n => i * n)) filter (c => areaUtil(c).isFeasible) yield minCounts map (n => i * n)) filter (c => areaUtil(c).isFeasible && slotUtil(c).isFeasible)
logger.trace("number of feasible counts: " + seqs.length) 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) logger.warn("No feasible composition found; please check starting composition ratios: " + NL + bd)
} }
// make sequence of CompositionEntries // make sequence of CompositionEntries
val ces = for { 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)) } yield bd.composition.map(_.kernel) zip s map (x => Composition.Entry(x._1, x._2))
// make full composition // make full composition
ces map (Composition(Paths.get("N/A"), Some("Generated composition."), _)) ces map (Composition(Paths.get("N/A"), Some("Generated composition."), _))
} else { } 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 ...@@ -30,9 +30,11 @@ import java.nio.file.Path
final case class SynthesisReport( final case class SynthesisReport(
override val file: Path, override val file: Path,
area: Option[AreaEstimate], 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(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 { object SynthesisReport {
...@@ -50,8 +52,8 @@ object SynthesisReport { ...@@ -50,8 +52,8 @@ object SynthesisReport {
val ff: Integer = ((xml \\ "AreaReport" \\ e \\ "FF") text).toInt val ff: Integer = ((xml \\ "AreaReport" \\ e \\ "FF") text).toInt
val dsp: Integer = ((xml \\ "AreaReport" \\ e \\ "DSP") text).toInt val dsp: Integer = ((xml \\ "AreaReport" \\ e \\ "DSP") text).toInt
val bram: Integer = ((xml \\ "AreaReport" \\ e \\ "BRAM") text).toInt val bram: Integer = ((xml \\ "AreaReport" \\ e \\ "BRAM") text).toInt
new ResourcesEstimate(slice, lut, ff, dsp, bram) ResourcesEstimate(slice, lut, ff, dsp, bram)
}).grouped(2).map(x => new AreaEstimate(x.head, x.tail.head)).toList.headOption }).grouped(2).map(x => AreaEstimate(x.head, x.tail.head)).toList.headOption
} catch { case e: Exception => logger.warn("parsing utilization report failed: " + e); None } } catch { case e: Exception => logger.warn("parsing utilization report failed: " + e); None }
/** Extracts the timing estimation from the given synthesis report file. /** Extracts the timing estimation from the given synthesis report file.
...@@ -64,9 +66,19 @@ object SynthesisReport { ...@@ -64,9 +66,19 @@ object SynthesisReport {
((xml \\ "TimingReport" \\ "TargetClockPeriod") text).toDouble)) ((xml \\ "TimingReport" \\ "TargetClockPeriod") text).toDouble))
} catch { case e: Exception => logger.warn("parsing timing report failed: " + e); None } } 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. **/ /** Produce SynthesisReport instance from file. **/
def apply(sr: Path): Option[SynthesisReport] = catchAllDefault(None: Option[SynthesisReport], def apply(sr: Path): Option[SynthesisReport] = catchAllDefault(None: Option[SynthesisReport],
"failed to read synthesis report %s: ".format(sr.toString)) { "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( ...@@ -90,3 +90,17 @@ final case class AreaEstimate(
def isFeasible: Boolean = List(slice, lut, ff, dsp, bram).map(x => x <= 100.0).reduce(_&&_) 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 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
}
}
Supports Markdown
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