Import.scala 6.26 KB
Newer Older
1
2
3
//
// Copyright (C) 2014 Jens Korinth, TU Darmstadt
//
4
// This file is part of Tapasco (TPC).
5
//
6
// Tapasco is free software: you can redistribute it and/or modify
7
8
9
10
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
11
// Tapasco is distributed in the hope that it will be useful,
12
13
14
15
16
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
17
// along with Tapasco.  If not, see <http://www.gnu.org/licenses/>.
18
19
20
21
22
23
24
25
26
27
//
/**
 * @file     Import.scala
 * @brief    The Import activity import an IP-XACT IP core in a .zip file
 *           into the currently configured core library for TPC. If no
 *           synthesis report can be found, it will use the EvaluateIP
 *           activity to generate an out-of-context synthesis report to
 *           estimate area utilization and max. operating frequency.
 * @authors  J. Korinth, TU Darmstadt (jk@esa.cs.tu-darmstadt.de)
 **/
28
29
30
31
32
package de.tu_darmstadt.cs.esa.tapasco.activity
import  de.tu_darmstadt.cs.esa.tapasco.base._
import  de.tu_darmstadt.cs.esa.tapasco.base.json._
import  de.tu_darmstadt.cs.esa.tapasco.util._
import  de.tu_darmstadt.cs.esa.tapasco.filemgmt.FileAssetManager
33
import  scala.sys.process._
34
35
36
37
38
39
40
41
42
import  java.nio.file._

/**
 * The Import activity imports an existing IP-XACT core into the cores library
 * of the current TPC configuration. Reports can either be supplied manually,
 * or will be generated by out-of-context synthesis, if not found.
 **/
object Import {
  private implicit final val logger =
43
    de.tu_darmstadt.cs.esa.tapasco.Logging.logger(getClass)
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

  /**
   * Import the given IP-XACT .zip file as Kernel with given id for the given target.
   * If no XML synthesis report is found (%NAME%_export.xml), will perform out-of-contex
   * synthesis and place-and-route for the Core to produce area and Fmax estimates.
   * @param zip Path to IP-XACT. zip.
   * @param id Kernel ID.
   * @param t Target Architecture + Platform combination to import for.
   * @param acc Average clock cycle count for a job execution on the PE (optional).
   * @param cfg Implicit [[base.Configuration]].
   **/
  def apply(zip: Path, id: Kernel.Id, t: Target, acc: Option[Int])(implicit cfg: Configuration): Boolean = {
    // get VLNV from the file
    val vlnv = VLNV.fromZip(zip)
    logger.trace("found VLNV in zip " + zip + ": " + vlnv)
    // extract version and name from VLNV, create Core
    val c = Core(
61
        descPath = zip.resolveSibling("core.json"),
62
        _zipPath = zip.getFileName,
63
64
65
66
        name = vlnv.name,
        id = id,
        version = vlnv.version.toString,
        _target = t,
67
        Some("imported from %s on %s".format(zip.toAbsolutePath.toString, java.time.LocalDateTime.now().toString)),
68
69
        acc)

70
71
    // write core.json to output directory (as per config)
    val p = cfg.outputDir(c, t).resolve("ipcore").resolve("core.json")
72
    importCore(c, t, p, vlnv)
73
74
75
76
77
78
79
80
81
82
  }

  /**
   * Imports the IP-XACT .zip to the default path structure (ipcore/) and performs
   * out-of-context synthesis (if no report from HLS was found).
   * @param c Core description.
   * @param t Target platform and architecture.
   * @param p Output path for core description file.
   * @param cfg Implicit [[Configuration]].
   **/
83
  private def importCore(c: Core, t: Target, p: Path, vlnv: VLNV)(implicit cfg: Configuration): Boolean = {
84
    Files.createDirectories(p.getParent)
85
    logger.trace("created output directories: {}", p.getParent.toString)
86

87
    // evaluate the ip core and store the report with the link
88
    val res = evaluateCore(c, t)
89

90
    // write core.json
91
    logger.debug("writing core description: {}", p.toString)
92
    Core.to(c.copy(descPath = p,_zipPath = Paths.get("%s.zip".format(vlnv.name))), p)
93
94
95
96
97
    res
  }

  /**
   * Searches for an existing synthesis report, otherwise performs out-of-context synthesis and
98
   * place-and-route to produce area and Fmax estimates and the netlist.
99
100
101
102
   * @param c Core description.
   * @param t Target Architecture + Platform combination.
   * @param cfg Implicit [[Configuration]].
   **/
103
  private def evaluateCore(c: Core, t: Target)(implicit cfg: Configuration): Boolean = {
104
105
106
107
108
109
110
111
112
113
114
    logger.trace("looking for SynthesisReport ...")
    val period = 1000.0 / t.pd.supportedFrequencies.sortWith(_>_).head
    val report = cfg.outputDir(c, t).resolve("ipcore").resolve("%s_export.xml".format(c.name))
    FileAssetManager.reports.synthReport(c.name, t) map { hls_report =>
      logger.trace("found existing synthesis report: " + hls_report)
      if (! report.equals(hls_report.file)) { // make link if not same
        java.nio.file.Files.createSymbolicLink(report, hls_report.file.toAbsolutePath)
      }
      true
    } getOrElse {
      logger.info("SynthesisReport for {} not found, starting evaluation ...", c.name)
115
      EvaluateIP(c.zipPath, period, t.pd.part, report)
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
    } && repack(c, t)
  }

  private def repack(c: Core, t: Target)(implicit cfg: Configuration): Boolean = {
    logger.debug("repacking {} for {} ...", c: Any, t)
    val out = cfg.outputDir(c, t).resolve("ipcore")
    val vivadoCmd: Seq[String] = Seq("vivado",
        "-mode", "batch",
        "-source", FileAssetManager.TAPASCO_HOME.resolve("common").resolve("repack-edn.tcl").toString,
        c.name,
        c.version,
        "-nolog", "-notrace", "-nojournal")
    // execute Vivado (max runtime: 1h)
    val r = InterruptibleProcess(Process(vivadoCmd, out.toFile),
        waitMillis = Some(60 * 60 * 1000)).!(InterruptibleProcess.io)
    r match {
      case InterruptibleProcess.TIMEOUT_RETCODE =>
        logger.error("repack-edn.tcl: Vivado timeout error")
      case 0 =>
        logger.info("repack-edn.tcl: Vivado finished successfully")
        val zip = cfg.outputDir(c, t).resolve("ipcore").resolve("%s.zip".format(c.name))
        val edn = zip.resolveSibling("%s.edn".format(c.name))
        val xml = zip.resolveSibling("component.xml")
        xml.toFile.deleteOnExit()
        ZipUtils.zipFile(zip, Seq(xml, edn))
      case _ =>
        logger.error("repack-edn.tcl: Vivado exited with non-zero exit-code ({})", r)
143
    }
144
    r == 0
145
146
  }
}